laspy
Laspy is a native Python library for reading, modifying, and creating ASPRS LAS and LAZ (compressed) LIDAR files, supporting specifications 1.0 through 1.5. It provides a Pythonic API via NumPy arrays for efficient point cloud data manipulation. The library is actively maintained with frequent releases, typically several times a year.
Warnings
- breaking laspy 2.0.0 introduced a significant API overhaul. The `laspy.file.File` class and its `open()` method were replaced by `laspy.read()` and `laspy.open()` returning `LasData` objects or readers/writers. Direct `get_*`/`set_*` methods on file objects were removed. The LAZ backend shifted from `lazperf` to `laszip-python` bindings or `lazrs`. Python 2.7 support was dropped.
- gotcha When accessing point dimensions, `laspy` differentiates between scaled float values (lowercase attributes like `las.x`, `las.y`, `las.z`) and raw integer values (uppercase attributes like `las.X`, `las.Y`, `las.Z`). While both support assignment, assigning to scaled dimensions can introduce rounding errors.
- gotcha The command-line interface (CLI) is an optional feature and requires additional dependencies (`rich` and `typer`). It must be installed using the `[cli]` extra.
- breaking Support for LAS 1.5 specification and Python 3.14 was added in version 2.7.0. Users relying on LAZ compression/decompression for these new features will need to ensure `laszip-python` and/or `lazrs` are updated to compatible versions.
- gotcha When creating a new LAS file from scratch, it is crucial to properly initialize the `LasHeader` with appropriate `offsets` and `scales`. If not specified, default values may not be suitable for your data, or some software might not read the file correctly.
Install
-
pip install laspy -
pip install laspy[lazrs] -
pip install laspy[laszip] -
pip install laspy[lazrs,laszip] -
pip install laspy[cli]
Imports
- laspy
import laspy
- File
laspy.read('file.las')
Quickstart
import laspy
import numpy as np
import os
# Create a dummy LAS file for demonstration
header = laspy.LasHeader(point_format=3, version="1.4")
header.offsets = np.array([0, 0, 0])
header.scales = np.array([0.01, 0.01, 0.01])
las_data = laspy.LasData(header)
n_points = 100
las_data.x = np.random.rand(n_points) * 100
las_data.y = np.random.rand(n_points) * 100
las_data.z = np.random.rand(n_points) * 50
las_data.classification = np.random.randint(0, 5, n_points)
output_filename = "output.las"
las_data.write(output_filename)
print(f"Created {output_filename} with {len(las_data.points)} points.")
# Read a LAS/LAZ file
try:
with laspy.open(output_filename) as fh:
print(f"Reading {fh.header.point_count} points from {output_filename}")
las = fh.read()
# Access point data (scaled and raw)
print(f"X (scaled): {las.x[:5]}") # Scaled coordinates
print(f"X (raw): {las.X[:5]}") # Raw integer coordinates
print(f"Classification: {las.classification[:5]}")
# Access header information
print(f"LAS Version: {las.header.version}")
print(f"Point Format ID: {las.header.point_format.id}")
print(f"Offset: {las.header.offsets}")
print(f"Scale: {las.header.scales}")
# Filter points (e.g., classification == 2)
ground_points = las.points[las.classification == 2]
print(f"Number of ground points (classification=2): {len(ground_points)}")
# Create a new LAS file with filtered points
new_las = laspy.create(point_format=las.header.point_format, file_version=las.header.version)
new_las.points = ground_points
filtered_output_filename = "filtered_output.las"
new_las.write(filtered_output_filename)
print(f"Created {filtered_output_filename} with {len(new_las.points)} filtered points.")
finally:
# Clean up dummy files
if os.path.exists(output_filename):
os.remove(output_filename)
if os.path.exists(filtered_output_filename):
os.remove(filtered_output_filename)