| Author: | Bradley Chambers |
|---|---|
| Contact: | brad.chambers@gmail.com |
| Date: | 01/21/2015 |
PDAL’s command-line application can be extended through the development of kernel functions. In this tutorial, we will give a brief example.
First, we provide a full listing of the kernel header.
// 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 validateSwitches();
void addSwitches();
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;
Again, we start with a full listing of the kernel source.
// MyKernel.cpp
#include "MyKernel.hpp"
#include <boost/program_options.hpp>
#include <pdal/Filter.hpp>
#include <pdal/Kernel.hpp>
#include <pdal/KernelFactory.hpp>
#include <pdal/KernelSupport.hpp>
#include <pdal/Options.hpp>
#include <pdal/pdal_macros.hpp>
#include <pdal/StageFactory.hpp>
#include <pdal/PointTable.hpp>
#include <memory>
#include <string>
namespace po = boost::program_options;
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::validateSwitches()
{
if (m_input_file == "")
throw pdal::app_usage_error("--input/-i required");
if (m_output_file == "")
throw pdal::app_usage_error("--output/-o required");
}
void MyKernel::addSwitches()
{
po::options_description* options = new po::options_description("file options");
options->add_options()
("input,i", po::value<std::string>(&m_input_file)->default_value(""), "input file name")
("output,o", po::value<std::string>(&m_output_file)->default_value(""), "output file name")
;
addSwitchSet(options);
addPositionalSwitch("input", 1);
addPositionalSwitch("output", 1);
}
int MyKernel::execute()
{
PointTable table;
StageFactory f;
Stage * reader = f.createStage("readers.las");
Options readerOptions;
readerOptions.add("filename", m_input_file);
reader->setOptions(readerOptions);
Stage * filter = f.createStage("filters.decimation");
Options filterOptions;
filterOptions.add("step", 10);
filter->setOptions(filterOptions);
filter->setInput(*reader);
Stage * writer = f.createStage("writers.text");
Options writerOptions;
writerOptions.add("filename", m_output_file);
writer->setOptions(writerOptions);
writer->setInput(*filter);
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.
static PluginInfo const s_info {
"kernels.mykernel",
"MyKernel",
"http://link/to/documentation"
};
CREATE_SHARED_PLUGIN(1, 0, MyKernel, Kernel, s_info);
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 PointTable and the StageFactory. The latter is used to create the various stages that will be used within the kernel.
StageFactory f;
The Reader is created from the 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 = f.createStage("readers.las");
Options readerOptions;
readerOptions.add("filename", m_input_file);
reader->setOptions(readerOptions);
The Filter is also created from the 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.
Stage * filter = f.createStage("filters.decimation");
Options filterOptions;
filterOptions.add("step", 10);
filter->setOptions(filterOptions);
filter->setInput(*reader);
Finally, the Writer is created from the StageFactory. This text writer, takes as input the previous stage (the decimation filter) and the output filename as its sole option.
Stage * writer = f.createStage("writers.text");
Options writerOptions;
writerOptions.add("filename", m_output_file);
writer->setOptions(writerOptions);
writer->setInput(*filter);
The final two steps are to prepare and execute the pipeline. This is achieved by calling prepare and execute on the final stage.
writer->prepare(table);
writer->execute(table);
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>.