# Writing a kernel

Author

  Bradley Chambers

Contact

  brad.chambers@gmail.com

Date

  11/02/2017

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// MyKernel.hpp
     2
     3#pragma once
     4
     5#include <pdal/Kernel.hpp>
     6
     7#include <string>
     8
     9namespace pdal
    10{
    11
    12class PDAL_DLL MyKernel : public Kernel
    13{
    14public:
    15    MyKernel();
    16
    17    std::string getName() const;
    18    int execute(); // override
    19
    20private:
    21    void addSwitches(ProgramArgs& args);
    22
    23    std::string m_input_file;
    24    std::string m_output_file;
    25};
    26
    27} // namespace pdal

As with other plugins, the MyKernel class needs to return a name.

        std::string getName() const;

## The source

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

     1// MyKernel.cpp
     2
     3#include "MyKernel.hpp"
     4
     5#include <pdal/Filter.hpp>
     6#include <pdal/Kernel.hpp>
     7#include <pdal/Options.hpp>
     8#include <pdal/PointTable.hpp>
     9
    10#include <memory>
    11#include <string>
    12
    13
    14namespace pdal {
    15
    16  static PluginInfo const s_info
    17  {
    18    "kernels.mykernel",
    19    "MyKernel",
    20    "http://link/to/documentation"
    21  };
    22
    23  CREATE_SHARED_KERNEL(MyKernel, s_info);
    24  std::string MyKernel::getName() const { return s_info.name; }
    25
    26  MyKernel::MyKernel() : Kernel()
    27  {}
    28
    29  void MyKernel::addSwitches(ProgramArgs& args)
    30  {
    31      args.add("input,i", "Input filename", m_input_file).setPositional();
    32      args.add("output,o", "Output filename", m_output_file).setPositional();
    33  }
    34
    35  int MyKernel::execute()
    36  {
    37    PointTable table;
    38
    39    Stage& reader = makeReader(m_input_file, "readers.las");
    40
    41    // Options should be added in the call to makeFilter, makeReader,
    42    // or makeWriter so that the system can override them with those
    43    // provided on the command line when applicable.
    44    Options filterOptions;
    45    filterOptions.add("step", 10);
    46    Stage& filter = makeFilter("filters.decimation", reader, filterOptions);
    47
    48    Stage& writer = makeWriter(m_output_file, filter, "writers.text");
    49    writer.prepare(table);
    50    writer.execute(table);
    51
    52    return 0;
    53  }
    54
    55} // namespace pdal

In your kernel implementation, you will use a macro defined in pdal_macros. This macro registers the plugin with the PluginManager.

      CREATE_SHARED_KERNEL(MyKernel, s_info);

To build up a processing pipeline in this example, we need to create two objects: the `pdal::PointTable`.

      int MyKernel::execute()
      {
        PointTable table;

        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;
      }

To implement the actual kernel logic we implement execute(). In this case, the kernel reads a las file, decimates the data (eliminates some points) and writes the result to a text file. The base kernel class provides functions (makeReader, makeFilter, makeWriter) to create stages with options as desired. The pipeline that has been created can be run by preparing and executing the last stage in the pipeline.

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 --drivers`, 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>`.

### Compilation

Set up a `CMakeLists.txt` file to compile your kernel against PDAL:

     1cmake_minimum_required(VERSION 2.8.12)
     2project(KernelTutorial)
     3
     4find_package(PDAL 2.0.0 REQUIRED CONFIG)
     5set(CMAKE_CXX_STANDARD 11)
     6set(CMAKE_CXX_STANDARD_REQUIRED ON)
     7
     8add_library(pdal_plugin_kernel_mykernel SHARED MyKernel.cpp)
     9target_link_libraries(pdal_plugin_kernel_mykernel PRIVATE ${PDAL_LIBRARIES})
    10target_include_directories(pdal_plugin_kernel_mykernel PRIVATE
    11                            ${PDAL_INCLUDE_DIRS})
    12target_link_directories(pdal_plugin_kernel_mykernel PRIVATE ${PDAL_LIBRARY_DIRS})