From 8da2c7560ba1ab097e5be315d9eeee926e958b24 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sat, 31 Dec 2022 11:26:09 -0500 Subject: [PATCH 1/4] Implemented `make_valid`, `point_count` and `geometry_name` on `Geometry`. --- CHANGES.md | 4 + src/cpl.rs | 34 +++++++- src/metadata.rs | 2 +- src/test_utils.rs | 26 ++++++ src/vector/geometry.rs | 153 ++++++++++++++++++++++++++++++--- src/vector/layer.rs | 7 +- src/vector/vector_tests/mod.rs | 20 +++-- src/vector/vector_tests/sql.rs | 2 + 8 files changed, 222 insertions(+), 26 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5f5ab631..4938b816 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,10 @@ - +- Exposed various functions on `Geometry`: `make_valid`, `geometry_name`, and `point_count`. + + - + ## 0.14 - Added new content to `README.md` and the root docs. diff --git a/src/cpl.rs b/src/cpl.rs index bfaa5efd..5268ad65 100644 --- a/src/cpl.rs +++ b/src/cpl.rs @@ -7,13 +7,15 @@ use std::ffi::CString; use std::fmt::{Debug, Formatter}; use std::ptr; -use gdal_sys::{CSLCount, CSLDestroy, CSLFetchNameValue, CSLSetNameValue}; +use gdal_sys::{CSLCount, CSLDestroy, CSLDuplicate, CSLFetchNameValue, CSLSetNameValue}; use libc::c_char; use crate::errors::{GdalError, Result}; use crate::utils::{_string, _string_tuple}; -/// Wraps a [`gdal_sys::CSLConstList`] (a.k.a. `char **papszStrList`) +/// Wraps a [`gdal_sys::CSLConstList`] (a.k.a. `char **papszStrList`). This data structure +/// (a null-terminated array of null-terminated strings) is used throughout GDAL to pass +/// `KEY=VALUE`-formatted options to various functions. /// /// See the [`CSL*` GDAL functions](https://gdal.org/api/cpl.html#cpl-string-h) for more details. pub struct CslStringList { @@ -21,6 +23,7 @@ pub struct CslStringList { } impl CslStringList { + /// Creates an empty GDAL string list. pub fn new() -> Self { Self { list_ptr: ptr::null_mut(), @@ -104,6 +107,14 @@ impl Default for CslStringList { } } +impl Clone for CslStringList { + fn clone(&self) -> Self { + let list_ptr = unsafe { CSLDuplicate(self.list_ptr) }; + Self { list_ptr } + } +} + +/// State for iterator over [`CslStringList`] entries. pub struct CslStringListIterator<'a> { list: &'a CslStringList, idx: usize, @@ -155,6 +166,25 @@ impl Debug for CslStringList { } } +/// Convenience shorthand for specifying an empty `CslStringList` to functions accepting +/// `Into`. +impl From<()> for CslStringList { + fn from(_: ()) -> Self { + CslStringList::default() + } +} + +/// Creates a [`CslStringList`] from a slice of _key_/_value_ tuples. +impl From<&[(&str, &str); N]> for CslStringList { + fn from(pairs: &[(&str, &str); N]) -> Self { + let mut result = Self::default(); + for (k, v) in pairs { + result.set_name_value(k, v).expect("valid key/value pair"); + } + result + } +} + #[cfg(test)] mod tests { use crate::cpl::CslStringList; diff --git a/src/metadata.rs b/src/metadata.rs index 9c1773c4..92d9eb60 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -235,7 +235,7 @@ pub trait Metadata: MajorObject { } } -/// Standalone metadata entry, as returned by iterator from [`Metadata::metadata_iter`]. +/// Standalone metadata entry, as returned by iterator from [`Metadata::metadata`]. /// /// Defined by it's parent `domain`, and `key`/`value` pair. #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] diff --git a/src/test_utils.rs b/src/test_utils.rs index bf8fb112..5665e0d4 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,3 +1,5 @@ +use std::ffi::c_void; +use std::marker::PhantomData; use std::path::{Path, PathBuf}; /// A struct that contains a temporary directory and a path to a file in that directory. @@ -49,3 +51,27 @@ pub(crate) fn fixture(filename: &str) -> PathBuf { .join("fixtures") .join(filename) } + +/// Scoped value for temporarily suppressing thread-local GDAL log messages. +/// +/// Useful for tests that expect GDAL errors and want to keep the output log clean +/// of distracting yet expected error messages. +pub(crate) struct SuppressGDALErrorLog { + // Make !Sync and !Send, and force use of `new`. + _private: PhantomData<*mut c_void>, +} + +impl SuppressGDALErrorLog { + pub(crate) fn new() -> Self { + unsafe { gdal_sys::CPLPushErrorHandler(Some(gdal_sys::CPLQuietErrorHandler)) }; + SuppressGDALErrorLog { + _private: PhantomData, + } + } +} + +impl Drop for SuppressGDALErrorLog { + fn drop(&mut self) { + unsafe { gdal_sys::CPLPopErrorHandler() }; + } +} diff --git a/src/vector/geometry.rs b/src/vector/geometry.rs index ea1a8513..88203628 100644 --- a/src/vector/geometry.rs +++ b/src/vector/geometry.rs @@ -7,6 +7,7 @@ use std::ptr::null_mut; use libc::{c_char, c_double, c_int, c_void}; +use crate::cpl::CslStringList; use gdal_sys::{self, OGRErr, OGRGeometryH, OGRwkbGeometryType}; use crate::errors::*; @@ -224,11 +225,16 @@ impl Geometry { unsafe { gdal_sys::OGR_G_AddPoint_2D(self.c_geometry(), x as c_double, y as c_double) }; } - pub fn get_point(&self, i: i32) -> (f64, f64, f64) { + /// Get point coordinates from a line string or a point geometry. + /// + /// `index` is the line string vertex index, from 0 to `point_count()-1`, or `0` when a point. + /// + /// Refer: [`OGR_G_GetPoint`](https://gdal.org/api/vector_c_api.html#_CPPv414OGR_G_GetPoint12OGRGeometryHiPdPdPd) + pub fn get_point(&self, index: i32) -> (f64, f64, f64) { let mut x: c_double = 0.; let mut y: c_double = 0.; let mut z: c_double = 0.; - unsafe { gdal_sys::OGR_G_GetPoint(self.c_geometry(), i, &mut x, &mut y, &mut z) }; + unsafe { gdal_sys::OGR_G_GetPoint(self.c_geometry(), index, &mut x, &mut y, &mut z) }; (x, y, z) } @@ -292,15 +298,50 @@ impl Geometry { Ok(unsafe { Geometry::with_c_geometry(c_geom, true) }) } + /// Get the geometry type ordinal + /// + /// Refer: [OGR_G_GetGeometryType](https://gdal.org/api/vector_c_api.html#_CPPv421OGR_G_GetGeometryType12OGRGeometryH) pub fn geometry_type(&self) -> OGRwkbGeometryType::Type { unsafe { gdal_sys::OGR_G_GetGeometryType(self.c_geometry()) } } + /// Get the WKT name for the type of this geometry. + /// + /// Refer: [`OGR_G_GetGeometryName`](https://gdal.org/api/vector_c_api.html#_CPPv421OGR_G_GetGeometryName12OGRGeometryH) + pub fn geometry_name(&self) -> String { + // Note: C API makes no statements about this possibly returning null. + // So we don't have to result wrap this, + let c_str = unsafe { gdal_sys::OGR_G_GetGeometryName(self.c_geometry()) }; + if c_str.is_null() { + "".into() + } else { + _string(c_str) + } + } + + /// Get the number of elements in a geometry, or number of geometries in container. + /// + /// Only geometries of type `wkbPolygon`, `wkbMultiPoint`, `wkbMultiLineString`, `wkbMultiPolygon` + /// or `wkbGeometryCollection` may return a non-zero value. Other geometry types will return 0. + /// + /// For a polygon, the returned number is the number of rings (exterior ring + interior rings). + /// + /// Refer: [`OGR_G_GetGeometryCount`](https://gdal.org/api/vector_c_api.html#_CPPv422OGR_G_GetGeometryCount12OGRGeometryH) pub fn geometry_count(&self) -> usize { let cnt = unsafe { gdal_sys::OGR_G_GetGeometryCount(self.c_geometry()) }; cnt as usize } + /// Get the number of points from a Point or a LineString/LinearRing geometry. + /// + /// Only `wkbPoint` or `wkbLineString` may return a non-zero value. Other geometry types will return 0. + /// + /// Refer: [`OGR_G_GetPointCount`](https://gdal.org/api/vector_c_api.html#_CPPv419OGR_G_GetPointCount12OGRGeometryH) + pub fn point_count(&self) -> usize { + let cnt = unsafe { gdal_sys::OGR_G_GetPointCount(self.c_geometry()) }; + cnt as usize + } + /// Returns the n-th sub-geometry as a non-owned Geometry. /// /// # Safety @@ -388,21 +429,18 @@ impl Geometry { unsafe { gdal_sys::OGR_G_Area(self.c_geometry()) } } - /// May or may not contain a reference to a SpatialRef: if not, it returns - /// an `Ok(None)`; if it does, it tries to build a SpatialRef. If that - /// succeeds, it returns an Ok(Some(SpatialRef)), otherwise, you get the - /// Err. + /// Get the spatial reference system for this geometry. /// + /// Returns `Some(SpatialRef)`, or `None` if one isn't defined. + /// + /// Refer [OGR_G_GetSpatialReference](https://gdal.org/doxygen/ogr__api_8h.html#abc393e40282eec3801fb4a4abc9e25bf) pub fn spatial_ref(&self) -> Option { let c_spatial_ref = unsafe { gdal_sys::OGR_G_GetSpatialReference(self.c_geometry()) }; if c_spatial_ref.is_null() { None } else { - match unsafe { SpatialRef::from_c_obj(c_spatial_ref) } { - Ok(sr) => Some(sr), - Err(_) => None, - } + unsafe { SpatialRef::from_c_obj(c_spatial_ref) }.ok() } } @@ -416,6 +454,61 @@ impl Geometry { pub fn to_geo(&self) -> Result> { self.try_into() } + + /// Attempts to make an invalid geometry valid without losing vertices. + /// + /// Already-valid geometries are cloned without further intervention. + /// + /// Extended options are available via [`CslStringList`] if GDAL is built with GEOS >= 3.8. + /// They are defined as follows: + /// + /// * `METHOD=LINEWORK`: Combines all rings into a set of node-ed lines and then extracts + /// valid polygons from that "linework". + /// * `METHOD=STRUCTURE`: First makes all rings valid, then merges shells and subtracts holes + /// from shells to generate valid result. Assumes holes and shells are correctly categorized. + /// * `KEEP_COLLAPSED=YES/NO`. Only for `METHOD=STRUCTURE`. + /// - `NO` (default): Collapses are converted to empty geometries + /// - `YES`: collapses are converted to a valid geometry of lower dimension + /// + /// When GEOS < 3.8, this method will return `Ok(self.clone())` if it is valid, or `Err` if not. + /// + /// Refer: [OGR_G_MakeValidEx](https://gdal.org/api/vector_c_api.html#_CPPv417OGR_G_MakeValidEx12OGRGeometryH12CSLConstList) + /// + /// # Example + /// ```rust, no_run + /// use gdal::vector::Geometry; + /// # fn main() -> gdal::errors::Result<()> { + /// let src = Geometry::from_wkt("POLYGON ((0 0,10 10,0 10,10 0,0 0))")?; + /// let dst = src.make_valid(())?; + /// assert_eq!("MULTIPOLYGON (((10 0,0 0,5 5,10 0)),((10 10,5 5,0 10,10 10)))", dst.wkt()?); + /// # Ok(()) + /// # } + /// ``` + pub fn make_valid>(&self, opts: O) -> Result { + let opts = opts.into(); + + fn inner(geom: &Geometry, opts: CslStringList) -> Result { + #[cfg(all(major_ge_3, minor_ge_4))] + let c_geom = unsafe { gdal_sys::OGR_G_MakeValidEx(geom.c_geometry(), opts.as_ptr()) }; + + #[cfg(not(all(major_ge_3, minor_ge_4)))] + let c_geom = { + if !opts.is_empty() { + return Err(GdalError::BadArgument( + "Options to make_valid require GDAL >= 3.4".into(), + )); + } + unsafe { gdal_sys::OGR_G_MakeValid(geom.c_geometry()) } + }; + + if c_geom.is_null() { + Err(_last_null_pointer_err("OGR_G_MakeValid")) + } else { + Ok(unsafe { Geometry::with_c_geometry(c_geom, true) }) + } + } + inner(self, opts) + } } impl Drop for Geometry { @@ -482,13 +575,14 @@ impl Debug for GeometryRef<'_> { #[cfg(test)] mod tests { + use super::*; use crate::spatial_ref::SpatialRef; - - use super::{geometry_type_to_name, Geometry}; + use crate::test_utils::SuppressGDALErrorLog; #[test] #[allow(clippy::float_cmp)] pub fn test_area() { + let _nolog = SuppressGDALErrorLog::new(); let geom = Geometry::empty(::gdal_sys::OGRwkbGeometryType::wkbMultiPolygon).unwrap(); assert_eq!(geom.area(), 0.0); @@ -593,4 +687,39 @@ mod tests { // We don't care what it returns when passed an invalid value, just that it doesn't crash. geometry_type_to_name(4372521); } + + #[test] + /// Simple clone case. + pub fn test_make_valid_clone() { + let src = Geometry::from_wkt("POINT (0 0)").unwrap(); + let dst = src.make_valid(()); + assert!(dst.is_ok()); + } + + #[test] + /// Un-repairable geometry case + pub fn test_make_valid_invalid() { + let _nolog = SuppressGDALErrorLog::new(); + let src = Geometry::from_wkt("LINESTRING (0 0)").unwrap(); + let dst = src.make_valid(()); + assert!(dst.is_err()); + } + + #[test] + /// Repairable case (self-intersecting) + pub fn test_make_valid_repairable() { + let src = Geometry::from_wkt("POLYGON ((0 0,10 10,0 10,10 0,0 0))").unwrap(); + let dst = src.make_valid(()); + assert!(dst.is_ok()); + } + + #[cfg(all(major_ge_3, minor_ge_4))] + #[test] + /// Repairable case, but use extended options + pub fn test_make_valid_ex() { + let src = + Geometry::from_wkt("POLYGON ((0 0,0 10,10 10,10 0,0 0),(5 5,15 10,15 0,5 5))").unwrap(); + let dst = src.make_valid(&[("STRUCTURE", "LINEWORK")]); + assert!(dst.is_ok(), "{dst:?}"); + } } diff --git a/src/vector/layer.rs b/src/vector/layer.rs index 90065cd5..55db06ab 100644 --- a/src/vector/layer.rs +++ b/src/vector/layer.rs @@ -251,7 +251,8 @@ pub trait LayerAccess: Sized { } /// Set a feature on this layer layer. - /// Refer[SetFeature](https://gdal.org/doxygen/classOGRLayer.html#a681139bfd585b74d7218e51a32144283) + /// + /// Refer [SetFeature](https://gdal.org/doxygen/classOGRLayer.html#a681139bfd585b74d7218e51a32144283) fn set_feature(&self, feature: Feature) -> Result<()> { unsafe { gdal_sys::OGR_L_SetFeature(self.c_layer(), feature.c_feature()) }; Ok(()) @@ -416,11 +417,11 @@ pub trait LayerAccess: Sized { } } - /// Fetch the spatial reference system for this layer. + /// Get the spatial reference system for this layer. /// /// Returns `Some(SpatialRef)`, or `None` if one isn't defined. /// - /// Refer [OGR_L_GetSpatialRef](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) + /// Refer: [OGR_L_GetSpatialRef](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) fn spatial_ref(&self) -> Option { let c_obj = unsafe { gdal_sys::OGR_L_GetSpatialRef(self.c_layer()) }; if c_obj.is_null() { diff --git a/src/vector/vector_tests/mod.rs b/src/vector/vector_tests/mod.rs index cd747b09..c7f1fcc0 100644 --- a/src/vector/vector_tests/mod.rs +++ b/src/vector/vector_tests/mod.rs @@ -105,6 +105,7 @@ where mod tests { use gdal_sys::OGRwkbGeometryType::{wkbLineString, wkbLinearRing, wkbPolygon}; + use crate::test_utils::SuppressGDALErrorLog; use crate::{ errors::{GdalError, Result}, DriverManager, @@ -826,14 +827,17 @@ mod tests { assert_eq!(layer.features().count(), 21); - // force error - assert!(matches!( - layer.set_attribute_filter("foo = bar").unwrap_err(), - GdalError::OgrError { - err: gdal_sys::OGRErr::OGRERR_CORRUPT_DATA, - method_name: "OGR_L_SetAttributeFilter", - } - )); + { + let _nolog = SuppressGDALErrorLog::new(); + // force error + assert!(matches!( + layer.set_attribute_filter("foo = bar").unwrap_err(), + GdalError::OgrError { + err: gdal_sys::OGRErr::OGRERR_CORRUPT_DATA, + method_name: "OGR_L_SetAttributeFilter", + } + )); + } }); } diff --git a/src/vector/vector_tests/sql.rs b/src/vector/vector_tests/sql.rs index 51e22ebb..faa2d225 100644 --- a/src/vector/vector_tests/sql.rs +++ b/src/vector/vector_tests/sql.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use crate::test_utils::SuppressGDALErrorLog; use crate::{ test_utils::fixture, vector::{sql, Geometry, LayerAccess}, @@ -117,6 +118,7 @@ fn test_sql_no_result() { #[test] fn test_sql_bad_query() { + let _nolog = SuppressGDALErrorLog::new(); let ds = Dataset::open(fixture("roads.geojson")).unwrap(); let query = "SELECT nope FROM roads"; From 94198a5c1c55288129fa86292c9991b68b0dfd9c Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sun, 8 Jan 2023 14:59:19 -0500 Subject: [PATCH 2/4] Documentation tweaks. --- src/cpl.rs | 24 +++++++++++++++++++++++- src/vector/geometry.rs | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/cpl.rs b/src/cpl.rs index 5268ad65..ee583d25 100644 --- a/src/cpl.rs +++ b/src/cpl.rs @@ -168,13 +168,35 @@ impl Debug for CslStringList { /// Convenience shorthand for specifying an empty `CslStringList` to functions accepting /// `Into`. +/// +/// # Example +/// +/// ```rust, no_run +/// use gdal::cpl::CslStringList; +/// fn count_opts>(opts: O) -> usize { +/// opts.into().len() +/// } +/// +/// assert_eq!(count_opts(()), 0); +/// ``` impl From<()> for CslStringList { fn from(_: ()) -> Self { CslStringList::default() } } -/// Creates a [`CslStringList`] from a slice of _key_/_value_ tuples. +/// Convenience for creating a [`CslStringList`] from a slice of _key_/_value_ tuples. +/// +/// # Example +/// +/// ```rust, no_run +/// use gdal::cpl::CslStringList; +/// fn count_opts>(opts: O) -> usize { +/// opts.into().len() +/// } +/// +/// assert_eq!(count_opts(&[("One", "1"), ("Two", "2"), ("Three", "3")]), 3); +/// ``` impl From<&[(&str, &str); N]> for CslStringList { fn from(pairs: &[(&str, &str); N]) -> Self { let mut result = Self::default(); diff --git a/src/vector/geometry.rs b/src/vector/geometry.rs index 88203628..90638fd9 100644 --- a/src/vector/geometry.rs +++ b/src/vector/geometry.rs @@ -433,7 +433,7 @@ impl Geometry { /// /// Returns `Some(SpatialRef)`, or `None` if one isn't defined. /// - /// Refer [OGR_G_GetSpatialReference](https://gdal.org/doxygen/ogr__api_8h.html#abc393e40282eec3801fb4a4abc9e25bf) + /// Refer: [OGR_G_GetSpatialReference](https://gdal.org/doxygen/ogr__api_8h.html#abc393e40282eec3801fb4a4abc9e25bf) pub fn spatial_ref(&self) -> Option { let c_spatial_ref = unsafe { gdal_sys::OGR_G_GetSpatialReference(self.c_geometry()) }; From bdf574e7eda8240a6f68671105bb6544701a3022 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 10 Jan 2023 14:25:42 -0500 Subject: [PATCH 3/4] Update src/vector/geometry.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laurențiu Nicola --- src/vector/geometry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vector/geometry.rs b/src/vector/geometry.rs index 90638fd9..6f7b2ba8 100644 --- a/src/vector/geometry.rs +++ b/src/vector/geometry.rs @@ -478,7 +478,7 @@ impl Geometry { /// ```rust, no_run /// use gdal::vector::Geometry; /// # fn main() -> gdal::errors::Result<()> { - /// let src = Geometry::from_wkt("POLYGON ((0 0,10 10,0 10,10 0,0 0))")?; + /// let src = Geometry::from_wkt("POLYGON ((0 0, 10 10, 0 10, 10 0, 0 0))")?; /// let dst = src.make_valid(())?; /// assert_eq!("MULTIPOLYGON (((10 0,0 0,5 5,10 0)),((10 10,5 5,0 10,10 10)))", dst.wkt()?); /// # Ok(()) From 0793f7d20e2f8e385b492e1f2b9e67a9b5eef1fa Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 10 Jan 2023 14:40:45 -0500 Subject: [PATCH 4/4] "Refer" -> "See" --- src/vector/geometry.rs | 19 ++++++++++--------- src/vector/layer.rs | 6 +++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vector/geometry.rs b/src/vector/geometry.rs index 6f7b2ba8..9a965a6e 100644 --- a/src/vector/geometry.rs +++ b/src/vector/geometry.rs @@ -229,7 +229,7 @@ impl Geometry { /// /// `index` is the line string vertex index, from 0 to `point_count()-1`, or `0` when a point. /// - /// Refer: [`OGR_G_GetPoint`](https://gdal.org/api/vector_c_api.html#_CPPv414OGR_G_GetPoint12OGRGeometryHiPdPdPd) + /// See: [`OGR_G_GetPoint`](https://gdal.org/api/vector_c_api.html#_CPPv414OGR_G_GetPoint12OGRGeometryHiPdPdPd) pub fn get_point(&self, index: i32) -> (f64, f64, f64) { let mut x: c_double = 0.; let mut y: c_double = 0.; @@ -300,14 +300,14 @@ impl Geometry { /// Get the geometry type ordinal /// - /// Refer: [OGR_G_GetGeometryType](https://gdal.org/api/vector_c_api.html#_CPPv421OGR_G_GetGeometryType12OGRGeometryH) + /// See: [OGR_G_GetGeometryType](https://gdal.org/api/vector_c_api.html#_CPPv421OGR_G_GetGeometryType12OGRGeometryH) pub fn geometry_type(&self) -> OGRwkbGeometryType::Type { unsafe { gdal_sys::OGR_G_GetGeometryType(self.c_geometry()) } } /// Get the WKT name for the type of this geometry. /// - /// Refer: [`OGR_G_GetGeometryName`](https://gdal.org/api/vector_c_api.html#_CPPv421OGR_G_GetGeometryName12OGRGeometryH) + /// See: [`OGR_G_GetGeometryName`](https://gdal.org/api/vector_c_api.html#_CPPv421OGR_G_GetGeometryName12OGRGeometryH) pub fn geometry_name(&self) -> String { // Note: C API makes no statements about this possibly returning null. // So we don't have to result wrap this, @@ -326,7 +326,7 @@ impl Geometry { /// /// For a polygon, the returned number is the number of rings (exterior ring + interior rings). /// - /// Refer: [`OGR_G_GetGeometryCount`](https://gdal.org/api/vector_c_api.html#_CPPv422OGR_G_GetGeometryCount12OGRGeometryH) + /// See: [`OGR_G_GetGeometryCount`](https://gdal.org/api/vector_c_api.html#_CPPv422OGR_G_GetGeometryCount12OGRGeometryH) pub fn geometry_count(&self) -> usize { let cnt = unsafe { gdal_sys::OGR_G_GetGeometryCount(self.c_geometry()) }; cnt as usize @@ -336,7 +336,7 @@ impl Geometry { /// /// Only `wkbPoint` or `wkbLineString` may return a non-zero value. Other geometry types will return 0. /// - /// Refer: [`OGR_G_GetPointCount`](https://gdal.org/api/vector_c_api.html#_CPPv419OGR_G_GetPointCount12OGRGeometryH) + /// See: [`OGR_G_GetPointCount`](https://gdal.org/api/vector_c_api.html#_CPPv419OGR_G_GetPointCount12OGRGeometryH) pub fn point_count(&self) -> usize { let cnt = unsafe { gdal_sys::OGR_G_GetPointCount(self.c_geometry()) }; cnt as usize @@ -472,7 +472,7 @@ impl Geometry { /// /// When GEOS < 3.8, this method will return `Ok(self.clone())` if it is valid, or `Err` if not. /// - /// Refer: [OGR_G_MakeValidEx](https://gdal.org/api/vector_c_api.html#_CPPv417OGR_G_MakeValidEx12OGRGeometryH12CSLConstList) + /// See: [OGR_G_MakeValidEx](https://gdal.org/api/vector_c_api.html#_CPPv417OGR_G_MakeValidEx12OGRGeometryH12CSLConstList) /// /// # Example /// ```rust, no_run @@ -480,7 +480,7 @@ impl Geometry { /// # fn main() -> gdal::errors::Result<()> { /// let src = Geometry::from_wkt("POLYGON ((0 0, 10 10, 0 10, 10 0, 0 0))")?; /// let dst = src.make_valid(())?; - /// assert_eq!("MULTIPOLYGON (((10 0,0 0,5 5,10 0)),((10 10,5 5,0 10,10 10)))", dst.wkt()?); + /// assert_eq!("MULTIPOLYGON (((10 0, 0 0, 5 5, 10 0)),((10 10, 5 5, 0 10, 10 10)))", dst.wkt()?); /// # Ok(()) /// # } /// ``` @@ -708,7 +708,7 @@ mod tests { #[test] /// Repairable case (self-intersecting) pub fn test_make_valid_repairable() { - let src = Geometry::from_wkt("POLYGON ((0 0,10 10,0 10,10 0,0 0))").unwrap(); + let src = Geometry::from_wkt("POLYGON ((0 0, 10 10, 0 10, 10 0, 0 0))").unwrap(); let dst = src.make_valid(()); assert!(dst.is_ok()); } @@ -718,7 +718,8 @@ mod tests { /// Repairable case, but use extended options pub fn test_make_valid_ex() { let src = - Geometry::from_wkt("POLYGON ((0 0,0 10,10 10,10 0,0 0),(5 5,15 10,15 0,5 5))").unwrap(); + Geometry::from_wkt("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0),(5 5, 15 10, 15 0, 5 5))") + .unwrap(); let dst = src.make_valid(&[("STRUCTURE", "LINEWORK")]); assert!(dst.is_ok(), "{dst:?}"); } diff --git a/src/vector/layer.rs b/src/vector/layer.rs index 55db06ab..22260b30 100644 --- a/src/vector/layer.rs +++ b/src/vector/layer.rs @@ -252,7 +252,7 @@ pub trait LayerAccess: Sized { /// Set a feature on this layer layer. /// - /// Refer [SetFeature](https://gdal.org/doxygen/classOGRLayer.html#a681139bfd585b74d7218e51a32144283) + /// See: [SetFeature](https://gdal.org/doxygen/classOGRLayer.html#a681139bfd585b74d7218e51a32144283) fn set_feature(&self, feature: Feature) -> Result<()> { unsafe { gdal_sys::OGR_L_SetFeature(self.c_layer(), feature.c_feature()) }; Ok(()) @@ -260,7 +260,7 @@ pub trait LayerAccess: Sized { /// Set a spatial filter on this layer. /// - /// Refer [OGR_L_SetSpatialFilter](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) + /// See: [OGR_L_SetSpatialFilter](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) fn set_spatial_filter(&mut self, geometry: &Geometry) { unsafe { gdal_sys::OGR_L_SetSpatialFilter(self.c_layer(), geometry.c_geometry()) }; } @@ -421,7 +421,7 @@ pub trait LayerAccess: Sized { /// /// Returns `Some(SpatialRef)`, or `None` if one isn't defined. /// - /// Refer: [OGR_L_GetSpatialRef](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) + /// See: [OGR_L_GetSpatialRef](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) fn spatial_ref(&self) -> Option { let c_obj = unsafe { gdal_sys::OGR_L_GetSpatialRef(self.c_layer()) }; if c_obj.is_null() {