1use 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
14pub struct CopcReader<R> {
16 start: u64,
18 read: R,
20 header: Header,
21 copc_info: CopcInfo,
22 laz_vlr: LazVlr,
23 hierarchy_entries: HashMap<VoxelKey, Entry>,
25}
26
27impl CopcReader<BufReader<File>> {
28 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 pub fn new(mut read: R) -> crate::Result<Self> {
39 let start = read.stream_position()?;
41
42 let raw_header = raw::Header::read_from(&mut read)?;
43
44 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 let mut builder = Builder::new(raw_header)?;
52
53 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 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 => {} Ordering::Greater => Err(las::Error::OffsetToPointDataTooSmall(
70 offset_to_point_data as u32,
71 ))?,
72 }
73
74 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 let header = builder.into_header()?;
86
87 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 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 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.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 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 pub fn header(&self) -> &Header {
150 &self.header
151 }
152
153 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 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 if current_node.entry.key.level >= level_max {
208 continue;
209 }
210
211 let entry = match self.hierarchy_entries.get(¤t_node.entry.key) {
212 None => continue, 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 if !bounds_intersect(¤t_node.bounds, bounds) {
220 continue;
221 }
222 }
223
224 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 if entry.point_count > 0
235 && (level_min..level_max).contains(¤t_node.entry.key.level)
236 {
237 current_node.entry = entry.clone();
238 satisfying_nodes.push(current_node);
239 }
240 }
241
242 satisfying_nodes.sort_by(|a, b| b.entry.offset.partial_cmp(&a.entry.offset).unwrap());
244
245 Ok(satisfying_nodes)
246 }
247
248 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 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
324pub enum LodSelection {
338 All,
340 Resolution(f64),
348 Level(i32),
350 LevelMinMax(i32, i32),
352}
353
354pub enum BoundsSelection {
356 All,
358 Within(Bounds),
360}
361
362pub 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 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}