From 7a810a97509a4cdee4f7958bd5bb310f7e54f4b0 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 15 Jan 2025 13:35:54 -0800 Subject: [PATCH] Add processor to compute geometry bounds --- geozero/CHANGELOG.md | 1 + geozero/src/bounds.rs | 123 ++++++++++++++++++++++++++++++++++++++++++ geozero/src/lib.rs | 3 ++ 3 files changed, 127 insertions(+) create mode 100644 geozero/src/bounds.rs diff --git a/geozero/CHANGELOG.md b/geozero/CHANGELOG.md index 394b8bb..9748a11 100644 --- a/geozero/CHANGELOG.md +++ b/geozero/CHANGELOG.md @@ -1,5 +1,6 @@ ## UNRELEASED +* Add `BoundsProcessor` to compute bounds of geometry * Update Deps: * BREAKING: `flatgeobuf` to 4.5.0 * BREAKING: `dbase` to 0.5.0 diff --git a/geozero/src/bounds.rs b/geozero/src/bounds.rs new file mode 100644 index 0000000..10b9e28 --- /dev/null +++ b/geozero/src/bounds.rs @@ -0,0 +1,123 @@ +use crate::{FeatureProcessor, GeomProcessor, PropertyProcessor}; + +#[derive(Default, Debug, Clone, PartialEq)] +pub struct Bounds { + min_x: f64, + min_y: f64, + max_x: f64, + max_y: f64, +} + +impl Bounds { + pub fn extend(&mut self, x: f64, y: f64) { + if x < self.min_x { + self.min_x = x; + } + if x > self.max_x { + self.max_x = x; + } + if y < self.min_y { + self.min_y = y; + } + if y > self.max_y { + self.max_y = y; + } + } + + pub fn min_x(&self) -> f64 { + self.min_x + } + + pub fn min_y(&self) -> f64 { + self.min_y + } + + pub fn max_x(&self) -> f64 { + self.max_x + } + + pub fn max_y(&self) -> f64 { + self.max_y + } +} + +/// Computes the bounds of a Geomtry +#[derive(Default, Debug)] +pub struct BoundsProcessor { + bounds: Option, +} + +impl BoundsProcessor { + pub fn new() -> Self { + Self::default() + } + pub fn bounds(&self) -> Option { + self.bounds.clone() + } +} + +impl GeomProcessor for BoundsProcessor { + fn xy(&mut self, x: f64, y: f64, _idx: usize) -> crate::error::Result<()> { + let Some(bounds) = self.bounds.as_mut() else { + self.bounds = Some(Bounds { + min_x: x, + min_y: y, + max_x: x, + max_y: y, + }); + return Ok(()); + }; + bounds.extend(x, y); + Ok(()) + } + + fn coordinate( + &mut self, + x: f64, + y: f64, + _z: Option, + _m: Option, + _t: Option, + _tm: Option, + idx: usize, + ) -> crate::error::Result<()> { + self.xy(x, y, idx) + } +} + +impl PropertyProcessor for BoundsProcessor {} +impl FeatureProcessor for BoundsProcessor {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::GeozeroGeometry; + + #[test] + fn test_bounds() { + let wkt = crate::wkt::Wkt("LINESTRING(1 2,3 4,5 6)"); + let mut bounds_processor = BoundsProcessor::new(); + + wkt.process_geom(&mut bounds_processor).unwrap(); + + assert_eq!( + bounds_processor.bounds, + Some(Bounds { + min_x: 1.0, + min_y: 2.0, + max_x: 5.0, + max_y: 6.0, + }) + ) + } + + #[test] + fn test_empty() { + let wkt = crate::wkt::Wkt("LINESTRING EMPTY"); + let mut bounds_processor = BoundsProcessor::new(); + + wkt.process_geom(&mut bounds_processor).unwrap(); + + assert_eq!(bounds_processor.bounds, None); + } +} diff --git a/geozero/src/lib.rs b/geozero/src/lib.rs index 9e3547f..7bb8851 100644 --- a/geozero/src/lib.rs +++ b/geozero/src/lib.rs @@ -124,6 +124,9 @@ pub use crate::wkt::conversion::*; #[cfg(feature = "with-mvt")] pub mod mvt; + +pub mod bounds; + #[cfg(feature = "with-mvt")] pub use crate::mvt::conversion::*;