CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-laspy

Native Python ASPRS LAS read/write library for processing LiDAR point cloud data

Pending
Overview
Eval results
Files

point-data.mddocs/

Point Data Handling

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.

Capabilities

Point Format Management

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")

Extra Dimension Parameters

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"
)

Dimension Information

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]: ...

Packed Point Records

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 setting

Usage 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 points

Scale-Aware Point Records

Point 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)

Array Views

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."""

Point Format Conversion

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)

Validation

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 name

Install with Tessl CLI

npx tessl i tessl/pypi-laspy

docs

compression.md

copc.md

core-io.md

data-containers.md

index.md

io-handlers.md

point-data.md

vlr.md

tile.json