# Writing a kernel

Author: | Bradley Chambers
Contact: | brad.chambers@gmail.com
Date: | 10/16/2016

PDAL’s command-line application can be extended through the development of kernel functions. In this tutorial, we will give a brief example.

## The header

First, we provide a full listing of the kernel header.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

    // MyKernel.hpp

    #pragma once

    #include <pdal/Kernel.hpp>
    #include <pdal/plugin.hpp>

    #include <string>

    namespace pdal
    {

    class PDAL_DLL MyKernel : public Kernel
    {
    public:
        static void * create();
        static int32_t destroy(void *);
        std::string getName() const;
        int execute(); // override

    private:
        MyKernel();
        void addSwitches(ProgramArgs& args);

        std::string m_input_file;
        std::string m_output_file;
    };

    } // namespace pdal

As with other plugins, the MyKernel class needs to have the following three methods declared for the plugin interface to be satisfied:

        static void * create();
        static int32_t destroy(void *);
        std::string getName() const;

## The source

Again, we start with a full listing of the kernel source.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58

    // MyKernel.cpp

    #include "MyKernel.hpp"

    #include <pdal/Filter.hpp>
    #include <pdal/Kernel.hpp>
    #include <pdal/KernelFactory.hpp>
    #include <pdal/Options.hpp>
    #include <pdal/pdal_macros.hpp>
    #include <pdal/StageFactory.hpp>
    #include <pdal/PointTable.hpp>

    #include <memory>
    #include <string>

    namespace pdal {

      static PluginInfo const s_info {
        "kernels.mykernel",
        "MyKernel",
        "http://link/to/documentation"
      };

      CREATE_SHARED_PLUGIN(1, 0, MyKernel, Kernel, s_info);
      std::string MyKernel::getName() const { return s_info.name; }

      MyKernel::MyKernel() : Kernel()
      {}

      void MyKernel::addSwitches(ProgramArgs& args)
      {
          args.add("input,i", "Input filename", m_input_file).setPositional();
          args.add("output,o", "Output filename", m_output_file).setPositional();
      }

      int MyKernel::execute()
      {
        PointTable table;
        StageFactory f;

        Stage& reader = makeReader(m_input_file, "readers.las");

        // Options should be added in the call to makeFilter, makeReader,
        // or makeWriter so that the system can override them with those
        // provided on the command line when applicable.
        Options filterOptions;
        filterOptions.add("step", 10);
        Stage& filter = makeFilter("filters.decimation", reader, filterOptions);

        Stage& writer = makeWriter(m_output_file, filter, "writers.text");
        writer.prepare(table);
        writer.execute(table);

        return 0;
      }

    } // namespace pdal

In your kernel implementation, you will use a macro defined in pdal_macros. This macro registers the plugin with the Kernel factory. It is only required by plugins.

      CREATE_SHARED_PLUGIN(1, 0, MyKernel, Kernel, s_info);
      std::string MyKernel::getName() const { return s_info.name; }

Note

A static plugin macro can also be used to integrate the kernel with the main code. This will not be described here. Using this as a shared plugin will be described later.

To build up a processing pipeline in this example, we need to create two objects: the `pdal::PointTable` and the `pdal::StageFactory`. The latter is used to create the various stages that will be used within the kernel.

        PointTable table;
        StageFactory f;

The `pdal::Reader` is created from the `pdal::StageFactory`, and is specified by the stage name, in this case an LAS reader. For brevity, we provide the reader a single option, the filename of the file to be read.

        Stage& reader = makeReader(m_input_file, "readers.las");

        // Options should be added in the call to makeFilter, makeReader,
        // or makeWriter so that the system can override them with those

The `pdal::Filter` is also created from the `pdal::StageFactory`. Here, we create a decimation filter that will pass every tenth point to subsequent stages. We also specify the input to this stage, which is the reader.

        Options filterOptions;
        filterOptions.add("step", 10);
        Stage& filter = makeFilter("filters.decimation", reader, filterOptions);

        Stage& writer = makeWriter(m_output_file, filter, "writers.text");

Finally, the `pdal::Writer` is created from the `pdal::StageFactory`. This writers.text, takes as input the previous stage (the filters.decimation) and the output filename as its sole option.

        writer.execute(table);

        return 0;
      }

The final two steps are to prepare and execute the pipeline. This is achieved by calling prepare and execute on the final stage.

    } // namespace pdal

When compiled, a dynamic library file will be created; in this case, `libpdal_plugin_kernel_mykernel.dylib`

Put this file in whatever directory `PDAL_DRIVER_PATH` is pointing to. Then, if you run `pdal --help`, you should see `mykernel` listed in the possible commands.

To run this kernel, you would use `pdal mykernel -i <input las file> -o <output text file>`.