Skip to content

io

occulus.io

Point cloud I/O — read and write LAS, LAZ, PLY, PCD, and XYZ formats.

Primary interface:

from occulus.io import read, write

cloud = read("scan.laz")
write(cloud, "output.las")

Delegates to format-specific backends. LAS/LAZ via laspy, PLY/PCD via Open3D (optional), XYZ via NumPy.

read(path, *, platform=Platform.UNKNOWN, subsample=None)

Read a point cloud file and return the appropriate PointCloud subtype.

Parameters:

Name Type Description Default
path str | Path

Path to the point cloud file. Format is inferred from the extension.

required
platform Platform | str

Acquisition platform hint. When provided, returns the appropriate subtype (AerialCloud, TerrestrialCloud, UAVCloud). Defaults to Platform.UNKNOWN which returns the base PointCloud.

UNKNOWN
subsample float | None

If provided, randomly subsample to this fraction of points (0.0–1.0) after reading. Useful for quickly exploring large files.

None

Returns:

Type Description
PointCloud

The loaded point cloud (or platform-specific subtype).

Raises:

Type Description
OcculusIOError

If the file does not exist or cannot be read.

OcculusValidationError

If the file extension is not supported or subsample is out of range.

Source code in src/occulus/io/readers.py
def read(
    path: str | Path,
    *,
    platform: Platform | str = Platform.UNKNOWN,
    subsample: float | None = None,
) -> PointCloud:
    """Read a point cloud file and return the appropriate PointCloud subtype.

    Parameters
    ----------
    path : str | Path
        Path to the point cloud file. Format is inferred from the extension.
    platform : Platform | str, optional
        Acquisition platform hint. When provided, returns the appropriate
        subtype (AerialCloud, TerrestrialCloud, UAVCloud). Defaults to
        ``Platform.UNKNOWN`` which returns the base PointCloud.
    subsample : float | None, optional
        If provided, randomly subsample to this fraction of points (0.0–1.0)
        after reading. Useful for quickly exploring large files.

    Returns
    -------
    PointCloud
        The loaded point cloud (or platform-specific subtype).

    Raises
    ------
    OcculusIOError
        If the file does not exist or cannot be read.
    OcculusValidationError
        If the file extension is not supported or ``subsample`` is out of range.
    """
    path = Path(path)
    if not path.exists():
        raise OcculusIOError(f"File not found: {path}")

    if subsample is not None and not (0.0 < subsample <= 1.0):
        raise OcculusValidationError(f"subsample must be in (0.0, 1.0], got {subsample}")

    ext = path.suffix.lower()
    if ext not in _SUPPORTED_EXTENSIONS:
        raise OcculusValidationError(
            f"Unsupported format '{ext}'. Supported: {sorted(_SUPPORTED_EXTENSIONS)}"
        )

    logger.debug("Reading %s (platform=%s)", path, platform)

    if ext in (".las", ".laz"):
        return _read_las(path, platform=platform, subsample=subsample)
    elif ext == ".ply":
        return _read_ply(path, platform=platform, subsample=subsample)
    elif ext == ".pcd":
        return _read_pcd(path, platform=platform, subsample=subsample)
    else:  # .xyz, .txt, .csv
        return _read_xyz(path, platform=platform, subsample=subsample)

write(cloud, path, *, compress=None)

Write a point cloud to file.

Parameters:

Name Type Description Default
cloud PointCloud

The point cloud to write. Must have a valid xyz array.

required
path str | Path

Output file path. Format is inferred from the extension.

required
compress bool | None

For LAS output, whether to compress to LAZ format. If None, compression is inferred from the extension (.laz → compressed).

None

Returns:

Type Description
Path

The resolved path of the written file.

Raises:

Type Description
OcculusIOError

If the file cannot be written.

OcculusValidationError

If the format is not supported.

Source code in src/occulus/io/writers.py
def write(
    cloud: PointCloud,
    path: str | Path,
    *,
    compress: bool | None = None,
) -> Path:
    """Write a point cloud to file.

    Parameters
    ----------
    cloud : PointCloud
        The point cloud to write. Must have a valid ``xyz`` array.
    path : str | Path
        Output file path. Format is inferred from the extension.
    compress : bool | None, optional
        For LAS output, whether to compress to LAZ format. If ``None``,
        compression is inferred from the extension (``.laz`` → compressed).

    Returns
    -------
    Path
        The resolved path of the written file.

    Raises
    ------
    OcculusIOError
        If the file cannot be written.
    OcculusValidationError
        If the format is not supported.
    """
    path = Path(path)
    ext = path.suffix.lower()

    if ext not in _SUPPORTED_EXTENSIONS:
        raise OcculusValidationError(
            f"Unsupported output format '{ext}'. Supported: {sorted(_SUPPORTED_EXTENSIONS)}"
        )

    logger.debug("Writing %d points to %s", cloud.n_points, path)

    if ext in (".las", ".laz"):
        compressed = compress if compress is not None else (ext == ".laz")
        return _write_las(cloud, path, compress=compressed)
    elif ext == ".ply":
        return _write_ply(cloud, path)
    else:  # .xyz, .txt, .csv
        return _write_xyz(cloud, path)