copc_rs/
reader.rs

1//! COPC file reader.
2
3use crate::copc::{CopcInfo, Entry, HierarchyPage, OctreeNode, VoxelKey};
4use crate::decompressor::CopcDecompressor;
5use las::raw;
6use las::{Bounds, Builder, Header, Transform, Vector, Vlr};
7use laz::LazVlr;
8use std::cmp::Ordering;
9use std::collections::HashMap;
10use std::fs::File;
11use std::io::{BufReader, Cursor, Read, Seek, SeekFrom};
12use std::path::Path;
13
14/// COPC file reader
15pub struct CopcReader<R> {
16    // the start position of the data of interest in the read, most often 0
17    start: u64,
18    // the read- and seekable data source, seeked to the beginning of the copc file data
19    read: R,
20    header: Header,
21    copc_info: CopcInfo,
22    laz_vlr: LazVlr,
23    /// Entries of loaded hierarchy pages
24    hierarchy_entries: HashMap<VoxelKey, Entry>,
25}
26
27impl CopcReader<BufReader<File>> {
28    /// Read a COPC file from a path, wraps the file in a BufRead for you
29    pub fn from_path<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
30        File::open(path)
31            .map_err(crate::Error::from)
32            .and_then(|file| CopcReader::new(BufReader::new(file)))
33    }
34}
35
36impl<R: Read + Seek> CopcReader<R> {
37    /// Setup by reading LAS header and LasZip VLRs
38    pub fn new(mut read: R) -> crate::Result<Self> {
39        // to be able to read a copc file not starting at the beginning of the read stream
40        let start = read.stream_position()?;
41
42        let raw_header = raw::Header::read_from(&mut read)?;
43
44        // store useful parts of the raw header before its consumed by the builder
45        let mut position = raw_header.header_size as u64;
46        let number_of_variable_length_records = raw_header.number_of_variable_length_records;
47        let offset_to_point_data = raw_header.offset_to_point_data as u64;
48        let evlr = raw_header.evlr;
49
50        // start building a header from a raw header
51        let mut builder = Builder::new(raw_header)?;
52
53        // add the vlrs to the builder
54        for _ in 0..number_of_variable_length_records {
55            let vlr = raw::Vlr::read_from(&mut read, false).map(Vlr::new)?;
56            position += vlr.len(false) as u64;
57            builder.vlrs.push(vlr);
58        }
59
60        // adjust read pointer position and add the padding if it exists
61        match position.cmp(&offset_to_point_data) {
62            Ordering::Less => {
63                let _ = read
64                    .by_ref()
65                    .take(offset_to_point_data + start - position)
66                    .read_to_end(&mut builder.vlr_padding)?;
67            }
68            Ordering::Equal => {} // pass
69            Ordering::Greater => Err(las::Error::OffsetToPointDataTooSmall(
70                offset_to_point_data as u32,
71            ))?,
72        }
73
74        // add the evlrs to the builder
75        if let Some(evlr) = evlr {
76            let _ = read.seek(SeekFrom::Start(evlr.start_of_first_evlr + start))?;
77            for _ in 0..evlr.number_of_evlrs {
78                builder
79                    .evlrs
80                    .push(raw::Vlr::read_from(&mut read, true).map(Vlr::new)?);
81            }
82        }
83
84        // build the header
85        let header = builder.into_header()?;
86
87        // check and store the relevant (e)vlrs
88        let mut copc_info = None;
89        let mut laszip_vlr = None;
90        let mut ept_hierarchy = None;
91
92        for vlr in header.all_vlrs() {
93            match (vlr.user_id.to_lowercase().as_str(), vlr.record_id) {
94                ("copc", 1) => {
95                    copc_info = Some(CopcInfo::read_from(vlr.data.as_slice())?);
96                }
97                ("copc", 1000) => {
98                    ept_hierarchy = Some(vlr);
99                }
100                ("laszip encoded", 22204) => {
101                    laszip_vlr = Some(LazVlr::read_from(vlr.data.as_slice())?);
102                }
103                _ => (),
104            }
105        }
106
107        let copc_info = copc_info.ok_or(crate::Error::CopcInfoVlrNotFound)?;
108
109        // store all ept-hierarchy entries in a hashmap
110        let hierarchy_entries = match ept_hierarchy {
111            None => return Err(crate::Error::EptHierarchyVlrNotFound),
112            Some(vlr) => {
113                let mut hierarchy_entries = HashMap::new();
114
115                let mut read_vlr = Cursor::new(vlr.data.as_slice());
116
117                // read the root hierarchy page
118                let mut page =
119                    HierarchyPage::read_from(&mut read_vlr, copc_info.root_hier_size)?.entries;
120
121                while let Some(entry) = page.pop() {
122                    if entry.point_count == -1 {
123                        // read a new hierarchy page
124                        read.seek(SeekFrom::Start(entry.offset - copc_info.root_hier_offset))?;
125                        page.extend(
126                            HierarchyPage::read_from(&mut read, entry.byte_size as u64)?.entries,
127                        );
128                    } else {
129                        hierarchy_entries.insert(entry.key.clone(), entry);
130                    }
131                }
132                hierarchy_entries
133            }
134        };
135
136        // set the read pointer to the start of the compressed data block
137        let _ = read.seek(SeekFrom::Start(offset_to_point_data + start))?;
138        Ok(CopcReader {
139            start,
140            read,
141            header,
142            copc_info,
143            laz_vlr: laszip_vlr.ok_or(crate::Error::LasZipVlrNotFound)?,
144            hierarchy_entries,
145        })
146    }
147
148    /// LAS header
149    pub fn header(&self) -> &Header {
150        &self.header
151    }
152
153    /// COPC info VLR content
154    pub fn copc_info(&self) -> &CopcInfo {
155        &self.copc_info
156    }
157
158    pub fn num_entries(&self) -> usize {
159        self.hierarchy_entries.len()
160    }
161
162    /// Loads the nodes of the COPC octree that
163    /// satisfies the parameters `query_bounds` and `level_range`.
164    ///
165    /// It returns the nodes of the matching 'sub-octree'
166    fn load_octree_for_query(
167        &mut self,
168        level_range: LodSelection,
169        query_bounds: &BoundsSelection,
170    ) -> crate::Result<Vec<OctreeNode>> {
171        let (level_min, level_max) = match level_range {
172            LodSelection::All => (0, i32::MAX),
173            LodSelection::Resolution(resolution) => {
174                if !resolution.is_normal() || !resolution.is_sign_positive() {
175                    return Err(crate::Error::InvalidResolution(resolution));
176                }
177                (
178                    0,
179                    1.max((self.copc_info.spacing / resolution).log2().ceil() as i32 + 1),
180                )
181            }
182            LodSelection::Level(level) => (level, level + 1),
183            LodSelection::LevelMinMax(min, max) => (min, max),
184        };
185
186        let root_bounds = Bounds {
187            min: Vector {
188                x: self.copc_info.center.x - self.copc_info.halfsize,
189                y: self.copc_info.center.y - self.copc_info.halfsize,
190                z: self.copc_info.center.z - self.copc_info.halfsize,
191            },
192            max: Vector {
193                x: self.copc_info.center.x + self.copc_info.halfsize,
194                y: self.copc_info.center.y + self.copc_info.halfsize,
195                z: self.copc_info.center.z + self.copc_info.halfsize,
196            },
197        };
198
199        let mut root_node = OctreeNode::new();
200        root_node.entry.key.level = 0;
201
202        let mut satisfying_nodes = Vec::new();
203        let mut node_stack = vec![root_node];
204
205        while let Some(mut current_node) = node_stack.pop() {
206            // bottom of tree of interest reached
207            if current_node.entry.key.level >= level_max {
208                continue;
209            }
210
211            let entry = match self.hierarchy_entries.get(&current_node.entry.key) {
212                None => continue, // no entries for this node
213                Some(e) => e,
214            };
215
216            current_node.bounds = current_node.entry.key.bounds(&root_bounds);
217            if let BoundsSelection::Within(bounds) = query_bounds {
218                // this octree node does not overlap with the bounds of interest
219                if !bounds_intersect(&current_node.bounds, bounds) {
220                    continue;
221                }
222            }
223
224            // the entry exists and intersects with our interests
225            // push its children to the node stack
226            for child_key in current_node.entry.key.children() {
227                let mut child_node = OctreeNode::new();
228                child_node.entry.key = child_key;
229                current_node.children.push(child_node.clone());
230                node_stack.push(child_node);
231            }
232
233            // this node has points and belongs to the LOD of interest
234            if entry.point_count > 0
235                && (level_min..level_max).contains(&current_node.entry.key.level)
236            {
237                current_node.entry = entry.clone();
238                satisfying_nodes.push(current_node);
239            }
240        }
241
242        // Sort nodes by decending offsets for sequential reading
243        satisfying_nodes.sort_by(|a, b| b.entry.offset.partial_cmp(&a.entry.offset).unwrap());
244
245        Ok(satisfying_nodes)
246    }
247
248    /// Point iterator for selected level and bounds
249    pub fn points(
250        &mut self,
251        levels: LodSelection,
252        bounds: BoundsSelection,
253    ) -> crate::Result<PointIter<R>> {
254        let nodes = self.load_octree_for_query(levels, &bounds)?;
255        let total_points_left = nodes.iter().map(|n| n.entry.point_count as usize).sum();
256
257        let transforms = *self.header().transforms();
258
259        // Reverse transform to unscaled values
260        let raw_bounds = match bounds {
261            BoundsSelection::All => None,
262            BoundsSelection::Within(bounds) => Some(RawBounds {
263                min: Vector {
264                    x: transforms.x.inverse(bounds.min.x)?,
265                    y: transforms.y.inverse(bounds.min.y)?,
266                    z: transforms.z.inverse(bounds.min.z)?,
267                },
268                max: Vector {
269                    x: transforms.x.inverse(bounds.max.x)?,
270                    y: transforms.y.inverse(bounds.max.y)?,
271                    z: transforms.z.inverse(bounds.max.z)?,
272                },
273            }),
274        };
275
276        self.read.seek(SeekFrom::Start(self.start))?;
277        let decompressor = CopcDecompressor::new(&mut self.read, &self.laz_vlr)?;
278        let point = vec![
279            0u8;
280            (self.header.point_format().len() + self.header.point_format().extra_bytes)
281                as usize
282        ];
283
284        Ok(PointIter {
285            nodes,
286            bounds: raw_bounds,
287            point_format: *self.header.point_format(),
288            transforms,
289            decompressor,
290            point_buffer: point,
291            node_points_left: 0,
292            total_points_left,
293        })
294    }
295}
296
297struct RawBounds {
298    min: Vector<i32>,
299    max: Vector<i32>,
300}
301
302impl RawBounds {
303    #[inline]
304    fn contains_point(&self, p: &las::raw::Point) -> bool {
305        !(p.x < self.min.x
306            || p.y < self.min.y
307            || p.z < self.min.z
308            || p.x > self.max.x
309            || p.y > self.max.y
310            || p.z > self.max.z)
311    }
312}
313
314#[inline]
315fn bounds_intersect(a: &Bounds, b: &Bounds) -> bool {
316    !(a.max.x < b.min.x
317        || a.max.y < b.min.y
318        || a.max.z < b.min.z
319        || a.min.x > b.max.x
320        || a.min.y > b.max.y
321        || a.min.z > b.max.z)
322}
323
324/// Limits the octree levels to be queried in order to have
325/// a point cloud with the requested resolution.
326///
327/// resolution: Limits the octree levels to be queried in order
328/// to have a point cloud with the requested resolution.
329///
330/// - The unit is the one of the data.
331/// - If absent, the resulting cloud will be at the
332///   full resolution offered by the COPC source
333///
334/// level: The level of detail (LOD).
335///
336/// If absent, all LOD are going to be considered
337pub enum LodSelection {
338    /// Full resolution (all LODs)
339    All,
340    /// requested minimal resolution of point cloud
341    /// given as space between points
342    /// based on the spacing given in the copc info vlr
343    /// defined as root-node side length / number of points in root node
344    /// when traversing the octree levels the spacing of level i is copc_spacing*2^-i
345    ///
346    /// Tldr; higher value -> fewer points / cube unit
347    Resolution(f64),
348    /// only points that that are of the requested LOD will be returned.
349    Level(i32),
350    /// points for which the LOD is within the range will be returned.
351    LevelMinMax(i32, i32),
352}
353
354/// Select points within bounds
355pub enum BoundsSelection {
356    /// No bounds filter.
357    All,
358    /// Select points within bounds.
359    Within(Bounds),
360}
361
362/// LasZip point iterator
363pub struct PointIter<'a, R: Read + Seek> {
364    nodes: Vec<OctreeNode>,
365    bounds: Option<RawBounds>,
366    point_format: las::point::Format,
367    transforms: Vector<Transform>,
368    decompressor: CopcDecompressor<'a, &'a mut R>,
369    point_buffer: Vec<u8>,
370    node_points_left: usize,
371    total_points_left: usize,
372}
373
374impl<R: Read + Seek> Iterator for PointIter<'_, R> {
375    type Item = las::point::Point;
376
377    fn next(&mut self) -> Option<Self::Item> {
378        if self.total_points_left == 0 {
379            return None;
380        }
381        let mut in_bounds;
382        loop {
383            while self.node_points_left == 0 {
384                // get the next node with points
385                if let Some(node) = self.nodes.pop() {
386                    self.decompressor.source_seek(node.entry.offset).unwrap();
387                    self.node_points_left = node.entry.point_count as usize;
388                } else {
389                    return None;
390                }
391            }
392            self.decompressor
393                .decompress_one(self.point_buffer.as_mut_slice())
394                .unwrap();
395            let raw_point =
396                las::raw::Point::read_from(self.point_buffer.as_slice(), &self.point_format)
397                    .unwrap();
398            self.node_points_left -= 1;
399            self.total_points_left -= 1;
400            in_bounds = if let Some(bounds) = &self.bounds {
401                bounds.contains_point(&raw_point)
402            } else {
403                true
404            };
405
406            if in_bounds {
407                return Some(las::point::Point::new(raw_point, &self.transforms));
408            }
409        }
410    }
411
412    fn size_hint(&self) -> (usize, Option<usize>) {
413        (self.total_points_left, Some(self.total_points_left))
414    }
415}