copc-core 0.9.0

Shared COPC metadata, hierarchy, streaming point, and error types
Documentation

copc-rust

copc-core crates.io copc-core docs.rs copc-reader crates.io copc-reader docs.rs copc-writer crates.io copc-writer docs.rs

Pure-Rust COPC reader, writer, and shared core primitives for cloud-optimized point clouds. No C libraries, no build scripts; internal unsafe is limited to read-only memory mapping of writer spill files.

Crates

Crate Description
copc-core Shared COPC metadata, hierarchy entries, voxel keys, bounds, LAS-native column batches, streaming LAS records, and errors
copc-reader Strict COPC header/info parsing, iterative hierarchy access, chunked-LAZ row iteration, and materialized column reads
copc-writer COPC writer with source-trait point access, column-batch source support, native LOD distribution, mmap spill support, and streaming LAS/LAZ intake

Usage

use copc_reader::CopcFile;

let file = CopcFile::open("cloud.copc.laz")?;
for entry in file.hierarchy_walk() {
    println!("{:?} points={}", entry.key, entry.point_count);
}
use copc_reader::{BoundsSelection, CopcReader, LodSelection};

let mut reader = CopcReader::from_path("cloud.copc.laz")?;
for point in reader.points(LodSelection::All, BoundsSelection::All)? {
    let point = point?;
    println!("{},{},{}", point.x, point.y, point.z);
}
use copc_core::{ColumnData, LasDimension};
use copc_reader::{ColumnSelection, CopcReader, PointQuery};

let mut reader = CopcReader::from_path("cloud.copc.laz")?;
let batch = reader.read_columns(
    PointQuery::all(),
    ColumnSelection::from_dimensions([
        LasDimension::X,
        LasDimension::Y,
        LasDimension::Z,
        LasDimension::Classification,
    ]),
)?;

if let Some(ColumnData::F64(xs)) = batch.column(LasDimension::X) {
    println!("decoded {} x coordinates", xs.len());
}
use copc_writer::{convert_las_to_copc_streaming, CopcWriterParams};

convert_las_to_copc_streaming(
    "input.laz".as_ref(),
    "output.copc.laz".as_ref(),
    &CopcWriterParams::default(),
    std::env::temp_dir().as_ref(),
    &copc_core::NeverCancel,
)?;

For GeoTIFF-only CRS inputs, resolve CRS externally and call convert_las_to_copc_streaming_with_crs_wkt_override with Some(wkt); the writer emits the supplied WKT CRS VLR without depending on a geodesy library.

Generated files carry caller metadata through CopcWriteMetadata (WKT CRS, GUID, identifiers, creation date, GPS time type, scale/offset overrides):

use copc_writer::{write_source, CopcWriteMetadata, CopcWriterParams};

let mut metadata = CopcWriteMetadata::default();
metadata.wkt_crs = Some(wgs84_wkt.to_string());
write_source(&path, &source, false, bounds, &CopcWriterParams::default(), &metadata)?;

Remote and partial reads go through CopcRangeReader, which fetches only the header and VLRs at open, loads hierarchy pages lazily as queries reach them, and coalesces adjacent chunk fetches into single range requests:

use copc_reader::{ColumnSelection, CopcRangeReader, HttpRangeReader, PointQuery};

// Requires the `http` feature (plus `http-tls` for HTTPS URLs).
let source = HttpRangeReader::new("http://example.com/cloud.copc.laz");
let mut reader = CopcRangeReader::open(source)?;
let batch = reader.read_columns(PointQuery::all(), ColumnSelection::xyz())?;

Any byte-range source works by implementing the RangeRead trait; std::fs::File implements it out of the box.

Feature Flags

All features are off by default, keeping the crates dependency-light.

Crate Feature Effect
copc-writer parallel rayon-parallel LAZ chunk compression (one octree node per chunk, written in order with a standard chunk table)
copc-reader parallel rayon-parallel chunk decoding for read_columns
copc-reader http HttpRangeReader over HTTP Range requests (plain HTTP)
copc-reader http-tls enables ureq/rustls so HttpRangeReader can fetch HTTPS URLs

Column Ownership Model

copc-core owns the LAS/COPC-native column model: LasDimension, ColumnSpec, ColumnData, ColumnView, ColumnSelection, and LasColumnBatch. These types are dependency-light and do not depend on Arrow, DataFusion, or engine-specific point-cloud crates.

copc-reader exposes materialized column batches with CopcReader::read_columns and CopcReader::read_columns_with_cancel. Existing row iteration with points, points_for_query, and points_with_cancel remains supported.

The column API is materialized. COPC point data is still read from compressed LAZ chunks, decoded, filtered, transformed, and appended into owned column buffers. It is not a zero-copy view into compressed COPC files.

Downstream engines should adapt LasColumnBatch into their own canonical memory model. For example, roteiro-engine maps these native batches into its PointCloud struct-of-arrays representation. Arrow conversion is intentionally out of scope for copc-rust today; it belongs in downstream engine code or behind a future optional feature.

Supported Now

  • Public COPC hierarchy types for availability, indexing, and tile serving
  • COPC info VLR and stack-safe iterative hierarchy page parsing
  • Chunked-LAZ point iteration in copc-reader
  • All-points, LOD-selected, and bounds-selected reader point iteration
  • Materialized LAS/COPC-native column batches in copc-reader
  • Source-trait writer API for caller-owned point storage
  • COPC writing from neutral LasColumnBatch values via ColumnBatchSource
  • Streaming LAS/LAZ-to-COPC conversion through a disk-backed mmap spill
  • Streaming conversion preserves WKT CRS records, LAS Extra Bytes payloads and descriptors, and source non-CRS VLRs/EVLRs
  • Streaming conversion can accept caller-resolved WKT for GeoTIFF-only CRS inputs without adding a geodesy dependency to copc-writer
  • Reading LAS 1.4 point formats 6, 7, and 8 with LAZ variable-size chunks
  • Writing LAS 1.4 point formats 6 and 7 with LAZ variable-size chunks
  • Interior-node representative points for native LOD reads
  • Lazy remote reads over byte-range sources (CopcRangeReader, RangeRead, optional HTTP source) with on-demand hierarchy loading and coalesced chunk fetches
  • Optional rayon parallelism for writer chunk compression and reader column decoding
  • Caller-supplied output metadata (WKT CRS, GUID, identifiers, creation date) for generated COPC files, which always carry the LAS 1.4 WKT global-encoding bit and a WKT CRS VLR

Not Yet Supported

  • Zero-copy column views directly over compressed COPC/LAZ point data
  • Built-in Arrow or DataFusion conversion
  • Built-in GeoTIFF-only CRS conversion to WKT during LAS/LAZ-to-COPC conversion

Testing

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --locked
cargo test --workspace --all-features --locked

Checked-in external COPC fixtures from PDAL and QGIS are exercised by:

cargo test -p copc-reader --test external_fixtures

License

MIT OR Apache-2.0