Native Python ASPRS LAS read/write library for processing LiDAR point cloud data
—
Comprehensive point format management including standard LAS point formats, custom extra dimensions, and efficient point record processing with coordinate scaling. This module provides the foundation for all point data operations in laspy.
Define and manage LAS point formats including standard formats and custom extra dimensions.
class PointFormat:
def __init__(self, point_format_id: int):
"""
Create point format from standard LAS point format ID.
Parameters:
- point_format_id: int - Standard LAS point format (0-10)
"""
@property
def id(self) -> int: ...
@property
def dimensions(self) -> List[DimensionInfo]: ...
@property
def standard_dimensions(self) -> Iterable[DimensionInfo]: ...
@property
def extra_dimensions(self) -> Iterable[DimensionInfo]: ...
@property
def size(self) -> int: ... # Total point size in bytes
@property
def num_standard_bytes(self) -> int: ...
@property
def num_extra_bytes(self) -> int: ...
@property
def has_waveform_packet(self) -> bool: ...
def dimension_by_name(self, name: str) -> DimensionInfo:
"""Get dimension info by name."""
def add_extra_dimension(self, params: ExtraBytesParams):
"""Add custom extra dimension to point format."""
def remove_extra_dimension(self, name: str):
"""Remove extra dimension by name."""
def dtype(self) -> np.dtype:
"""Get NumPy dtype for this point format."""Usage Example:
import laspy
from laspy import PointFormat, ExtraBytesParams
# Create standard point format
fmt = PointFormat(3) # Point format 3 (X,Y,Z,Intensity,Returns,Classification,etc.)
print(f"Format {fmt.id} has {len(fmt.dimensions)} dimensions")
print(f"Standard size: {fmt.num_standard_bytes} bytes")
# Add custom extra dimension
extra_param = ExtraBytesParams(
name="temperature",
type="f4", # 32-bit float
description="Temperature in Celsius"
)
fmt.add_extra_dimension(extra_param)
print(f"With extra dims: {fmt.size} bytes total")
# Get dimension info
temp_dim = fmt.dimension_by_name("temperature")
print(f"Temperature dimension: {temp_dim.kind}, {temp_dim.num_bits} bits")Define custom extra dimensions with proper typing, scaling, and metadata.
class ExtraBytesParams:
def __init__(self, name: str, type, description="", offsets=None, scales=None, no_data=None):
"""
Define parameters for extra bytes dimension.
Parameters:
- name: str - Dimension name
- type: str, np.dtype, or type - Data type ('u1', 'f4', np.uint8, etc.)
- description: str - Human-readable description
- offsets: array-like - Offset values for scaling
- scales: array-like - Scale factors
- no_data: array-like - No-data sentinel values
"""
@property
def name(self) -> str: ...
@property
def type(self) -> np.dtype: ...
@property
def description(self) -> str: ...
@property
def offsets(self) -> Optional[np.ndarray]: ...
@property
def scales(self) -> Optional[np.ndarray]: ...
@property
def no_data(self) -> Optional[np.ndarray]: ...Usage Examples:
import numpy as np
from laspy import ExtraBytesParams
# Simple integer field
intensity_extra = ExtraBytesParams(
name="intensity_corrected",
type="u2", # 16-bit unsigned int
description="Radiometrically corrected intensity"
)
# Scaled float field
temperature = ExtraBytesParams(
name="temperature",
type="u1", # Store as uint8 to save space
description="Temperature in Celsius",
scales=np.array([0.1]), # Scale factor: stored_value * 0.1 = actual_value
offsets=np.array([-20.0]), # Offset: (stored_value * scale) + offset = actual_value
no_data=np.array([255]) # 255 = no data
)
# Multi-element field (RGB)
rgb_extra = ExtraBytesParams(
name="rgb_corrected",
type="3u1", # 3 uint8 values
description="Color-corrected RGB values"
)Detailed metadata about point dimensions including data types, scaling, and validation.
class DimensionInfo:
name: str
kind: DimensionKind
num_bits: int
num_elements: int
is_standard: bool
description: str
offsets: Optional[np.ndarray]
scales: Optional[np.ndarray]
no_data: Optional[np.ndarray]
@property
def num_bytes(self) -> int: ...
@property
def num_bytes_singular_element(self) -> int: ...
@property
def is_scaled(self) -> bool: ...
@property
def dtype(self) -> Optional[np.dtype]: ...
@property
def max(self): ...
@property
def min(self): ...
def type_str(self) -> Optional[str]: ...
@classmethod
def from_extra_bytes_param(cls, params: ExtraBytesParams) -> DimensionInfo: ...
@classmethod
def from_dtype(cls, name: str, dtype: np.dtype, is_standard=True, description="", offsets=None, scales=None) -> DimensionInfo: ...
@classmethod
def from_bitmask(cls, name: str, bit_mask: int, is_standard=False) -> DimensionInfo: ...
class DimensionKind(Enum):
SignedInteger = 0
UnsignedInteger = 1
FloatingPoint = 2
BitField = 3
@classmethod
def from_letter(cls, letter: str) -> DimensionKind: ...
def letter(self) -> Optional[str]: ...Raw packed point data containers providing direct access to binary point data.
class PackedPointRecord:
def __init__(self, data: np.ndarray, point_format: PointFormat):
"""
Create packed point record from raw data array.
Parameters:
- data: np.ndarray - Raw point data
- point_format: PointFormat - Point format definition
"""
@property
def point_size(self) -> int: ...
@property
def array(self) -> np.ndarray: ...
@property
def point_format(self) -> PointFormat: ...
@staticmethod
def zeros(point_count, point_format) -> PackedPointRecord:
"""Create zero-initialized point record."""
@staticmethod
def empty(point_format) -> PackedPointRecord:
"""Create empty point record."""
@classmethod
def from_point_record(cls, other_point_record, new_point_format) -> PackedPointRecord:
"""Create from existing point record with format conversion."""
@classmethod
def from_buffer(cls, buffer, point_format, count=-1, offset=0) -> PackedPointRecord:
"""Create from memory buffer."""
def copy_fields_from(self, other_record: PackedPointRecord):
"""Copy compatible fields from another record."""
def copy(self) -> PackedPointRecord:
"""Create deep copy."""
def memoryview(self) -> memoryview:
"""Get memory view of underlying data."""
def resize(self, new_size: int):
"""Resize point record."""
def validate_dimension_name(self, key: str) -> DimensionNameValidity:
"""Validate dimension name."""
def __len__(self) -> int: ...
def __getitem__(self, key): ... # Access dimension by name or index
def __setitem__(self, key, value): ... # Set dimension values
def __getattr__(self, name): ... # Direct attribute access to dimensions
def __setattr__(self, name, value): ... # Direct attribute settingUsage Example:
import laspy
import numpy as np
# Create point format and empty record
point_format = laspy.PointFormat(3)
points = laspy.PackedPointRecord.zeros(1000, point_format)
# Set point data
points.x = np.random.uniform(0, 100, 1000).astype(np.int32)
points.y = np.random.uniform(0, 100, 1000).astype(np.int32)
points.z = np.random.uniform(0, 10, 1000).astype(np.int32)
points.classification = np.full(1000, 2, dtype=np.uint8) # Ground class
# Access data
print(f"Point count: {len(points)}")
print(f"X range: {points.x.min()} to {points.x.max()}")
# Copy and modify
subset = points.copy()
subset.resize(100) # Keep only first 100 pointsPoint records with automatic coordinate scaling and transformation support.
class ScaleAwarePointRecord(PackedPointRecord):
def __init__(self, array, point_format, scales, offsets):
"""
Create scale-aware point record with coordinate transformation.
Parameters:
- array: np.ndarray - Raw point data
- point_format: PointFormat - Point format definition
- scales: np.ndarray - Scale factors for X,Y,Z coordinates
- offsets: np.ndarray - Offset values for X,Y,Z coordinates
"""
@property
def scales(self) -> np.ndarray: ...
@property
def offsets(self) -> np.ndarray: ...
@staticmethod
def zeros(point_count, *, point_format=None, scales=None, offsets=None, header=None) -> ScaleAwarePointRecord:
"""Create zero-initialized scaled point record."""
@staticmethod
def empty(point_format=None, scales=None, offsets=None, header=None) -> ScaleAwarePointRecord:
"""Create empty scaled point record."""
def change_scaling(self, scales=None, offsets=None):
"""Update coordinate scaling parameters."""Usage Example:
import laspy
import numpy as np
# Create with coordinate scaling
scales = np.array([0.01, 0.01, 0.001]) # 1cm XY, 1mm Z precision
offsets = np.array([500000, 4000000, 0]) # UTM-like offsets
points = laspy.ScaleAwarePointRecord.zeros(
1000,
point_format=laspy.PointFormat(3),
scales=scales,
offsets=offsets
)
# Set scaled coordinates (automatically converted to/from raw integers)
points.x = np.random.uniform(500000, 501000, 1000) # Real-world coordinates
points.y = np.random.uniform(4000000, 4001000, 1000)
points.z = np.random.uniform(100, 200, 1000)
# Access scaled values (real-world coordinates)
print(f"Real X range: {points.x.min():.2f} to {points.x.max():.2f}")
# Change scaling if needed
new_scales = np.array([0.001, 0.001, 0.0001]) # Higher precision
points.change_scaling(scales=new_scales)Specialized array views for scaled coordinates and bit field access.
class ArrayView:
"""Abstract base class for specialized array views."""
def __array__(self): ...
def __getitem__(self, key): ...
def __setitem__(self, key, value): ...
def copy(self) -> np.ndarray: ...
@property
def dtype(self): ...
@property
def shape(self): ...
@property
def ndim(self): ...
class ScaledArrayView(ArrayView):
def __init__(self, array: np.ndarray, scale, offset):
"""
Array view with automatic scaling/offset transformation.
Parameters:
- array: np.ndarray - Raw integer array
- scale: float or np.ndarray - Scale factor(s)
- offset: float or np.ndarray - Offset value(s)
"""
def scaled_array(self):
"""Get scaled floating-point array."""
class SubFieldView(ArrayView):
def __init__(self, array: np.ndarray, bit_mask):
"""
Array view for bit field access.
Parameters:
- array: np.ndarray - Raw array containing bit fields
- bit_mask: int - Bit mask for field extraction
"""
def masked_array(self):
"""Get underlying masked array."""Utilities for converting between point formats and checking compatibility.
def lost_dimensions(point_fmt_in, point_fmt_out) -> List[str]:
"""
Get list of dimension names that will be lost in format conversion.
Parameters:
- point_fmt_in: PointFormat - Source point format
- point_fmt_out: PointFormat - Target point format
Returns:
List[str]: Names of dimensions that will be lost
"""Usage Example:
import laspy
from laspy.point.format import lost_dimensions
# Check what gets lost converting from format 6 to format 3
fmt_6 = laspy.PointFormat(6) # Has GPS time, RGB
fmt_3 = laspy.PointFormat(3) # Basic format
lost = lost_dimensions(fmt_6, fmt_3)
print(f"Converting 6->3 loses: {lost}") # ['gps_time', 'red', 'green', 'blue']
# Check reverse conversion
lost_reverse = lost_dimensions(fmt_3, fmt_6)
print(f"Converting 3->6 loses: {lost_reverse}") # [] (no data lost, new fields get defaults)Dimension name validation for safe attribute access.
class DimensionNameValidity(Enum):
Valid = ... # Valid dimension name
Unsupported = ... # Valid but not supported in this format
Invalid = ... # Invalid dimension nameInstall with Tessl CLI
npx tessl i tessl/pypi-laspy