# Writing a filter¶

PDAL can be extended through the development of filter functions.

For more on filters and their role in PDAL, please refer to PDAL Architecture Overview.

Every filter stage in PDAL is implemented as a plugin (sometimes referred to as a “driver”). Filters native to PDAL, such as filters.ferry, are implemented as _static_ filters and are statically linked into the PDAL library. Filters that require extra/optional dependencies, or are external to the core PDAL codebase altogether, such as filters.pmf, are implemented as _shared_ filters, and are built as individual shared libraries, discoverable by PDAL at runtime.

In this tutorial, we will give a brief example of a filter, with notes on how to make it static or shared.

First, we provide a full listing of the filter 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 30 31 32 33 34 35 36 // MyFilter.hpp #pragma once #include #include #include namespace pdal { class Options; class PointLayout; class PointView; class PDAL_DLL MyFilter : public Filter { public: MyFilter() : Filter() {} std::string getName() const; private: double m_value; Dimension::Id m_myDimension; virtual void addDimensions(PointLayoutPtr layout); virtual void addArgs(ProgramArgs& args); virtual PointViewSet run(PointViewPtr view); MyFilter& operator=(const MyFilter&); // not implemented MyFilter(const MyFilter&); // not implemented }; } // namespace pdal 

This header should be relatively straightforward, but we will point out three methods that must be declared for the plugin interface to be satisfied.

private:
double m_value;


In many instances, you should be able to copy this header template verbatim, changing only the filter class name, includes, and member functions/variables as required by your implementation.

## The 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 // MyFilter.cpp #include "MyFilter.hpp" #include #include #include #include namespace pdal { static PluginInfo const s_info = PluginInfo("filters.name", "My awesome filter", "http://link/to/documentation"); CREATE_SHARED_PLUGIN(1, 0, MyFilter, Filter, s_info) std::string MyFilter::getName() const { return s_info.name; } void MyFilter::addArgs(ProgramArgs& args) { args.add("param", "Some parameter", m_value, 1.0); } void MyFilter::addDimensions(PointLayoutPtr layout) { layout->registerDim(Dimension::Id::Intensity); m_myDimension = layout->registerOrAssignDim("MyDimension", Dimension::Type::Unsigned8); } PointViewSet MyFilter::run(PointViewPtr input) { PointViewSet viewSet; viewSet.insert(input); return viewSet; } } // namespace pdal 

For your filter to be available to PDAL at runtime, it must adhere to the PDAL plugin interface. As a convenience, we provide the macros in pdal_macros.hpp to do just this.

We begin by creating a PluginInfo struct containing three identifying elements - the filter name, description, and a link to documentation.

 1 2 3  "http://link/to/documentation"); CREATE_SHARED_PLUGIN(1, 0, MyFilter, Filter, s_info) 

PDAL requires that filter names always begin with filters., and end with a string that uniquely identifies the filter. The description will be displayed to users of the PDAL CLI (pdal --drivers).

Next, we pass the following to the CREATE_STATIC_STAGE macro, passin in the name of the stage and the PluginInfo struct.

std::string MyFilter::getName() const


To create a shared stage, we simply change CREATE_STATIC_STAGE to CREATE_SHARED_STAGE.

Finally, we implement a method to get the plugin name, which is primarily used by the PDAL CLI when using the --drivers or --options arguments.

 1 2 3 4  return s_info.name; } void MyFilter::addArgs(ProgramArgs& args) 

Now that the filter has implemented the proper plugin interface, we will begin to implement some methods that actually implement the filter. First, getDefaultOptions() is used to advertise those options that the filter provides. Within PDAL, this is primarily used as a means of displaying options via the PDAL CLI with the --options argument. It provides the user with the option names, descriptions, and default values.

 1 2 3 4  args.add("param", "Some parameter", m_value, 1.0); } void MyFilter::addDimensions(PointLayoutPtr layout) 

The addArgs() method is used to register and bind any provided options to the stage. Here, we get the value of param, if provided, else we populate m_value with the default value of 1.0.

 1 2 3 4 5 6  layout->registerDim(Dimension::Id::Intensity); m_myDimension = layout->registerOrAssignDim("MyDimension", Dimension::Type::Unsigned8); } PointViewSet MyFilter::run(PointViewPtr input) 

In addDimensions() we make sure that the known Intensity dimension is registered. We can also add a custom dimension, MyDimension, which will be populated within run().

 1 2 3 4 5 6  PointViewSet viewSet; viewSet.insert(input); return viewSet; } } // namespace pdal 

Finally, we define run(), which takes as input a PointViewPtr and returns a PointViewSet. It is here that we can transform existing dimensions, add data to new dimensions, or selectively add/remove individual points.

We suggest you take a closer look at our existing filters to get an idea of the power of the Filter stage and inspiration for your own filters!

## Compilation¶

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

 1 2 3 4 5 6 7 8 9 cmake_minimum_required(VERSION 2.8.12) project(FilterTutorial) find_package(PDAL 1.6.0 REQUIRED CONFIG) add_library(pdal_plugin_filter_myfilter SHARED MyFilter.cpp) target_link_libraries(pdal_plugin_filter_myfilter PRIVATE ${PDAL_LIBRARIES}) target_include_directories(pdal_plugin_filter_myfilter PRIVATE${PDAL_INCLUDE_DIRS})