Skip to content

Commit

Permalink
Merge pull request #456 from gadomski/rxp-driver
Browse files Browse the repository at this point in the history
Add the rxp reader
  • Loading branch information
hobu committed Sep 15, 2014
2 parents 06502f6 + 51b1149 commit feab6d7
Show file tree
Hide file tree
Showing 15 changed files with 1,049 additions and 2 deletions.
25 changes: 25 additions & 0 deletions CMakeLists.txt
Expand Up @@ -25,6 +25,12 @@ cmake_policy(SET CMP0022 OLD) # interface link libraries
cmake_policy(SET CMP0042 OLD) # osx rpath
endif()

# Must come before pdal_options.cmake is included
# Silences warnings when building with rivlib
if (WITH_RIVLIB)
cmake_policy(SET CMP0022 OLD) # interface link libraries
endif (WITH_RIVLIB)

# per http://www.cmake.org/Wiki/CMake_RPATH_handling
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
Expand Down Expand Up @@ -625,6 +631,25 @@ if(WITH_PYTHON)
endif()
endif()

# RiVLib, Riegl's V-Line Scanner Library
# defaults to off
#
# RiVLib uses that funky mixed case to name itself, but PDAL will
# simply call it 'rivlib' for ease of typing.
option(WITH_RIVLIB "Choose if rivlib support should be built" FALSE)
if(WITH_RIVLIB)
find_package(RiVLib COMPONENTS scanlib)
if(RiVLib_FOUND)
set(PDAL_HAVE_RIVLIB 1)
include_directories(${RiVLib_INCLUDE_DIRS})
add_definitions(-DHAVE_RIVLIB=1)
message(STATUS "Found RiVLib at: ${RiVLib_INCLUDE_DIRS}")
else()
set(RiVLib_SCANLIB_LIBRARY "")
endif()
endif()


option(USE_PDAL_PLUGIN_TEXT "Build the text driver as a plugin rather than embedding" FALSE)
option(USE_PDAL_PLUGIN_SOCI "Build the soci driver as a plugin rather than embedding" FALSE)
option(USE_PDAL_PLUGIN_OCI "Build the oci driver as a plugin rather than embedding" FALSE)
Expand Down
121 changes: 121 additions & 0 deletions doc/stages/drivers.rxp.reader.rst
@@ -0,0 +1,121 @@
.. _drivers.rxp.reader:

drivers.rxp.reader
==================

The **RXP reader** read from files in the RXP format, the in-house streaming format used by `RIEGL Laser Measurement Systems`_.


Installation
------------

To build PDAL with rxp support, set RiVLib_DIR to the path of your local RiVLib installation.
RiVLib can be obtained from the `RIEGL download pages`_ with a properly enabled user account.
The RiVLib files do not need to be in a system-level directory, though they could be (e.g. they could be in ``/usr/local``, or just in your home directory somewhere).
For help building PDAL with optional libraries, see `the optional library documentation`_.


Example
-------

This example rescales the points, given in the scanner's own coordinate system, to values that can be written to a las file.
Only points with a valid gps time, as determined by a pps pulse, are read from the rxp, since the ``sync_to_pps`` option is "true".

.. code-block:: xml
<?xml version="1.0" encoding="utf-8"?>
<Pipeline version="1.0">
<Writer type="drivers.las.writer">
<Option name="filename">output.las</Option>
<Option name="discard_high_return_numbers">true</Option>
<Filter type="filters.scaling">
<Option name="dimension">
X
<Options>
<Option name="scale">0.001</Option>
<Option name="offset">0</Option>
</Options>
</Option>
<Option name="dimension">
Y
<Options>
<Option name="scale">0.001</Option>
<Option name="offset">0</Option>
</Options>
</Option>
<Option name="dimension">
Z
<Options>
<Option name="scale">0.001</Option>
<Option name="offset">0</Option>
</Options>
</Option>
<Filter type="filters.programmable">
<Option name="source">
import numpy
def reflectance_to_intensity(ins, outs):
ref = ins["Reflectance"]
min = numpy.amin(ref)
max = numpy.amax(ref)
outs["Intensity"] = (65536 * (ref - min) / (max - min)).astype(numpy.uint16)
return True
</Option>
<Option name="function">reflectance_to_intensity</Option>
<Option name="module">pyrxp</Option>
<Option name="add_dimension">Intensity</Option>
<Reader type="drivers.rxp.reader">
<Option name="filename">120304_204030.rxp</Option>
<Option name="sync_to_pps">true</Option>
</Reader>
</Filter>
</Filter>
</Writer>
</Pipeline>
A few things to note:

- We use a ``filters.programmable`` to remap Reflectance values to Intensity values, scaling them so the entire range of Reflectance values fit into the Intensity field.
This is analogous to the "Export reflectance as intensity" option in RiSCAN Pro.
You could also explicitly set the minimum and maximum Reflectance values as you would in RiSCAN Pro using the same programmable filter.
You could also use "Amplitude" instead of "Reflectance".
If you do not need Intensity values in your output file, you can delete the programmable filter.
- We set the ``discard_high_return_numbers`` option to ``true`` on the las writer.
RXP files can contain more returns per shot than is supported by las, and so we need to explicitly tell the las writer to ignore those high return number points.
You could also use ``filters.predicate`` to filter those points earlier in the pipeline, or modify the returns values with a ``filters.programmable``.


Options
-------

filename
File to read from [Required if rdtp is not provided]

rdtp
URI for a network-assessable scanner [Required if filename is not provided]

sync_to_pps
If "true", ensure all incoming points have a valid pps timestamp, usually provided by some sort of GPS clock.
If "false", use the scanner's internal time.
Defaults to "true"

minimal
If "true", only write X, Y, Z, and time values to the data stream.
If "false", write all available values as derived from the rxp file.
Use this feature to reduce the memory footprint of a PDAL run, if you don't need any values but the points themselves.
Defaults to "false".

inclination_fix
*EXPERIMENTAL*: If "true", use inclination values in the rxp file to dynamically correct for inclination changes throughout the scan, using a moving average of 2 * ``inclination_fix_window`` inclination readings (see below).
This is an experimental feature that will remove some points from the data stream and modify many others.
Use with caution.
If "false", disable this feature.
Defaults to "false".

inclination_fix_window
*EXPERIMENTAL*: Sets the half-size of the inclination fix window (see above).
Use of this feature should be considered highly experimental.


.. _RIEGL Laser Measurement Systems: http://www.riegl.com
.. _RIEGL download pages: http://www.riegl.com/members-area/software-downloads/libraries/
.. _the optional library documentation: http://www.pdal.io/compilation/unix.html#configure-your-optional-libraries
1 change: 1 addition & 0 deletions doc/stages/index.rst
Expand Up @@ -25,6 +25,7 @@ Readers & Writers
drivers.oci.writer
drivers.p2g.writer
drivers.qfit.reader
drivers.rxp.reader
drivers.sbet.reader
drivers.text.writer

Expand Down
72 changes: 71 additions & 1 deletion include/pdal/Dimension.hpp
Expand Up @@ -140,6 +140,8 @@ enum Enum
Y,
Z,
Intensity,
Amplitude,
Reflectance,
ReturnNumber,
NumberOfReturns,
ScanDirectionFlag,
Expand All @@ -152,14 +154,18 @@ enum Enum
Green,
Blue,
GpsTime,
InternalTime,
OffsetTime,
IsPpsLocked,
StartPulse,
ReflectedPulse,
Pdop,
Pitch,
Roll,
PulseWidth,
Deviation,
PassiveSignal,
BackgroundRadiation,
PassiveX,
PassiveY,
PassiveZ,
Expand All @@ -176,7 +182,8 @@ enum Enum
ZBodyAngRate,
Flag,
Mark,
Alpha
Alpha,
EchoRange
};
} // namespace Id
typedef std::vector<Id::Enum> IdList;
Expand All @@ -196,6 +203,16 @@ inline std::string description(Id::Enum id)
return "Z coordinate";
case Id::Intensity:
return "Representation of the pulse return magnitude";
case Id::Amplitude:
return "This is the ratio of the received power to the "
"power received at the detection limit expressed in dB";
case Id::Reflectance:
return "This is the ratio of the received power to the "
"power that would be received from a white diffuse target "
"at the same distance expressed in dB. The reflectance "
"represents a range independent property of the target. "
"The surface normal of this target is assumed to be in "
"parallel to the laser beam direction.";
case Id::ReturnNumber:
return "Pulse return number for a given output pulse. A given output "
"laser pulse can have many returns, and they must be marked in "
Expand Down Expand Up @@ -227,8 +244,13 @@ inline std::string description(Id::Enum id)
"indicates that the point originated in the current file";
case Id::GpsTime:
return "GPS time that the point was acquired";
case Id::InternalTime:
return "Scanner's internal time when the point was aquired, in seconds";
case Id::OffsetTime:
return "Milliseconds from first acquired point";
case Id::IsPpsLocked:
return "The external PPS signal was found to be synchronized at the "
"time of the current laser shot.";
case Id::Red:
return "Red image channel value";
case Id::Green:
Expand All @@ -249,8 +271,12 @@ inline std::string description(Id::Enum id)
return "GPS PDOP (dilution of precision)";
case Id::PulseWidth:
return "Laser received pulse width (digitizer samples)";
case Id::Deviation:
return "A larger value for deviation indicates larger distortion.";
case Id::PassiveSignal:
return "Relative passive signal";
case Id::BackgroundRadiation:
return "A measure of background radiation.";
case Id::PassiveX:
return "Passive X footprint";
case Id::PassiveY:
Expand Down Expand Up @@ -283,6 +309,8 @@ inline std::string description(Id::Enum id)
return "Mark";
case Id::Flag:
return "Flag";
case Id::EchoRange:
return "The distance from the laser origin to the target.";
case Id::Unknown:
return "";
}
Expand All @@ -300,6 +328,10 @@ inline Id::Enum id(std::string s)
return Id::Z;
else if (s == "INTENSITY")
return Id::Intensity;
else if (s == "AMPLITUDE")
return Id::Amplitude;
else if (s == "REFLECTANCE")
return Id::Reflectance;
else if (s == "RETURNNUMBER")
return Id::ReturnNumber;
else if (s == "NUMBEROFRETURNS")
Expand All @@ -326,8 +358,12 @@ inline Id::Enum id(std::string s)
return Id::Alpha;
else if (s == "GPSTIME")
return Id::GpsTime;
else if (s == "INTERNALTIME")
return Id::InternalTime;
else if (s == "TIME" || s == "OFFSETTIME")
return Id::OffsetTime;
else if (s == "ISPPSLOCKED")
return Id::IsPpsLocked;
else if (s == "STARTPULSE")
return Id::StartPulse;
else if (s == "RELFECTEDPULSE")
Expand All @@ -340,8 +376,12 @@ inline Id::Enum id(std::string s)
return Id::Pdop;
else if (s == "PULSEWIDTH")
return Id::PulseWidth;
else if (s == "DEVIATION")
return Id::Deviation;
else if (s == "PASSIVESIGNAL")
return Id::PassiveSignal;
else if (s == "BACKGROUNDRADIATION")
return Id::BackgroundRadiation;
else if (s == "PASSIVEX")
return Id::PassiveX;
else if (s == "PASSIVEY")
Expand Down Expand Up @@ -374,6 +414,8 @@ inline Id::Enum id(std::string s)
return Id::Mark;
else if (s == "FLAG")
return Id::Flag;
else if (s == "ECHORANGE")
return Id::EchoRange;
return Id::Unknown;
}

Expand All @@ -389,6 +431,10 @@ inline std::string name(Id::Enum id)
return "Z";
case Id::Intensity:
return "Intensity";
case Id::Amplitude:
return "Amplitude";
case Id::Reflectance:
return "Reflectance";
case Id::ReturnNumber:
return "ReturnNumber";
case Id::NumberOfReturns:
Expand All @@ -415,8 +461,12 @@ inline std::string name(Id::Enum id)
return "Alpha";
case Id::GpsTime:
return "GpsTime";
case Id::InternalTime:
return "InternalTime";
case Id::OffsetTime:
return "OffsetTime";
case Id::IsPpsLocked:
return "IsPpsLocked";
case Id::StartPulse:
return "StartPulse";
case Id::ReflectedPulse:
Expand All @@ -429,8 +479,12 @@ inline std::string name(Id::Enum id)
return "Pdop";
case Id::PulseWidth:
return "PulseWidth";
case Id::Deviation:
return "Deviation";
case Id::PassiveSignal:
return "PassiveSignal";
case Id::BackgroundRadiation:
return "BackgroundRadiation";
case Id::PassiveX:
return "PassiveX";
case Id::PassiveY:
Expand Down Expand Up @@ -463,6 +517,8 @@ inline std::string name(Id::Enum id)
return "Mark";
case Id::Flag:
return "Flag";
case Id::EchoRange:
return "EchoRange";
case Id::Unknown:
return "";
}
Expand All @@ -484,6 +540,10 @@ inline Type::Enum defaultType(Id::Enum id)
return Double;
case Id::Intensity:
return Unsigned16;
case Id::Amplitude:
return Float;
case Id::Reflectance:
return Float;
case Id::ReturnNumber:
return Unsigned8;
case Id::NumberOfReturns:
Expand All @@ -502,8 +562,12 @@ inline Type::Enum defaultType(Id::Enum id)
return Unsigned16;
case Id::GpsTime:
return Double;
case Id::InternalTime:
return Double;
case Id::OffsetTime:
return Unsigned32;
case Id::IsPpsLocked:
return Unsigned8;
case Id::Red:
return Unsigned16;
case Id::Green:
Expand All @@ -524,8 +588,12 @@ inline Type::Enum defaultType(Id::Enum id)
return Float;
case Id::PulseWidth:
return Float;
case Id::Deviation:
return Float;
case Id::PassiveSignal:
return Signed32;
case Id::BackgroundRadiation:
return Float;
case Id::PassiveX:
return Double;
case Id::PassiveY:
Expand Down Expand Up @@ -558,6 +626,8 @@ inline Type::Enum defaultType(Id::Enum id)
return Unsigned8;
case Id::Flag:
return Unsigned8;
case Id::EchoRange:
return Double;
case Id::Unknown:
throw pdal_error("No type for undefined dimension ID.");
}
Expand Down

0 comments on commit feab6d7

Please sign in to comment.