Skip to main content

copc_streaming/
chunk.rs

1//! Point chunk fetching and LAZ decompression.
2
3use std::io::Cursor;
4
5use laz::LazVlr;
6
7use crate::byte_source::ByteSource;
8use crate::error::CopcError;
9use crate::hierarchy::HierarchyEntry;
10use crate::types::VoxelKey;
11
12/// A decompressed point data chunk.
13#[non_exhaustive]
14pub struct DecompressedChunk {
15    /// The octree node this chunk belongs to.
16    pub key: VoxelKey,
17    /// Raw decompressed point record bytes.
18    pub data: Vec<u8>,
19    /// Number of points in this chunk.
20    pub point_count: u32,
21    /// Size of a single point record in bytes.
22    pub point_record_length: u16,
23}
24
25/// Fetch and decompress a single chunk.
26pub async fn fetch_and_decompress(
27    source: &impl ByteSource,
28    entry: &HierarchyEntry,
29    laz_vlr: &LazVlr,
30    point_record_length: u16,
31) -> Result<DecompressedChunk, CopcError> {
32    let compressed = source
33        .read_range(entry.offset, entry.byte_size as u64)
34        .await?;
35
36    let decompressed_size = entry.point_count as usize * point_record_length as usize;
37    let mut decompressed = vec![0u8; decompressed_size];
38
39    laz::decompress_buffer(&compressed, &mut decompressed, laz_vlr.clone())?;
40
41    Ok(DecompressedChunk {
42        key: entry.key,
43        data: decompressed,
44        point_count: entry.point_count,
45        point_record_length,
46    })
47}
48
49/// Parse all points from a decompressed chunk into `las::Point` values.
50pub fn read_points(
51    chunk: &DecompressedChunk,
52    header: &las::Header,
53) -> Result<Vec<las::Point>, CopcError> {
54    read_points_range(chunk, header, 0..chunk.point_count)
55}
56
57/// Parse a sub-range of points from a decompressed chunk.
58///
59/// Only the points in `range` are parsed — bytes outside the range are skipped.
60/// Returns an error if the range extends beyond the chunk's point count.
61pub fn read_points_range(
62    chunk: &DecompressedChunk,
63    header: &las::Header,
64    range: std::ops::Range<u32>,
65) -> Result<Vec<las::Point>, CopcError> {
66    if range.end > chunk.point_count {
67        return Err(CopcError::Io(std::io::Error::new(
68            std::io::ErrorKind::InvalidInput,
69            format!(
70                "point range {}..{} exceeds chunk point count {}",
71                range.start, range.end, chunk.point_count
72            ),
73        )));
74    }
75
76    let format = header.point_format();
77    let transforms = header.transforms();
78    let record_len = chunk.point_record_length as u64;
79
80    let start = (range.start as u64 * record_len) as usize;
81    let count = range.end.saturating_sub(range.start) as usize;
82
83    let mut cursor = Cursor::new(&chunk.data[start..]);
84    let mut points = Vec::with_capacity(count);
85
86    for _ in 0..count {
87        let raw = las::raw::Point::read_from(&mut cursor, format)?;
88        points.push(las::Point::new(raw, transforms));
89    }
90
91    Ok(points)
92}