Skip to main content

Point cloud data processing

Project description

PDAL Python support allows you to process data with PDAL into Numpy arrays. It provides a PDAL extension module to control Python interaction with PDAL. Additionally, you can use it to fetch schema and metadata from PDAL operations.

Installation

PyPI

PDAL Python support is installable via PyPI:

pip install PDAL

GitHub

The repository for PDAL’s Python extension is available at https://github.com/PDAL/python

Python support released independently from PDAL itself as of PDAL 1.7.

Usage

Simple

Given the following pipeline, which simply reads an ASPRS LAS file and sorts it by the X dimension:

json = """
{
  "pipeline": [
    "1.2-with-color.las",
    {
        "type": "filters.sort",
        "dimension": "X"
    }
  ]
}"""

import pdal
pipeline = pdal.Pipeline(json)
count = pipeline.execute()
arrays = pipeline.arrays
metadata = pipeline.metadata
log = pipeline.log

Programmatic Pipeline Construction

The previous example specified the pipeline as a JSON string. Alternatively, a pipeline can be constructed by creating Stage instances and piping them together. For example, the previous pipeline can be specified as:

pipeline = pdal.Reader("1.2-with-color.las") | pdal.Filter.sort(dimension="X")

Stage Objects

  • A stage is an instance of pdal.Reader, pdal.Filter or pdal.Writer.

  • A stage can be instantiated by passing as keyword arguments the options applicable to the respective PDAL stage. For more on PDAL stages and their options, check the PDAL documentation on Stage Objects.

    • The filename option of Readers and Writers as well as the type option of Filters can be passed positionally as the first argument.

    • The inputs option specifies a sequence of stages to be set as input to the current stage. Each input can be either the string tag of another stage, or the Stage instance itself.

  • The Reader, Filter and Writer classes come with static methods for all the respective PDAL drivers. For example, pdal.Filter.head() is a shortcut for pdal.Filter(type="filters.head"). These methods are auto-generated by introspecting pdal and the available options are included in each method’s docstring:

>>> help(pdal.Filter.head)
Help on function head in module pdal.pipeline:

head(**kwargs)
    Return N points from beginning of the point cloud.

    user_data: User JSON
    log: Debug output filename
    option_file: File from which to read additional options
    where: Expression describing points to be passed to this filter
    where_merge='auto': If 'where' option is set, describes how skipped points should be merged with kept points in standard mode.
    count='10': Number of points to return from beginning.  If 'invert' is true, number of points to drop from the beginning.
    invert='false': If true, 'count' specifies the number of points to skip from the beginning.

Pipeline Objects

A pdal.Pipeline instance can be created from:

  • a JSON string: Pipeline(json_string)

  • a sequence of Stage instances: Pipeline([stage1, stage2])

  • a single Stage with the Stage.pipeline method: stage.pipeline()

  • nothing: Pipeline() creates a pipeline with no stages.

  • joining Stage and/or other Pipeline instances together with the pipe operator (|):

    • stage1 | stage2

    • stage1 | pipeline1

    • pipeline1 | stage1

    • pipeline1 | pipeline2

Every application of the pipe operator creates a new Pipeline instance. To update an existing Pipeline use the respective in-place pipe operator (|=):

# update pipeline in-place
pipeline = pdal.Pipeline()
pipeline |= stage
pipeline |= pipeline2

Reading using Numpy Arrays

The following more complex scenario demonstrates the full cycling between PDAL and Python:

  • Read a small testfile from GitHub into a Numpy array

  • Filters the array with Numpy for Intensity

  • Pass the filtered array to PDAL to be filtered again

  • Write the final filtered array to a LAS file and a TileDB array via the TileDB-PDAL integration using the TileDB writer plugin

import pdal

data = "https://github.com/PDAL/PDAL/blob/master/test/data/las/1.2-with-color.las?raw=true"

pipeline = pdal.Reader.las(filename=data).pipeline()
print(pipeline.execute())  # 1065 points

# Get the data from the first array
# [array([(637012.24, 849028.31, 431.66, 143, 1,
# 1, 1, 0, 1,  -9., 132, 7326, 245380.78254963,  68,  77,  88),
# dtype=[('X', '<f8'), ('Y', '<f8'), ('Z', '<f8'), ('Intensity', '<u2'),
# ('ReturnNumber', 'u1'), ('NumberOfReturns', 'u1'), ('ScanDirectionFlag', 'u1'),
# ('EdgeOfFlightLine', 'u1'), ('Classification', 'u1'), ('ScanAngleRank', '<f4'),
# ('UserData', 'u1'), ('PointSourceId', '<u2'),
# ('GpsTime', '<f8'), ('Red', '<u2'), ('Green', '<u2'), ('Blue', '<u2')])
arr = pipeline.arrays[0]

# Filter out entries that have intensity < 50
intensity = arr[arr["Intensity"] > 30]
print(len(intensity))  # 704 points

# Now use pdal to clamp points that have intensity 100 <= v < 300
pipeline = pdal.Filter.range(limits="Intensity[100:300)").pipeline(intensity)
print(pipeline.execute())  # 387 points
clamped = pipeline.arrays[0]

# Write our intensity data to a LAS file and a TileDB array. For TileDB it is
# recommended to use Hilbert ordering by default with geospatial point cloud data,
# which requires specifying a domain extent. This can be determined automatically
# from a stats filter that computes statistics about each dimension (min, max, etc.).
pipeline = pdal.Writer.las(
    filename="clamped.las",
    offset_x="auto",
    offset_y="auto",
    offset_z="auto",
    scale_x=0.01,
    scale_y=0.01,
    scale_z=0.01,
).pipeline(clamped)
pipeline |= pdal.Filter.stats() | pdal.Writer.tiledb(array_name="clamped")
print(pipeline.execute())  # 387 points

# Dump the TileDB array schema
import tiledb
with tiledb.open("clamped") as a:
    print(a.schema)

Executing Streamable Pipelines

Streamable pipelines (pipelines that consist exclusively of streamable PDAL stages) can be executed in streaming mode via Pipeline.iterator(). This returns an iterator object that yields Numpy arrays of up to chunk_size size (default=10000) at a time.

import pdal
pipeline = pdal.Reader("test/data/autzen-utm.las") | pdal.Filter.range(limits="Intensity[80:120)")
for array in pipeline.iterator(chunk_size=500):
    print(len(array))
# or to concatenate all arrays into one
# full_array = np.concatenate(list(pipeline))

Pipeline.iterator() also takes an optional prefetch parameter (default=0) to allow prefetching up to to this number of arrays in parallel and buffering them until they are yielded to the caller.

If you just want to execute a streamable pipeline in streaming mode and don’t need to access the data points (typically when the pipeline has Writer stage(s)), you can use the Pipeline.execute_streaming(chunk_size) method instead. This is functionally equivalent to sum(map(len, pipeline.iterator(chunk_size))) but more efficient as it avoids allocating and filling any arrays in memory.

Accessing Mesh Data

Some PDAL stages (for instance filters.delaunay) create TIN type mesh data.

This data can be accessed in Python using the Pipeline.meshes property, which returns a numpy.ndarray of shape (1,n) where n is the number of Triangles in the mesh.

If the PointView contains no mesh data, then n = 0.

Each Triangle is a tuple (A,B,C) where A, B and C are indices into the PointView identifying the point that is the vertex for the Triangle.

Meshio Integration

The meshes property provides the face data but is not easy to use as a mesh. Therefore, we have provided optional Integration into the Meshio library.

The pdal.Pipeline class provides the get_meshio(idx: int) -> meshio.Mesh method. This method creates a Mesh object from the PointView array and mesh properties.

Simple use of the functionality could be as follows:

import pdal

...
pl = pdal.Pipeline(pipeline)
pl.execute()

mesh = pl.get_meshio(0)
mesh.write('test.obj')

Advanced Mesh Use Case

USE-CASE : Take a LiDAR map, create a mesh from the ground points, split into tiles and store the tiles in PostGIS.

(example using 1.2-with-color.las and not doing the ground classification for clarity)

import pdal
import psycopg2
import io

pl = (
    pdal.Reader(".../python/test/data/1.2-with-color.las")
    | pdal.Filter.splitter(length=1000)
    | pdal.Filter.delaunay()
)
pl.execute()

conn = psycopg(%CONNNECTION_STRING%)
buffer = io.StringIO

for idx in range(len(pl.meshes)):
    m =  pl.get_meshio(idx)
    if m:
        m.write(buffer,  file_format = "wkt")
        with conn.cursor() as curr:
          curr.execute(
              "INSERT INTO %table-name% (mesh) VALUES (ST_GeomFromEWKT(%(ewkt)s)",
              { "ewkt": buffer.getvalue()}
          )

conn.commit()
conn.close()
buffer.close()
https://github.com/PDAL/python/workflows/Build/badge.svg

Requirements

  • PDAL 2.4+

  • Python >=3.7

  • Pybind11 (eg pip install pybind11[global])

  • Numpy (eg pip install numpy)

  • scikit-build (eg pip install scikit-build)

Changes

3.2.0

  • PDAL base library 2.4.0+ is required

  • CMake project name updated to pdal-python

  • srswkt2 property added to allow fetching of SRS info

  • pip builds require cmake >= 3.11

  • CMAKE_CXX_STANDARD set to c++17 to match PDAL 2.4.x

  • Driver and options actually uses the library instead of shelling out to pdal application :)

  • _get_json renamed to toJSON and made public

  • Fix #119, ‘json’ optional kwarg put back for now

  • DEVELOPMENT_COMPONENT in CMake FindPython skipped on OSX

  • Make sure ‘type’ gets set when serializing to JSON

3.1.0

3.0.0

2.3.5

2.3.0

  • PDAL Python support 2.3.0 requires PDAL 2.1+. Older PDAL base libraries likely will not work.

  • Python support built using scikit-build

  • readers.numpy and filters.python are installed along with the extension.

  • Pipeline can take in a list of arrays that are passed to readers.numpy

  • readers.numpy now supports functions that return arrays. See https://pdal.io/stages/readers.numpy.html for more detail.

2.0.0

  • PDAL Python extension is now in its own repository on its own release schedule at https://github.com/PDAL/python

  • Extension now builds and works under PDAL OSGeo4W64 on Windows.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pdal-3.2.0.tar.gz (238.4 kB view details)

Uploaded Source

Built Distributions

pdal-3.2.0-cp310-cp310-win_amd64.whl (146.6 kB view details)

Uploaded CPython 3.10 Windows x86-64

pdal-3.2.0-cp310-cp310-macosx_11_0_x86_64.whl (150.3 kB view details)

Uploaded CPython 3.10 macOS 11.0+ x86-64

pdal-3.2.0-cp39-cp39-win_amd64.whl (146.6 kB view details)

Uploaded CPython 3.9 Windows x86-64

pdal-3.2.0-cp39-cp39-macosx_11_0_x86_64.whl (150.4 kB view details)

Uploaded CPython 3.9 macOS 11.0+ x86-64

pdal-3.2.0-cp38-cp38-win_amd64.whl (146.4 kB view details)

Uploaded CPython 3.8 Windows x86-64

pdal-3.2.0-cp38-cp38-macosx_10_16_x86_64.whl (150.2 kB view details)

Uploaded CPython 3.8 macOS 10.16+ x86-64

pdal-3.2.0-cp37-cp37m-win_amd64.whl (147.0 kB view details)

Uploaded CPython 3.7m Windows x86-64

pdal-3.2.0-cp37-cp37m-macosx_10_16_x86_64.whl (149.0 kB view details)

Uploaded CPython 3.7m macOS 10.16+ x86-64

File details

Details for the file pdal-3.2.0.tar.gz.

File metadata

  • Download URL: pdal-3.2.0.tar.gz
  • Upload date:
  • Size: 238.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.14

File hashes

Hashes for pdal-3.2.0.tar.gz
Algorithm Hash digest
SHA256 68ac2a25a8e7fb2c0b119fb1de823f12a8c6359cc6948d9e8b773cc0bfd51104
MD5 09f4f5be76bc069c795ecf6d7fede279
BLAKE2b-256 9be836b69666074cf28efe75418b409dbdfdc6064de41e9a73e64b0c7aad22df

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: pdal-3.2.0-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 146.6 kB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.14

File hashes

Hashes for pdal-3.2.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 914fec797b58a8abbc6e8a6c06311091d6e84b5d2f877171e9a347a2d4455be0
MD5 28984202fe5dae3c016bd837d89ba946
BLAKE2b-256 875308bc46a6178e7572d69494ee43c2d3a27912fd970d8b445148ccd4b8265e

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp310-cp310-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for pdal-3.2.0-cp310-cp310-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 5b832292812134fe32f4779a1e7e902dff785439153912b44422f9cf3b40b5e6
MD5 96ebadc6fa301588ae00cd5685b06280
BLAKE2b-256 ac6db99ccc7e290d56238931a540c4d0083a9027f318a1e9845767a30c826298

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp39-cp39-win_amd64.whl.

File metadata

  • Download URL: pdal-3.2.0-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 146.6 kB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.14

File hashes

Hashes for pdal-3.2.0-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 489181c7d4defb0656a42f61c1339f83e58e001815b0ff9f199d7b02296cdef5
MD5 d01653e6efd459349425c61a9a1fa50b
BLAKE2b-256 8c9f80a6fb6dead13099d248b4cfc76f6676e050045ebfde6b3471a8040144d6

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp39-cp39-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for pdal-3.2.0-cp39-cp39-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 2f0d9f252201c1f025cdab8ba8121a110eab6771ec9f2056512ba5c993e0f867
MD5 ab9066400d984dce5fbe72a474a0b90c
BLAKE2b-256 69fb2919e77e5c584f32cac3e86419cd9c3e2872deb78c9fdfe0a27177766f3f

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: pdal-3.2.0-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 146.4 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.14

File hashes

Hashes for pdal-3.2.0-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 f80025d2f9a18342cc859d94fafe077710df290549c7547a69bc6677b044bcac
MD5 9c42fd73767c808bf4518c3f76c8d760
BLAKE2b-256 c6f94e0efa120291aca9d25120aa178638fc25d1717c2745cf9816bf63349394

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp38-cp38-macosx_10_16_x86_64.whl.

File metadata

File hashes

Hashes for pdal-3.2.0-cp38-cp38-macosx_10_16_x86_64.whl
Algorithm Hash digest
SHA256 1e5556f8a3f6310c1192ef5f6a5ee66f89668cd5ea0664d4e2b3dcdf0e8bd9fe
MD5 51efedc6d1d23865e64f7c0d9a943987
BLAKE2b-256 744509d626357bf45bc2710bf3715fe52b4aef9315e4f9859700ddd0419d3b59

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp37-cp37m-win_amd64.whl.

File metadata

  • Download URL: pdal-3.2.0-cp37-cp37m-win_amd64.whl
  • Upload date:
  • Size: 147.0 kB
  • Tags: CPython 3.7m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.14

File hashes

Hashes for pdal-3.2.0-cp37-cp37m-win_amd64.whl
Algorithm Hash digest
SHA256 37a7854d2433a8cdfc28f84c7d77f89a5f3c880e3977219547233982b2b12e93
MD5 bb93ae8f68f37cb373e0b838d692b6ed
BLAKE2b-256 c45b644530fd28cade9bc99a82e693e234e137b91e405117cd9c33781e768f58

See more details on using hashes here.

Provenance

File details

Details for the file pdal-3.2.0-cp37-cp37m-macosx_10_16_x86_64.whl.

File metadata

File hashes

Hashes for pdal-3.2.0-cp37-cp37m-macosx_10_16_x86_64.whl
Algorithm Hash digest
SHA256 34fa8c1bbd3a8d0280777bb33b4bfb7f1e496783ac4388a399d6a559f493d0ff
MD5 49ea2a73236aaadc736b5f59eb2f3b39
BLAKE2b-256 38c78c5e0d10a4891696005638efc826a83f8bf831e6640f3f2ae655640af5c4

See more details on using hashes here.

Provenance

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page