diff --git a/CHANGES.md b/CHANGES.md index fddd70c56..a78c91bff 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -35,6 +35,10 @@ - +- Add `GeometryRef<'a>` to reference owned nested geometry in a lifetime-safe way. + + - + - Add support for MDArray API - diff --git a/src/vector/geometry.rs b/src/vector/geometry.rs index 11f5bb3e6..a52272c9a 100644 --- a/src/vector/geometry.rs +++ b/src/vector/geometry.rs @@ -1,6 +1,8 @@ use std::cell::RefCell; use std::ffi::CString; -use std::fmt::{self, Debug}; +use std::fmt::{self, Debug, Formatter}; +use std::marker::PhantomData; +use std::ops::Deref; use std::ptr::null_mut; use libc::{c_char, c_double, c_int, c_void}; @@ -310,6 +312,15 @@ impl Geometry { Geometry::with_c_geometry(c_geom, false) } + /// Get a reference to the geometry at given `index` + pub fn get_geometry(&self, index: usize) -> GeometryRef { + let geom = unsafe { self.get_unowned_geometry(index) }; + GeometryRef { + geom, + _lifetime: PhantomData::default(), + } + } + pub fn add_geometry(&mut self, mut sub: Geometry) -> Result<()> { assert!(sub.owned); sub.owned = false; @@ -444,6 +455,26 @@ pub fn geometry_type_to_name(ty: OGRwkbGeometryType::Type) -> String { _string(rv) } +/// Reference to owned geometry +pub struct GeometryRef<'a> { + geom: Geometry, + _lifetime: PhantomData<&'a ()>, +} + +impl Deref for GeometryRef<'_> { + type Target = Geometry; + + fn deref(&self) -> &Self::Target { + &self.geom + } +} + +impl Debug for GeometryRef<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.geom, f) + } +} + #[cfg(test)] mod tests { use crate::spatial_ref::SpatialRef; diff --git a/src/vector/vector_tests/mod.rs b/src/vector/vector_tests/mod.rs index fb3d933a0..6e2f3781f 100644 --- a/src/vector/vector_tests/mod.rs +++ b/src/vector/vector_tests/mod.rs @@ -122,6 +122,7 @@ where mod tests { use super::*; use crate::errors::{GdalError, Result}; + use gdal_sys::OGRwkbGeometryType::{wkbLineString, wkbLinearRing, wkbPolygon}; #[test] fn test_feature_count() { @@ -508,6 +509,43 @@ mod tests { }); } + #[test] + fn test_ring_points() { + let mut ring = Geometry::empty(wkbLinearRing).unwrap(); + ring.add_point_2d((1179091.1646903288, 712782.8838459781)); + ring.add_point_2d((1161053.0218226474, 667456.2684348812)); + ring.add_point_2d((1214704.933941905, 641092.8288590391)); + ring.add_point_2d((1228580.428455506, 682719.3123998424)); + ring.add_point_2d((1218405.0658121984, 721108.1805541387)); + ring.add_point_2d((1179091.1646903288, 712782.8838459781)); + assert!(!ring.is_empty()); + assert_eq!(ring.get_point_vec().len(), 6); + let mut poly = Geometry::empty(wkbPolygon).unwrap(); + poly.add_geometry(ring.to_owned()).unwrap(); + // Points are in ring, not containing geometry. + // NB: In Python SWIG bindings, `GetPoints` is fallible. + assert!(poly.get_point_vec().is_empty()); + assert_eq!(poly.geometry_count(), 1); + let ring_out = poly.get_geometry(0); + // NB: `wkb()` shows it to be a `LINEARRING`, but returned type is LineString + assert_eq!(ring_out.geometry_type(), wkbLineString); + assert!(!&ring_out.is_empty()); + assert_eq!(ring.get_point_vec(), ring_out.get_point_vec()); + } + + #[test] + fn test_get_inner_points() { + let geom = Geometry::bbox(0., 0., 1., 1.).unwrap(); + assert!(!geom.is_empty()); + assert_eq!(geom.geometry_count(), 1); + assert!(geom.area() > 0.); + assert_eq!(geom.geometry_type(), OGRwkbGeometryType::wkbPolygon); + assert!(geom.json().unwrap().contains("Polygon")); + let inner = geom.get_geometry(0); + let points = inner.get_point_vec(); + assert!(!points.is_empty()); + } + #[test] fn test_wkt() { with_feature("roads.geojson", 236194095, |feature| {