- Authors
Bradley Chambers, Scott Lewis
- Contact
- Date
10/26/2016
PDAL's command-line application can be extended through the development of reader functions. In this tutorial, we will give a brief example.
First, we provide a full listing of the reader header.
../../examples/writing-reader/MyReader.hpp
In your MyReader class, you will declare the necessary methods and variables needed to make the reader work and meet the plugin specifications.
../../examples/writing-reader/MyReader.hpp
These methods are required to fulfill the specs for defining a new plugin.
../../examples/writing-reader/MyReader.hpp
m_stream
is used to process the input, while m_index
is used to track the index of the records. m_scale_z
is specific to MyReader, and will be described later.
../../examples/writing-reader/MyReader.hpp
Various other override methods for the stage. There are a few others that could be overridden, which will not be discussed in this tutorial.
Note
See ./include/pdal/Reader.hpp
of the source tree for more methods that a reader can override or implement.
Again, we start with a full listing of the reader source.
../../examples/writing-reader/MyReader.cpp
In your reader implementation, you will use a macro defined in pdal_macros.
../../examples/writing-reader/MyReader.cpp
This macro registers the plugin with the PDAL code. In this case, we are declaring this as a SHARED plugin, meaning that it will be located external to the main PDAL installation. The macro is supplied with a version number (major and minor), the class of the plugin, the parent class (in this case, to identify it as a reader), and an object with information. This information includes the name of the plugin, a description, and a link to documentation.
Creating STATIC plugins requires a few more steps which will not be covered in this tutorial.
../../examples/writing-reader/MyReader.cpp
This method will process a options for the reader. In this example, we are setting the z_scale value to a default of 1.0, indicating that the Z values we read should remain as-is. (In our reader, this could be changed if, for example, the Z values in the file represented mm values, and we want to represent them as m in the storage model). addArgs
will bind values given for the argument to the m_scale_z
variable of the stage.
../../examples/writing-reader/MyReader.cpp
This method registers the various dimensions the reader will use. In our case, we are using the X, Y, and Z built-in dimensions, as well as a custom dimension MyData.
../../examples/writing-reader/MyReader.cpp
This method is called when the Reader is ready for use. It will only be called once, regardless of the number of PointViews that are to be processed.
../../examples/writing-reader/MyReader.cpp
This is a helper function, which will convert a string value into the type specified when it's called. In our example, it will be used to convert strings to doubles when reading from the input stream.
../../examples/writing-reader/MyReader.cpp
This method is the main processing method for the reader. It takes a pointer to a Point View which we will build as we read from the file. We initialize some variables as well, and then reset the input stream with the filename used for the reader. Note that in other readers, the contents of this method could be very different depending on the format of the file being read, but this should serve as a good start for how to build the PointView object.
../../examples/writing-reader/MyReader.cpp
In preparation for reading the file, we prepare to skip some header lines. In our case, the header is only a single line.
../../examples/writing-reader/MyReader.cpp
Here we begin our main loop. In our example file, the first line is a header, and each line thereafter is a single point. If the file had a different format the method of looping and reading would have to change as appropriate. We make sure we are skipping the header lines here before moving on.
../../examples/writing-reader/MyReader.cpp
Here we take the line we read in the for block header, split it, and make sure that we have the proper number of fields.
../../examples/writing-reader/MyReader.cpp
Here we take the values we read and put them into the PointView object. The X and Y fields are simply converted from the file and put into the respective fields. MyData is done likewise with the custom dimension we defined. The Z value is read, and multiplied by the scale_z option (defaulted to 1.0), before the converted value is put into the field.
When putting the value into the PointView object, we pass in the Dimension that we are assigning it to, the ID of the point (which is incremented in each iteration of the loop), and the dimension value.
../../examples/writing-reader/MyReader.cpp
Finally, we increment the nextId and make a call into the progress callback if we have one with our nextId. After the loop is done, we set the index and number read, and return that value as the number of points read. This could differ in cases where we read multiple streams, but that won't be covered here.
When the read method is finished, the done method is called for any cleanup. In this case, we simply make sure the stream is reset.
The MyReader.cpp code can be compiled. For this example, we'll use cmake. Here is the CMakeLists.txt file we will use:
../../examples/writing-reader/CMakeLists.txt
If this file is in the directory containing MyReader.hpp and MyReader.cpp, simply run cmake .
, followed by make
. This will generate a file called libpdal_plugin_reader_myreader.dylib
.
Put this dylib file into the directory pointed to by PDAL_DRIVER_PATH
, and then when you run pdal --drivers
, you should see an entry for readers.myreader.
To test the reader, we will put it into a pipeline and output a text file.
Please download the pipeline-myreader.json and test-reader-input.txt files.
In the directory with those two files, run pdal pipeline pipeline-myreader.json
. You should have an output file called output.txt
, which will have the same data as in the input file, except in a CSV style format, and with the Z values scaled by .001.