# Writing a filter

PDAL can be extended through the development of filter functions.

See also

For more on filters and their role in PDAL, and their lifecycle 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.python, 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.

## The header

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

     1// MyFilter.hpp
     2
     3#pragma once
     4
     5#include <pdal/pdal_internal.hpp>
     6#include <pdal/Filter.hpp>
     7
     8namespace pdal
     9{
    10
    11class PDAL_DLL MyFilter : public Filter
    12{
    13public:
    14    MyFilter() : Filter()
    15    {}
    16    std::string getName() const;
    17
    18private:
    19    double m_value;
    20    Dimension::Id m_myDimension;
    21
    22    virtual void addDimensions(PointLayoutPtr layout);
    23    virtual void addArgs(ProgramArgs& args);
    24    virtual PointViewSet run(PointViewPtr view);
    25
    26    MyFilter& operator=(const MyFilter&); // not implemented
    27    MyFilter(const MyFilter&); // not implemented
    28};
    29
    30} // namespace pdal

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

        std::string getName() const;

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

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

     1// MyFilter.cpp
     2
     3#include "MyFilter.hpp"
     4
     5#include <pdal/pdal_internal.hpp>
     6
     7namespace pdal
     8{
     9
    10static PluginInfo const s_info
    11{
    12    "filters.name",
    13    "My awesome filter",
    14    "http://link/to/documentation"
    15};
    16
    17CREATE_SHARED_STAGE(MyFilter, s_info)
    18
    19std::string MyFilter::getName() const { return s_info.name; }
    20
    21void MyFilter::addArgs(ProgramArgs& args)
    22{
    23    args.add("param", "Some parameter", m_value, 1.0);
    24}
    25
    26void MyFilter::addDimensions(PointLayoutPtr layout)
    27{
    28    layout->registerDim(Dimension::Id::Intensity);
    29    m_myDimension = layout->registerOrAssignDim("MyDimension",
    30            Dimension::Type::Unsigned8);
    31}
    32
    33PointViewSet MyFilter::run(PointViewPtr input)
    34{
    35    PointViewSet viewSet;
    36    viewSet.insert(input);
    37    return viewSet;
    38}
    39
    40} // 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 macros to do just this.

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

    1static PluginInfo const s_info
    2{
    3    "filters.name",
    4    "My awesome filter",
    5    "http://link/to/documentation"
    6};

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`). When making a shared plugin, the name of the shared library must correspond with the name of the filter provided here. The name of the generated shared object must be

    libpdal_plugin_filter_<filter name>.<shared library extension>

Next, we pass the following to the `CREATE_SHARED_STAGE` macro, passing in the name of the stage and the `PluginInfo` struct.

    CREATE_SHARED_STAGE(MyFilter, s_info)

To create a static stage, we simply change `CREATE_SHARED_STAGE` to `CREATE_STATC_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.

    1std::string MyFilter::getName() const { return s_info.name; }

Now that the filter has implemented the proper plugin interface, we will begin to implement some methods that actually implement the filter. 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`. Option names, descriptions, and default values specified in `addArgs()` will be displayed via the PDAL CLI with the `--options` argument.

    1void MyFilter::addArgs(ProgramArgs& args)
    2{
    3    args.add("param", "Some parameter", m_value, 1.0);
    4}

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()`.

    1void MyFilter::addDimensions(PointLayoutPtr layout)
    2{
    3    layout->registerDim(Dimension::Id::Intensity);
    4    m_myDimension = layout->registerOrAssignDim("MyDimension",
    5            Dimension::Type::Unsigned8);
    6}

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:

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

Note

CMakeLists.txt contents may vary slightly depending on your project requirements, operating system, and compilter.