From 13729f17ba473bdb7805a00d5377916e7b42efde Mon Sep 17 00:00:00 2001 From: Pete Gadomski Date: Thu, 15 Aug 2024 08:56:14 -0600 Subject: [PATCH] fix: start of first evlr --- CHANGELOG.md | 4 ++++ src/header/builder.rs | 2 ++ src/header/mod.rs | 16 +++++++++++++--- src/writer/las.rs | 4 ++++ src/writer/laz.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/writer/mod.rs | 33 ++++++++++++++++++++------------- 6 files changed, 81 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faf30d3c..5f68acae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Reorganize reading, including removing the lifetime specifier on `Reader` ([#89](https://github.com/gadomski/las-rs/pull/89)) - Conslidate errors to a single enum ([#87](https://github.com/gadomski/las-rs/pull/87)) +### Fixed + +- Start of first EVLR ([#91](https://github.com/gadomski/las-rs/pull/91)) + ## Deprecated - `Read` trait ([#88](https://github.com/gadomski/las-rs/pull/88)) diff --git a/src/header/builder.rs b/src/header/builder.rs index 2fa6b355..eb865bf2 100644 --- a/src/header/builder.rs +++ b/src/header/builder.rs @@ -207,6 +207,7 @@ impl Builder { if self.version.supports::() || evlr.has_large_data() { evlrs.push(evlr); } else { + log::warn!("moving Evlr to Vlr because version does not support Evlrs: user_id={}, record_id={}, description={}", evlr.user_id, evlr.record_id, evlr.description); vlrs.push(evlr); } } @@ -237,6 +238,7 @@ impl Builder { padding: self.padding, point_format: self.point_format, point_padding: self.point_padding, + start_of_first_evlr: None, system_identifier: self.system_identifier, transforms: self.transforms, version: self.version, diff --git a/src/header/mod.rs b/src/header/mod.rs index 372cd222..bd97bee9 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -91,6 +91,7 @@ pub struct Header { padding: Vec, point_format: Format, point_padding: Vec, + start_of_first_evlr: Option, system_identifier: String, transforms: Vector, version: Version, @@ -524,6 +525,10 @@ impl Header { Ok(()) } + pub(crate) fn set_start_of_first_evlr(&mut self, start_of_first_evlr: u64) { + self.start_of_first_evlr = Some(start_of_first_evlr); + } + fn global_encoding(&self) -> u16 { let mut bits = self.gps_time_type.into(); if self.has_synthetic_return_numbers { @@ -631,9 +636,13 @@ impl Header { } else if n > u32::MAX as usize { Err(Error::TooManyEvlrs(n)) } else { - let start_of_first_evlr = self.point_data_len() - + self.point_padding.len() as u64 - + u64::from(self.offset_to_point_data()?); + let start_of_first_evlr = if let Some(start_of_fist_evlr) = self.start_of_first_evlr { + start_of_fist_evlr + } else { + self.point_data_len() + + self.point_padding.len() as u64 + + u64::from(self.offset_to_point_data()?) + }; Ok(Some(raw::header::Evlr { start_of_first_evlr, number_of_evlrs: n as u32, @@ -681,6 +690,7 @@ impl Default for Header { padding: Vec::new(), point_format: Default::default(), point_padding: Vec::new(), + start_of_first_evlr: None, system_identifier: "las-rs".to_string(), transforms: Default::default(), version: Default::default(), diff --git a/src/writer/las.rs b/src/writer/las.rs index 38cc96e1..65da938a 100644 --- a/src/writer/las.rs +++ b/src/writer/las.rs @@ -33,6 +33,10 @@ impl WritePoint for PointWriter { &self.header } + fn header_mut(&mut self) -> &mut Header { + &mut self.header + } + fn done(&mut self) -> Result<()> { Ok(()) } diff --git a/src/writer/laz.rs b/src/writer/laz.rs index f680120f..728711b2 100644 --- a/src/writer/laz.rs +++ b/src/writer/laz.rs @@ -49,8 +49,46 @@ impl WritePoint for PointWriter<'_, W> { &self.header } + fn header_mut(&mut self) -> &mut Header { + &mut self.header + } + fn done(&mut self) -> Result<()> { self.compressor.done()?; Ok(()) } } + +#[cfg(test)] +mod tests { + use crate::{Builder, Point, Reader, Vlr, Writer}; + use std::io::Cursor; + + #[test] + fn evlr() { + let mut vlr = Vlr::default(); + vlr.user_id = "@gadomski".to_string(); + vlr.record_id = 42; + vlr.description = "A great vlr".to_string(); + vlr.data = b"some data".to_vec(); + let mut builder = Builder::default(); + builder.version.minor = 4; + builder.point_format.is_compressed = true; + builder.evlrs.push(vlr); + let header = builder.into_header().unwrap(); + let cursor = Cursor::new(Vec::new()); + let mut writer = Writer::new(cursor, header).unwrap(); + for i in 0..5 { + let mut point = Point::default(); + point.return_number = i; + writer.write_point(point).unwrap(); + } + let cursor = writer.into_inner().unwrap(); + let reader = Reader::new(cursor).unwrap(); + let evlr = &reader.header().evlrs()[0]; + assert_eq!(evlr.user_id, "@gadomski"); + assert_eq!(evlr.record_id, 42); + assert_eq!(evlr.description, "A great vlr"); + assert_eq!(evlr.data, b"some data"); + } +} diff --git a/src/writer/mod.rs b/src/writer/mod.rs index 32299242..5e6af4c5 100644 --- a/src/writer/mod.rs +++ b/src/writer/mod.rs @@ -49,6 +49,7 @@ trait WritePoint: Send { fn into_inner(self: Box) -> W; fn get_mut(&mut self) -> &mut W; fn header(&self) -> &Header; + fn header_mut(&mut self) -> &mut Header; fn done(&mut self) -> Result<()>; } @@ -67,6 +68,9 @@ impl WritePoint for ClosedPointWriter { fn header(&self) -> &Header { unreachable!() } + fn header_mut(&mut self) -> &mut Header { + unreachable!() + } fn done(&mut self) -> Result<()> { unreachable!() } @@ -188,6 +192,11 @@ impl Writer { let point_padding = self.header().point_padding().clone(); self.point_writer.get_mut().write_all(&point_padding)?; + + let start_of_first_evlr = self.point_writer.get_mut().stream_position()?; + self.point_writer + .header_mut() + .set_start_of_first_evlr(start_of_first_evlr); let raw_evlrs: Vec> = { self.point_writer .header() @@ -258,20 +267,7 @@ impl Writer { pub fn write(&mut self, point: Point) -> Result<()> { self.write_point(point) } -} - -#[allow(deprecated)] -impl Write for Writer { - fn header(&self) -> &Header { - self.header() - } - - fn write(&mut self, point: Point) -> Result<()> { - self.write(point) - } -} -impl Writer { /// Closes this writer and returns its inner `Write`, seeked to the beginning of the las data. /// /// # Examples @@ -300,6 +296,17 @@ impl Writer { } } +#[allow(deprecated)] +impl Write for Writer { + fn header(&self) -> &Header { + self.header() + } + + fn write(&mut self, point: Point) -> Result<()> { + self.write(point) + } +} + impl Writer> { /// Creates a new writer for a path. ///