copc_streaming/lib.rs
1//! Async streaming reader for [COPC](https://copc.io/) (Cloud-Optimized Point Cloud) files.
2//!
3//! COPC organises LAS/LAZ point cloud data in a spatial octree so that clients can
4//! fetch only the regions they need. This crate reads COPC files incrementally through
5//! the [`ByteSource`] trait — any random-access backend (local files, HTTP range
6//! requests, in-memory buffers) works out of the box.
7//!
8//! # Quick start
9//!
10//! ```rust,ignore
11//! use copc_streaming::{Aabb, CopcStreamingReader, FileSource};
12//!
13//! let mut reader = CopcStreamingReader::open(
14//! FileSource::open("points.copc.laz")?,
15//! ).await?;
16//!
17//! // One call: loads hierarchy, fetches chunks, filters points by bounds.
18//! let points = reader.query_points(&my_query_box).await?;
19//! // each `point` has .x, .y, .z, .gps_time, .color, etc.
20//! ```
21//!
22//! # With LOD control
23//!
24//! ```rust,ignore
25//! // Load points with at most 0.5 m between samples.
26//! let level = reader.copc_info().level_for_resolution(0.5);
27//! let points = reader.query_points_to_level(&my_query_box, level).await?;
28//! ```
29//!
30//! # Low-level access
31//!
32//! For full control over hierarchy loading and chunk processing:
33//!
34//! ```rust,ignore
35//! let mut reader = CopcStreamingReader::open(
36//! FileSource::open("points.copc.laz")?,
37//! ).await?;
38//!
39//! let root_bounds = reader.copc_info().root_bounds();
40//! reader.load_hierarchy_for_bounds(&my_query_box).await?;
41//!
42//! for (key, entry) in reader.entries() {
43//! if entry.point_count == 0 { continue; }
44//! if !key.bounds(&root_bounds).intersects(&my_query_box) { continue; }
45//!
46//! let chunk = reader.fetch_chunk(key).await?;
47//! let points = reader.read_points_in_bounds(&chunk, &my_query_box)?;
48//! }
49//! ```
50//!
51//! # Hierarchy loading
52//!
53//! The COPC hierarchy is stored as a tree of *pages*. Each page contains metadata
54//! for a group of octree nodes (typically several levels deep) plus pointers to
55//! child pages covering deeper subtrees.
56//!
57//! [`CopcStreamingReader::open`] reads the LAS header, COPC info, and the **root
58//! hierarchy page**. This gives you the coarse octree nodes immediately (often
59//! levels 0–3, depending on the file). Any subtrees stored in separate pages
60//! are tracked as *pending pages* — they haven't been fetched yet.
61//!
62//! You then control when and which deeper pages are loaded:
63//!
64//! - [`load_hierarchy_for_bounds`](CopcStreamingReader::load_hierarchy_for_bounds) —
65//! load only pages whose subtree intersects a bounding box. Call this when the
66//! camera moves or a spatial query arrives.
67//! - [`load_hierarchy_for_bounds_to_level`](CopcStreamingReader::load_hierarchy_for_bounds_to_level) —
68//! same, but stops at a maximum octree level. Use with
69//! [`CopcInfo::level_for_resolution`] for LOD control.
70//! - [`load_pending_pages`](CopcStreamingReader::load_pending_pages) — fetch the
71//! next batch of pending pages (all of them). Useful when you don't need spatial
72//! filtering and just want to go one level deeper.
73//! - [`load_all_hierarchy`](CopcStreamingReader::load_all_hierarchy) — convenience
74//! to pull every remaining page in one go.
75//! - [`children`](CopcStreamingReader::children) — list loaded children of a node.
76//! Returns only children already in the cache; if deeper pages haven't been
77//! loaded yet this may return fewer than exist in the file.
78//! - [`has_pending_pages`](CopcStreamingReader::has_pending_pages) — check if there
79//! are still unloaded pages.
80//!
81//! # Custom byte sources
82//!
83//! Implement [`ByteSource`] to read from any backend. The trait requires only
84//! `read_range(offset, length)` and `size()`. A default `read_ranges` implementation
85//! issues sequential reads — override it for backends that support parallel fetches
86//! (e.g. HTTP/2 multiplexing).
87//!
88//! Built-in implementations: [`FileSource`] (local files), `Vec<u8>` and `&[u8]`
89//! (in-memory).
90//!
91//! Futures returned by `ByteSource` are *not* required to be `Send`, so the crate
92//! works in single-threaded runtimes and WASM environments.
93
94mod byte_source;
95mod chunk;
96mod error;
97mod file_source;
98mod header;
99mod hierarchy;
100mod reader;
101mod types;
102
103pub use byte_source::ByteSource;
104pub use chunk::{DecompressedChunk, fetch_and_decompress};
105pub use error::CopcError;
106pub use file_source::FileSource;
107pub use header::{CopcHeader, CopcInfo};
108pub use hierarchy::{HierarchyCache, HierarchyEntry};
109pub use reader::{CopcStreamingReader, filter_points_by_bounds};
110pub use types::{Aabb, VoxelKey};
111
112/// Re-export `las::Point` — the point type returned by all read methods.
113pub use las::Point;