Skip to main content

copc_temporal/
lib.rs

1//! Reader for the [COPC Temporal Index Extension](https://github.com/360-geo/copc/blob/master/copc-temporal/docs/temporal-index-spec.md).
2//!
3//! When a COPC file contains data from multiple survey passes over the same area,
4//! a spatial query alone returns points from *every* pass that touched that region.
5//! The temporal index extension adds per-node GPS time metadata so that clients can
6//! filter by time **before** decompressing any point data.
7//!
8//! This crate reads the temporal index incrementally via [`ByteSource`], matching
9//! the streaming design of [`copc_streaming`].
10//!
11//! # Quick start
12//!
13//! ```rust,ignore
14//! use copc_streaming::{CopcStreamingReader, FileSource};
15//! use copc_temporal::{GpsTime, TemporalCache};
16//!
17//! let mut reader = CopcStreamingReader::open(
18//!     FileSource::open("survey.copc.laz")?,
19//! ).await?;
20//! reader.load_all_hierarchy().await?;
21//!
22//! // Load the temporal index (returns None if the file has no temporal EVLR).
23//! // from_reader loads only the header and root page — not the entire index.
24//! let mut temporal = match TemporalCache::from_reader(&reader).await? {
25//!     Some(t) => t,
26//!     None => return Ok(()), // no temporal index in this file
27//! };
28//!
29//! let start = GpsTime(1_000_000.0);
30//! let end   = GpsTime(1_000_010.0);
31//! let root_bounds = reader.copc_info().root_bounds();
32//!
33//! // Query loads only the temporal pages that overlap the time range,
34//! // then returns matching nodes. Pages outside the range are never fetched.
35//! for entry in temporal.query(reader.source(), start, end).await? {
36//!     let hier = reader.get(&entry.key).unwrap();
37//!     if !entry.key.bounds(&root_bounds).intersects(&my_query_box) { continue; }
38//!
39//!     // Estimate which points fall in the time window, then read only those.
40//!     let range = entry.estimate_point_range(
41//!         start, end, temporal.stride(), hier.point_count,
42//!     );
43//!     let chunk = reader.fetch_chunk(&entry.key).await?;
44//!     let points = reader.read_points_range(&chunk, range)?;
45//! }
46//! ```
47//!
48//! # Incremental page loading
49//!
50//! # How it works
51//!
52//! [`TemporalCache::from_reader`] loads the header and root page.
53//! [`TemporalCache::query`] then loads only the pages whose subtree time bounds
54//! overlap the requested range and returns matching nodes — pages outside the
55//! range are never fetched.
56//!
57//! For advanced use cases you can call [`TemporalCache::load_pages_for_time_range`]
58//! and [`TemporalCache::nodes_in_range`] separately, or
59//! [`TemporalCache::load_all_pages`] to fetch the entire index at once.
60
61mod error;
62mod gps_time;
63mod temporal_cache;
64mod temporal_index;
65mod vlr;
66
67pub use error::TemporalError;
68pub use gps_time::GpsTime;
69pub use temporal_cache::{TemporalCache, TemporalHeader};
70pub use temporal_index::NodeTemporalEntry;
71
72// Re-export copc-streaming types that temporal consumers will need.
73pub use copc_streaming::{Aabb, ByteSource, VoxelKey};