From eb15b5c047713acd1168e69abe9238460504d5a1 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sat, 11 Feb 2023 11:00:43 -0500 Subject: [PATCH] Added `envelope*` methods to `Geometry`. --- CHANGES.md | 4 ++++ src/spatial_ref/srs.rs | 2 +- src/vector/geometry.rs | 53 ++++++++++++++++++++++++++++++++++++++++++ src/vector/layer.rs | 33 +++++++++----------------- src/vector/mod.rs | 6 +++++ 5 files changed, 75 insertions(+), 23 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5ead730a8..47d496bb5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -50,6 +50,10 @@ - +- Added `Geometry::envelope` and `Geometry::envelope_3d`. + + - + ## 0.14 - Added new content to `README.md` and the root docs. diff --git a/src/spatial_ref/srs.rs b/src/spatial_ref/srs.rs index f14d5f2b6..725d02c61 100644 --- a/src/spatial_ref/srs.rs +++ b/src/spatial_ref/srs.rs @@ -46,7 +46,7 @@ impl CoordTransform { /// Some([f64; 4]) with bounds in axis order of target SpatialRef /// None if there is an error. #[cfg(all(major_ge_3, minor_ge_4))] - pub fn transform_bounds(&self, bounds: &[f64; 4], densify_pts: i32) -> Result<([f64; 4])> { + pub fn transform_bounds(&self, bounds: &[f64; 4], densify_pts: i32) -> Result<[f64; 4]> { let mut out_xmin: f64 = 0.; let mut out_ymin: f64 = 0.; let mut out_xmax: f64 = 0.; diff --git a/src/vector/geometry.rs b/src/vector/geometry.rs index 92dbd6058..9f95ff101 100644 --- a/src/vector/geometry.rs +++ b/src/vector/geometry.rs @@ -2,6 +2,7 @@ use std::cell::RefCell; use std::ffi::CString; use std::fmt::{self, Debug, Formatter}; use std::marker::PhantomData; +use std::mem::MaybeUninit; use std::ops::Deref; use std::ptr::null_mut; @@ -13,6 +14,7 @@ use gdal_sys::{self, OGRErr, OGRGeometryH, OGRwkbGeometryType}; use crate::errors::*; use crate::spatial_ref::{CoordTransform, SpatialRef}; use crate::utils::{_last_null_pointer_err, _string}; +use crate::vector::{Envelope, Envelope3D}; /// OGR Geometry pub struct Geometry { @@ -424,10 +426,38 @@ impl Geometry { Ok(unsafe { Geometry::with_c_geometry(new_c_geom, true) }) } + /// Compute geometry area in square units of the spatial reference system in use. + /// + /// Supported for `LinearRing`, `Polygon` or `MultiPolygon`. + /// Returns zero for all other geometry types. + /// + /// See: [`OGR_G_Area`](https://gdal.org/api/vector_c_api.html#_CPPv410OGR_G_Area12OGRGeometryH) pub fn area(&self) -> f64 { unsafe { gdal_sys::OGR_G_Area(self.c_geometry()) } } + /// Computes and returns the axis-aligned 2D bounding envelope for this geometry. + /// + /// See: [`OGR_G_GetEnvelope`](https://gdal.org/api/vector_c_api.html#_CPPv417OGR_G_GetEnvelope12OGRGeometryHP11OGREnvelope) + pub fn envelope(&self) -> Envelope { + let mut envelope = MaybeUninit::uninit(); + unsafe { + gdal_sys::OGR_G_GetEnvelope(self.c_geometry(), envelope.as_mut_ptr()); + envelope.assume_init() + } + } + + /// Computes and returns the axis aligned 3D bounding envelope for this geometry. + /// + /// See: [`OGR_G_GetEnvelope3D`](https://gdal.org/api/vector_c_api.html#_CPPv419OGR_G_GetEnvelope3D12OGRGeometryHP13OGREnvelope3D) + pub fn envelope_3d(&self) -> Envelope3D { + let mut envelope = MaybeUninit::uninit(); + unsafe { + gdal_sys::OGR_G_GetEnvelope3D(self.c_geometry(), envelope.as_mut_ptr()); + envelope.assume_init() + } + } + /// Get the spatial reference system for this geometry. /// /// Returns `Some(SpatialRef)`, or `None` if one isn't defined. @@ -585,6 +615,7 @@ impl Debug for GeometryRef<'_> { #[cfg(test)] mod tests { use super::*; + use crate::assert_almost_eq; use crate::spatial_ref::SpatialRef; use crate::test_utils::SuppressGDALErrorLog; @@ -738,4 +769,26 @@ mod tests { assert!(dst.is_ok(), "{dst:?}"); assert!(dst.unwrap().is_valid()); } + + #[test] + fn test_envelope() { + let geom = Geometry::from_wkt("MULTIPOINT((1.0 2.0), (2.0 4.0))").unwrap(); + let envelope = geom.envelope(); + assert_almost_eq(envelope.MinX, 1.0); + assert_almost_eq(envelope.MaxX, 2.0); + assert_almost_eq(envelope.MinY, 2.0); + assert_almost_eq(envelope.MaxY, 4.0); + } + + #[test] + fn test_envelope3d() { + let geom = Geometry::from_wkt("MULTIPOINT((1.0 2.0 3.0), (2.0 4.0 5.0))").unwrap(); + let envelope = geom.envelope_3d(); + assert_almost_eq(envelope.MinX, 1.0); + assert_almost_eq(envelope.MaxX, 2.0); + assert_almost_eq(envelope.MinY, 2.0); + assert_almost_eq(envelope.MaxY, 4.0); + assert_almost_eq(envelope.MinZ, 3.0); + assert_almost_eq(envelope.MaxZ, 5.0); + } } diff --git a/src/vector/layer.rs b/src/vector/layer.rs index 40ab48206..bdcbe24fc 100644 --- a/src/vector/layer.rs +++ b/src/vector/layer.rs @@ -2,12 +2,11 @@ use crate::metadata::Metadata; use crate::spatial_ref::SpatialRef; use crate::utils::{_last_null_pointer_err, _string}; use crate::vector::defn::Defn; -use crate::vector::{Feature, FieldValue, Geometry}; +use crate::vector::{Envelope, Feature, FieldValue, Geometry}; use crate::{dataset::Dataset, gdal_major_object::MajorObject}; -use gdal_sys::{ - self, GDALMajorObjectH, OGREnvelope, OGRErr, OGRFieldDefnH, OGRFieldType, OGRLayerH, -}; +use gdal_sys::{self, GDALMajorObjectH, OGRErr, OGRFieldDefnH, OGRFieldType, OGRLayerH}; use libc::c_int; +use std::mem::MaybeUninit; use std::ptr::null_mut; use std::{convert::TryInto, ffi::CString, marker::PhantomData}; @@ -371,22 +370,17 @@ pub trait LayerAccess: Sized { /// /// Layers without any geometry may return [`OGRErr::OGRERR_FAILURE`] to indicate that no /// meaningful extents could be collected. - fn get_extent(&self) -> Result { - let mut envelope = OGREnvelope { - MinX: 0.0, - MaxX: 0.0, - MinY: 0.0, - MaxY: 0.0, - }; + fn get_extent(&self) -> Result { + let mut envelope = MaybeUninit::uninit(); let force = 1; - let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), &mut envelope, force) }; + let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), envelope.as_mut_ptr(), force) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { err: rv, method_name: "OGR_L_GetExtent", }); } - Ok(envelope) + Ok(unsafe { envelope.assume_init() }) } /// Returns the extent of this layer as an axis-aligned bounding box, if it is possible to @@ -398,15 +392,10 @@ pub trait LayerAccess: Sized { /// Depending on the driver, the returned extent may or may not take the [spatial /// filter](`Layer::set_spatial_filter`) into account. So it is safer to call `try_get_extent` /// without setting a spatial filter. - fn try_get_extent(&self) -> Result> { - let mut envelope = OGREnvelope { - MinX: 0.0, - MaxX: 0.0, - MinY: 0.0, - MaxY: 0.0, - }; + fn try_get_extent(&self) -> Result> { + let mut envelope = MaybeUninit::uninit(); let force = 0; - let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), &mut envelope, force) }; + let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), envelope.as_mut_ptr(), force) }; if rv == OGRErr::OGRERR_FAILURE { Ok(None) } else { @@ -416,7 +405,7 @@ pub trait LayerAccess: Sized { method_name: "OGR_L_GetExtent", }); } - Ok(Some(envelope)) + Ok(Some(unsafe { envelope.assume_init() })) } } diff --git a/src/vector/mod.rs b/src/vector/mod.rs index d49dcec25..981ebdd09 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -89,5 +89,11 @@ pub trait ToGdal { fn to_gdal(&self) -> Result; } +/// Axis aligned 2D bounding box. +pub type Envelope = gdal_sys::OGREnvelope; + +/// Axis aligned 3D bounding box. +pub type Envelope3D = gdal_sys::OGREnvelope3D; + #[cfg(test)] mod vector_tests;