(liblas-to-pdal)=

# libLAS C API to PDAL transition guide

This page shows how to port code using libLAS C API to PDAL API
(which is C++). The new code is not using full power of PDAL but
it uses just what is necessary to read content of a LAS file.

## Includes

libLAS include:

```cpp
#include <liblas/capi/liblas.h>
```

For PDAL, in addition to PDAL headers, we also include standard headers
which will be useful later:

```cpp
#include <memory>
#include <pdal/PointTable.hpp>
#include <pdal/PointView.hpp>
#include <pdal/LasReader.hpp>
#include <pdal/LasHeader.hpp>
#include <pdal/Options.hpp>
```

## Initial steps

Opening the dataset in libLAS:

```cpp
LASReaderH LAS_reader;
LASHeaderH LAS_header;
LASSRSH LAS_srs;
LAS_reader = LASReader_Create(in_opt->answer);
LAS_header = LASReader_GetHeader(LAS_reader);
```

The higher level of abstraction in PDAL requires a little bit more code
for the initial steps:

```cpp
pdal::Option las_opt("filename", in_opt->answer);
pdal::Options las_opts;
las_opts.add(las_opt);
pdal::PointTable table;
pdal::LasReader las_reader;
las_reader.setOptions(las_opts);
las_reader.prepare(table);
pdal::PointViewSet point_view_set = las_reader.execute(table);
pdal::PointViewPtr point_view = *point_view_set.begin();
pdal::Dimension::IdList dims = point_view->dims();
pdal::LasHeader las_header = las_reader.header();
```

The PDAL code is also different in the way that we read all the data
right away while in libLAS we just open the file.
To make use of other readers supported by PDAL, see `StageFactory` class.

The test if the file was loaded successfully, the test of the header
pointer was used with libLAS:

```cpp
if (LAS_header == NULL) {
    /* fail */
}
```

In general, PDAL will throw a `pdal_error` exception in case something
is wrong and it can't recover such in the case when the file can't be opened.
To handle the exceptional state by yourself, you can wrap the code
in `try-catch` block:

```cpp
try {
    /* actual code */
} catch {
    /* fail in your own way */
}
```

## Dataset properties

We assume we defined all the following variables as `double`.

The general properties from the LAS file are retrieved from the
header in libLAS:

```cpp
scale_x = LASHeader_GetScaleX(LAS_header);
scale_y = LASHeader_GetScaleY(LAS_header);
scale_z = LASHeader_GetScaleZ(LAS_header);

offset_x = LASHeader_GetOffsetX(LAS_header);
offset_y = LASHeader_GetOffsetY(LAS_header);
offset_z = LASHeader_GetOffsetZ(LAS_header);

xmin = LASHeader_GetMinX(LAS_header);
xmax = LASHeader_GetMaxX(LAS_header);
ymin = LASHeader_GetMinY(LAS_header);
ymax = LASHeader_GetMaxY(LAS_header);
```

And the same applies PDAL:

```cpp
scale_x = las_header.scaleX();
scale_y = las_header.scaleY();
scale_z = las_header.scaleZ();

offset_x = las_header.offsetX();
offset_y = las_header.offsetY();
offset_z = las_header.offsetZ();

xmin = las_header.minX();
xmax = las_header.maxX();
ymin = las_header.minY();
ymax = las_header.maxY();
```

The point record count in libLAS:

```cpp
unsigned int n_features = LASHeader_GetPointRecordsCount(LAS_header);
```

is just point count in PDAL:

```cpp
unsigned int n_features = las_header.pointCount();
```

WKT of a spatial reference system is obtained from the header in libLAS:

```cpp
LAS_srs = LASHeader_GetSRS(LAS_header);
char* projstr = LASSRS_GetWKT_CompoundOK(LAS_srs);
```

In PDAL, spatial reference is part of the `PointTable`:

```cpp
char* projstr = table.spatialRef().getWKT(pdal::SpatialReference::eCompoundOK).c_str();
```

Whether the time or color is supported by the LAS format, one would
have to determine from the format ID in libLAS:

```cpp
las_point_format = LASHeader_GetDataFormatId(LAS_header);
have_time = (las_point_format == 1 ...
```

In PDAL, there is a convenient function for it in the header:

```cpp
have_time = las_header.hasTime();
have_color = las_header.hasColor();
```

The presence of color, time and other dimensions can be also determined
with:

```cpp
pdal::Dimension::IdList dims = point_view->dims();
```

## Iterating over points

libLAS:

```cpp
while ((LAS_point = LASReader_GetNextPoint(LAS_reader)) != NULL) {
    // ...
}
```

PDAL:

```cpp
for (pdal::PointId idx = 0; idx < point_view->size(); ++idx) {
    // ...
}
```

## Point validity

The correct usage of libLAS required to test point validity:

```cpp
LASPoint_IsValid(LAS_point)
```

In PDAL, there is no need to do that and the caller can assume that
all the points provided by PDAL are valid.

## Coordinates

libLAS:

```cpp
x = LASPoint_GetX(LAS_point);
y = LASPoint_GetY(LAS_point);
z = LASPoint_GetZ(LAS_point);
```

In PDAL, point coordinates are one of the dimensions:

```cpp
using namespace pdal::Dimension;
x = point_view->getFieldAs<double>(Id::X, idx);
y = point_view->getFieldAs<double>(Id::Y, idx);
z = point_view->getFieldAs<double>(Id::Z, idx);
```

Thanks to `using namespace pdal::Dimension` we can just write `Id::X` etc.

## Returns

libLAS:

```cpp
int return_no = LASPoint_GetReturnNumber(LAS_point);
int n_returns = LASPoint_GetNumberOfReturns(LAS_point);
```

PDAL:

```cpp
int return_no = point_view->getFieldAs<int>(Id::ReturnNumber, idx);
int n_returns = point_view->getFieldAs<int>(Id::NumberOfReturns, idx);
```

## Classes

libLAS:

```cpp
int point_class = (int) LASPoint_GetClassification(LAS_point);
```

PDAL:

```cpp
int point_class = point_view->getFieldAs<int>(Id::Classification, idx);
```

## Color

libLAS:

```cpp
LASColorH LAS_color = LASPoint_GetColor(LAS_point);
int red = LASColor_GetRed(LAS_color);
int green = LASColor_GetGreen(LAS_color);
int blue = LASColor_GetBlue(LAS_color);
```

PDAL:

```cpp
int red = point_view->getFieldAs<int>(Id::Red, idx);
int green = point_view->getFieldAs<int>(Id::Green, idx);
int blue = point_view->getFieldAs<int>(Id::Blue, idx);
```

For LAS format, `hasColor()` method of `LasHeader` to see if the
format supports RGB. However, in general, you can test use
`hasDim(Id::Red)`, `hasDim(Id::Green)` and `hasDim(Id::Blue)`
method calls on the point, to see if the color was defined.

## Time

libLAS:

```cpp
double time = LASPoint_GetTime(LAS_point);
```

PDAL:

```cpp
double time = point_view->getFieldAs<double>(Id::GpsTime, idx);
```

## Other point attributes

libLAS:

```cpp
LASPoint_GetIntensity(LAS_point)
LASPoint_GetScanDirection(LAS_point)
LASPoint_GetFlightLineEdge(LAS_point)
LASPoint_GetScanAngleRank(LAS_point)
LASPoint_GetPointSourceId(LAS_point)
LASPoint_GetUserData(LAS_point)
```

PDAL:

```cpp
point_view->getFieldAs<int>(Id::Intensity, idx)
point_view->getFieldAs<int>(Id::ScanDirectionFlag, idx)
point_view->getFieldAs<int>(Id::EdgeOfFlightLine, idx)
point_view->getFieldAs<int>(Id::ScanAngleRank, idx)
point_view->getFieldAs<int>(Id::PointSourceId, idx)
point_view->getFieldAs<int>(Id::UserData, idx)
```

## Memory management

In libLAS C API, we need to explicitly take care of freeing the memory:

```cpp
LASSRS_Destroy(LAS_srs);
LASHeader_Destroy(LAS_header);
LASReader_Destroy(LAS_reader);
```

When using C++ and PDAL, the objects created on stack free the memory
when they go out of scope. When using smart pointers, they will take
care of the memory they manage. This does not apply to special cases
such as `exit()` function calls.
