diff --git a/.travis.yml b/.travis.yml index 648f5f8..967bc38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust -dist: xenial +dist: groovy rust: - stable @@ -13,14 +13,33 @@ matrix: before_install: - sudo apt-get update - - sudo apt-get install libgeos-dev libgdal-dev valgrind + - sudo apt-get install valgrind + - sudo apt-get remove libgeos-dev -y script: + # First we install the 3.8 version of libgeos + - git clone https://github.com/libgeos/geos + - cd geos + # checking out 3.8.1 version + - git checkout 24650422b3982f17cc493fe92a70228f2ad624b4 + - ./autogen.sh + - ./configure + - make + - sudo make install + - sudo ldconfig + - cd .. + - export LD_LIBRARY_PATH=/usr/local/lib + # Now we can run tests + - cargo build - cargo build --features v3_8_0 - cargo build --features v3_7_0 - cargo build --features v3_6_0 + - cargo test --features v3_8_0 + - cargo test --features v3_7_0 + - cargo test --features v3_6_0 - cargo test --features geo - cargo test --features json + - cargo test --features 'v3_8_0,geo,json' - cargo test - cargo doc --features dox - cargo doc @@ -31,7 +50,10 @@ script: - cargo run --features geo --example from_geo # run valgrind to check that there are no memoryleaks # Note: cargo seems to randomly name the executable, so we use find to find all the tests - - find ./target/debug/deps -name "geos*" -type f -executable | xargs -n 1 valgrind --leak-check=full --error-exitcode=42 + # + # As long as leaks come from "geos::geom::GeometryFactory::getDefaultInstance", then + # it's fine (singleton). + - find ./target/debug/deps -name "geos*" -type f -executable | xargs -n 1 valgrind --leak-check=full --error-exitcode=42 --show-leak-kinds=all - valgrind --leak-check=full --error-exitcode=42 ./target/debug/examples/from_geo - valgrind --leak-check=full --error-exitcode=42 ./target/debug/examples/verbose_example - valgrind --leak-check=full --error-exitcode=42 ./target/debug/examples/prepared_geom diff --git a/Cargo.toml b/Cargo.toml index 33526c7..54ceec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "geos" -version = "5.0.0" +version = "6.0.0" authors = ["Matthieu Viry ", "Adrien Matissart ", "Antoine Desbordes ", "Guillaume Pinot ", "Guillaume Gomez "] license = "MIT" repository = "https://github.com/georust/geos" @@ -21,10 +21,10 @@ dox = ["geo-types", "wkt", "json"] libc = "0.2" num = "0.2" c_vec = "1.3" -geojson = { version = "0.17", optional = true } +geojson = { version = "0.19", optional = true } geo-types = { version = "0.4", optional = true } wkt = { version = "0.5", optional = true } -geos-sys = "1.0" +geos-sys = "2.0" doc-comment = "0.3" [package.metadata.docs.rs] diff --git a/examples/from_geo.rs b/examples/from_geo.rs index 9f30e32..69cf820 100644 --- a/examples/from_geo.rs +++ b/examples/from_geo.rs @@ -7,7 +7,7 @@ use geo_types::{Coordinate, LineString, Polygon}; #[cfg(feature = "geo")] use geos::from_geo::TryInto; #[cfg(feature = "geo")] -use geos::{Error, Geometry}; +use geos::{Error, Geom, Geometry}; #[cfg(feature = "geo")] fn fun() -> Result<(), Error> { @@ -45,8 +45,7 @@ fn main() { fun().unwrap(); } - #[cfg(not(feature = "geo"))] fn main() { - eprintln!("You need to enable the \"geo\" feature to run this example!", ); + eprintln!("You need to enable the \"geo\" feature to run this example!",); } diff --git a/examples/prepared_geom.rs b/examples/prepared_geom.rs index 0e53802..2def652 100644 --- a/examples/prepared_geom.rs +++ b/examples/prepared_geom.rs @@ -1,7 +1,7 @@ #[cfg(feature = "geo")] extern crate geos; #[cfg(feature = "geo")] -use geos::{version, Error, Geometry, PreparedGeometry}; +use geos::{version, Error, Geom, Geometry, PreparedGeometry}; #[cfg(feature = "geo")] fn fun() -> Result<(), Error> { @@ -24,7 +24,7 @@ fn fun() -> Result<(), Error> { Geometry::new_from_wkt("POINT (0.4 4.1)").unwrap(), ]; for geom in &vec_geoms { - print!("{:?} ", pg1.intersects(&geom)?); + print!("{:?} ", pg1.intersects(geom)?); } println!(""); Ok(()) @@ -37,5 +37,5 @@ fn main() { #[cfg(not(feature = "geo"))] fn main() { - eprintln!("You need to enable the \"geo\" feature to run this example!", ); + eprintln!("You need to enable the \"geo\" feature to run this example!",); } diff --git a/examples/verbose_example.rs b/examples/verbose_example.rs index 17c28b2..4237445 100644 --- a/examples/verbose_example.rs +++ b/examples/verbose_example.rs @@ -1,8 +1,11 @@ extern crate geos; -use geos::{version, Error, Geometry}; +use geos::{version, Error, Geom, Geometry}; fn fun() -> Result<(), Error> { - println!("geos_c version: {}", version().expect("failed to get version")); + println!( + "geos_c version: {}", + version().expect("failed to get version") + ); let g1 = Geometry::new_from_wkt("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))")?; println!("Geometry 1 created"); println!("Area : {}", g1.area()?); diff --git a/src/context_handle.rs b/src/context_handle.rs index 7bc2073..dfad6e0 100644 --- a/src/context_handle.rs +++ b/src/context_handle.rs @@ -3,14 +3,17 @@ use error::{Error, GResult}; use geos_sys::*; use libc::{c_char, c_void, strlen}; use std::ffi::CStr; +use std::ops::Deref; use std::slice; use std::sync::Mutex; -use std::ops::Deref; macro_rules! set_callbacks { ($c_func:ident, $kind:ident, $callback_name:ident, $last:ident) => { fn $kind<'a>(ptr: GEOSContextHandle_t, nf: *mut InnerContext<'a>) { - unsafe extern "C" fn message_handler_func<'a>(message: *const c_char, data: *mut c_void) { + unsafe extern "C" fn message_handler_func<'a>( + message: *const c_char, + data: *mut c_void, + ) { let inner_context: &InnerContext<'a> = &*(data as *mut _); if let Ok(callback) = inner_context.$callback_name.lock() { @@ -31,8 +34,18 @@ macro_rules! set_callbacks { }; } -set_callbacks!(GEOSContext_setNoticeMessageHandler_r, set_notif, notif_callback, last_notification); -set_callbacks!(GEOSContext_setErrorMessageHandler_r, set_error, error_callback, last_error); +set_callbacks!( + GEOSContext_setNoticeMessageHandler_r, + set_notif, + notif_callback, + last_notification +); +set_callbacks!( + GEOSContext_setErrorMessageHandler_r, + set_error, + error_callback, + last_error +); pub(crate) struct PtrWrap(pub T); @@ -77,7 +90,10 @@ impl<'a> ContextHandle<'a> { let ptr = unsafe { GEOS_init_r() }; if ptr.is_null() { return if let Some(ref caller) = caller { - Err(Error::GenericError(format!("GEOS_init_r failed from \"{}\"", caller))) + Err(Error::GenericError(format!( + "GEOS_init_r failed from \"{}\"", + caller + ))) } else { Err(Error::GenericError("GEOS_init_r failed".to_owned())) }; @@ -85,8 +101,10 @@ impl<'a> ContextHandle<'a> { let last_notification = Mutex::new(None); let last_error = Mutex::new(None); - let notif_callback: Mutex> = Mutex::new(Box::new(|_| {})); - let error_callback: Mutex> = Mutex::new(Box::new(|_| {})); + let notif_callback: Mutex> = + Mutex::new(Box::new(|_| {})); + let error_callback: Mutex> = + Mutex::new(Box::new(|_| {})); let inner = Box::into_raw(Box::new(InnerContext { notif_callback, diff --git a/src/coord_seq.rs b/src/coord_seq.rs index 6f20d37..bd21942 100644 --- a/src/coord_seq.rs +++ b/src/coord_seq.rs @@ -1,16 +1,11 @@ -use error::{Error, GResult}; -use context_handle::PtrWrap; -use geos_sys::*; +use crate::enums::TryFrom; use crate::{ - AsRaw, - ContextHandling, - ContextInteractions, - CoordDimensions, - ContextHandle, - Geometry, - Ordinate, + AsRaw, AsRawMut, ContextHandle, ContextHandling, ContextInteractions, CoordDimensions, + Geometry, Ordinate, }; -use crate::enums::TryFrom; +use context_handle::PtrWrap; +use error::{Error, GResult}; +use geos_sys::*; use std::sync::Arc; /// `CoordSeq` represents a list of coordinates inside a [`Geometry`]. @@ -64,12 +59,10 @@ impl<'a> CoordSeq<'a> { /// ``` pub fn new(size: u32, dims: CoordDimensions) -> GResult> { match ContextHandle::init_e(Some("CoordSeq::new")) { - Ok(context_handle) => { - unsafe { - let ptr = GEOSCoordSeq_create_r(context_handle.as_raw(), size, dims.into()); - CoordSeq::new_from_raw(ptr, Arc::new(context_handle), size, dims.into(), "new") - } - } + Ok(context_handle) => unsafe { + let ptr = GEOSCoordSeq_create_r(context_handle.as_raw(), size, dims.into()); + CoordSeq::new_from_raw(ptr, Arc::new(context_handle), size, dims.into(), "new") + }, Err(e) => Err(e), } } @@ -108,33 +101,42 @@ impl<'a> CoordSeq<'a> { return Err(Error::GenericError(e.to_owned())); } if !data.iter().skip(1).all(|x| x.as_ref().len() == dims) { - return Err(Error::GenericError("All vec entries must have the same size!".into())); + return Err(Error::GenericError( + "All vec entries must have the same size!".into(), + )); } match ContextHandle::init_e(Some("CoordSeq::new_from_vec")) { - Ok(context_handle) => { - unsafe { - let ptr = GEOSCoordSeq_create_r(context_handle.as_raw(), - size as _, - dims as _); - CoordSeq::new_from_raw(ptr, Arc::new(context_handle), size as _, dims as _, - "new_from_vec") - } - } + Ok(context_handle) => unsafe { + let ptr = GEOSCoordSeq_create_r(context_handle.as_raw(), size as _, dims as _); + CoordSeq::new_from_raw( + ptr, + Arc::new(context_handle), + size as _, + dims as _, + "new_from_vec", + ) + }, Err(e) => return Err(e), - }.and_then(|coord| { + } + .and_then(|mut coord| { let raw_context = coord.get_raw_context(); - let raw_coord = coord.as_raw(); + let raw_coord = coord.as_raw_mut(); - let funcs = [GEOSCoordSeq_setX_r, - GEOSCoordSeq_setY_r, - GEOSCoordSeq_setZ_r]; + let funcs = [ + GEOSCoordSeq_setX_r, + GEOSCoordSeq_setY_r, + GEOSCoordSeq_setZ_r, + ]; for (line, line_data) in data.iter().enumerate() { for (pos, elem) in line_data.as_ref().iter().enumerate() { unsafe { if funcs[pos](raw_context, raw_coord, line as _, *elem) == 0 { - let err = format!("Failed to set value at position {} on \ - line {}", pos, line); + let err = format!( + "Failed to set value at position {} on \ + line {}", + pos, line + ); return Err(Error::GenericError(err)); } } @@ -143,7 +145,9 @@ impl<'a> CoordSeq<'a> { Ok(coord) }) } else { - Err(Error::GenericError("Can't determine dimension for the CoordSeq".to_owned())) + Err(Error::GenericError( + "Can't determine dimension for the CoordSeq".to_owned(), + )) } } @@ -160,9 +164,17 @@ impl<'a> CoordSeq<'a> { } else { String::new() }; - return Err(Error::NoConstructionFromNullPtr(format!("CoordSeq::{}{}", caller, extra))); + return Err(Error::NoConstructionFromNullPtr(format!( + "CoordSeq::{}{}", + caller, extra + ))); } - Ok(CoordSeq { ptr: PtrWrap(ptr), context, nb_dimensions: dims as _, nb_lines: size as _ }) + Ok(CoordSeq { + ptr: PtrWrap(ptr), + context, + nb_dimensions: dims as _, + nb_lines: size as _, + }) } /// Sets the X position value at the given `line`. @@ -181,7 +193,7 @@ impl<'a> CoordSeq<'a> { assert!(line < self.nb_lines); let ret_val = unsafe { - GEOSCoordSeq_setX_r(self.get_raw_context(), self.as_raw(), line as _, val) + GEOSCoordSeq_setX_r(self.get_raw_context(), self.as_raw_mut(), line as _, val) }; if ret_val == 0 { Err(Error::GeosError("impossible to set x for coord".into())) @@ -209,7 +221,7 @@ impl<'a> CoordSeq<'a> { assert!(self.nb_dimensions >= 2); let ret_val = unsafe { - GEOSCoordSeq_setY_r(self.get_raw_context(), self.as_raw(), line as _, val) + GEOSCoordSeq_setY_r(self.get_raw_context(), self.as_raw_mut(), line as _, val) }; if ret_val == 0 { Err(Error::GeosError("impossible to set y for coord".into())) @@ -237,7 +249,7 @@ impl<'a> CoordSeq<'a> { assert!(self.nb_dimensions >= 3); let ret_val = unsafe { - GEOSCoordSeq_setZ_r(self.get_raw_context(), self.as_raw(), line as _, val) + GEOSCoordSeq_setZ_r(self.get_raw_context(), self.as_raw_mut(), line as _, val) }; if ret_val == 0 { Err(Error::GeosError("impossible to set z for coord".into())) @@ -259,19 +271,27 @@ impl<'a> CoordSeq<'a> { /// .expect("failed to create CoordSeq"); /// coords.set_ordinate(0, Ordinate::Z, 10.); /// assert_eq!(coords.get_z(0), Ok(10.)); - /// assert_eq!(coords.get_ordinate(0, Ordinate::Z), 10.); + /// assert_eq!(coords.get_ordinate(0, Ordinate::Z), Ok(10.)); /// ``` pub fn set_ordinate(&mut self, line: usize, ordinate: Ordinate, val: f64) -> GResult<()> { - let ordinate = ordinate.into(); + let ordinate: u32 = ordinate.into(); assert!(line < self.nb_lines); - assert!(self.nb_dimensions > ordinate); + assert!(self.nb_dimensions > ordinate as _); let ret_val = unsafe { - GEOSCoordSeq_setOrdinate_r(self.get_raw_context(), self.as_raw(), line as _, - ordinate, val) + GEOSCoordSeq_setOrdinate_r( + self.get_raw_context(), + self.as_raw_mut(), + line as _, + ordinate, + val, + ) }; if ret_val == 0 { - Err(Error::GeosError(format!("impossible to set value for ordinate {}", ordinate))) + Err(Error::GeosError(format!( + "impossible to set value for ordinate {}", + ordinate + ))) } else { Ok(()) } @@ -297,7 +317,9 @@ impl<'a> CoordSeq<'a> { GEOSCoordSeq_getX_r(self.get_raw_context(), self.as_raw(), line as _, &mut n) }; if ret_val == 0 { - Err(Error::GeosError("failed to get coordinates from CoordSeq".into())) + Err(Error::GeosError( + "failed to get coordinates from CoordSeq".into(), + )) } else { Ok(n as f64) } @@ -326,7 +348,9 @@ impl<'a> CoordSeq<'a> { GEOSCoordSeq_getY_r(self.get_raw_context(), self.as_raw(), line as _, &mut n) }; if ret_val == 0 { - Err(Error::GeosError("failed to get coordinates from CoordSeq".into())) + Err(Error::GeosError( + "failed to get coordinates from CoordSeq".into(), + )) } else { Ok(n as f64) } @@ -355,7 +379,9 @@ impl<'a> CoordSeq<'a> { GEOSCoordSeq_getZ_r(self.get_raw_context(), self.as_raw(), line as _, &mut n) }; if ret_val == 0 { - Err(Error::GeosError("failed to get coordinates from CoordSeq".into())) + Err(Error::GeosError( + "failed to get coordinates from CoordSeq".into(), + )) } else { Ok(n as f64) } @@ -374,15 +400,27 @@ impl<'a> CoordSeq<'a> { /// .expect("failed to create CoordSeq"); /// coords.set_ordinate(0, Ordinate::Z, 10.); /// assert_eq!(coords.get_z(0), Ok(10.)); - /// assert_eq!(coords.get_ordinate(0, Ordinate::Z), 10.); + /// assert_eq!(coords.get_ordinate(0, Ordinate::Z), Ok(10.)); /// ``` - pub fn get_ordinate(&self, line: usize, ordinate: Ordinate) -> f64 { - let ordinate = ordinate.into(); + pub fn get_ordinate(&self, line: usize, ordinate: Ordinate) -> GResult { + let ordinate: u32 = ordinate.into(); + let mut val = 0f64; assert!(line < self.nb_lines); - assert!(self.nb_dimensions > ordinate); - - unsafe { - GEOSCoordSeq_getOrdinate_r(self.get_raw_context(), self.as_raw(), line as _, ordinate) + assert!(self.nb_dimensions > ordinate as _); + + if unsafe { + GEOSCoordSeq_getOrdinate_r( + self.get_raw_context(), + self.as_raw(), + line as _, + ordinate, + &mut val, + ) + } != 1 + { + Err(Error::GeosError("getting size from CoordSeq".into())) + } else { + Ok(val) } } @@ -403,9 +441,8 @@ impl<'a> CoordSeq<'a> { /// ``` pub fn size(&self) -> GResult { let mut n = 0; - let ret_val = unsafe { - GEOSCoordSeq_getSize_r(self.get_raw_context(), self.as_raw(), &mut n) - }; + let ret_val = + unsafe { GEOSCoordSeq_getSize_r(self.get_raw_context(), self.as_raw(), &mut n) }; if ret_val == 0 { Err(Error::GeosError("getting size from CoordSeq".into())) } else { @@ -451,9 +488,8 @@ impl<'a> CoordSeq<'a> { /// ``` pub fn dimensions(&self) -> GResult { let mut n = 0; - let ret_val = unsafe { - GEOSCoordSeq_getDimensions_r(self.get_raw_context(), self.as_raw(), &mut n) - }; + let ret_val = + unsafe { GEOSCoordSeq_getDimensions_r(self.get_raw_context(), self.as_raw(), &mut n) }; if ret_val == 0 { Err(Error::GeosError("getting dimensions from CoordSeq".into())) } else { @@ -469,7 +505,9 @@ impl<'a> CoordSeq<'a> { unsafe { let mut is_ccw = 0; if GEOSCoordSeq_isCCW_r(self.get_raw_context(), self.as_raw(), &mut is_ccw) != 1 { - Err(Error::GenericError("GEOSCoordSeq_isCCW_r failed".to_owned())) + Err(Error::GenericError( + "GEOSCoordSeq_isCCW_r failed".to_owned(), + )) } else { Ok(is_ccw == 1) } @@ -481,7 +519,7 @@ impl<'a> CoordSeq<'a> { /// # Example /// /// ``` - /// use geos::{CoordDimensions, CoordSeq, Geometry}; + /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; /// /// let coords = CoordSeq::new_from_vec(&[&[1., 2.]]) /// .expect("failed to create CoordSeq"); @@ -499,7 +537,7 @@ impl<'a> CoordSeq<'a> { /// # Example /// /// ``` - /// use geos::{CoordDimensions, CoordSeq, Geometry}; + /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; /// /// let coords = CoordSeq::new_from_vec(&[&[1., 2.], &[3., 4.]]) /// .expect("failed to create CoordSeq"); @@ -528,21 +566,20 @@ impl<'a> Drop for CoordSeq<'a> { if self.ptr.is_null() { return; } - unsafe { GEOSCoordSeq_destroy_r(self.get_raw_context(), self.as_raw()) }; + unsafe { GEOSCoordSeq_destroy_r(self.get_raw_context(), self.as_raw_mut()) }; } } impl<'a> Clone for CoordSeq<'a> { /// Also pass the context to the newly created `CoordSeq`. fn clone(&self) -> CoordSeq<'a> { - let context = self.clone_context(); - let ptr = unsafe { GEOSCoordSeq_clone_r(context.as_raw(), self.as_raw()) }; + let ptr = unsafe { GEOSCoordSeq_clone_r(self.get_raw_context(), self.as_raw()) }; if ptr.is_null() { panic!("Couldn't clone CoordSeq..."); } CoordSeq { ptr: PtrWrap(ptr), - context, + context: self.clone_context(), nb_dimensions: self.nb_dimensions, nb_lines: self.nb_lines, } @@ -579,9 +616,17 @@ impl<'a> ContextInteractions<'a> for CoordSeq<'a> { } impl<'a> AsRaw for CoordSeq<'a> { - type RawType = *mut GEOSCoordSequence; + type RawType = GEOSCoordSequence; + + fn as_raw(&self) -> *const Self::RawType { + *self.ptr + } +} + +impl<'a> AsRawMut for CoordSeq<'a> { + type RawType = GEOSCoordSequence; - fn as_raw(&self) -> Self::RawType { + unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType { *self.ptr } } diff --git a/src/enums.rs b/src/enums.rs index 17e465f..3d54ae7 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -240,9 +240,9 @@ impl From for Orientation { fn from(orientation: c_int) -> Self { match orientation { -1 => Orientation::CounterClockwise, - 0 => Orientation::Clockwise, - 1 => Orientation::Colinear, - _ => panic!("invalid value for Orientation!"), + 0 => Orientation::Clockwise, + 1 => Orientation::Colinear, + _ => panic!("invalid value for Orientation!"), } } } @@ -253,9 +253,9 @@ impl TryFrom for Orientation { fn try_from(orientation: c_int) -> Result { match orientation { -1 => Ok(Orientation::CounterClockwise), - 0 => Ok(Orientation::Clockwise), - 1 => Ok(Orientation::Colinear), - _ => Err("value must be -1, 0 or 1"), + 0 => Ok(Orientation::Clockwise), + 1 => Ok(Orientation::Colinear), + _ => Err("value must be -1, 0 or 1"), } } } @@ -301,8 +301,8 @@ impl TryFrom for Ordinate { } } -impl Into for Ordinate { - fn into(self) -> size_t { +impl Into for Ordinate { + fn into(self) -> u32 { match self { Ordinate::X => 0, Ordinate::Y => 1, diff --git a/src/error.rs b/src/error.rs index ea8e722..f0d879f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,9 +22,11 @@ impl fmt::Display for Error { "error while calling libgeos method {} (error number = {})", p, e ), - Error::NoConstructionFromNullPtr(ref s) => { - write!(f, "impossible to build a geometry from a nullptr in \"{}\"", s) - } + Error::NoConstructionFromNullPtr(ref s) => write!( + f, + "impossible to build a geometry from a nullptr in \"{}\"", + s + ), Error::ConversionError(ref s) => write!(f, "impossible to convert geometry, {}", s), Error::GenericError(ref s) => write!(f, "generic error: {}", s), } diff --git a/src/from_geo.rs b/src/from_geo.rs index 93a9c2b..b9a5d2e 100644 --- a/src/from_geo.rs +++ b/src/from_geo.rs @@ -1,4 +1,4 @@ -use crate::{CoordDimensions, CoordSeq, Geometry as GGeom}; +use crate::{CoordDimensions, CoordSeq, Geometry as GGeometry}; use error::Error; use geo_types::{Coordinate, LineString, MultiPolygon, Point, Polygon}; use std; @@ -18,8 +18,8 @@ fn create_coord_seq<'a, 'b, It>(points: It, len: usize) -> Result, where It: Iterator>, { - let mut coord_seq = CoordSeq::new(len as u32, CoordDimensions::TwoD) - .expect("failed to create CoordSeq"); + let mut coord_seq = + CoordSeq::new(len as u32, CoordDimensions::TwoD).expect("failed to create CoordSeq"); for (i, p) in points.enumerate() { coord_seq.set_x(i, p.x)?; coord_seq.set_y(i, p.y)?; @@ -27,36 +27,36 @@ where Ok(coord_seq) } -impl<'a> TryInto> for &'a Point { +impl<'a> TryInto> for &'a Point { type Err = Error; - fn try_into(self) -> Result, Self::Err> { + fn try_into(self) -> Result, Self::Err> { let coord_seq = create_coord_seq(std::iter::once(&self.0), 1)?; - GGeom::create_point(coord_seq) + GGeometry::create_point(coord_seq) } } -impl<'a, T: Borrow>> TryInto> for &'a [T] { +impl<'a, T: Borrow>> TryInto> for &'a [T] { type Err = Error; - fn try_into(self) -> Result, Self::Err> { + fn try_into(self) -> Result, Self::Err> { let geom_points = self .into_iter() .map(|p| p.borrow().try_into()) .collect::, _>>()?; - GGeom::create_multipoint(geom_points) + GGeometry::create_multipoint(geom_points) } } -impl<'a> TryInto> for &'a LineString { +impl<'a> TryInto> for &'a LineString { type Err = Error; - fn try_into(self) -> Result, Self::Err> { + fn try_into(self) -> Result, Self::Err> { let coord_seq = create_coord_seq_from_vec(self.0.as_slice())?; - GGeom::create_line_string(coord_seq) + GGeometry::create_line_string(coord_seq) } } @@ -66,10 +66,10 @@ struct LineRing<'a>(&'a LineString); /// Convert a geo_types::LineString to a geos LinearRing /// a LinearRing should be closed so cloase the geometry if needed -impl<'a, 'b> TryInto> for &'a LineRing<'b> { +impl<'a, 'b> TryInto> for &'a LineRing<'b> { type Err = Error; - fn try_into(self) -> Result, Self::Err> { + fn try_into(self) -> Result, Self::Err> { let points = &(self.0).0; let nb_points = points.len(); if nb_points > 0 && nb_points < 3 { @@ -91,16 +91,16 @@ impl<'a, 'b> TryInto> for &'a LineRing<'b> { } else { create_coord_seq(points.iter(), nb_points)? }; - GGeom::create_linear_ring(coord_seq) + GGeometry::create_linear_ring(coord_seq) } } -impl<'a> TryInto> for &'a Polygon { +impl<'a> TryInto> for &'a Polygon { type Err = Error; - fn try_into(self) -> Result, Self::Err> { + fn try_into(self) -> Result, Self::Err> { let ring = LineRing(self.exterior()); - let geom_exterior: GGeom = ring.try_into()?; + let geom_exterior: GGeometry = ring.try_into()?; let interiors: Vec<_> = self .interiors() @@ -108,28 +108,28 @@ impl<'a> TryInto> for &'a Polygon { .map(|i| LineRing(i).try_into()) .collect::, _>>()?; - GGeom::create_polygon(geom_exterior, interiors) + GGeometry::create_polygon(geom_exterior, interiors) } } -impl<'a> TryInto> for &'a MultiPolygon { +impl<'a> TryInto> for &'a MultiPolygon { type Err = Error; - fn try_into(self) -> Result, Self::Err> { + fn try_into(self) -> Result, Self::Err> { let polygons: Vec<_> = self .0 .iter() .map(|p| p.try_into()) .collect::, _>>()?; - GGeom::create_multipolygon(polygons) + GGeometry::create_multipolygon(polygons) } } #[cfg(test)] mod test { - use super::GGeom; use super::LineRing; + use crate::{Geom, Geometry as GGeometry}; use from_geo::TryInto; use geo_types::{Coordinate, LineString, MultiPolygon, Polygon}; @@ -158,7 +158,7 @@ mod test { assert_eq!(p.exterior(), &exterior); assert_eq!(p.interiors(), interiors.as_slice()); - let geom: GGeom = (&p).try_into().unwrap(); + let geom: GGeometry = (&p).try_into().unwrap(); assert!(geom.contains(&geom).unwrap()); assert!(!geom.contains(&(&exterior).try_into().unwrap()).unwrap()); @@ -186,7 +186,7 @@ mod test { let p = Polygon::new(exterior, interiors); let mp = MultiPolygon(vec![p.clone()]); - let geom: GGeom = (&mp).try_into().unwrap(); + let geom: GGeometry = (&mp).try_into().unwrap(); assert!(geom.contains(&geom).unwrap()); assert!(geom.contains(&(&p).try_into().unwrap()).unwrap()); @@ -231,7 +231,7 @@ mod test { #[test] fn empty_linear_ring() { let ls = LineString(vec![]); - let geom: GGeom = LineRing(&ls).try_into().unwrap(); + let geom: GGeometry = LineRing(&ls).try_into().unwrap(); assert!(geom.is_valid()); assert!(geom.is_ring().unwrap()); @@ -242,7 +242,7 @@ mod test { #[test] fn one_elt_linear_ring() { let ls = LineString(coords(vec![(0., 0.)])); - let geom: Result = LineRing(&ls).try_into(); + let geom: Result = LineRing(&ls).try_into(); let error = geom.err().unwrap(); assert_eq!(format!("{}", error), "Invalid geometry, impossible to create a LinearRing, A LinearRing must have at least 3 coordinates".to_string()); } @@ -251,7 +251,7 @@ mod test { #[test] fn two_elt_linear_ring() { let ls = LineString(coords(vec![(0., 0.), (0., 1.)])); - let geom: Result = LineRing(&ls).try_into(); + let geom: Result = LineRing(&ls).try_into(); let error = geom.err().unwrap(); assert_eq!(format!("{}", error), "Invalid geometry, impossible to create a LinearRing, A LinearRing must have at least 3 coordinates".to_string()); } @@ -260,7 +260,7 @@ mod test { #[test] fn unclosed_linear_ring() { let ls = LineString(coords(vec![(0., 0.), (0., 1.), (1., 2.)])); - let geom: GGeom = LineRing(&ls).try_into().unwrap(); + let geom: GGeometry = LineRing(&ls).try_into().unwrap(); assert!(geom.is_valid()); assert!(geom.is_ring().unwrap()); @@ -282,7 +282,7 @@ mod test { #[test] fn closed_2_points_linear_ring() { let ls = LineString(coords(vec![(0., 0.), (0., 1.), (1., 1.)])); - let geom: GGeom = LineRing(&ls).try_into().unwrap(); + let geom: GGeometry = LineRing(&ls).try_into().unwrap(); assert!(geom.is_valid()); assert!(geom.is_ring().expect("is_ring failed")); @@ -293,7 +293,7 @@ mod test { #[test] fn good_linear_ring() { let ls = LineString(coords(vec![(0., 0.), (0., 1.), (1., 2.), (0., 0.)])); - let geom: GGeom = LineRing(&ls).try_into().unwrap(); + let geom: GGeometry = LineRing(&ls).try_into().unwrap(); assert!(geom.is_valid()); assert!(geom.is_ring().unwrap()); diff --git a/src/from_geojson.rs b/src/from_geojson.rs index 9d136ac..eda2bdc 100644 --- a/src/from_geojson.rs +++ b/src/from_geojson.rs @@ -1,8 +1,7 @@ -use crate::{CoordDimensions, CoordSeq, Geometry as GGeom}; -use error::{GResult, Error}; -use geojson::{Value, Geometry}; -use std; - +use crate::{CoordDimensions, CoordSeq, Geometry as GGeometry}; +use error::{Error, GResult}; +use geojson::{Geometry, Value}; +use std::iter; pub trait TryInto { type Err; @@ -17,8 +16,8 @@ fn create_coord_seq<'a, 'b, It>(points: It, len: usize) -> Result, where It: Iterator>, { - let mut coord_seq = CoordSeq::new(len as u32, CoordDimensions::TwoD) - .expect("failed to create CoordSeq"); + let mut coord_seq = + CoordSeq::new(len as u32, CoordDimensions::TwoD).expect("failed to create CoordSeq"); for (i, p) in points.enumerate() { coord_seq.set_x(i, p[0])?; @@ -37,111 +36,104 @@ fn create_closed_coord_seq_from_vec<'a>(points: &'a [Vec]) -> Result 0 && (!is_closed || nb_points == 3); if need_closing { - create_coord_seq( - points.iter().chain(std::iter::once(&points[0])), - nb_points + 1, - ) + create_coord_seq(points.iter().chain(iter::once(&points[0])), nb_points + 1) } else { create_coord_seq(points.iter(), nb_points) } } -impl<'a> TryInto> for &'a Geometry { +impl<'a> TryInto> for &'a Geometry { type Err = Error; - fn try_into(self) -> Result, Self::Err> { + fn try_into(self) -> Result, Self::Err> { match self.value { - Value::Point(ref c) => { - GGeom::create_point(create_coord_seq(std::iter::once(c), 1)?) - }, - Value::MultiPoint(ref pts) => { - let ggpts = pts.iter() - .map(|pt| { - GGeom::create_point(create_coord_seq(std::iter::once(pt), 1)?) - }) - .collect::>>()?; - GGeom::create_multipoint(ggpts) - }, + Value::Point(ref c) => GGeometry::create_point(create_coord_seq(iter::once(c), 1)?), + Value::MultiPoint(ref pts) => { + let ggpts = pts + .iter() + .map(|pt| GGeometry::create_point(create_coord_seq(iter::once(pt), 1)?)) + .collect::>>()?; + GGeometry::create_multipoint(ggpts) + } Value::LineString(ref line) => { let coord_seq = create_coord_seq_from_vec(line.as_slice())?; - GGeom::create_line_string(coord_seq) - }, + GGeometry::create_line_string(coord_seq) + } Value::MultiLineString(ref lines) => { - let gglines = lines.iter() + let gglines = lines + .iter() .map(|line| { let coord_seq = create_coord_seq_from_vec(line.as_slice())?; - GGeom::create_line_string(coord_seq) + GGeometry::create_line_string(coord_seq) }) - .collect::>>()?; - GGeom::create_multiline_string(gglines) - }, + .collect::>>()?; + GGeometry::create_multiline_string(gglines) + } Value::Polygon(ref rings) => { - let exterior_ring = GGeom::create_linear_ring( - create_closed_coord_seq_from_vec(rings[0].as_slice())? + let exterior_ring = GGeometry::create_linear_ring( + create_closed_coord_seq_from_vec(rings[0].as_slice())?, )?; - let interiors = rings.iter() + let interiors = rings + .iter() .skip(1) .map(|r| { - GGeom::create_linear_ring( - create_closed_coord_seq_from_vec(r.as_slice())?) + GGeometry::create_linear_ring(create_closed_coord_seq_from_vec( + r.as_slice(), + )?) }) - .collect::>>()?; - GGeom::create_polygon(exterior_ring, interiors) - }, + .collect::>>()?; + GGeometry::create_polygon(exterior_ring, interiors) + } Value::MultiPolygon(ref polygons) => { - let ggpolys = polygons.iter() - .map(|rings|{ - let exterior_ring = GGeom::create_linear_ring( - create_closed_coord_seq_from_vec(rings[0].as_slice())? + let ggpolys = polygons + .iter() + .map(|rings| { + let exterior_ring = GGeometry::create_linear_ring( + create_closed_coord_seq_from_vec(rings[0].as_slice())?, )?; - let interiors = rings.iter() + let interiors = rings + .iter() .skip(1) .map(|r| { - GGeom::create_linear_ring( - create_closed_coord_seq_from_vec(r.as_slice())?) + GGeometry::create_linear_ring(create_closed_coord_seq_from_vec( + r.as_slice(), + )?) }) - .collect::>>()?; - GGeom::create_polygon(exterior_ring, interiors) + .collect::>>()?; + GGeometry::create_polygon(exterior_ring, interiors) }) - .collect::>>()?; - GGeom::create_multipolygon(ggpolys) - }, + .collect::>>()?; + GGeometry::create_multipolygon(ggpolys) + } Value::GeometryCollection(ref geoms) => { let _geoms = geoms .iter() .map(|ref geom| geom.try_into()) - .collect::>>()?; - GGeom::create_geometry_collection(_geoms) + .collect::>>()?; + GGeometry::create_geometry_collection(_geoms) } } } } - #[cfg(test)] mod test { - use super::GGeom; use crate::from_geojson::TryInto; + use crate::{Geom, Geometry as GGeometry}; use geojson::{Geometry, Value}; #[test] fn geom_from_geojson_point() { let geojson_pt = Geometry::new(Value::Point(vec![1., 1.])); - let gpoint: GGeom = geojson_pt.try_into().unwrap(); + let gpoint: GGeometry = geojson_pt.try_into().unwrap(); - assert_eq!( - gpoint.to_wkt_precision(0), - Ok("POINT (1 1)".to_string()), - ); + assert_eq!(gpoint.to_wkt_precision(0), Ok("POINT (1 1)".to_string()),); } #[test] fn geom_from_geojson_multipoint() { - let geojson_pts = Geometry::new(Value::MultiPoint(vec![ - vec![1., 1.], - vec![2., 2.], - ])); - let gpts: GGeom = geojson_pts.try_into().unwrap(); + let geojson_pts = Geometry::new(Value::MultiPoint(vec![vec![1., 1.], vec![2., 2.]])); + let gpts: GGeometry = geojson_pts.try_into().unwrap(); assert_eq!( gpts.to_wkt_precision(0), Ok("MULTIPOINT (1 1, 2 2)".to_string()), @@ -150,11 +142,8 @@ mod test { #[test] fn geom_from_geojson_line() { - let geojson_line = Geometry::new(Value::LineString(vec![ - vec![1., 1.], - vec![2., 2.], - ])); - let gline: GGeom = geojson_line.try_into().unwrap(); + let geojson_line = Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]])); + let gline: GGeometry = geojson_line.try_into().unwrap(); assert_eq!( gline.to_wkt_precision(0), Ok("LINESTRING (1 1, 2 2)".to_string()), @@ -164,16 +153,10 @@ mod test { #[test] fn geom_from_geojson_multiline() { let geojson_lines = Geometry::new(Value::MultiLineString(vec![ - vec![ - vec![1., 1.], - vec![2., 2.], - ], - vec![ - vec![3., 3.], - vec![4., 4.], - ], + vec![vec![1., 1.], vec![2., 2.]], + vec![vec![3., 3.], vec![4., 4.]], ])); - let glines: GGeom = geojson_lines.try_into().unwrap(); + let glines: GGeometry = geojson_lines.try_into().unwrap(); assert_eq!( glines.to_wkt_precision(0), Ok("MULTILINESTRING ((1 1, 2 2), (3 3, 4 4))".to_string()), @@ -182,25 +165,23 @@ mod test { #[test] fn geom_from_geojson_polygon() { - let geojson_polygon = Geometry::new(Value::Polygon( + let geojson_polygon = Geometry::new(Value::Polygon(vec![ + vec![ + vec![0., 0.], + vec![0., 3.], + vec![3., 3.], + vec![3., 0.], + vec![0., 0.], + ], vec![ - vec![ - vec![0., 0.], - vec![0., 3.], - vec![3., 3.], - vec![3., 0.], - vec![0., 0.], - ], - vec![ - vec![0.2, 0.2], - vec![0.2, 2.], - vec![2., 2.], - vec![2., 0.2], - vec![0.2, 0.2], - ], + vec![0.2, 0.2], + vec![0.2, 2.], + vec![2., 2.], + vec![2., 0.2], + vec![0.2, 0.2], ], - )); - let gpolygon: GGeom = geojson_polygon.try_into().unwrap(); + ])); + let gpolygon: GGeometry = geojson_polygon.try_into().unwrap(); assert_eq!( gpolygon.to_wkt_precision(1), Ok("POLYGON ((0.0 0.0, 0.0 3.0, 3.0 3.0, 3.0 0.0, 0.0 0.0), (0.2 0.2, 0.2 2.0, 2.0 2.0, 2.0 0.2, 0.2 0.2))" @@ -210,24 +191,17 @@ mod test { #[test] fn geom_from_geojson_polygon_with_unclosed_interior_ring() { - let geojson_polygon = Geometry::new(Value::Polygon( + let geojson_polygon = Geometry::new(Value::Polygon(vec![ vec![ - vec![ - vec![0., 0.], - vec![0., 3.], - vec![3., 3.], - vec![3., 0.], - vec![0., 0.], - ], - vec![ - vec![0.2, 0.2], - vec![0.2, 2.], - vec![2., 2.], - vec![2., 0.2], - ], + vec![0., 0.], + vec![0., 3.], + vec![3., 3.], + vec![3., 0.], + vec![0., 0.], ], - )); - let gpolygon: GGeom = geojson_polygon.try_into().unwrap(); + vec![vec![0.2, 0.2], vec![0.2, 2.], vec![2., 2.], vec![2., 0.2]], + ])); + let gpolygon: GGeometry = geojson_polygon.try_into().unwrap(); assert_eq!( gpolygon.to_wkt_precision(1), Ok("POLYGON ((0.0 0.0, 0.0 3.0, 3.0 3.0, 3.0 0.0, 0.0 0.0), (0.2 0.2, 0.2 2.0, 2.0 2.0, 2.0 0.2, 0.2 0.2))" @@ -235,19 +209,16 @@ mod test { ); } - #[test] fn geom_from_geojson_multipolygon() { - let geojson_multipolygon = Geometry::new(Value::MultiPolygon( - vec![vec![vec![ - vec![0., 0.], - vec![0., 1.], - vec![1., 1.], - vec![1., 0.], - vec![0., 0.], - ]]] - )); - let gmultipolygon: GGeom = geojson_multipolygon.try_into().unwrap(); + let geojson_multipolygon = Geometry::new(Value::MultiPolygon(vec![vec![vec![ + vec![0., 0.], + vec![0., 1.], + vec![1., 1.], + vec![1., 0.], + vec![0., 0.], + ]]])); + let gmultipolygon: GGeometry = geojson_multipolygon.try_into().unwrap(); assert_eq!( gmultipolygon.to_wkt_precision(0), Ok("MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)))".to_string()), @@ -256,13 +227,11 @@ mod test { #[test] fn geom_from_geojson_geometry_collection() { - let geojson_gc = Geometry::new(Value::GeometryCollection( - vec![ - Geometry::new(Value::Point(vec![1., 1.])), - Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]])), - ] - )); - let gc: GGeom = geojson_gc.try_into().unwrap(); + let geojson_gc = Geometry::new(Value::GeometryCollection(vec![ + Geometry::new(Value::Point(vec![1., 1.])), + Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]])), + ])); + let gc: GGeometry = geojson_gc.try_into().unwrap(); assert_eq!( gc.to_wkt_precision(0), Ok("GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))".to_string()), diff --git a/src/functions.rs b/src/functions.rs index 6bd6726..5a5d9be 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -1,13 +1,13 @@ +use crate::{AsRawMut, ContextHandle, ContextHandling, Geom}; +use context_handle::PtrWrap; use enums::*; -use std::os::raw::{c_char}; use error::{Error, GResult, PredicateType}; -use geos_sys::*; use geometry::Geometry; +use geos_sys::*; use std::ffi::CStr; -use std::sync::Arc; +use std::os::raw::c_char; use std::str; -use crate::{ContextHandle, AsRaw, ContextHandling}; -use context_handle::PtrWrap; +use std::sync::Arc; // We need to cleanup only the char* from geos, the const char* are not to be freed. // this has to be checked method by method in geos @@ -15,14 +15,18 @@ use context_handle::PtrWrap; // and one that does not free it pub(crate) unsafe fn unmanaged_string(raw_ptr: *const c_char, caller: &str) -> GResult { if raw_ptr.is_null() { - return Err(Error::NoConstructionFromNullPtr(format!("{}::unmanaged_string", caller))); + return Err(Error::NoConstructionFromNullPtr(format!( + "{}::unmanaged_string", + caller + ))); } let c_str = CStr::from_ptr(raw_ptr); match str::from_utf8(c_str.to_bytes()) { Ok(s) => Ok(s.to_string()), - Err(e) => { - Err(Error::GenericError(format!("{}::unmanaged_string failed: {}", caller, e))) - } + Err(e) => Err(Error::GenericError(format!( + "{}::unmanaged_string failed: {}", + caller, e + ))), } } @@ -32,7 +36,10 @@ pub(crate) unsafe fn managed_string( caller: &str, ) -> GResult { if raw_ptr.is_null() { - return Err(Error::NoConstructionFromNullPtr(format!("{}::managed_string", caller))); + return Err(Error::NoConstructionFromNullPtr(format!( + "{}::managed_string", + caller + ))); } let s = unmanaged_string(raw_ptr, caller); GEOSFree_r(context.as_raw(), raw_ptr as *mut _); @@ -40,8 +47,8 @@ pub(crate) unsafe fn managed_string( } #[allow(dead_code)] -pub fn clip_by_rect<'a>( - g: &Geometry<'a>, +pub fn clip_by_rect<'a, G: Geom<'a>>( + g: &G, xmin: f64, ymin: f64, xmax: f64, @@ -49,14 +56,7 @@ pub fn clip_by_rect<'a>( ) -> GResult> { unsafe { let context = g.clone_context(); - let ptr = GEOSClipByRect_r( - context.as_raw(), - g.as_raw(), - xmin, - ymin, - xmax, - ymax, - ); + let ptr = GEOSClipByRect_r(context.as_raw(), g.as_raw(), xmin, ymin, xmax, ymax); Geometry::new_from_raw(ptr, context, "clip_by_rect") } } @@ -65,11 +65,11 @@ pub fn version() -> GResult { unsafe { unmanaged_string(GEOSversion(), "version") } } -pub(crate) fn check_geos_predicate(val: i32, p: PredicateType) -> GResult { +pub(crate) fn check_geos_predicate(val: i8, p: PredicateType) -> GResult { match val { 1 => Ok(true), 0 => Ok(false), - _ => Err(Error::GeosFunctionError(p, val)), + _ => Err(Error::GeosFunctionError(p, val as _)), } } @@ -98,7 +98,7 @@ pub(crate) fn create_multi_geom<'a>( geoms[0].clone_context() }; let res = { - let mut geoms: Vec<*mut GEOSGeometry> = geoms.iter_mut().map(|g| g.as_raw()).collect(); + let mut geoms: Vec<*mut GEOSGeometry> = geoms.iter_mut().map(|g| g.as_raw_mut()).collect(); unsafe { let ptr = GEOSGeom_createCollection_r( context.as_raw(), @@ -128,16 +128,20 @@ pub fn orientation_index( py: f64, ) -> GResult { match ContextHandle::init() { - Ok(context) => { - unsafe { - match Orientation::try_from( - GEOSOrientationIndex_r(context.as_raw(), ax, ay, bx, by, px, py) - ) { - Ok(o) => Ok(o), - Err(e) => Err(Error::GenericError(e.to_owned())), - } + Ok(context) => unsafe { + match Orientation::try_from(GEOSOrientationIndex_r( + context.as_raw(), + ax, + ay, + bx, + by, + px, + py, + )) { + Ok(o) => Ok(o), + Err(e) => Err(Error::GenericError(e.to_owned())), } - } + }, Err(e) => Err(e), } } @@ -157,22 +161,33 @@ pub fn segment_intersection( by1: f64, ) -> GResult> { match ContextHandle::init() { - Ok(context) => { - unsafe { - let mut cx = 0.; - let mut cy = 0.; - - let ret = GEOSSegmentIntersection_r( - context.as_raw(), ax0, ay0, ax1, ay1, bx0, by0, bx1, by1, &mut cx, &mut cy); - if ret == -1 { - Ok(None) - } else if ret == 0 { - Ok(Some((cx, cy))) - } else { - Err(Error::GenericError("GEOSSegmentIntersection_r failed".to_owned())) - } + Ok(context) => unsafe { + let mut cx = 0.; + let mut cy = 0.; + + let ret = GEOSSegmentIntersection_r( + context.as_raw(), + ax0, + ay0, + ax1, + ay1, + bx0, + by0, + bx1, + by1, + &mut cx, + &mut cy, + ); + if ret == -1 { + Ok(None) + } else if ret == 0 { + Ok(Some((cx, cy))) + } else { + Err(Error::GenericError( + "GEOSSegmentIntersection_r failed".to_owned(), + )) } - } + }, Err(e) => Err(e), } } diff --git a/src/geometry.rs b/src/geometry.rs index 51d32e0..5077917 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,2281 +1,2529 @@ -use std::borrow::Borrow; -use crate::{ - CoordSeq, ContextHandle, AsRaw, ContextHandling, ContextInteractions, PreparedGeometry, - WKTWriter, -}; #[cfg(any(feature = "v3_6_0", feature = "dox"))] use crate::Precision; +use crate::{ + AsRaw, AsRawMut, ContextHandle, ContextHandling, ContextInteractions, CoordSeq, + PreparedGeometry, WKTWriter, +}; +use c_vec::CVec; use context_handle::PtrWrap; use enums::*; use error::{Error, GResult, PredicateType}; -use geos_sys::*; use functions::*; +use geos_sys::*; +use std::borrow::Borrow; use std::ffi::CString; -use std::{self, str}; -use c_vec::CVec; use std::sync::Arc; +use std::{self, str}; /// Representation of a GEOS geometry. /// /// # Example /// /// ``` -/// use geos::Geometry; +/// use geos::{Geom, Geometry}; /// -/// let point_geom = Geometry::new_from_wkt("POINT (2.5 3.5)").expect("Invalid geometry"); +/// let point_geom = Geometry::new_from_wkt("POINT (2.5 3.5)") +/// .expect("Invalid geometry"); /// assert_eq!(point_geom.get_x(), Ok(2.5)); /// assert_eq!(point_geom.get_y(), Ok(3.5)); /// ``` pub struct Geometry<'a> { pub(crate) ptr: PtrWrap<*mut GEOSGeometry>, - context: Arc>, - owned: bool, + pub(crate) context: Arc>, } -impl<'a> Geometry<'a> { - /// Creates a `Geometry` from the WKT format. +// Representation of a GEOS geometry. Since it's only a view over another GEOS geometry data, +/// only not mutable operations are implemented on it. +/// +/// # Example +/// +/// ``` +/// use geos::{Geom, Geometry}; +/// +///let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0),\ +/// (1 1, 2 1, 2 5, 1 5, 1 1),\ +/// (8 5, 8 4, 9 4, 9 5, 8 5))") +/// .expect("Invalid geometry"); +/// let point_geom = geom +/// .get_interior_ring_n(0) +/// .expect("failed to get const geometry"); +/// ``` +pub struct ConstGeometry<'a, 'b> { + pub(crate) ptr: PtrWrap<*const GEOSGeometry>, + pub(crate) original: &'b Geometry<'a>, +} + +pub trait Geom<'a>: + AsRaw + ContextHandling>> +{ + /// Returns the type of the geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))") + /// .expect("Invalid geometry"); + /// assert_eq!(geom.get_type(), Ok("Polygon".to_owned())); /// ``` - pub fn new_from_wkt(wkt: &str) -> GResult> { - match ContextHandle::init_e(Some("Geometry::new_from_wkt")) { - Ok(context_handle) => { - match CString::new(wkt) { - Ok(c_str) => { - unsafe { - let reader = GEOSWKTReader_create_r(context_handle.as_raw()); - let ptr = GEOSWKTReader_read_r(context_handle.as_raw(), reader, - c_str.as_ptr()); - GEOSWKTReader_destroy_r(context_handle.as_raw(), reader); - Geometry::new_from_raw(ptr, Arc::new(context_handle), "new_from_wkt") - } - } - Err(e) => { - Err(Error::GenericError(format!("Conversion to CString failed: {}", e))) - } - } - } - Err(e) => Err(e), - } - } - - /// Create a new [`Geometry`] from the HEX format. + fn get_type(&self) -> GResult; + fn geometry_type(&self) -> GeometryTypes; + /// Checks if the geometry is valid. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let hex_buf = point_geom.to_hex().expect("conversion to HEX failed"); + /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))") + /// .expect("Invalid geometry"); + /// assert!(geom.is_valid() == false); + /// ``` + fn is_valid(&self) -> bool; + /// Returns an explanation on why the geometry is invalid. + /// + /// # Example /// - /// // The interesting part is here: - /// let new_geom = Geometry::new_from_hex(hex_buf.as_ref()) - /// .expect("conversion from HEX failed"); - /// assert_eq!(point_geom.equals(&new_geom), Ok(true)); /// ``` - pub fn new_from_hex(hex: &[u8]) -> GResult> { - match ContextHandle::init_e(Some("Geometry::new_from_hex")) { - Ok(context) => { - unsafe { - let ptr = GEOSGeomFromHEX_buf_r(context.as_raw(), hex.as_ptr(), hex.len()); - Geometry::new_from_raw(ptr, Arc::new(context), "new_from_hex") - } - } - Err(e) => Err(e), - } - } - - /// Create a new [`Geometry`] from the WKB format. + /// use geos::{Geom, Geometry}; + /// + /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))") + /// .expect("Invalid geometry"); + /// assert_eq!( + /// geom.is_valid_reason(), + /// Ok("Self-intersection[0 0]".to_owned()), + /// ); + /// ``` + fn is_valid_reason(&self) -> GResult; + /// Get the underlying geos CoordSeq object from the geometry + /// + /// Note: this clones the underlying CoordSeq to avoid double free + /// (because CoordSeq handles the object ptr and the CoordSeq is still owned by the geos + /// geometry) if this method's performance becomes a bottleneck, feel free to open an issue, + /// we could skip this clone with cleaner code. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let wkb_buf = point_geom.to_wkb().expect("conversion to WKB failed"); + /// let geom = Geometry::new_from_wkt("POINT (2 3)") + /// .expect("Invalid geometry"); + /// let coord_seq = geom.get_coord_seq().expect("get_coord_seq failed"); /// - /// // The interesting part is here: - /// let new_geom = Geometry::new_from_wkb(wkb_buf.as_ref()) - /// .expect("conversion from WKB failed"); - /// assert_eq!(point_geom.equals(&new_geom), Ok(true)); + /// assert_eq!(coord_seq.get_x(0), Ok(2.)); + /// assert_eq!(coord_seq.get_y(0), Ok(3.)); /// ``` - pub fn new_from_wkb(wkb: &[u8]) -> GResult> { - match ContextHandle::init_e(Some("Geometry::new_from_wkb")) { - Ok(context) => { - unsafe { - let ptr = GEOSGeomFromWKB_buf_r(context.as_raw(), wkb.as_ptr(), wkb.len()); - Geometry::new_from_raw(ptr, Arc::new(context), "new_from_wkb") - } - } - Err(e) => Err(e), - } - } - - /// Converts a [`Geometry`] to the HEX format. For more control over the generated output, - /// use the [`WKBWriter`] type. + fn get_coord_seq(&self) -> GResult>; + /// Returns the area of the geometry. Units are specified by the SRID of the given geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let hex_buf = point_geom.to_hex().expect("conversion to WKB failed"); + /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); + /// assert_eq!(geom1.area(), Ok(60.)); /// ``` - pub fn to_hex(&self) -> GResult> { - let mut size = 0; - unsafe { - let ptr = GEOSGeomToHEX_buf_r(self.get_raw_context(), self.as_raw(), &mut size); - if ptr.is_null() { - Err(Error::NoConstructionFromNullPtr( - "Geometry::to_hex failed: GEOSGeomToHEX_buf_r returned null pointer".to_owned()) - ) - } else { - Ok(CVec::new(ptr, size as _)) - } - } - } - - /// Converts a [`Geometry`] to the WKB format. For more control over the generated output, - /// use the [`WKBWriter`] type. + fn area(&self) -> GResult; + /// Returns a WKT representation of the geometry. It defaults to 2 dimensions output. Use + /// [`WKTWriter`] type directly if you want more control. + /// + /// # Examples + /// + /// ``` + /// use geos::{Geom, Geometry, OutputDimension, WKTWriter}; + /// + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); + /// assert_eq!( + /// point_geom.to_wkt().unwrap(), + /// "POINT (2.5000000000000000 2.5000000000000000)", + /// ); + /// + /// // A three dimension point will be output just as a 2 dimension: + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 3)") + /// .expect("Invalid geometry"); + /// assert_eq!( + /// point_geom.to_wkt().unwrap(), + /// "POINT (2.5000000000000000 2.5000000000000000)", + /// ); + /// + /// // To "fix" it, use `WKTWriter` instead: + /// let mut wkt_writer = WKTWriter::new() + /// .expect("Failed to create WKTWriter"); + /// wkt_writer.set_output_dimension(OutputDimension::ThreeD); + /// assert_eq!( + /// wkt_writer.write(&point_geom).unwrap(), + /// "POINT Z (2.5000000000000000 2.5000000000000000 3.0000000000000000)", + /// ); + /// ``` + fn to_wkt(&self) -> GResult; + /// Returns a WKT representation of the geometry with the given `precision`. It is a wrapper + /// around [`WKTWriter::set_rounding_precision`]. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry, WKTWriter}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let wkb_buf = point_geom.to_wkb().expect("conversion to WKB failed"); + /// assert_eq!(point_geom.to_wkt_precision(2).unwrap(), "POINT (2.50 2.50)"); + /// + /// // It is a wrapper around: + /// let mut writer = WKTWriter::new().expect("Failed to create WKTWriter"); + /// writer.set_rounding_precision(2); + /// assert_eq!(writer.write(&point_geom).unwrap(), "POINT (2.50 2.50)"); /// ``` - pub fn to_wkb(&self) -> GResult> { - let mut size = 0; - unsafe { - let ptr = GEOSGeomToWKB_buf_r(self.get_raw_context(), self.as_raw(), &mut size); - if ptr.is_null() { - Err(Error::NoConstructionFromNullPtr( - "Geometry::to_wkb failed: GEOSGeomToWKB_buf_r returned null pointer".to_owned()) - ) - } else { - Ok(CVec::new(ptr, size as _)) - } - } - } - - /// Creates a new [`PreparedGeometry`] from the current `Geometry`. + fn to_wkt_precision(&self, precision: u32) -> GResult; + /// Returns `true` if the geometry is a ring. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let prepared_geom = point_geom.to_prepared_geom().expect("failed to create prepared geom"); + /// let circle = Geometry::new_from_wkt("LINESTRING(0 0, 0 1, 1 1, 0 0)").expect("Invalid geometry"); + /// assert_eq!(circle.is_ring(), Ok(true)); /// ``` - pub fn to_prepared_geom(&self) -> GResult> { - PreparedGeometry::new(self) - } - - /// Creates an areal geometry formed by the constituent linework of given geometry. + fn is_ring(&self) -> GResult; + /// Returns `true` if `self` shares any portion of space with `other`. So if any of this is + /// `true`: /// - /// You can find new illustrations on [postgis](https://postgis.net/docs/ST_BuildArea.html) - /// documentation. + /// * `self` overlaps `other` + /// * `self` touches `other` + /// * `self` is within `other` /// - /// Available using the `v3_8_0` feature. + /// Then `intersects` will return `true` as well. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT(100 90)").expect("Invalid geometry"); - /// let small_geom = geom.buffer(25., 8).expect("buffer failed"); - /// let big_geom = geom.buffer(50., 8).expect("buffer failed"); + /// let geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("invalid geometry"); + /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("invalid geometry"); /// - /// let union_geom = small_geom.union(&big_geom).expect("union failed"); - /// let build_area_geom = union_geom.build_area().expect("build_area failed"); + /// assert_eq!(geom1.intersects(&geom2), Ok(false)); + /// assert_eq!(geom1.intersects(&geom3), Ok(true)); + /// ``` + fn intersects<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns `true` if `self` and `other` have at least one interior into each other. + /// + /// # Example /// - /// // Looks like a donut. - /// assert_eq!(union_geom.to_wkt_precision(1).unwrap(), - /// "POLYGON ((150.0 90.0, 149.0 80.2, 146.2 70.9, 141.6 62.2, 135.4 54.6, \ - /// 127.8 48.4, 119.1 43.8, 109.8 41.0, 100.0 40.0, 90.2 41.0, \ - /// 80.9 43.8, 72.2 48.4, 64.6 54.6, 58.4 62.2, 53.8 70.9, 51.0 80.2, \ - /// 50.0 90.0, 51.0 99.8, 53.8 109.1, 58.4 117.8, 64.6 125.4, \ - /// 72.2 131.6, 80.9 136.2, 90.2 139.0, 100.0 140.0, 109.8 139.0, \ - /// 119.1 136.2, 127.8 131.6, 135.4 125.4, 141.6 117.8, 146.2 109.1, \ - /// 149.0 99.8, 150.0 90.0))"); /// ``` - #[cfg(any(feature = "v3_8_0", feature = "dox"))] - pub fn build_area(&self) -> GResult> { - unsafe { - let ptr = GEOSBuildArea_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "build_area") - } - } - - /// Description from [postgis](https://postgis.net/docs/ST_Polygonize.html): + /// use geos::{Geom, Geometry}; /// - /// > Creates a GeometryCollection containing possible polygons formed from the constituent - /// > linework of a set of geometries. + /// let geom1 = Geometry::new_from_wkt("LINESTRING(1 1,2 2)").expect("invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 1,1 2)").expect("invalid geometry"); /// - /// # Example: + /// assert_eq!(geom1.crosses(&geom2), Ok(true)); + /// ``` + fn crosses<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns `true` if `self` doesn't: + /// + /// * Overlap `other` + /// * Touch `other` + /// * Is within `other` + /// + /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((-71.040878 42.285678,\ - /// -71.040943 42.2856,\ - /// -71.04096 42.285752,\ - /// -71.040878 42.285678))") - /// .expect("Failed to create geometry"); - /// let geom2 = Geometry::new_from_wkt("POLYGON((-71.17166 42.353675,\ - /// -71.172026 42.354044,\ - /// -71.17239 42.354358,\ - /// -71.171794 42.354971,\ - /// -71.170511 42.354855,\ - /// -71.17112 42.354238,\ - /// -71.17166 42.353675))") - /// .expect("Failed to create geometry"); + /// let geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("invalid geometry"); + /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("invalid geometry"); /// - /// let polygonized = Geometry::polygonize(&[geom1, geom2]).expect("polygonize failed"); - /// assert_eq!(polygonized.to_wkt().unwrap(), - /// "GEOMETRYCOLLECTION (POLYGON ((-71.0408780000000064 42.2856779999999972, \ - /// -71.0409429999999986 42.2856000000000023, \ - /// -71.0409599999999983 42.2857520000000022, \ - /// -71.0408780000000064 42.2856779999999972)), \ - /// POLYGON ((-71.1716600000000028 42.3536750000000026, \ - /// -71.1720260000000025 42.3540440000000018, \ - /// -71.1723899999999929 42.3543579999999977, \ - /// -71.1717940000000056 42.3549709999999990, \ - /// -71.1705110000000047 42.3548550000000006, \ - /// -71.1711200000000019 42.3542380000000023, \ - /// -71.1716600000000028 42.3536750000000026)))"); + /// assert_eq!(geom1.disjoint(&geom2), Ok(true)); + /// assert_eq!(geom1.disjoint(&geom3), Ok(false)); /// ``` - pub fn polygonize>>(geometries: &[T]) -> GResult> { - unsafe { - let context = match geometries.get(0) { - Some(g) => g.borrow().clone_context(), - None => { - match ContextHandle::init_e(Some("Geometry::polygonize")) { - Ok(context) => Arc::new(context), - Err(e) => return Err(e), - } - } - }; - let geoms = geometries.iter() - .map(|g| g.borrow().as_raw() as *const _) - .collect::>(); - let ptr = GEOSPolygonize_r(context.as_raw(), geoms.as_ptr(), geoms.len() as _); - Geometry::new_from_raw(ptr, context, "polygonize") - } - } - - pub fn polygonizer_get_cut_edges>>( - &self, - geometries: &[T], - ) -> GResult> { - unsafe { - let context = match geometries.get(0) { - Some(g) => g.borrow().clone_context(), - None => { - match ContextHandle::init_e(Some("Geometry::polygonizer_get_cut_edges")) { - Ok(context) => Arc::new(context), - Err(e) => return Err(e), - } - } - }; - let geoms = geometries.iter() - .map(|g| g.borrow().as_raw() as *const _) - .collect::>(); - let ptr = GEOSPolygonizer_getCutEdges_r( - context.as_raw(), - geoms.as_ptr(), - geoms.len() as _, - ); - Geometry::new_from_raw(ptr, context, "polygonizer_get_cut_edges") - } - } - - /// Merges `Multi Line String` geometry into a (set of) `Line String`. - /// - /// ### Warning - /// - /// If you use this function on something else than a `Multi Line String` or a - /// `Line String`, it'll return an empty `Geometry collection`. + fn disjoint<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns `true` if the only points in common between `self` and `other` lie in the union of + /// the boundaries of `self` and `other`. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let lines = Geometry::new_from_wkt("MULTILINESTRING((-29 -27,-30 -29.7,-36 -31,-45 -33),\ - /// (-45 -33,-46 -32))") - /// .expect("Invalid geometry"); - /// let lines_merged = lines.line_merge().expect("line merge failed"); - /// assert_eq!(lines_merged.to_wkt_precision(1).unwrap(), - /// "LINESTRING (-29.0 -27.0, -30.0 -29.7, -36.0 -31.0, -45.0 -33.0, -46.0 -32.0)"); - /// ``` - pub fn line_merge(&self) -> GResult> { - unsafe { - let ptr = GEOSLineMerge_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "line_merge") - } - } - - /// Reverses the order of the vertexes. + /// let geom1 = Geometry::new_from_wkt("LINESTRING(0 0, 1 1, 0 2)").expect("invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT(1 1)").expect("invalid geometry"); /// - /// Available using the `v3_7_0` feature. + /// assert_eq!(geom1.touches(&geom2), Ok(false)); /// - /// # Example + /// let geom2 = Geometry::new_from_wkt("POINT(0 2)").expect("invalid geometry"); /// + /// assert_eq!(geom1.touches(&geom2), Ok(true)); /// ``` - /// use geos::Geometry; + fn touches<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns `true` if `self` spatially overlaps `other`. /// - /// let line = Geometry::new_from_wkt("LINESTRING(1 10,1 2)").expect("invalid geometry"); - /// let reversed_line = line.reverse().expect("reverse failed"); + /// # Example /// - /// assert_eq!(reversed_line.to_wkt_precision(1).unwrap(), "LINESTRING (1.0 2.0, 1.0 10.0)"); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn reverse(&self) -> GResult> { - unsafe { - let ptr = GEOSReverse_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "reverse") - } - } - - /// Returns a simplified version of the given geometry. - pub fn simplify(&self, tolerance: f64) -> GResult> { - unsafe { - let ptr = GEOSSimplify_r(self.get_raw_context(), self.as_raw(), tolerance); - Geometry::new_from_raw(ptr, self.clone_context(), "simplify") - } - } - - /// Returns a simplified version of the given geometry. It will avoid creating invalid derived - /// geometries. - pub fn topology_preserve_simplify(&self, tolerance: f64) -> GResult> { - unsafe { - let ptr = GEOSTopologyPreserveSimplify_r( - self.get_raw_context(), - self.as_raw(), - tolerance); - Geometry::new_from_raw(ptr, self.clone_context(), "topology_preserve_simplify") - } - } - - pub(crate) unsafe fn new_from_raw( - ptr: *mut GEOSGeometry, - context: Arc>, - caller: &str, - ) -> GResult> { - if ptr.is_null() { - let extra = if let Some(x) = context.get_last_error() { - format!("\nLast error: {}", x) - } else { - String::new() - }; - return Err(Error::NoConstructionFromNullPtr(format!("Geometry::{}{}", caller, extra))); - } - Ok(Geometry { ptr: PtrWrap(ptr), context, owned: true, }) - } - - /// Checks if the geometry is valid. + /// use geos::{Geom, Geometry}; /// - /// # Example + /// let geom1 = Geometry::new_from_wkt("POINT(1 0.5)").expect("invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(1 0, 1 1, 3 5)").expect("invalid geometry"); /// - /// ``` - /// use geos::Geometry; + /// assert_eq!(geom1.overlaps(&geom2), Ok(false)); /// - /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))") - /// .expect("Invalid geometry"); - /// assert!(geom.is_valid() == false); + /// let geom1 = geom1.buffer(3., 8).expect("buffer failed"); + /// let geom2 = geom2.buffer(0.5, 8).expect("buffer failed"); + /// + /// assert_eq!(geom1.overlaps(&geom2), Ok(true)); /// ``` - pub fn is_valid(&self) -> bool { - unsafe { GEOSisValid_r(self.get_raw_context(), self.as_raw()) == 1 } - } - - /// Returns an explanation on why the geometry is invalid. + fn overlaps<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns `true` if `self` is completely inside `other`. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))") - /// .expect("Invalid geometry"); - /// assert_eq!(geom.is_valid_reason(), Ok("Self-intersection[0 0]".to_owned())); + /// let geom = Geometry::new_from_wkt("POINT(50 50)").expect("invalid geometry"); + /// let small_geom = geom.buffer(20., 8).expect("buffer failed"); + /// let big_geom = geom.buffer(40., 8).expect("buffer failed"); + /// + /// assert_eq!(small_geom.within(&small_geom), Ok(true)); + /// assert_eq!(small_geom.within(&big_geom), Ok(true)); + /// assert_eq!(big_geom.within(&small_geom), Ok(false)); /// ``` - pub fn is_valid_reason(&self) -> GResult { - unsafe { - let ptr = GEOSisValidReason_r(self.get_raw_context(), self.as_raw()); - managed_string(ptr, self.get_context_handle(), "GGeom::is_valid_reason") - } - } - - /// Returns the type of the geometry. + fn within<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Checks if the two [`Geometry`] objects are equal. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))") - /// .expect("Invalid geometry"); - /// assert_eq!(geom.get_type(), Ok("Polygon".to_owned())); - /// ``` - pub fn get_type(&self) -> GResult { - unsafe { - let ptr = GEOSGeomType_r(self.get_raw_context(), self.as_raw()); - managed_string(ptr, self.get_context_handle(), "GGeom::get_type") - } - } - - /// Get the underlying geos CoordSeq object from the geometry + /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)").expect("Invalid geometry"); + /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); /// - /// Note: this clones the underlying CoordSeq to avoid double free - /// (because CoordSeq handles the object ptr and the CoordSeq is still owned by the geos - /// geometry) if this method's performance becomes a bottleneck, feel free to open an issue, - /// we could skip this clone with cleaner code. + /// assert!(geom1.equals(&geom2) == Ok(false)); + /// assert!(geom1.equals(&geom3) == Ok(true)); + /// ``` /// - /// # Example + /// Note that you can also use method through the `PartialEq` trait: /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT (2 3)").expect("Invalid geometry"); - /// let coord_seq = geom.get_coord_seq().expect("get_coord_seq failed"); + /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)").expect("Invalid geometry"); + /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); /// - /// assert_eq!(coord_seq.get_x(0), Ok(2.)); - /// assert_eq!(coord_seq.get_y(0), Ok(3.)); + /// assert!(geom1 != geom2); + /// assert!(geom1 == geom3); /// ``` - pub fn get_coord_seq(&self) -> GResult> { - let type_geom = self.geometry_type(); - match type_geom { - GeometryTypes::Point | GeometryTypes::LineString | GeometryTypes::LinearRing => unsafe { - let coord = GEOSGeom_getCoordSeq_r(self.get_raw_context(), self.as_raw()); - let t = GEOSCoordSeq_clone_r(self.get_raw_context(), coord); - let mut size = 0; - let mut dims = 0; - - if GEOSCoordSeq_getSize_r(self.get_raw_context(), coord, &mut size) == 0 { - return Err(Error::GenericError("GEOSCoordSeq_getSize_r failed".to_owned())); - } - if GEOSCoordSeq_getDimensions_r(self.get_raw_context(), coord, &mut dims) == 0 { - return Err(Error::GenericError("GEOSCoordSeq_getDimensions_r failed".to_owned())); - } - CoordSeq::new_from_raw(t, self.clone_context(), size, dims, "get_coord_seq") - }, - _ => Err(Error::ImpossibleOperation( - "Geometry must be a Point, LineString or LinearRing to extract its coordinates" - .into(), - )), - } - } - - pub fn geometry_type(&self) -> GeometryTypes { - let type_geom = unsafe { GEOSGeomTypeId_r(self.get_raw_context(), self.as_raw()) as i32 }; - - GeometryTypes::from(type_geom) - } - - /// Returns the area of the geometry. Units are specified by the SRID of the given geometry. + fn equals<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Checks if the two [`Geometry`] objects are exactly equal. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))").expect("Invalid geometry"); - /// assert_eq!(geom1.area(), Ok(60.)); + /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)").expect("Invalid geometry"); + /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// + /// assert_eq!(geom1.equals_exact(&geom2, 0.1), Ok(false)); + /// assert_eq!(geom1.equals_exact(&geom3, 0.1), Ok(true)); /// ``` - pub fn area(&self) -> GResult { - let mut n = 0.; - - let res = unsafe { GEOSArea_r(self.get_raw_context(), self.as_raw(), &mut n) }; - if res != 1 { - Err(Error::GeosError(format!("area failed with code {}", res))) - } else { - Ok(n as f64) - } - } - - /// Returns a WKT representation of the geometry. It defaults to 2 dimensions output. Use - /// [`WKTWriter`] type directly if you want more control. + fn equals_exact<'b, G: Geom<'b>>(&self, other: &G, precision: f64) -> GResult; + /// Returns `true` if no point of `other` is outside of `self`. /// - /// # Examples + /// # Example /// /// ``` - /// use geos::{Geometry, OutputDimension, WKTWriter}; - /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// assert_eq!(point_geom.to_wkt().unwrap(), "POINT (2.5000000000000000 2.5000000000000000)"); + /// use geos::{Geom, Geometry}; /// - /// // A three dimension point will be output just as a 2 dimension: - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 3)").expect("Invalid geometry"); - /// assert_eq!(point_geom.to_wkt().unwrap(), "POINT (2.5000000000000000 2.5000000000000000)"); + /// let geom = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let little_geom = geom.buffer(10., 8).expect("buffer failed"); + /// let big_geom = geom.buffer(20., 8).expect("buffer failed"); /// - /// // To "fix" it, use `WKTWriter` instead: - /// let mut wkt_writer = WKTWriter::new().expect("Failed to create WKTWriter"); - /// wkt_writer.set_output_dimension(OutputDimension::ThreeD); - /// assert_eq!(wkt_writer.write(&point_geom).unwrap(), - /// "POINT Z (2.5000000000000000 2.5000000000000000 3.0000000000000000)"); + /// assert_eq!(little_geom.covers(&big_geom), Ok(false)); + /// assert_eq!(big_geom.covers(&little_geom), Ok(true)); /// ``` - pub fn to_wkt(&self) -> GResult { - match WKTWriter::new_with_context(self.clone_context()) { - Ok(w) => w.write(self), - Err(e) => Err(e), - } - } - - - /// Returns a WKT representation of the geometry with the given `precision`. It is a wrapper - /// around [`WKTWriter::set_rounding_precision`]. + fn covers<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns `true` if no point of `self` is outside of `other`. /// /// # Example /// /// ``` - /// use geos::{Geometry, WKTWriter}; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// assert_eq!(point_geom.to_wkt_precision(2).unwrap(), "POINT (2.50 2.50)"); + /// let geom = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let little_geom = geom.buffer(10., 8).expect("buffer failed"); + /// let big_geom = geom.buffer(20., 8).expect("buffer failed"); /// - /// // It is a wrapper around: - /// let mut writer = WKTWriter::new().expect("Failed to create WKTWriter"); - /// writer.set_rounding_precision(2); - /// assert_eq!(writer.write(&point_geom).unwrap(), "POINT (2.50 2.50)"); + /// assert_eq!(little_geom.covered_by(&big_geom), Ok(true)); + /// assert_eq!(big_geom.covered_by(&little_geom), Ok(false)); /// ``` - pub fn to_wkt_precision(&self, precision: u32) -> GResult { - unsafe { - let writer = GEOSWKTWriter_create_r(self.get_raw_context()); - GEOSWKTWriter_setRoundingPrecision_r(self.get_raw_context(), writer, precision as _); - let c_result = GEOSWKTWriter_write_r(self.get_raw_context(), writer, self.as_raw()); - GEOSWKTWriter_destroy_r(self.get_raw_context(), writer); - managed_string(c_result, self.get_context_handle(), "GResult::to_wkt_precision") - } - } - - /// Returns `true` if the geometry is a ring. + fn covered_by<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns `true` if no points of the `other` geometry is outside the exterior of `self`. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let circle = Geometry::new_from_wkt("LINESTRING(0 0, 0 1, 1 1, 0 0)").expect("Invalid geometry"); - /// assert_eq!(circle.is_ring(), Ok(true)); - /// ``` - pub fn is_ring(&self) -> GResult { - let rv = unsafe { GEOSisRing_r(self.get_raw_context(), self.as_raw()) }; - check_geos_predicate(rv as _, PredicateType::IsRing) - } - - /// Returns `true` if `self` shares any portion of space with `other`. So if any of this is - /// `true`: + /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); /// - /// * `self` overlaps `other` - /// * `self` touches `other` - /// * `self` is within `other` + /// assert_eq!(geom1.contains(&geom2), Ok(true)); + /// ``` + fn contains<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns a geometry which represents all points whose distance from `self` is less than or + /// equal to distance. /// - /// Then `intersects` will return `true` as well. + /// You can find nice examples about this in [postgis](https://postgis.net/docs/ST_Buffer.html) + /// documentation. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("invalid geometry"); - /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("invalid geometry"); + /// let geom = Geometry::new_from_wkt("POINT(1 3)").expect("Invalid geometry"); + /// let buffer_geom = geom.buffer(50., 2).expect("buffer failed"); /// - /// assert_eq!(geom1.intersects(&geom2), Ok(false)); - /// assert_eq!(geom1.intersects(&geom3), Ok(true)); + /// assert_eq!(buffer_geom.to_wkt_precision(1).unwrap(), + /// "POLYGON ((51.0 3.0, 36.4 -32.4, 1.0 -47.0, -34.4 -32.4, -49.0 3.0, -34.4 38.4, \ + /// 1.0 53.0, 36.4 38.4, 51.0 3.0))"); /// ``` - pub fn intersects<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSIntersects_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Intersects) - } - - /// Returns `true` if `self` and `other` have at least one interior into each other. + fn buffer(&self, width: f64, quadsegs: i32) -> GResult>; + /// Returns `true` if the given geometry is empty. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING(1 1,2 2)").expect("invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 1,1 2)").expect("invalid geometry"); + /// let geom = Geometry::create_empty_polygon().expect("Invalid geometry"); + /// assert_eq!(geom.is_empty(), Ok(true)); /// - /// assert_eq!(geom1.crosses(&geom2), Ok(true)); - /// ``` - pub fn crosses<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSCrosses_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Crosses) - } - - /// Returns `true` if `self` doesn't: + /// let geom = Geometry::new_from_wkt("POLYGON EMPTY").expect("Invalid geometry"); + /// assert_eq!(geom.is_empty(), Ok(true)); /// - /// * Overlap `other` - /// * Touch `other` - /// * Is within `other` + /// let geom = Geometry::new_from_wkt("POINT(1 3)").expect("Invalid geometry"); + /// assert_eq!(geom.is_empty(), Ok(false)); + /// ``` + fn is_empty(&self) -> GResult; + /// Returns true if the given geometry has no anomalous geometric points, such as self + /// intersection or self tangency. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("invalid geometry"); - /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("invalid geometry"); + /// let geom = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); + /// assert_eq!(geom.is_simple(), Ok(true)); /// - /// assert_eq!(geom1.disjoint(&geom2), Ok(true)); - /// assert_eq!(geom1.disjoint(&geom3), Ok(false)); + /// let geom = Geometry::new_from_wkt("LINESTRING(1 1,2 2,2 3.5,1 3,1 2,2 1)") + /// .expect("Invalid geometry"); + /// assert_eq!(geom.is_simple(), Ok(false)); /// ``` - pub fn disjoint<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSDisjoint_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Disjoint) - } - - /// Returns `true` if the only points in common between `self` and `other` lie in the union of - /// the boundaries of `self` and `other`. + fn is_simple(&self) -> GResult; + /// Returns a geometry which represents part of `self` that doesn't intersect with `other`. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING(0 0, 1 1, 0 2)").expect("invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT(1 1)").expect("invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(geom1.touches(&geom2), Ok(false)); + /// let geom1 = Geometry::new_from_wkt("LINESTRING(50 100, 50 200)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(50 50, 50 150)").expect("Invalid geometry"); /// - /// let geom2 = Geometry::new_from_wkt("POINT(0 2)").expect("invalid geometry"); + /// let difference_geom = geom1.difference(&geom2).expect("envelope failed"); /// - /// assert_eq!(geom1.touches(&geom2), Ok(true)); + /// assert_eq!(difference_geom.to_wkt_precision(1).unwrap(), + /// "LINESTRING (50.0 150.0, 50.0 200.0)"); /// ``` - pub fn touches<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSTouches_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Touches) - } - - /// Returns `true` if `self` spatially overlaps `other`. + fn difference<'b, G: Geom<'b>>(&self, other: &G) -> GResult>; + /// Returns the minimum bouding box of the given geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT(1 0.5)").expect("invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(1 0, 1 1, 3 5)").expect("invalid geometry"); + /// let geom = Geometry::new_from_wkt("POINT(1 3)").expect("Invalid geometry"); + /// let envelope_geom = geom.envelope().expect("envelope failed"); /// - /// assert_eq!(geom1.overlaps(&geom2), Ok(false)); + /// assert_eq!(envelope_geom.to_wkt_precision(1).unwrap(), "POINT (1.0 3.0)"); /// - /// let geom1 = geom1.buffer(3., 8).expect("buffer failed"); - /// let geom2 = geom2.buffer(0.5, 8).expect("buffer failed"); + /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 1 3)").expect("Invalid geometry"); + /// let envelope_geom = geom.envelope().expect("envelope failed"); /// - /// assert_eq!(geom1.overlaps(&geom2), Ok(true)); + /// assert_eq!(envelope_geom.to_wkt_precision(1).unwrap(), + /// "POLYGON ((0.0 0.0, 1.0 0.0, 1.0 3.0, 0.0 3.0, 0.0 0.0))"); /// ``` - pub fn overlaps<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSOverlaps_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Overlaps) - } - - /// Returns `true` if `self` is completely inside `other`. + fn envelope(&self) -> GResult>; + /// Returns a geometry which represents the parts of `self` and `other` that don't intersect. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT(50 50)").expect("invalid geometry"); - /// let small_geom = geom.buffer(20., 8).expect("buffer failed"); - /// let big_geom = geom.buffer(40., 8).expect("buffer failed"); + /// let geom1 = Geometry::new_from_wkt("LINESTRING(50 100, 50 200)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(50 50, 50 150)").expect("Invalid geometry"); /// - /// assert_eq!(small_geom.within(&small_geom), Ok(true)); - /// assert_eq!(small_geom.within(&big_geom), Ok(true)); - /// assert_eq!(big_geom.within(&small_geom), Ok(false)); + /// let sym_diff_geom = geom1.sym_difference(&geom2).expect("sym_difference failed"); + /// + /// assert_eq!(sym_diff_geom.to_wkt_precision(1).unwrap(), + /// "MULTILINESTRING ((50.0 150.0, 50.0 200.0), (50.0 50.0, 50.0 100.0))"); /// ``` - pub fn within<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSWithin_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Within) - } - - /// Checks if the two [`Geometry`] objects are equal. + fn sym_difference<'b, G: Geom<'b>>(&self, other: &G) -> GResult>; + /// Aggregates the given geometry with another one. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)").expect("Invalid geometry"); - /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom1 = Geometry::new_from_wkt("POINT(1 2)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT(3 4)").expect("Invalid geometry"); /// - /// assert!(geom1.equals(&geom2) == Ok(false)); - /// assert!(geom1.equals(&geom3) == Ok(true)); + /// let union_geom = geom1.union(&geom2).expect("union failed"); + /// + /// assert_eq!(union_geom.to_wkt_precision(1).unwrap(), "MULTIPOINT (1.0 2.0, 3.0 4.0)"); /// ``` + fn union<'b, G: Geom<'b>>(&self, other: &G) -> GResult>; + /// Returns the geometric center or (equivalently) the center of mass of the given geometry as + /// a point. /// - /// Note that you can also use method through the `PartialEq` trait: + /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)").expect("Invalid geometry"); - /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom = Geometry::new_from_wkt("MULTIPOINT(-1 0, -1 2, -1 3, -1 4, -1 7, 0 1, 0 3, 1 1)") + /// .expect("Invalid geometry"); + /// let centroid = geom.get_centroid().expect("failed to get centroid"); /// - /// assert!(geom1 != geom2); - /// assert!(geom1 == geom3); + /// assert_eq!(centroid.to_wkt_precision(1).unwrap(), "POINT (-0.5 2.6)"); /// ``` - pub fn equals<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSEquals_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Equals) - } - - /// Checks if the two [`Geometry`] objects are exactly equal. + fn get_centroid(&self) -> GResult>; + /// Documentation from [postgis](https://postgis.net/docs/ST_UnaryUnion.html): + /// + /// > Unlike ST_Union, ST_UnaryUnion does dissolve boundaries between components of a + /// > multipolygon (invalid) and does perform union between the components of a + /// > geometrycollection. Each components of the input geometry is assumed to be valid, so you + /// > won't get a valid multipolygon out of a bow-tie polygon (invalid). + /// > + /// > You may use this function to node a set of linestrings. You may mix ST_UnaryUnion with + /// > ST_Collect to fine-tune how many geometries at once you want to dissolve to be nice on + /// > both memory size and CPU time, finding the balance between ST_Union and ST_MemUnion. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)").expect("Invalid geometry"); - /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POLYGON((1 1, 2 1, 2 5, 1 5, 1 1))") + /// .expect("Invalid geometry"); /// - /// assert_eq!(geom1.equals_exact(&geom2, 0.1), Ok(false)); - /// assert_eq!(geom1.equals_exact(&geom3, 0.1), Ok(true)); + /// let geom = Geometry::create_multipolygon(vec![geom1, geom2]) + /// .expect("Failed to build multipolygon"); + /// + /// let union_geom = geom.unary_union().expect("unary_union failed"); + /// + /// assert_eq!(union_geom.to_wkt_precision(1).unwrap(), + /// "POLYGON ((0.0 0.0, 0.0 6.0, 10.0 6.0, 10.0 0.0, 0.0 0.0))"); /// ``` - pub fn equals_exact<'b>(&self, other: &Geometry<'b>, precision: f64) -> GResult { - let ret_val = unsafe { - GEOSEqualsExact_r(self.get_raw_context(), self.as_raw(), other.as_raw(), precision) - }; - check_geos_predicate(ret_val as _, PredicateType::EqualsExact) - } - - /// Returns `true` if no point of `other` is outside of `self`. + fn unary_union(&self) -> GResult>; + /// Create a voronoi diagram. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); - /// let little_geom = geom.buffer(10., 8).expect("buffer failed"); - /// let big_geom = geom.buffer(20., 8).expect("buffer failed"); + /// let input = Geometry::new_from_wkt("MULTIPOINT((100 200), (105 202), (110 200), (140 230), + /// (210 240), (220 190), (170 170), (170 260), + /// (213 245), (220 190))") + /// .expect("Invalid geometry"); + /// let mut expected = Geometry::new_from_wkt( + /// "GEOMETRYCOLLECTION(POLYGON ((-20 50, -20 380, -3.75 380, 105 235, 105 115, 77.14285714285714 50, -20 50)),\ + /// POLYGON ((247 50, 77.14285714285714 50, 105 115, 145 195, 178.33333333333334 211.66666666666666, 183.51851851851853 208.7037037037037, 247 50)),\ + /// POLYGON ((-3.75 380, 20.000000000000007 380, 176.66666666666666 223.33333333333334, 178.33333333333334 211.66666666666666, 145 195, 105 235, -3.75 380)),\ + /// POLYGON ((105 115, 105 235, 145 195, 105 115)),\ + /// POLYGON ((20.000000000000007 380, 255 380, 176.66666666666666 223.33333333333334, 20.000000000000007 380)),\ + /// POLYGON ((255 380, 340 380, 340 240, 183.51851851851853 208.7037037037037, 178.33333333333334 211.66666666666666, 176.66666666666666 223.33333333333334, 255 380)),\ + /// POLYGON ((340 240, 340 50, 247 50, 183.51851851851853 208.7037037037037, 340 240)))") + /// .expect("Invalid geometry"); /// - /// assert_eq!(little_geom.covers(&big_geom), Ok(false)); - /// assert_eq!(big_geom.covers(&little_geom), Ok(true)); + /// let mut voronoi = input.voronoi(None::<&Geometry>, 6., false) + /// .expect("voronoi failed"); + /// + /// expected.normalize().expect("normalize failed"); + /// voronoi.normalize().expect("normalize failed"); + /// + /// assert_eq!(expected.equals(&voronoi), Ok(true)); /// ``` - pub fn covers<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSCovers_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Covers) - } - - /// Returns `true` if no point of `self` is outside of `other`. + fn voronoi<'b, G: Geom<'b>>( + &self, + envelope: Option<&G>, + tolerance: f64, + only_edges: bool, + ) -> GResult>; + /// Returns a geometry representing the intersection between `self` and `other`. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); - /// let little_geom = geom.buffer(10., 8).expect("buffer failed"); - /// let big_geom = geom.buffer(20., 8).expect("buffer failed"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(little_geom.covered_by(&big_geom), Ok(true)); - /// assert_eq!(big_geom.covered_by(&little_geom), Ok(false)); - /// ``` - pub fn covered_by<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSCoveredBy_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::CoveredBy) - } - - /// Returns `true` if no points of the `other` geometry is outside the exterior of `self`. + /// let mut geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("Invalid geometry"); + /// let mut geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("Invalid geometry"); /// - /// # Example + /// let intersection_geom = geom1.intersection(&geom2).expect("intersection failed"); /// - /// ``` - /// use geos::Geometry; + /// // No intersection. + /// assert_eq!(intersection_geom.is_empty(), Ok(true)); /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// // We slighty change the linestring so we have an intersection: + /// let mut geom2 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("Invalid geometry"); /// - /// assert_eq!(geom1.contains(&geom2), Ok(true)); + /// let intersection_geom = geom1.intersection(&geom2).expect("intersection failed"); + /// + /// // Intersection! + /// assert_eq!(intersection_geom.to_wkt_precision(1).unwrap(), "POINT (0.0 0.0)"); /// ``` - pub fn contains<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSContains_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; - check_geos_predicate(ret_val as _, PredicateType::Contains) - } - - /// Returns a geometry which represents all points whose distance from `self` is less than or - /// equal to distance. + fn intersection<'b, G: Geom<'b>>(&self, other: &G) -> GResult>; + /// Documentation from [postgis](https://postgis.net/docs/ST_ConvexHull.html): /// - /// You can find nice examples about this in [postgis](https://postgis.net/docs/ST_Buffer.html) - /// documentation. + /// > The convex hull of a geometry represents the minimum convex geometry that encloses all + /// > geometries within the set. + /// > + /// > One can think of the convex hull as the geometry you get by wrapping an elastic band + /// > around a set of geometries. This is different from a concave hull which is analogous to + /// > shrink-wrapping your geometries. + /// > + /// > It is usually used with MULTI and Geometry Collections. Although it is not an aggregate - + /// > you can use it in conjunction with ST_Collect to get the convex hull of a set of points. + /// > ST_ConvexHull(ST_Collect(somepointfield)). + /// > + /// > It is often used to determine an affected area based on a set of point observations. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT(1 3)").expect("Invalid geometry"); - /// let buffer_geom = geom.buffer(50., 2).expect("buffer failed"); + /// let mut geom1 = Geometry::new_from_wkt("MULTILINESTRING((100 190,10 8), + /// (150 10, 20 30))") + /// .expect("Invalid geometry"); + /// let mut geom2 = Geometry::new_from_wkt("MULTIPOINT(50 5, 150 30, 50 10, 10 10)") + /// .expect("Invalid geometry"); /// - /// assert_eq!(buffer_geom.to_wkt_precision(1).unwrap(), - /// "POLYGON ((51.0 3.0, 36.4 -32.4, 1.0 -47.0, -34.4 -32.4, -49.0 3.0, -34.4 38.4, \ - /// 1.0 53.0, 36.4 38.4, 51.0 3.0))"); + /// let geom = geom1.union(&geom2).expect("union failed"); + /// let convex_hull_geom = geom.convex_hull().expect("convex_hull failed"); + /// + /// assert_eq!(convex_hull_geom.to_wkt_precision(1).unwrap(), + /// "POLYGON ((50.0 5.0, 10.0 8.0, 10.0 10.0, 100.0 190.0, 150.0 30.0, 150.0 10.0, 50.0 5.0))"); /// ``` - pub fn buffer(&self, width: f64, quadsegs: i32) -> GResult> { - assert!(quadsegs > 0); - unsafe { - let ptr = GEOSBuffer_r( - self.get_raw_context(), - self.as_raw(), - width, - quadsegs as _, - ); - Geometry::new_from_raw(ptr, self.clone_context(), "buffer") - } - } - - /// Returns `true` if the given geometry is empty. + fn convex_hull(&self) -> GResult>; + /// Returns the closure of the combinatorial boundary of `self`. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::create_empty_polygon().expect("Invalid geometry"); - /// assert_eq!(geom.is_empty(), Ok(true)); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POLYGON EMPTY").expect("Invalid geometry"); - /// assert_eq!(geom.is_empty(), Ok(true)); + /// let geom = Geometry::new_from_wkt("LINESTRING(1 1,0 0, -1 1)").expect("Invalid geometry"); + /// let boundary_geom = geom.boundary().expect("boundary failed"); /// - /// let geom = Geometry::new_from_wkt("POINT(1 3)").expect("Invalid geometry"); - /// assert_eq!(geom.is_empty(), Ok(false)); + /// assert_eq!(boundary_geom.to_wkt_precision(1).unwrap(), "MULTIPOINT (1.0 1.0, -1.0 1.0)"); /// ``` - pub fn is_empty(&self) -> GResult { - let ret_val = unsafe { GEOSisEmpty_r(self.get_raw_context(), self.as_raw()) }; - check_geos_predicate(ret_val as _, PredicateType::IsEmpty) - } - - /// Returns true if the given geometry has no anomalous geometric points, such as self - /// intersection or self tangency. + fn boundary(&self) -> GResult>; + /// Returns `true` if `self` has a Z coordinate. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT (2.5 2.5)") - /// .expect("Invalid geometry"); - /// assert_eq!(geom.is_simple(), Ok(true)); + /// let geom = Geometry::new_from_wkt("POINT(1 2 3)").expect("Invalid geometry"); + /// assert_eq!(geom.has_z(), Ok(true)); /// - /// let geom = Geometry::new_from_wkt("LINESTRING(1 1,2 2,2 3.5,1 3,1 2,2 1)") - /// .expect("Invalid geometry"); - /// assert_eq!(geom.is_simple(), Ok(false)); + /// let geom = Geometry::new_from_wkt("POINT(1 2)").expect("Invalid geometry"); + /// assert_eq!(geom.has_z(), Ok(false)); /// ``` - pub fn is_simple(&self) -> GResult { - let ret_val = unsafe { GEOSisSimple_r(self.get_raw_context(), self.as_raw()) }; - check_geos_predicate(ret_val as _, PredicateType::IsSimple) - } - - /// Returns a geometry which represents part of `self` that doesn't intersect with `other`. + fn has_z(&self) -> GResult; + /// Returns `true` if start and end point are coincident. + /// + /// Only works on `LineString` and `MultiLineString`. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING(50 100, 50 200)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(50 50, 50 150)").expect("Invalid geometry"); + /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 1 1)").expect("Invalid geometry"); + /// assert_eq!(geom.is_closed(), Ok(false)); /// - /// let difference_geom = geom1.difference(&geom2).expect("envelope failed"); + /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 0 1, 1 1, 0 0)").expect("Invalid geometry"); + /// assert_eq!(geom.is_closed(), Ok(true)); /// - /// assert_eq!(difference_geom.to_wkt_precision(1).unwrap(), - /// "LINESTRING (50.0 150.0, 50.0 200.0)"); + /// let geom = Geometry::new_from_wkt("MULTILINESTRING((0 0, 0 1, 1 1, 0 0),(0 0, 1 1))") + /// .expect("Invalid geometry"); + /// assert_eq!(geom.is_closed(), Ok(false)); /// ``` - pub fn difference<'b>(&self, other: &Geometry<'b>) -> GResult> { - unsafe { - let ptr = GEOSDifference_r(self.get_raw_context(), self.as_raw(), other.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "difference") - } - } - - /// Returns the minimum bouding box of the given geometry. + fn is_closed(&self) -> GResult; + /// Returns the length of `self`. The unit depends of the SRID. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("POINT(1 3)").expect("Invalid geometry"); - /// let envelope_geom = geom.envelope().expect("envelope failed"); - /// - /// assert_eq!(envelope_geom.to_wkt_precision(1).unwrap(), "POINT (1.0 3.0)"); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 1 3)").expect("Invalid geometry"); - /// let envelope_geom = geom.envelope().expect("envelope failed"); + /// let geom = Geometry::new_from_wkt("LINESTRING(743238 2967416,743238 2967450)") + /// .expect("Invalid geometry"); /// - /// assert_eq!(envelope_geom.to_wkt_precision(1).unwrap(), - /// "POLYGON ((0.0 0.0, 1.0 0.0, 1.0 3.0, 0.0 3.0, 0.0 0.0))"); + /// assert_eq!( + /// geom.length().map(|x| format!("{:.2}", x)).unwrap(), + /// "34.00", + /// ); /// ``` - pub fn envelope(&self) -> GResult> { - unsafe { - let ptr = GEOSEnvelope_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "envelope") - } - } - - /// Returns a geometry which represents the parts of `self` and `other` that don't intersect. + fn length(&self) -> GResult; + /// Returns the distance between `self` and `other`. The unit depends of the SRID. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING(50 100, 50 200)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(50 50, 50 150)").expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let sym_diff_geom = geom1.sym_difference(&geom2).expect("sym_difference failed"); + /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); /// - /// assert_eq!(sym_diff_geom.to_wkt_precision(1).unwrap(), - /// "MULTILINESTRING ((50.0 150.0, 50.0 200.0), (50.0 50.0, 50.0 100.0))"); + /// assert_eq!(geom1.distance(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "1.00"); /// ``` - pub fn sym_difference<'b>(&self, other: &Geometry<'b>) -> GResult> { - unsafe { - let ptr = GEOSSymDifference_r(self.get_raw_context(), self.as_raw(), other.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "sym_difference") - } - } - - /// Aggregates the given geometry with another one. + fn distance<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns the indexed distance between `self` and `other`. The unit depends of the SRID. + /// + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POINT(1 2)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT(3 4)").expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let union_geom = geom1.union(&geom2).expect("union failed"); + /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); /// - /// assert_eq!(union_geom.to_wkt_precision(1).unwrap(), "MULTIPOINT (1.0 2.0, 3.0 4.0)"); + /// assert_eq!(geom1.distance_indexed(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "1.00"); /// ``` - pub fn union(&self, other: &Geometry<'a>) -> GResult> { - unsafe { - let ptr = GEOSUnion_r(self.get_raw_context(), self.as_raw(), other.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "union") - } - } - - /// Returns the geometric center or (equivalently) the center of mass of the given geometry as - /// a point. + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn distance_indexed<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns the hausdorff distance between `self` and `other`. The unit depends of the SRID. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("MULTIPOINT(-1 0, -1 2, -1 3, -1 4, -1 7, 0 1, 0 3, 1 1)") - /// .expect("Invalid geometry"); - /// let centroid = geom.get_centroid().expect("failed to get centroid"); + /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); /// - /// assert_eq!(centroid.to_wkt_precision(1).unwrap(), "POINT (-0.5 2.6)"); + /// assert_eq!(geom1.hausdorff_distance(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "1.00"); /// ``` - pub fn get_centroid(&self) -> GResult> { - unsafe { - let ptr = GEOSGetCentroid_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "get_centroid") - } - } - - /// Creates an empty polygon geometry. + fn hausdorff_distance<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns the hausdorff distance between `self` and `other`. The unit depends of the SRID. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_empty_polygon().expect("Failed to build empty polygon"); + /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); /// - /// assert_eq!(geom.to_wkt().unwrap(), "POLYGON EMPTY"); + /// assert_eq!(geom1.hausdorff_distance_densify(&geom2, 1.).map(|x| format!("{:.2}", x)) + /// .unwrap(), "1.00"); /// ``` - pub fn create_empty_polygon() -> GResult> { - match ContextHandle::init_e(Some("Geometry::create_empty_polygon")) { - Ok(context) => { - unsafe { - let ptr = GEOSGeom_createEmptyPolygon_r(context.as_raw()); - Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_polygon") - } - } - Err(e) => Err(e), - } - } - - /// Creates an empty point geometry. + fn hausdorff_distance_densify<'b, G: Geom<'b>>( + &self, + other: &G, + distance_frac: f64, + ) -> GResult; + /// Returns the frechet distance between `self` and `other`. The unit depends of the SRID. + /// + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_empty_point().expect("Failed to build empty point"); + /// let geom1 = Geometry::new_from_wkt("LINESTRING (0 0, 100 0)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING (0 0, 50 50, 100 0)").expect("Invalid geometry"); /// - /// assert_eq!(geom.to_wkt().unwrap(), "POINT EMPTY"); + /// assert_eq!(geom1.frechet_distance(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "70.71"); /// ``` - pub fn create_empty_point() -> GResult> { - match ContextHandle::init_e(Some("Geometry::create_empty_point")) { - Ok(context) => { - unsafe { - let ptr = GEOSGeom_createEmptyPoint_r(context.as_raw()); - Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_point") - } - } - Err(e) => Err(e), - } - } - - /// Creates an empty line string geometry. + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn frechet_distance<'b, G: Geom<'b>>(&self, other: &G) -> GResult; + /// Returns the frechet distance between `self` and `other`. The unit depends of the SRID. + /// + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_empty_line_string().expect("Failed to build empty line string"); + /// let geom1 = Geometry::new_from_wkt("LINESTRING (0 0, 100 0)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING (0 0, 50 50, 100 0)").expect("Invalid geometry"); /// - /// assert_eq!(geom.to_wkt().unwrap(), "LINESTRING EMPTY"); + /// assert_eq!(geom1.frechet_distance_densify(&geom2, 1.).map(|x| format!("{:.2}", x)) + /// .unwrap(), "70.71"); /// ``` - pub fn create_empty_line_string() -> GResult> { - match ContextHandle::init_e(Some("Geometry::create_empty_line_string")) { - Ok(context) => { - unsafe { - let ptr = GEOSGeom_createEmptyLineString_r(context.as_raw()); - Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_line_string") - } - } - Err(e) => Err(e), - } - } - - /// Creates an empty collection. - /// - /// The `type_` must be one of: - /// - /// * [`GeometryTypes::GeometryCollection`] - /// * [`GeometryTypes::MultiPoint`] - /// * [`GeometryTypes::MultiLineString`] - /// * [`GeometryTypes::MultiPolygon`] + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn frechet_distance_densify<'b, G: Geom<'b>>( + &self, + other: &G, + distance_frac: f64, + ) -> GResult; + /// Returns the length of the given geometry. /// /// # Example /// /// ``` - /// use geos::{Geometry, GeometryTypes}; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_empty_collection(GeometryTypes::MultiPolygon) - /// .expect("Failed to build empty collection"); + /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4, 5 6)") + /// .expect("Invalid geometry"); /// - /// assert_eq!(geom.to_wkt().unwrap(), "MULTIPOLYGON EMPTY"); + /// assert_eq!(geom.get_length().map(|x| format!("{:.2}", x)).unwrap(), "5.66"); /// ``` - pub fn create_empty_collection(type_: GeometryTypes) -> GResult> { - match type_ { - GeometryTypes::GeometryCollection | - GeometryTypes::MultiPoint | - GeometryTypes::MultiLineString | - GeometryTypes::MultiPolygon => {} - _ => return Err(Error::GenericError("Invalid geometry type".to_owned())), - } - match ContextHandle::init_e(Some("Geometry::create_empty_collection")) { - Ok(context) => { - unsafe { - let ptr = GEOSGeom_createEmptyCollection_r(context.as_raw(), type_.into()); - Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_collection") - } - } - Err(e) => Err(e), - } - } - - /// Creates a polygon formed by the given shell and array of holes. - /// - /// ### Note + fn get_length(&self) -> GResult; + /// Documentation from [postgis](https://postgis.net/docs/ST_Snap.html): /// - /// `exterior` must be a `LinearRing`. + /// > Snaps the vertices and segments of a geometry another Geometry's vertices. A snap + /// > distance tolerance is used to control where snapping is performed. The result geometry is + /// > the input geometry with the vertices snapped. If no snapping occurs then the input + /// > geometry is returned unchanged. + /// > + /// > Snapping one geometry to another can improve robustness for overlay operations by + /// > eliminating nearly-coincident edges (which cause problems during noding and intersection + /// > calculation). + /// > + /// > Too much snapping can result in invalid topology being created, so the number and location + /// > of snapped vertices is decided using heuristics to determine when it is safe to snap. This + /// > can result in some potential snaps being omitted, however. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("LINEARRING(75.15 29.53,77 29,77.6 29.5, 75.15 29.53)") - /// .expect("Invalid geometry"); - /// let polygon_geom = Geometry::create_polygon(geom, vec![]).expect("create_polygon failed"); + /// let geom1 = Geometry::new_from_wkt("MULTIPOLYGON(((26 125, 26 200, 126 200, 126 125, 26 125), + /// (51 150, 101 150, 76 175, 51 150)), + /// ((151 100, 151 200, 176 175, 151 100)))") + /// .expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(5 107, 54 84, 101 100)") + /// .expect("Invalid geometry"); + /// + /// let distance = geom1.distance(&geom2).expect("distance failed"); + /// let snap_geom = geom1.snap(&geom2, distance * 1.25).expect("snap failed"); /// - /// assert_eq!(polygon_geom.to_wkt_precision(1).unwrap(), - /// "POLYGON ((75.2 29.5, 77.0 29.0, 77.6 29.5, 75.2 29.5))"); + /// assert_eq!(snap_geom.to_wkt_precision(1).unwrap(), + /// "MULTIPOLYGON (((5.0 107.0, 26.0 200.0, 126.0 200.0, 126.0 125.0, 101.0 100.0, 54.0 84.0, 5.0 107.0), \ + /// (51.0 150.0, 101.0 150.0, 76.0 175.0, 51.0 150.0)), \ + /// ((151.0 100.0, 151.0 200.0, 176.0 175.0, 151.0 100.0)))"); /// ``` - pub fn create_polygon<'b>( - mut exterior: Geometry<'a>, - mut interiors: Vec>, - ) -> GResult> { - if exterior.geometry_type() != GeometryTypes::LinearRing { - return Err(Error::GenericError("exterior must be a LinearRing".to_owned())); - } - let context_handle = exterior.clone_context(); - let nb_interiors = interiors.len(); - let res = unsafe { - let mut geoms: Vec<*mut GEOSGeometry> = interiors.iter_mut() - .map(|g| g.as_raw()) - .collect(); - let ptr = GEOSGeom_createPolygon_r( - context_handle.as_raw(), - exterior.as_raw(), - geoms.as_mut_ptr() as *mut *mut GEOSGeometry, - nb_interiors as _, - ); - Geometry::new_from_raw(ptr, context_handle, "create_polygon") - }; - - // We transfered the ownership of the ptr to the new Geometry, - // so the old ones need to forget their c ptr to avoid double free. - exterior.ptr = PtrWrap(::std::ptr::null_mut()); - for i in interiors.iter_mut() { - i.ptr = PtrWrap(::std::ptr::null_mut()); - } - - res - } - - /// Create a geometry collection. + fn snap<'b, G: Geom<'b>>(&self, other: &G, tolerance: f64) -> GResult>; + /// Returns unique points of `self`. + fn extract_unique_points(&self) -> GResult>; + fn nearest_points<'b, G: Geom<'b>>(&self, other: &G) -> GResult>; + /// Returns the X position. The given `Geometry` must be a `Point`, otherwise it'll fail. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") - /// .expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (3.0 4.0)").expect("Invalid geometry"); - /// - /// let geom = Geometry::create_geometry_collection(vec![geom1, geom2]) - /// .expect("Failed to build multipolygon"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), - /// "GEOMETRYCOLLECTION (POLYGON ((0.0 0.0, 10.0 0.0, 10.0 6.0, 0.0 6.0, 0.0 0.0)), \ - /// POINT (3.0 4.0))"); + /// let point_geom = Geometry::new_from_wkt("POINT (1.5 2.5 3.5)").expect("Invalid geometry"); + /// assert!(point_geom.get_x() == Ok(1.5)); /// ``` - pub fn create_geometry_collection(geoms: Vec>) -> GResult> { - create_multi_geom(geoms, GeometryTypes::GeometryCollection) - } - - /// Create a multi polygon geometry. + fn get_x(&self) -> GResult; + /// Returns the Y position. The given `Geometry` must be a `Point`, otherwise it'll fail. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") - /// .expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POLYGON((3 3, 10 3, 10 6, 3 6, 3 3))") - /// .expect("Invalid geometry"); + /// let point_geom = Geometry::new_from_wkt("POINT (1.5 2.5 3.5)").expect("Invalid geometry"); + /// assert!(point_geom.get_y() == Ok(2.5)); + /// ``` + fn get_y(&self) -> GResult; + /// Returns the Z position. The given `Geometry` must be a `Point`, otherwise it'll fail. /// - /// let geom = Geometry::create_multipolygon(vec![geom1, geom2]) - /// .expect("Failed to build multipolygon"); + /// Available using the `v3_7_0` feature. + /// + /// # Example /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), - /// "MULTIPOLYGON (((0.0 0.0, 10.0 0.0, 10.0 6.0, 0.0 6.0, 0.0 0.0)), \ - /// ((3.0 3.0, 10.0 3.0, 10.0 6.0, 3.0 6.0, 3.0 3.0)))"); /// ``` - pub fn create_multipolygon(polygons: Vec>) -> GResult> { - if !check_same_geometry_type(&polygons, GeometryTypes::Polygon) { - return Err(Error::ImpossibleOperation( - "all the provided geometry have to be of type Polygon".to_owned(), - )); - } - create_multi_geom(polygons, GeometryTypes::MultiPolygon) - } - - /// Create a multiline string geometry. + /// use geos::{Geom, Geometry}; + /// + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); + /// assert!(point_geom.get_z() == Ok(4.0)); + /// ``` + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_z(&self) -> GResult; + /// Returns the nth point of the given geometry. + /// + /// The given `Geometry` must be a `LineString`, otherwise it'll fail. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING (1.0 2.0, 3.0 4.0)").expect("invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING (5.0 6.0, 7.0 8.0)").expect("invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_multiline_string(vec![geom1, geom2]) - /// .expect("Failed to build multiline string"); + /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4, 5 6)") + /// .expect("Invalid geometry"); + /// let nth_point = geom.get_point_n(1).expect("get_point_n failed"); /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), - /// "MULTILINESTRING ((1.0 2.0, 3.0 4.0), (5.0 6.0, 7.0 8.0))"); + /// assert_eq!(nth_point.to_wkt_precision(1).unwrap(), "POINT (3.0 4.0)"); /// ``` - pub fn create_multiline_string(linestrings: Vec>) -> GResult> { - if !check_same_geometry_type(&linestrings, GeometryTypes::LineString) { - return Err(Error::ImpossibleOperation( - "all the provided geometry have to be of type LineString".to_owned(), - )); - } - create_multi_geom(linestrings, GeometryTypes::MultiLineString) - } - - /// Creates a multi point geometry. + fn get_point_n(&self, n: usize) -> GResult>; + /// Returns the start point of `self`. + /// + /// The given `Geometry` must be a `LineString`, otherwise it'll fail. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POINT (1.0 2.0)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (3.0 4.0)").expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_multipoint(vec![geom1, geom2]) - /// .expect("Failed to build multipoint"); + /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)") + /// .expect("Invalid geometry"); + /// let start_point = geom.get_start_point().expect("get_start_point failed"); /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), "MULTIPOINT (1.0 2.0, 3.0 4.0)"); + /// assert_eq!(start_point.to_wkt_precision(1).unwrap(), "POINT (1.0 2.0)"); /// ``` - pub fn create_multipoint(points: Vec>) -> GResult> { - if !check_same_geometry_type(&points, GeometryTypes::Point) { - return Err(Error::ImpossibleOperation( - "all the provided geometry have to be of type Point".to_owned(), - )); - } - create_multi_geom(points, GeometryTypes::MultiPoint) - } - - /// Creates a point geometry. + fn get_start_point(&self) -> GResult>; + /// Returns the end point of `self`. + /// + /// The given `Geometry` must be a `LineString`, otherwise it'll fail. /// /// # Example /// /// ``` - /// use geos::{CoordDimensions, CoordSeq, Geometry}; - /// - /// let coords = CoordSeq::new_from_vec(&[&[1., 2.]]) - /// .expect("failed to create CoordSeq"); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_point(coords).expect("Failed to create a point"); + /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)") + /// .expect("Invalid geometry"); + /// let end_point = geom.get_end_point().expect("get_end_point failed"); /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), "POINT (1.0 2.0)"); + /// assert_eq!(end_point.to_wkt_precision(1).unwrap(), "POINT (3.0 4.0)"); /// ``` - pub fn create_point(mut s: CoordSeq<'a>) -> GResult> { - unsafe { - let ptr = GEOSGeom_createPoint_r(s.get_raw_context(), s.as_raw()); - let res = Geometry::new_from_raw(ptr, s.clone_context(), "create_point"); - s.ptr = PtrWrap(::std::ptr::null_mut()); - res - } - } - - /// Creates a line string geometry. + fn get_end_point(&self) -> GResult>; + /// Returns the number of points of `self`. + /// + /// The given `Geometry` must be a `LineString`, otherwise it'll fail. /// /// # Example /// /// ``` - /// use geos::{CoordDimensions, CoordSeq, Geometry}; - /// - /// let coords = CoordSeq::new_from_vec(&[&[1., 2.], &[3., 4.]]) - /// .expect("failed to create CoordSeq"); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_line_string(coords).expect("Failed to create a line string"); + /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)") + /// .expect("Invalid geometry"); /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), "LINESTRING (1.0 2.0, 3.0 4.0)"); + /// assert_eq!(geom.get_num_points(), Ok(2)); /// ``` - pub fn create_line_string(mut s: CoordSeq<'a>) -> GResult> { - unsafe { - let ptr = GEOSGeom_createLineString_r(s.get_raw_context(), s.as_raw()); - let res = Geometry::new_from_raw(ptr, s.clone_context(), "create_line_string"); - s.ptr = PtrWrap(::std::ptr::null_mut()); - res - } - } - - /// Creates a linear ring geometry. + fn get_num_points(&self) -> GResult; + /// Returns the number of interior rings. /// /// # Example /// /// ``` - /// use geos::{CoordDimensions, CoordSeq, Geometry}; + /// use geos::{Geom, Geometry}; /// - /// let coords = CoordSeq::new_from_vec(&[&[75.15, 29.53], - /// &[77., 29.], - /// &[77.6, 29.5], - /// &[75.15, 29.53]]) - /// .expect("failed to create CoordSeq"); + /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0),\ + /// (1 1, 2 1, 2 5, 1 5, 1 1),\ + /// (8 5, 8 4, 9 4, 9 5, 8 5))") + /// .expect("Invalid geometry"); /// - /// let geom = Geometry::create_linear_ring(coords).expect("Failed to create a linea ring"); - /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), - /// "LINEARRING (75.2 29.5, 77.0 29.0, 77.6 29.5, 75.2 29.5)"); - /// ``` - pub fn create_linear_ring(mut s: CoordSeq<'a>) -> GResult> { - unsafe { - let ptr = GEOSGeom_createLinearRing_r(s.get_raw_context(), s.as_raw()); - let res = Geometry::new_from_raw(ptr, s.clone_context(), "create_linear_ring"); - s.ptr = PtrWrap(::std::ptr::null_mut()); - res - } - } - - /// Documentation from [postgis](https://postgis.net/docs/ST_UnaryUnion.html): - /// - /// > Unlike ST_Union, ST_UnaryUnion does dissolve boundaries between components of a - /// > multipolygon (invalid) and does perform union between the components of a - /// > geometrycollection. Each components of the input geometry is assumed to be valid, so you - /// > won't get a valid multipolygon out of a bow-tie polygon (invalid). - /// > - /// > You may use this function to node a set of linestrings. You may mix ST_UnaryUnion with - /// > ST_Collect to fine-tune how many geometries at once you want to dissolve to be nice on - /// > both memory size and CPU time, finding the balance between ST_Union and ST_MemUnion. + /// assert_eq!(geom.get_num_interior_rings(), Ok(2)); + /// ``` + fn get_num_interior_rings(&self) -> GResult; + /// Returns the number of coordinates inside `self`. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") - /// .expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POLYGON((1 1, 2 1, 2 5, 1 5, 1 1))") - /// .expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::create_multipolygon(vec![geom1, geom2]) - /// .expect("Failed to build multipolygon"); - /// - /// let union_geom = geom.unary_union().expect("unary_union failed"); + /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); /// - /// assert_eq!(union_geom.to_wkt_precision(1).unwrap(), - /// "POLYGON ((0.0 0.0, 0.0 6.0, 10.0 6.0, 10.0 0.0, 0.0 0.0))"); + /// assert_eq!(geom.get_num_coordinates(), Ok(5)); /// ``` - pub fn unary_union(&self) -> GResult> { - unsafe { - let ptr = GEOSUnaryUnion_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "unary_union") - } - } - - /// Create a voronoi diagram. + fn get_num_coordinates(&self) -> GResult; + /// Returns the number of dimensions used in `self`. /// /// # Example /// - /// /// ``` - /// use geos::Geometry; - /// - /// let input = Geometry::new_from_wkt("MULTIPOINT((100 200), (105 202), (110 200), (140 230), - /// (210 240), (220 190), (170 170), (170 260), - /// (213 245), (220 190))") - /// .expect("Invalid geometry"); - /// let mut expected = Geometry::new_from_wkt( - /// "GEOMETRYCOLLECTION(POLYGON ((-20 50, -20 380, -3.75 380, 105 235, 105 115, 77.14285714285714 50, -20 50)),\ - /// POLYGON ((247 50, 77.14285714285714 50, 105 115, 145 195, 178.33333333333334 211.66666666666666, 183.51851851851853 208.7037037037037, 247 50)),\ - /// POLYGON ((-3.75 380, 20.000000000000007 380, 176.66666666666666 223.33333333333334, 178.33333333333334 211.66666666666666, 145 195, 105 235, -3.75 380)),\ - /// POLYGON ((105 115, 105 235, 145 195, 105 115)),\ - /// POLYGON ((20.000000000000007 380, 255 380, 176.66666666666666 223.33333333333334, 20.000000000000007 380)),\ - /// POLYGON ((255 380, 340 380, 340 240, 183.51851851851853 208.7037037037037, 178.33333333333334 211.66666666666666, 176.66666666666666 223.33333333333334, 255 380)),\ - /// POLYGON ((340 240, 340 50, 247 50, 183.51851851851853 208.7037037037037, 340 240)))") - /// .expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let mut voronoi = input.voronoi(None, 6., false).expect("voronoi failed"); - /// - /// expected.normalize().expect("normalize failed"); - /// voronoi.normalize().expect("normalize failed"); + /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); /// - /// assert_eq!(expected.equals(&voronoi), Ok(true)); + /// assert_eq!(geom.get_num_dimensions(), Ok(2)); /// ``` - pub fn voronoi<'b>( - &self, - envelope: Option<&Geometry<'b>>, - tolerance: f64, - only_edges: bool, - ) -> GResult> { - unsafe { - let raw_voronoi = GEOSVoronoiDiagram_r( - self.get_raw_context(), - self.as_raw(), - envelope - .map(|e| e.as_raw()) - .unwrap_or(std::ptr::null_mut()), - tolerance, - only_edges as _, - ); - Self::new_from_raw(raw_voronoi, self.clone_context(), "voronoi") - } - } - - /// Normalizes `self` in its normalized/canonical form. May reorder vertices in polygon rings, - /// rings in a polygon, elements in a multi-geometry complex. + fn get_num_dimensions(&self) -> GResult; + /// Return in which coordinate dimension the geometry is. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Dimensions, Geom, Geometry}; /// - /// let mut geom = Geometry::new_from_wkt("GEOMETRYCOLLECTION(POINT(2 3), - /// MULTILINESTRING((0 0, 1 1),(2 2, 3 3)))") - /// .expect("Invalid geometry"); - /// - /// geom.normalize(); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); + /// assert!(point_geom.get_coordinate_dimension() == Ok(Dimensions::ThreeD)); /// - /// assert_eq!(geom.to_wkt_precision(1).unwrap(), - /// "GEOMETRYCOLLECTION (MULTILINESTRING ((2.0 2.0, 3.0 3.0), (0.0 0.0, 1.0 1.0)), \ - /// POINT (2.0 3.0))"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 4.0)").expect("Invalid geometry"); + /// assert!(point_geom.get_coordinate_dimension() == Ok(Dimensions::TwoD)); /// ``` - pub fn normalize(&mut self) -> GResult { - let ret_val = unsafe { GEOSNormalize_r(self.get_raw_context(), self.as_raw()) }; - check_geos_predicate(ret_val, PredicateType::Normalize) - } - - /// Returns a geometry representing the intersection between `self` and `other`. + fn get_coordinate_dimension(&self) -> GResult; + /// This functions attempts to return a valid representation of `self`. + /// + /// Available using the `v3_8_0` feature. + #[cfg(any(feature = "v3_8_0", feature = "dox"))] + fn make_valid(&self) -> GResult>; + /// Returns the number of geometries. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let mut geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("Invalid geometry"); - /// let mut geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let intersection_geom = geom1.intersection(&geom2).expect("intersection failed"); + /// let geom = Geometry::new_from_wkt("LINESTRING(77.29 29.07,77.42 29.26,77.27 29.31,77.29 29.07)") + /// .expect("Invalid geometry"); + /// assert_eq!(geom.get_num_geometries(), Ok(1)); /// - /// // No intersection. - /// assert_eq!(intersection_geom.is_empty(), Ok(true)); + /// let geom = Geometry::new_from_wkt("GEOMETRYCOLLECTION(MULTIPOINT(-2 3 , -2 2),\ + /// LINESTRING(5 5 ,10 10),\ + /// POLYGON((-7 4.2,-7.1 5,-7.1 4.3,-7 4.2)))") + /// .expect("Invalid geometry"); + /// assert_eq!(geom.get_num_geometries(), Ok(3)); + /// ``` + fn get_num_geometries(&self) -> GResult; + /// Get SRID of `self`. /// - /// // We slighty change the linestring so we have an intersection: - /// let mut geom2 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("Invalid geometry"); + /// # Example /// - /// let intersection_geom = geom1.intersection(&geom2).expect("intersection failed"); + /// ``` + /// use geos::{Geom, Geometry}; /// - /// // Intersection! - /// assert_eq!(intersection_geom.to_wkt_precision(1).unwrap(), "POINT (0.0 0.0)"); + /// let mut point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)") + /// .expect("Invalid geometry"); + /// point_geom.set_srid(4326); + /// assert_eq!(point_geom.get_srid(), Ok(4326)); /// ``` - pub fn intersection<'b>(&self, other: &Geometry<'b>) -> GResult> { - unsafe { - let ptr = GEOSIntersection_r(self.get_raw_context(), self.as_raw(), other.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "intersection") - } - } - - /// Documentation from [postgis](https://postgis.net/docs/ST_ConvexHull.html): + fn get_srid(&self) -> GResult; + /// Returns the precision of `self`. /// - /// > The convex hull of a geometry represents the minimum convex geometry that encloses all - /// > geometries within the set. - /// > - /// > One can think of the convex hull as the geometry you get by wrapping an elastic band - /// > around a set of geometries. This is different from a concave hull which is analogous to - /// > shrink-wrapping your geometries. - /// > - /// > It is usually used with MULTI and Geometry Collections. Although it is not an aggregate - - /// > you can use it in conjunction with ST_Collect to get the convex hull of a set of points. - /// > ST_ConvexHull(ST_Collect(somepointfield)). - /// > - /// > It is often used to determine an affected area based on a set of point observations. + /// Available using the `v3_6_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let mut geom1 = Geometry::new_from_wkt("MULTILINESTRING((100 190,10 8), - /// (150 10, 20 30))") - /// .expect("Invalid geometry"); - /// let mut geom2 = Geometry::new_from_wkt("MULTIPOINT(50 5, 150 30, 50 10, 10 10)") - /// .expect("Invalid geometry"); - /// - /// let geom = geom1.union(&geom2).expect("union failed"); - /// let convex_hull_geom = geom.convex_hull().expect("convex_hull failed"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(convex_hull_geom.to_wkt_precision(1).unwrap(), - /// "POLYGON ((50.0 5.0, 10.0 8.0, 10.0 10.0, 100.0 190.0, 150.0 30.0, 150.0 10.0, 50.0 5.0))"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); + /// assert_eq!(point_geom.get_precision().map(|x| format!("{:.2}", x)).unwrap(), "0.00"); /// ``` - pub fn convex_hull(&self) -> GResult> { - unsafe { - let ptr = GEOSConvexHull_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "convex_hull") - } - } - - /// Returns the closure of the combinatorial boundary of `self`. + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn get_precision(&self) -> GResult; + /// Returns the precision of `self`. + /// + /// Available using the `v3_6_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry, Precision}; /// - /// let geom = Geometry::new_from_wkt("LINESTRING(1 1,0 0, -1 1)").expect("Invalid geometry"); - /// let boundary_geom = geom.boundary().expect("boundary failed"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); /// - /// assert_eq!(boundary_geom.to_wkt_precision(1).unwrap(), "MULTIPOINT (1.0 1.0, -1.0 1.0)"); + /// point_geom.set_precision(1., Precision::KeepCollapsed); + /// assert_eq!(point_geom.get_precision().map(|x| format!("{:.2}", x)).unwrap(), "0.00"); /// ``` - pub fn boundary(&self) -> GResult> { - unsafe { - let ptr = GEOSBoundary_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "boundary") - } - } - - /// Returns `true` if `self` has a Z coordinate. + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn set_precision(&self, grid_size: f64, flags: Precision) -> GResult>; + /// Returns the biggest X of the geometry. + /// + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("POINT(1 2 3)").expect("Invalid geometry"); - /// assert_eq!(geom.has_z(), Ok(true)); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT(1 2)").expect("Invalid geometry"); - /// assert_eq!(geom.has_z(), Ok(false)); + /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); + /// assert_eq!(line.get_x_max(), Ok(5.)); /// ``` - pub fn has_z(&self) -> GResult { - let ret_val = unsafe { GEOSHasZ_r(self.get_raw_context(), self.as_raw()) }; - check_geos_predicate(ret_val as _, PredicateType::IsSimple) - } - - /// Returns `true` if start and end point are coincident. + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_x_max(&self) -> GResult; + /// Returns the smallest X of the geometry. /// - /// Only works on `LineString` and `MultiLineString`. + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 1 1)").expect("Invalid geometry"); - /// assert_eq!(geom.is_closed(), Ok(false)); - /// - /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 0 1, 1 1, 0 0)").expect("Invalid geometry"); - /// assert_eq!(geom.is_closed(), Ok(true)); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("MULTILINESTRING((0 0, 0 1, 1 1, 0 0),(0 0, 1 1))") - /// .expect("Invalid geometry"); - /// assert_eq!(geom.is_closed(), Ok(false)); + /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); + /// assert_eq!(line.get_x_min(), Ok(1.)); /// ``` - pub fn is_closed(&self) -> GResult { - if self.geometry_type() != GeometryTypes::LineString && - self.geometry_type() != GeometryTypes::MultiLineString { - return Err(Error::GenericError("Geometry must be a LineString or a MultiLineString".to_owned())); - } - let ret_val = unsafe { GEOSisClosed_r(self.get_raw_context(), self.as_raw()) }; - check_geos_predicate(ret_val as _, PredicateType::IsSimple) - } - - /// Returns the length of `self`. The unit depends of the SRID. + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_x_min(&self) -> GResult; + /// Returns the biggest Y of the geometry. + /// + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("LINESTRING(743238 2967416,743238 2967450)") - /// .expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(geom.length().map(|x| format!("{:.2}", x)).unwrap(), "34.00"); + /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); + /// assert_eq!(line.get_y_max(), Ok(6.)); /// ``` - pub fn length(&self) -> GResult { - let mut length = 0.; - unsafe { - let ret = GEOSLength_r(self.get_raw_context(), self.as_raw(), &mut length); - check_ret(ret, PredicateType::IsSimple).map(|_| length) - } - } - - /// Returns the distance between `self` and `other`. The unit depends of the SRID. + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_y_max(&self) -> GResult; + /// Returns the smallest Y of the geometry. + /// + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); - /// - /// assert_eq!(geom1.distance(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "1.00"); + /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); + /// assert_eq!(line.get_y_min(), Ok(3.)); /// ``` - pub fn distance<'b>(&self, other: &Geometry<'b>) -> GResult { - let mut distance = 0.; - unsafe { - let ret = GEOSDistance_r( - self.get_raw_context(), - self.as_raw(), - other.as_raw(), - &mut distance); - check_ret(ret, PredicateType::IsSimple).map(|_| distance) - } - } - - /// Returns the indexed distance between `self` and `other`. The unit depends of the SRID. + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_y_min(&self) -> GResult; + /// Returns the smallest distance by which a vertex of `self` could be moved to produce an + /// invalid geometry. /// - /// Available using the `v3_7_0` feature. + /// Available using the `v3_6_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(geom1.distance_indexed(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "1.00"); + /// let geom = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); + /// assert_eq!(geom.minimum_clearance().map(|x| format!("{:.8}", x)).unwrap(), "5.00000000"); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn distance_indexed<'b>(&self, other: &Geometry<'b>) -> GResult { - unsafe { - let mut distance = 0.; - if GEOSDistanceIndexed_r(self.get_raw_context(), - self.as_raw(), - other.as_raw(), - &mut distance) != 1 { - Err(Error::GenericError("GEOSDistanceIndexed_r failed".to_owned())) - } else { - Ok(distance) - } - } - } - - /// Returns the hausdorff distance between `self` and `other`. The unit depends of the SRID. + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_clearance(&self) -> GResult; + /// Returns the two-point LineString spanning of `self`'s minimum clearance. + /// + /// Available using the `v3_6_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(geom1.hausdorff_distance(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "1.00"); + /// let geom = Geometry::new_from_wkt("POLYGON ((0 0, 1 0, 1 1, 0.5 3.2e-4, 0 0))") + /// .expect("Invalid WKT"); + /// let line = geom.minimum_clearance_line().expect("minimum_clearance_line failed"); + /// assert_eq!(line.to_wkt_precision(1).unwrap(), "LINESTRING (0.5 0.0, 0.5 0.0)"); /// ``` - pub fn hausdorff_distance<'b>(&self, other: &Geometry<'b>) -> GResult { - let mut distance = 0.; - unsafe { - let ret = GEOSHausdorffDistance_r( - self.get_raw_context(), - self.as_raw(), - other.as_raw(), - &mut distance); - check_ret(ret, PredicateType::IsSimple).map(|_| distance) - } - } - - /// Returns the hausdorff distance between `self` and `other`. The unit depends of the SRID. + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_clearance_line(&self) -> GResult>; + /// Returns the minimum rotated rectangle inside of `self`. + /// + /// Available using the `v3_6_0` feature. + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_rotated_rectangle(&self) -> GResult>; + /// Returns the minimum width inside of `self`. + /// + /// Available using the `v3_6_0` feature. + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_width(&self) -> GResult>; + /// Returns a [delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) + /// around the vertices of `self`. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("POINT (2 2)").expect("Invalid geometry"); + /// let geom1 = Geometry::new_from_wkt("POLYGON((175 150, 20 40, 50 60, 125 100, 175 150))") + /// .expect("Invalid WKT"); + /// let geom2 = Geometry::new_from_wkt("POINT(110 170)").expect("Invalid WKT"); + /// let geom2 = geom2.buffer(20., 8).expect("buffer failed"); /// - /// assert_eq!(geom1.hausdorff_distance_densify(&geom2, 1.).map(|x| format!("{:.2}", x)) - /// .unwrap(), "1.00"); + /// let geom = geom1.union(&geom2).expect("union failed"); + /// + /// let final_geom = geom.delaunay_triangulation(0.001, false).expect("delaunay_triangulation failed"); /// ``` - pub fn hausdorff_distance_densify<'b>(&self, other: &Geometry<'b>, distance_frac: f64) -> GResult { - let mut distance = 0.; - unsafe { - let ret = GEOSHausdorffDistanceDensify_r( - self.get_raw_context(), - self.as_raw(), - other.as_raw(), - distance_frac, - &mut distance); - check_ret(ret, PredicateType::IsSimple).map(|_| distance) - } - } - - /// Returns the frechet distance between `self` and `other`. The unit depends of the SRID. + fn delaunay_triangulation(&self, tolerance: f64, only_edges: bool) -> GResult>; + fn interpolate(&self, d: f64) -> GResult>; + fn interpolate_normalized(&self, d: f64) -> GResult>; + fn project<'b, G: Geom<'b>>(&self, p: &G) -> GResult; + fn project_normalized<'b, G: Geom<'b>>(&self, p: &G) -> GResult; + fn node(&self) -> GResult>; + /// Return an offset line at a given distance and side from an input line. All points of the + /// returned geometries are not further than the given distance from the input geometry. /// - /// Available using the `v3_7_0` feature. + /// ### Parameters description: + /// + /// #### width + /// + /// * If `width` is positive, the offset will be at the left side of the input line and retain + /// the same direction. + /// * If `width` is negative, it'll be at the right side and in the opposite direction. + /// + /// #### quadrant_segments + /// + /// * If `quadrant_segments` is >= 1, joins are round, and `quadrant_segments` indicates the + /// number of segments to use to approximate a quarter-circle. + /// * If `quadrant_segments` == 0, joins are bevelled (flat). + /// * If `quadrant_segments` < 0, joins are mitred, and the value of `quadrant_segments` + /// indicates the mitre ration limit as `mitre_limit = |quadrant_segments|` + /// + /// #### mitre_limit + /// + /// The mitre ratio is the ratio of the distance from the corner to the end of the mitred offset + /// corner. When two line segments meet at a sharp angle, a miter join will extend far beyond + /// the original geometry (and in the extreme case will be infinitely far). To prevent + /// unreasonable geometry, the mitre limit allows controlling the maximum length of the join + /// corner. Corners with a ratio which exceed the limit will be beveled. + fn offset_curve( + &self, + width: f64, + quadrant_segments: i32, + join_style: JoinStyle, + mitre_limit: f64, + ) -> GResult>; + fn point_on_surface(&self) -> GResult>; + /// Returns, in the tuple elements order: + /// + /// 1. The polygonized geometry. + /// 2. The cuts geometries collection. + /// 3. The dangles geometries collection. + /// 4. The invalid geometries collection. + fn polygonize_full( + &self, + ) -> GResult<( + Geometry<'a>, + Option>, + Option>, + Option>, + )>; + fn shared_paths<'b, G: Geom<'b>>(&self, other: &G) -> GResult>; + /// Converts a [`Geometry`] to the HEX format. For more control over the generated output, + /// use the [`WKBWriter`] type. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING (0 0, 100 0)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING (0 0, 50 50, 100 0)").expect("Invalid geometry"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); + /// let hex_buf = point_geom.to_hex().expect("conversion to WKB failed"); + /// ``` + fn to_hex(&self) -> GResult>; + /// Converts a [`Geometry`] to the WKB format. For more control over the generated output, + /// use the [`WKBWriter`] type. + /// + /// # Example /// - /// assert_eq!(geom1.frechet_distance(&geom2).map(|x| format!("{:.2}", x)).unwrap(), "70.71"); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn frechet_distance<'b>(&self, other: &Geometry<'b>) -> GResult { - let mut distance = 0.; - unsafe { - let ret = GEOSFrechetDistance_r( - self.get_raw_context(), - self.as_raw(), - other.as_raw(), - &mut distance); - check_ret(ret, PredicateType::IsSimple).map(|_| distance) - } - } - - /// Returns the frechet distance between `self` and `other`. The unit depends of the SRID. + /// use geos::{Geom, Geometry}; /// - /// Available using the `v3_7_0` feature. + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); + /// let wkb_buf = point_geom.to_wkb().expect("conversion to WKB failed"); + /// ``` + fn to_wkb(&self) -> GResult>; + /// Creates a new [`PreparedGeometry`] from the current `Geometry`. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING (0 0, 100 0)").expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING (0 0, 50 50, 100 0)").expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// assert_eq!(geom1.frechet_distance_densify(&geom2, 1.).map(|x| format!("{:.2}", x)) - /// .unwrap(), "70.71"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); + /// let prepared_geom = point_geom.to_prepared_geom().expect("failed to create prepared geom"); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn frechet_distance_densify<'b>(&self, other: &Geometry<'b>, distance_frac: f64) -> GResult { - let mut distance = 0.; - unsafe { - let ret = GEOSFrechetDistanceDensify_r( - self.get_raw_context(), - self.as_raw(), - other.as_raw(), - distance_frac, - &mut distance); - check_ret(ret, PredicateType::IsSimple).map(|_| distance) - } - } - - /// Returns the length of the given geometry. + fn to_prepared_geom(&self) -> GResult>; + /// Also passes the context to the newly created `Geometry`. + fn clone(&self) -> Geometry<'a>; + /// Returns the 1-based nth geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4, 5 6)") + /// let geom = Geometry::new_from_wkt("MULTIPOINT(1 1, 2 2, 3 3, 4 4)") /// .expect("Invalid geometry"); + /// let point_nb3 = geom + /// .get_geometry_n(2) + /// .expect("failed to get third point"); + /// assert_eq!( + /// point_nb3.to_wkt().unwrap(), + /// "POINT (3.0000000000000000 3.0000000000000000)", + /// ); + /// ``` + fn get_geometry_n<'c>(&'c self, n: usize) -> GResult>; + /// Returns the nth interior ring. + /// + /// # Example /// - /// assert_eq!(geom.get_length().map(|x| format!("{:.2}", x)).unwrap(), "5.66"); /// ``` - pub fn get_length(&self) -> GResult { - let mut length = 0.; - unsafe { - let ret = GEOSGeomGetLength_r(self.get_raw_context(), self.as_raw(), &mut length); - check_ret(ret, PredicateType::IsSimple).map(|_| length) - } - } - - /// Documentation from [postgis](https://postgis.net/docs/ST_Snap.html): + /// use geos::{Geom, Geometry}; /// - /// > Snaps the vertices and segments of a geometry another Geometry's vertices. A snap - /// > distance tolerance is used to control where snapping is performed. The result geometry is - /// > the input geometry with the vertices snapped. If no snapping occurs then the input - /// > geometry is returned unchanged. - /// > - /// > Snapping one geometry to another can improve robustness for overlay operations by - /// > eliminating nearly-coincident edges (which cause problems during noding and intersection - /// > calculation). - /// > - /// > Too much snapping can result in invalid topology being created, so the number and location - /// > of snapped vertices is decided using heuristics to determine when it is safe to snap. This - /// > can result in some potential snaps being omitted, however. + /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0),\ + /// (1 1, 2 1, 2 5, 1 5, 1 1),\ + /// (8 5, 8 4, 9 4, 9 5, 8 5))") + /// .expect("Invalid geometry"); + /// let interior = geom + /// .get_interior_ring_n(0) + /// .expect("failed to get interior ring"); + /// assert_eq!(interior.to_wkt().unwrap(), + /// "LINEARRING (1.0000000000000000 1.0000000000000000, \ + /// 2.0000000000000000 1.0000000000000000, \ + /// 2.0000000000000000 5.0000000000000000, \ + /// 1.0000000000000000 5.0000000000000000, \ + /// 1.0000000000000000 1.0000000000000000)"); + /// ``` + fn get_interior_ring_n<'c>(&'c self, n: u32) -> GResult>; + /// Returns the exterior ring. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("MULTIPOLYGON(((26 125, 26 200, 126 200, 126 125, 26 125), - /// (51 150, 101 150, 76 175, 51 150)), - /// ((151 100, 151 200, 176 175, 151 100)))") - /// .expect("Invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(5 107, 54 84, 101 100)") - /// .expect("Invalid geometry"); + /// use geos::{Geom, Geometry}; /// - /// let distance = geom1.distance(&geom2).expect("distance failed"); - /// let snap_geom = geom1.snap(&geom2, distance * 1.25).expect("snap failed"); + /// let point_geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0),\ + /// (1 1, 2 1, 2 5, 1 5, 1 1))") + /// .expect("Invalid geometry"); /// - /// assert_eq!(snap_geom.to_wkt_precision(1).unwrap(), - /// "MULTIPOLYGON (((5.0 107.0, 26.0 200.0, 126.0 200.0, 126.0 125.0, 101.0 100.0, 54.0 84.0, 5.0 107.0), \ - /// (51.0 150.0, 101.0 150.0, 76.0 175.0, 51.0 150.0)), \ - /// ((151.0 100.0, 151.0 200.0, 176.0 175.0, 151.0 100.0)))"); + /// let exterior = point_geom + /// .get_exterior_ring() + /// .expect("failed to get exterior ring"); + /// assert_eq!(exterior.to_wkt().unwrap(), + /// "LINEARRING (0.0000000000000000 0.0000000000000000, \ + /// 10.0000000000000000 0.0000000000000000, \ + /// 10.0000000000000000 6.0000000000000000, \ + /// 0.0000000000000000 6.0000000000000000, \ + /// 0.0000000000000000 0.0000000000000000)"); /// ``` - pub fn snap<'b>(&self, other: &Geometry<'b>, tolerance: f64) -> GResult> { + fn get_exterior_ring<'c>(&'c self) -> GResult>; +} + +macro_rules! impl_geom { + ($ty_name:ident) => ( + impl_geom!($ty_name,,); + ); + ($ty_name:ident, $lt:lifetime) => ( + impl_geom!($ty_name, $lt, original); + ); + ($ty_name:ident, $($lt:lifetime)?, $($field:ident)?) => ( +impl<'a$(, $lt)?> Geom<'a> for $ty_name<'a$(, $lt)?> { + fn get_type(&self) -> GResult { + unsafe { + let ptr = GEOSGeomType_r(self.get_raw_context(), self.as_raw()); + managed_string(ptr, self.get_context_handle(), "GGeom::get_type") + } + } + + fn geometry_type(&self) -> GeometryTypes { + let type_geom = unsafe { GEOSGeomTypeId_r(self.get_raw_context(), self.as_raw()) as i32 }; + + GeometryTypes::from(type_geom) + } + + fn is_valid(&self) -> bool { + unsafe { GEOSisValid_r(self.get_raw_context(), self.as_raw()) == 1 } + } + + fn is_valid_reason(&self) -> GResult { + unsafe { + let ptr = GEOSisValidReason_r(self.get_raw_context(), self.as_raw()); + managed_string(ptr, self.get_context_handle(), "GGeom::is_valid_reason") + } + } + + fn get_coord_seq(&self) -> GResult> { + let type_geom = self.geometry_type(); + match type_geom { + GeometryTypes::Point | GeometryTypes::LineString | GeometryTypes::LinearRing => unsafe { + let coord = GEOSGeom_getCoordSeq_r(self.get_raw_context(), self.as_raw()); + let t = GEOSCoordSeq_clone_r(self.get_raw_context(), coord); + let mut size = 0; + let mut dims = 0; + + if GEOSCoordSeq_getSize_r(self.get_raw_context(), coord, &mut size) == 0 { + return Err(Error::GenericError("GEOSCoordSeq_getSize_r failed".to_owned())); + } + if GEOSCoordSeq_getDimensions_r(self.get_raw_context(), coord, &mut dims) == 0 { + return Err(Error::GenericError("GEOSCoordSeq_getDimensions_r failed".to_owned())); + } + CoordSeq::new_from_raw(t, self.clone_context(), size, dims, "get_coord_seq") + }, + _ => Err(Error::ImpossibleOperation( + "Geometry must be a Point, LineString or LinearRing to extract its coordinates" + .into(), + )), + } + } + + fn area(&self) -> GResult { + let mut n = 0.; + + let res = unsafe { GEOSArea_r(self.get_raw_context(), self.as_raw(), &mut n) }; + if res != 1 { + Err(Error::GeosError(format!("area failed with code {}", res))) + } else { + Ok(n as f64) + } + } + + fn to_wkt(&self) -> GResult { + match WKTWriter::new_with_context(self.clone_context()) { + Ok(mut w) => w.write(self), + Err(e) => Err(e), + } + } + + fn to_wkt_precision(&self, precision: u32) -> GResult { + unsafe { + let writer = GEOSWKTWriter_create_r(self.get_raw_context()); + GEOSWKTWriter_setRoundingPrecision_r(self.get_raw_context(), writer, precision as _); + let c_result = GEOSWKTWriter_write_r(self.get_raw_context(), writer, self.as_raw()); + GEOSWKTWriter_destroy_r(self.get_raw_context(), writer); + managed_string(c_result, self.get_context_handle(), "GResult::to_wkt_precision") + } + } + + fn is_ring(&self) -> GResult { + let rv = unsafe { GEOSisRing_r(self.get_raw_context(), self.as_raw()) }; + check_geos_predicate(rv as _, PredicateType::IsRing) + } + + fn intersects<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSIntersects_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Intersects) + } + + fn crosses<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSCrosses_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Crosses) + } + + fn disjoint<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSDisjoint_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Disjoint) + } + + fn touches<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSTouches_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Touches) + } + + fn overlaps<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSOverlaps_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Overlaps) + } + + fn within<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSWithin_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Within) + } + + fn equals<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSEquals_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Equals) + } + + fn equals_exact<'b, G: Geom<'b>>(&self, other: &G, precision: f64) -> GResult { + let ret_val = unsafe { + GEOSEqualsExact_r(self.get_raw_context(), self.as_raw(), other.as_raw(), precision) + }; + check_geos_predicate(ret_val as _, PredicateType::EqualsExact) + } + + fn covers<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSCovers_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Covers) + } + + fn covered_by<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSCoveredBy_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::CoveredBy) + } + + fn contains<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = unsafe { + GEOSContains_r(self.get_raw_context(), self.as_raw(), other.as_raw()) + }; + check_geos_predicate(ret_val as _, PredicateType::Contains) + } + + fn buffer(&self, width: f64, quadsegs: i32) -> GResult> { + assert!(quadsegs > 0); + unsafe { + let ptr = GEOSBuffer_r( + self.get_raw_context(), + self.as_raw(), + width, + quadsegs as _, + ); + Geometry::new_from_raw(ptr, self.clone_context(), "buffer") + } + } + + fn is_empty(&self) -> GResult { + let ret_val = unsafe { GEOSisEmpty_r(self.get_raw_context(), self.as_raw()) }; + check_geos_predicate(ret_val as _, PredicateType::IsEmpty) + } + + fn is_simple(&self) -> GResult { + let ret_val = unsafe { GEOSisSimple_r(self.get_raw_context(), self.as_raw()) }; + check_geos_predicate(ret_val as _, PredicateType::IsSimple) + } + + fn difference<'b, G: Geom<'b>>(&self, other: &G) -> GResult> { + unsafe { + let ptr = GEOSDifference_r(self.get_raw_context(), self.as_raw(), other.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "difference") + } + } + + fn envelope(&self) -> GResult> { + unsafe { + let ptr = GEOSEnvelope_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "envelope") + } + } + + fn sym_difference<'b, G: Geom<'b>>(&self, other: &G) -> GResult> { + unsafe { + let ptr = GEOSSymDifference_r(self.get_raw_context(), self.as_raw(), other.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "sym_difference") + } + } + + fn union<'b, G: Geom<'b>>(&self, other: &G) -> GResult> { + unsafe { + let ptr = GEOSUnion_r(self.get_raw_context(), self.as_raw(), other.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "union") + } + } + + fn get_centroid(&self) -> GResult> { + unsafe { + let ptr = GEOSGetCentroid_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "get_centroid") + } + } + + fn unary_union(&self) -> GResult> { + unsafe { + let ptr = GEOSUnaryUnion_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "unary_union") + } + } + + fn voronoi<'b, G: Geom<'b>>( + &self, + envelope: Option<&G>, + tolerance: f64, + only_edges: bool, + ) -> GResult> { + unsafe { + let raw_voronoi = GEOSVoronoiDiagram_r( + self.get_raw_context(), + self.as_raw(), + envelope + .map(|e| e.as_raw()) + .unwrap_or(std::ptr::null_mut()), + tolerance, + only_edges as _, + ); + Geometry::new_from_raw(raw_voronoi, self.clone_context(), "voronoi") + } + } + + fn intersection<'b, G: Geom<'b>>(&self, other: &G) -> GResult> { + unsafe { + let ptr = GEOSIntersection_r(self.get_raw_context(), self.as_raw(), other.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "intersection") + } + } + + fn convex_hull(&self) -> GResult> { + unsafe { + let ptr = GEOSConvexHull_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "convex_hull") + } + } + + fn boundary(&self) -> GResult> { + unsafe { + let ptr = GEOSBoundary_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "boundary") + } + } + + fn has_z(&self) -> GResult { + let ret_val = unsafe { GEOSHasZ_r(self.get_raw_context(), self.as_raw()) }; + check_geos_predicate(ret_val as _, PredicateType::IsSimple) + } + + fn is_closed(&self) -> GResult { + if self.geometry_type() != GeometryTypes::LineString && + self.geometry_type() != GeometryTypes::MultiLineString { + return Err(Error::GenericError("Geometry must be a LineString or a MultiLineString".to_owned())); + } + let ret_val = unsafe { GEOSisClosed_r(self.get_raw_context(), self.as_raw()) }; + check_geos_predicate(ret_val as _, PredicateType::IsSimple) + } + + fn length(&self) -> GResult { + let mut length = 0.; + unsafe { + let ret = GEOSLength_r(self.get_raw_context(), self.as_raw(), &mut length); + check_ret(ret, PredicateType::IsSimple).map(|_| length) + } + } + + fn distance<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let mut distance = 0.; + unsafe { + let ret = GEOSDistance_r( + self.get_raw_context(), + self.as_raw(), + other.as_raw(), + &mut distance); + check_ret(ret, PredicateType::IsSimple).map(|_| distance) + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn distance_indexed<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + unsafe { + let mut distance = 0.; + if GEOSDistanceIndexed_r(self.get_raw_context(), + self.as_raw(), + other.as_raw(), + &mut distance) != 1 { + Err(Error::GenericError("GEOSDistanceIndexed_r failed".to_owned())) + } else { + Ok(distance) + } + } + } + + fn hausdorff_distance<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let mut distance = 0.; + unsafe { + let ret = GEOSHausdorffDistance_r( + self.get_raw_context(), + self.as_raw(), + other.as_raw(), + &mut distance); + check_ret(ret, PredicateType::IsSimple).map(|_| distance) + } + } + + fn hausdorff_distance_densify<'b, G: Geom<'b>>(&self, other: &G, distance_frac: f64) -> GResult { + let mut distance = 0.; + unsafe { + let ret = GEOSHausdorffDistanceDensify_r( + self.get_raw_context(), + self.as_raw(), + other.as_raw(), + distance_frac, + &mut distance); + check_ret(ret, PredicateType::IsSimple).map(|_| distance) + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn frechet_distance<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let mut distance = 0.; + unsafe { + let ret = GEOSFrechetDistance_r( + self.get_raw_context(), + self.as_raw(), + other.as_raw(), + &mut distance); + check_ret(ret, PredicateType::IsSimple).map(|_| distance) + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn frechet_distance_densify<'b, G: Geom<'b>>(&self, other: &G, distance_frac: f64) -> GResult { + let mut distance = 0.; + unsafe { + let ret = GEOSFrechetDistanceDensify_r( + self.get_raw_context(), + self.as_raw(), + other.as_raw(), + distance_frac, + &mut distance); + check_ret(ret, PredicateType::IsSimple).map(|_| distance) + } + } + + fn get_length(&self) -> GResult { + let mut length = 0.; + unsafe { + let ret = GEOSGeomGetLength_r(self.get_raw_context(), self.as_raw(), &mut length); + check_ret(ret, PredicateType::IsSimple).map(|_| length) + } + } + + fn snap<'b, G: Geom<'b>>(&self, other: &G, tolerance: f64) -> GResult> { + unsafe { + let ptr = GEOSSnap_r(self.get_raw_context(), self.as_raw(), other.as_raw(), tolerance); + Geometry::new_from_raw(ptr, self.clone_context(), "snap") + } + } + + fn extract_unique_points(&self) -> GResult> { + unsafe { + let ptr = GEOSGeom_extractUniquePoints_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "extract_unique_points") + } + } + + fn nearest_points<'b, G: Geom<'b>>(&self, other: &G) -> GResult> { + unsafe { + let ptr = GEOSNearestPoints_r( + self.get_raw_context(), + self.as_raw(), + other.as_raw(), + ); + let mut size = 0; + let mut dims = 0; + + if GEOSCoordSeq_getSize_r(self.get_raw_context(), ptr, &mut size) == 0 { + return Err(Error::GenericError("GEOSCoordSeq_getSize_r failed".to_owned())); + } + if GEOSCoordSeq_getDimensions_r(self.get_raw_context(), ptr, &mut dims) == 0 { + return Err(Error::GenericError("GEOSCoordSeq_getDimensions_r failed".to_owned())); + } + CoordSeq::new_from_raw(ptr, self.clone_context(), size, dims, "nearest_points") + } + } + + fn get_x(&self) -> GResult { + if self.geometry_type() != GeometryTypes::Point { + return Err(Error::GenericError("Geometry must be a point".to_owned())); + } + let mut x = 0.; + unsafe { + if GEOSGeomGetX_r(self.get_raw_context(), self.as_raw(), &mut x) == 1 { + Ok(x) + } else { + Err(Error::GenericError("GEOSGeomGetX_r failed".to_owned())) + } + } + } + + fn get_y(&self) -> GResult { + if self.geometry_type() != GeometryTypes::Point { + return Err(Error::GenericError("Geometry must be a point".to_owned())); + } + let mut y = 0.; + unsafe { + if GEOSGeomGetY_r(self.get_raw_context(), self.as_raw(), &mut y) == 1 { + Ok(y) + } else { + Err(Error::GenericError("GEOSGeomGetY_r failed".to_owned())) + } + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_z(&self) -> GResult { + if self.geometry_type() != GeometryTypes::Point { + return Err(Error::GenericError("Geometry must be a point".to_owned())); + } + let mut z = 0.; + unsafe { + if GEOSGeomGetZ_r(self.get_raw_context(), self.as_raw(), &mut z) == 1 { + Ok(z) + } else { + Err(Error::GenericError("GEOSGeomGetZ_r failed".to_owned())) + } + } + } + + fn get_point_n(&self, n: usize) -> GResult> { + if self.geometry_type() != GeometryTypes::LineString { + return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + } + unsafe { + let ptr = GEOSGeomGetPointN_r(self.get_raw_context(), self.as_raw(), n as _); + Geometry::new_from_raw(ptr, self.clone_context(), "get_point_n") + } + } + + fn get_start_point(&self) -> GResult> { + if self.geometry_type() != GeometryTypes::LineString { + return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + } + unsafe { + let ptr = GEOSGeomGetStartPoint_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "get_start_point") + } + } + + fn get_end_point(&self) -> GResult> { + if self.geometry_type() != GeometryTypes::LineString { + return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + } + unsafe { + let ptr = GEOSGeomGetEndPoint_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "get_end_point") + } + } + + fn get_num_points(&self) -> GResult { + if self.geometry_type() != GeometryTypes::LineString { + return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + } + unsafe { + let ret = GEOSGeomGetNumPoints_r(self.get_raw_context(), self.as_raw()); + if ret == -1 { + Err(Error::GenericError("GEOSGeomGetNumPoints_r failed".to_owned())) + } else { + Ok(ret as _) + } + } + } + + fn get_num_interior_rings(&self) -> GResult { + unsafe { + let ret = GEOSGetNumInteriorRings_r(self.get_raw_context(), self.as_raw()); + if ret == -1 { + Err(Error::GenericError("GEOSGetNumInteriorRings_r failed".to_owned())) + } else { + Ok(ret as _) + } + } + } + + fn get_num_coordinates(&self) -> GResult { + unsafe { + let ret = GEOSGetNumCoordinates_r(self.get_raw_context(), self.as_raw()); + if ret == -1 { + Err(Error::GenericError("GEOSGetNumCoordinates_r failed".to_owned())) + } else { + Ok(ret as _) + } + } + } + + fn get_num_dimensions(&self) -> GResult { + unsafe { + let ret = GEOSGeom_getDimensions_r(self.get_raw_context(), self.as_raw()); + if ret == -1 { + Err(Error::GenericError("GEOSGeom_getDimensions_r failed".to_owned())) + } else { + Ok(ret as _) + } + } + } + + fn get_coordinate_dimension(&self) -> GResult { + unsafe { + let ret = GEOSGeom_getCoordinateDimension_r(self.get_raw_context(), self.as_raw()); + if ret != 2 && ret != 3 { + Err(Error::GenericError("GEOSGeom_getCoordinateDimension_r failed".to_owned())) + } else { + Ok(Dimensions::from(ret)) + } + } + } + + #[cfg(any(feature = "v3_8_0", feature = "dox"))] + fn make_valid(&self) -> GResult> { + unsafe { + let ptr = GEOSMakeValid_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "make_valid") + } + } + + fn get_num_geometries(&self) -> GResult { + unsafe { + let ret = GEOSGetNumGeometries_r(self.get_raw_context(), self.as_raw()); + if ret < 1 { + Err(Error::GenericError("GEOSGetNumGeometries_r failed".to_owned())) + } else { + Ok(ret as _) + } + } + } + + fn get_srid(&self) -> GResult { + unsafe { + let ret = GEOSGetSRID_r(self.get_raw_context(), self.as_raw()); + if ret < 1 { + Err(Error::GenericError("GEOSGetSRID_r failed".to_owned())) + } else { + Ok(ret as _) + } + } + } + + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn get_precision(&self) -> GResult { + unsafe { + let ret = GEOSGeom_getPrecision_r(self.get_raw_context(), self.as_raw()); + if ret == -1. { + Err(Error::GenericError("GEOSGeom_getPrecision_r failed".to_owned())) + } else { + Ok(ret) + } + } + } + + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn set_precision(&self, grid_size: f64, flags: Precision) -> GResult> { + unsafe { + let ptr = GEOSGeom_setPrecision_r(self.get_raw_context(), + self.as_raw(), + grid_size, + flags.into()); + Geometry::new_from_raw(ptr, self.clone_context(), "set_precision") + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_x_max(&self) -> GResult { + unsafe { + let mut value = 0.; + if GEOSGeom_getXMax_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { + Err(Error::GenericError("GEOSGeom_getXMax_r failed".to_owned())) + } else { + Ok(value) + } + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_x_min(&self) -> GResult { + unsafe { + let mut value = 0.; + if GEOSGeom_getXMin_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { + Err(Error::GenericError("GEOSGeom_getXMin_r failed".to_owned())) + } else { + Ok(value) + } + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_y_max(&self) -> GResult { + unsafe { + let mut value = 0.; + if GEOSGeom_getYMax_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { + Err(Error::GenericError("GEOSGeom_getYMax_r failed".to_owned())) + } else { + Ok(value) + } + } + } + + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + fn get_y_min(&self) -> GResult { + unsafe { + let mut value = 0.; + if GEOSGeom_getYMin_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { + Err(Error::GenericError("GEOSGeom_getYMin_r failed".to_owned())) + } else { + Ok(value) + } + } + } + + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_clearance(&self) -> GResult { + unsafe { + let mut value = 0.; + if GEOSMinimumClearance_r(self.get_raw_context(), self.as_raw(), &mut value) != 0 { + Err(Error::GenericError("GEOSMinimumClearance_r failed".to_owned())) + } else { + Ok(value) + } + } + } + + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_clearance_line(&self) -> GResult> { + unsafe { + let ptr = GEOSMinimumClearanceLine_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "minimum_clearance_line") + } + } + + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_rotated_rectangle(&self) -> GResult> { + unsafe { + let ptr = GEOSMinimumRotatedRectangle_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "minimum_rotated_rectangle") + } + } + + #[cfg(any(feature = "v3_6_0", feature = "dox"))] + fn minimum_width(&self) -> GResult> { + unsafe { + let ptr = GEOSMinimumWidth_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "minimum_width") + } + } + + fn delaunay_triangulation(&self, tolerance: f64, only_edges: bool) -> GResult> { + unsafe { + let ptr = GEOSDelaunayTriangulation_r( + self.get_raw_context(), + self.as_raw(), + tolerance, + only_edges as _, + ); + Geometry::new_from_raw(ptr, self.clone_context(), "delaunay_triangulation") + } + } + + fn interpolate(&self, d: f64) -> GResult> { + if self.geometry_type() != GeometryTypes::LineString { + return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + } + unsafe { + let ptr = GEOSInterpolate_r(self.get_raw_context(), self.as_raw(), d); + Geometry::new_from_raw(ptr, self.clone_context(), "interpolate") + } + } + + fn interpolate_normalized(&self, d: f64) -> GResult> { + if self.geometry_type() != GeometryTypes::LineString { + return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + } + unsafe { + let ptr = GEOSInterpolateNormalized_r(self.get_raw_context(), self.as_raw(), d); + Geometry::new_from_raw(ptr, self.clone_context(), "interpolate_normalized") + } + } + + fn project<'b, G: Geom<'b>>(&self, p: &G) -> GResult { + if p.geometry_type() != GeometryTypes::Point { + return Err(Error::GenericError("Second geometry must be a Point".to_owned())); + } + unsafe { + let ret = GEOSProject_r(self.get_raw_context(), self.as_raw(), p.as_raw()); + if ret == -1. { + Err(Error::GenericError("GEOSProject_r failed".to_owned())) + } else { + Ok(ret) + } + } + } + + fn project_normalized<'b, G: Geom<'b>>(&self, p: &G) -> GResult { + if p.geometry_type() != GeometryTypes::Point { + return Err(Error::GenericError("Second geometry must be a Point".to_owned())); + } + unsafe { + let ret = GEOSProjectNormalized_r(self.get_raw_context(), self.as_raw(), p.as_raw()); + if ret == -1. { + Err(Error::GenericError("GEOSProjectNormalized_r failed".to_owned())) + } else { + Ok(ret) + } + } + } + + fn node(&self) -> GResult> { + unsafe { + let ptr = GEOSNode_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "node") + } + } + + fn offset_curve( + &self, + width: f64, + quadrant_segments: i32, + join_style: JoinStyle, + mitre_limit: f64, + ) -> GResult> { + if self.geometry_type() != GeometryTypes::LineString { + return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + } unsafe { - let ptr = GEOSSnap_r(self.get_raw_context(), self.as_raw(), other.as_raw(), tolerance); - Geometry::new_from_raw(ptr, self.clone_context(), "snap") + let ptr = GEOSOffsetCurve_r(self.get_raw_context(), self.as_raw(), width, + quadrant_segments, join_style.into(), mitre_limit); + Geometry::new_from_raw(ptr, self.clone_context(), "offset_curve") } } - /// Returns unique points of `self`. - pub fn extract_unique_points(&self) -> GResult> { + fn point_on_surface(&self) -> GResult> { unsafe { - let ptr = GEOSGeom_extractUniquePoints_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "extract_unique_points") + let ptr = GEOSPointOnSurface_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "point_on_surface") } } - pub fn nearest_points<'b>(&self, other: &Geometry<'b>) -> GResult> { + fn polygonize_full( + &self, + ) -> GResult<(Geometry<'a>, Option>, Option>, Option>)> { + let mut cuts: *mut GEOSGeometry = ::std::ptr::null_mut(); + let mut dangles: *mut GEOSGeometry = ::std::ptr::null_mut(); + let mut invalids: *mut GEOSGeometry = ::std::ptr::null_mut(); + unsafe { - let ptr = GEOSNearestPoints_r( + let ptr = GEOSPolygonize_full_r( self.get_raw_context(), self.as_raw(), - other.as_raw(), + &mut cuts, + &mut dangles, + &mut invalids, ); - let mut size = 0; - let mut dims = 0; - - if GEOSCoordSeq_getSize_r(self.get_raw_context(), ptr, &mut size) == 0 { - return Err(Error::GenericError("GEOSCoordSeq_getSize_r failed".to_owned())); - } - if GEOSCoordSeq_getDimensions_r(self.get_raw_context(), ptr, &mut dims) == 0 { - return Err(Error::GenericError("GEOSCoordSeq_getDimensions_r failed".to_owned())); - } - CoordSeq::new_from_raw(ptr, self.clone_context(), size, dims, "nearest_points") + let cuts = if !cuts.is_null() { + Geometry::new_from_raw(cuts, self.clone_context(), "polygonize_full").ok() + } else { + None + }; + let dangles = if !dangles.is_null() { + Geometry::new_from_raw(dangles, self.clone_context(), "polygonize_full").ok() + } else { + None + }; + let invalids = if !invalids.is_null() { + Geometry::new_from_raw(invalids, self.clone_context(), "polygonize_full").ok() + } else { + None + }; + Geometry::new_from_raw(ptr, self.clone_context(), "polygonize_full") + .map(|x| (x, cuts, dangles, invalids)) } } - /// Returns the X position. The given `Geometry` must be a `Point`, otherwise it'll fail. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let point_geom = Geometry::new_from_wkt("POINT (1.5 2.5 3.5)").expect("Invalid geometry"); - /// assert!(point_geom.get_x() == Ok(1.5)); - /// ``` - pub fn get_x(&self) -> GResult { - if self.geometry_type() != GeometryTypes::Point { - return Err(Error::GenericError("Geometry must be a point".to_owned())); - } - let mut x = 0.; + fn shared_paths<'b, G: Geom<'b>>(&self, other: &G) -> GResult> { unsafe { - if GEOSGeomGetX_r(self.get_raw_context(), self.as_raw(), &mut x) == 1 { - Ok(x) - } else { - Err(Error::GenericError("GEOSGeomGetX_r failed".to_owned())) - } + let ptr = GEOSSharedPaths_r(self.get_raw_context(), self.as_raw(), other.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "shared_paths") } } - /// Returns the Y position. The given `Geometry` must be a `Point`, otherwise it'll fail. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let point_geom = Geometry::new_from_wkt("POINT (1.5 2.5 3.5)").expect("Invalid geometry"); - /// assert!(point_geom.get_y() == Ok(2.5)); - /// ``` - pub fn get_y(&self) -> GResult { - if self.geometry_type() != GeometryTypes::Point { - return Err(Error::GenericError("Geometry must be a point".to_owned())); - } - let mut y = 0.; + fn to_hex(&self) -> GResult> { + let mut size = 0; unsafe { - if GEOSGeomGetY_r(self.get_raw_context(), self.as_raw(), &mut y) == 1 { - Ok(y) + let ptr = GEOSGeomToHEX_buf_r(self.get_raw_context(), self.as_raw(), &mut size); + if ptr.is_null() { + Err(Error::NoConstructionFromNullPtr( + "Geometry::to_hex failed: GEOSGeomToHEX_buf_r returned null pointer".to_owned()) + ) } else { - Err(Error::GenericError("GEOSGeomGetY_r failed".to_owned())) + Ok(CVec::new(ptr, size as _)) } } } - /// Returns the Z position. The given `Geometry` must be a `Point`, otherwise it'll fail. - /// - /// Available using the `v3_7_0` feature. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); - /// assert!(point_geom.get_z() == Ok(4.0)); - /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn get_z(&self) -> GResult { - if self.geometry_type() != GeometryTypes::Point { - return Err(Error::GenericError("Geometry must be a point".to_owned())); - } - let mut z = 0.; + fn to_wkb(&self) -> GResult> { + let mut size = 0; unsafe { - if GEOSGeomGetZ_r(self.get_raw_context(), self.as_raw(), &mut z) == 1 { - Ok(z) + let ptr = GEOSGeomToWKB_buf_r(self.get_raw_context(), self.as_raw(), &mut size); + if ptr.is_null() { + Err(Error::NoConstructionFromNullPtr( + "Geometry::to_wkb failed: GEOSGeomToWKB_buf_r returned null pointer".to_owned()) + ) } else { - Err(Error::GenericError("GEOSGeomGetZ_r failed".to_owned())) + Ok(CVec::new(ptr, size as _)) } } } - /// Returns the nth point of the given geometry. - /// - /// The given `Geometry` must be a `LineString`, otherwise it'll fail. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4, 5 6)") - /// .expect("Invalid geometry"); - /// let nth_point = geom.get_point_n(1).expect("get_point_n failed"); - /// - /// assert_eq!(nth_point.to_wkt_precision(1).unwrap(), "POINT (3.0 4.0)"); - /// ``` - pub fn get_point_n(&self, n: usize) -> GResult> { - if self.geometry_type() != GeometryTypes::LineString { - return Err(Error::GenericError("Geometry must be a LineString".to_owned())); - } - unsafe { - let ptr = GEOSGeomGetPointN_r(self.get_raw_context(), self.as_raw(), n as _); - Geometry::new_from_raw(ptr, self.clone_context(), "get_point_n") - } + fn to_prepared_geom(&self) -> GResult> { + PreparedGeometry::new(self) } - /// Returns the start point of `self`. - /// - /// The given `Geometry` must be a `LineString`, otherwise it'll fail. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)") - /// .expect("Invalid geometry"); - /// let start_point = geom.get_start_point().expect("get_start_point failed"); - /// - /// assert_eq!(start_point.to_wkt_precision(1).unwrap(), "POINT (1.0 2.0)"); - /// ``` - pub fn get_start_point(&self) -> GResult> { - if self.geometry_type() != GeometryTypes::LineString { - return Err(Error::GenericError("Geometry must be a LineString".to_owned())); + fn clone(&self) -> Geometry<'a> { + let context = self.clone_context(); + let ptr = unsafe { GEOSGeom_clone_r(context.as_raw(), self.as_raw()) }; + if ptr.is_null() { + panic!("Couldn't clone geometry..."); } - unsafe { - let ptr = GEOSGeomGetStartPoint_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "get_start_point") + Geometry { + ptr: PtrWrap(ptr), + context, } } - /// Returns the end point of `self`. - /// - /// The given `Geometry` must be a `LineString`, otherwise it'll fail. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)") - /// .expect("Invalid geometry"); - /// let end_point = geom.get_end_point().expect("get_end_point failed"); - /// - /// assert_eq!(end_point.to_wkt_precision(1).unwrap(), "POINT (3.0 4.0)"); - /// ``` - pub fn get_end_point(&self) -> GResult> { - if self.geometry_type() != GeometryTypes::LineString { - return Err(Error::GenericError("Geometry must be a LineString".to_owned())); - } + fn get_geometry_n<'c>(&'c self, n: usize) -> GResult> { unsafe { - let ptr = GEOSGeomGetEndPoint_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "get_end_point") + let ptr = GEOSGetGeometryN_r(self.get_raw_context(), self.as_raw(), n as _); + ConstGeometry::new_from_raw(ptr, self$(.$field)?, "get_geometry_n") } } - /// Returns the number of points of `self`. - /// - /// The given `Geometry` must be a `LineString`, otherwise it'll fail. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)") - /// .expect("Invalid geometry"); - /// - /// assert_eq!(geom.get_num_points(), Ok(2)); - /// ``` - pub fn get_num_points(&self) -> GResult { - if self.geometry_type() != GeometryTypes::LineString { - return Err(Error::GenericError("Geometry must be a LineString".to_owned())); - } + fn get_interior_ring_n<'c>(&'c self, n: u32) -> GResult> { unsafe { - let ret = GEOSGeomGetNumPoints_r(self.get_raw_context(), self.as_raw()); - if ret == -1 { - Err(Error::GenericError("GEOSGeomGetNumPoints_r failed".to_owned())) - } else { - Ok(ret as _) - } + let ptr = GEOSGetInteriorRingN_r(self.get_raw_context(), self.as_raw(), n as _); + ConstGeometry::new_from_raw(ptr, self$(.$field)?, "get_interior_ring_n") } } - /// Returns the number of interior rings. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0),\ - /// (1 1, 2 1, 2 5, 1 5, 1 1),\ - /// (8 5, 8 4, 9 4, 9 5, 8 5))") - /// .expect("Invalid geometry"); - /// - /// assert_eq!(geom.get_num_interior_rings(), Ok(2)); - /// ``` - pub fn get_num_interior_rings(&self) -> GResult { + fn get_exterior_ring<'c>(&'c self) -> GResult> { unsafe { - let ret = GEOSGetNumInteriorRings_r(self.get_raw_context(), self.as_raw()); - if ret == -1 { - Err(Error::GenericError("GEOSGetNumInteriorRings_r failed".to_owned())) - } else { - Ok(ret as _) - } + let ptr = GEOSGetExteriorRing_r(self.get_raw_context(), self.as_raw()); + ConstGeometry::new_from_raw(ptr, self$(.$field)?, "get_exterior_ring") } } +} - /// Returns the nth interior ring. +impl<'a, 'b$(, $lt)?, G: Geom<'b>> PartialEq for $ty_name<'a$(, $lt)?> { + fn eq(&self, other: &G) -> bool { + self.equals(other).unwrap_or_else(|_| false) + } +} + +unsafe impl<'a$(, $lt)?> Send for $ty_name<'a$(, $lt)?> {} +unsafe impl<'a$(, $lt)?> Sync for $ty_name<'a$(, $lt)?> {} + ) +} + +impl_geom!(Geometry); +impl_geom!(ConstGeometry, 'd); + +impl<'a> Geometry<'a> { + /// Creates a `Geometry` from the WKT format. /// /// # Example /// /// ``` /// use geos::Geometry; /// - /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0),\ - /// (1 1, 2 1, 2 5, 1 5, 1 1),\ - /// (8 5, 8 4, 9 4, 9 5, 8 5))") - /// .expect("Invalid geometry"); - /// let interior = geom.get_interior_ring_n(0).expect("failed to get interior ring"); - /// assert_eq!(interior.to_wkt().unwrap(), - /// "LINEARRING (1.0000000000000000 1.0000000000000000, \ - /// 2.0000000000000000 1.0000000000000000, \ - /// 2.0000000000000000 5.0000000000000000, \ - /// 1.0000000000000000 5.0000000000000000, \ - /// 1.0000000000000000 1.0000000000000000)"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); /// ``` - pub fn get_interior_ring_n(&self, n: u32) -> GResult> { - unsafe { - let ptr = GEOSGetInteriorRingN_r(self.get_raw_context(), self.as_raw(), n as _); - match Geometry::new_from_raw(ptr, self.clone_context(), "get_interior_ring_n") { - Ok(mut g) => { - g.owned = false; - Ok(g) - } - e => e, - } + pub fn new_from_wkt(wkt: &str) -> GResult> { + match ContextHandle::init_e(Some("Geometry::new_from_wkt")) { + Ok(context_handle) => match CString::new(wkt) { + Ok(c_str) => unsafe { + let reader = GEOSWKTReader_create_r(context_handle.as_raw()); + let ptr = GEOSWKTReader_read_r(context_handle.as_raw(), reader, c_str.as_ptr()); + GEOSWKTReader_destroy_r(context_handle.as_raw(), reader); + Geometry::new_from_raw(ptr, Arc::new(context_handle), "new_from_wkt") + }, + Err(e) => Err(Error::GenericError(format!( + "Conversion to CString failed: {}", + e + ))), + }, + Err(e) => Err(e), } } - /// Returns the exterior ring. + /// Create a new [`Geometry`] from the HEX format. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0),\ - /// (1 1, 2 1, 2 5, 1 5, 1 1))") - /// .expect("Invalid geometry"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let hex_buf = point_geom.to_hex().expect("conversion to HEX failed"); /// - /// let exterior = point_geom.get_exterior_ring().expect("failed to get exterior ring"); - /// assert_eq!(exterior.to_wkt().unwrap(), - /// "LINEARRING (0.0000000000000000 0.0000000000000000, \ - /// 10.0000000000000000 0.0000000000000000, \ - /// 10.0000000000000000 6.0000000000000000, \ - /// 0.0000000000000000 6.0000000000000000, \ - /// 0.0000000000000000 0.0000000000000000)"); + /// // The interesting part is here: + /// let new_geom = Geometry::new_from_hex(hex_buf.as_ref()) + /// .expect("conversion from HEX failed"); + /// assert_eq!(point_geom.equals(&new_geom), Ok(true)); /// ``` - pub fn get_exterior_ring(&self) -> GResult> { - unsafe { - let ptr = GEOSGetExteriorRing_r(self.get_raw_context(), self.as_raw()); - match Geometry::new_from_raw(ptr, self.clone_context(), "get_exterior_ring") { - Ok(mut g) => { - g.owned = false; - Ok(g) - } - e => e, - } + pub fn new_from_hex(hex: &[u8]) -> GResult> { + match ContextHandle::init_e(Some("Geometry::new_from_hex")) { + Ok(context) => unsafe { + let ptr = GEOSGeomFromHEX_buf_r(context.as_raw(), hex.as_ptr(), hex.len()); + Geometry::new_from_raw(ptr, Arc::new(context), "new_from_hex") + }, + Err(e) => Err(e), } } - /// Returns the number of coordinates inside `self`. + /// Create a new [`Geometry`] from the WKB format. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") - /// .expect("Invalid geometry"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let wkb_buf = point_geom.to_wkb().expect("conversion to WKB failed"); /// - /// assert_eq!(geom.get_num_coordinates(), Ok(5)); + /// // The interesting part is here: + /// let new_geom = Geometry::new_from_wkb(wkb_buf.as_ref()) + /// .expect("conversion from WKB failed"); + /// assert_eq!(point_geom.equals(&new_geom), Ok(true)); /// ``` - pub fn get_num_coordinates(&self) -> GResult { - unsafe { - let ret = GEOSGetNumCoordinates_r(self.get_raw_context(), self.as_raw()); - if ret == -1 { - Err(Error::GenericError("GEOSGetNumCoordinates_r failed".to_owned())) - } else { - Ok(ret as _) - } + pub fn new_from_wkb(wkb: &[u8]) -> GResult> { + match ContextHandle::init_e(Some("Geometry::new_from_wkb")) { + Ok(context) => unsafe { + let ptr = GEOSGeomFromWKB_buf_r(context.as_raw(), wkb.as_ptr(), wkb.len()); + Geometry::new_from_raw(ptr, Arc::new(context), "new_from_wkb") + }, + Err(e) => Err(e), } } - /// Returns the number of dimensions used in `self`. + /// Creates an areal geometry formed by the constituent linework of given geometry. + /// + /// You can find new illustrations on [postgis](https://postgis.net/docs/ST_BuildArea.html) + /// documentation. + /// + /// Available using the `v3_8_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") - /// .expect("Invalid geometry"); + /// let geom = Geometry::new_from_wkt("POINT(100 90)").expect("Invalid geometry"); + /// let small_geom = geom.buffer(25., 8).expect("buffer failed"); + /// let big_geom = geom.buffer(50., 8).expect("buffer failed"); /// - /// assert_eq!(geom.get_num_dimensions(), Ok(2)); + /// let union_geom = small_geom.union(&big_geom).expect("union failed"); + /// let build_area_geom = union_geom.build_area().expect("build_area failed"); + /// + /// // Looks like a donut. + /// assert_eq!(union_geom.to_wkt_precision(1).unwrap(), + /// "POLYGON ((150.0 90.0, 149.0 80.2, 146.2 70.9, 141.6 62.2, 135.4 54.6, \ + /// 127.8 48.4, 119.1 43.8, 109.8 41.0, 100.0 40.0, 90.2 41.0, \ + /// 80.9 43.8, 72.2 48.4, 64.6 54.6, 58.4 62.2, 53.8 70.9, 51.0 80.2, \ + /// 50.0 90.0, 51.0 99.8, 53.8 109.1, 58.4 117.8, 64.6 125.4, \ + /// 72.2 131.6, 80.9 136.2, 90.2 139.0, 100.0 140.0, 109.8 139.0, \ + /// 119.1 136.2, 127.8 131.6, 135.4 125.4, 141.6 117.8, 146.2 109.1, \ + /// 149.0 99.8, 150.0 90.0))"); /// ``` - pub fn get_num_dimensions(&self) -> GResult { + #[cfg(any(feature = "v3_8_0", feature = "dox"))] + pub fn build_area(&self) -> GResult> { unsafe { - let ret = GEOSGeom_getDimensions_r(self.get_raw_context(), self.as_raw()); - if ret == -1 { - Err(Error::GenericError("GEOSGeom_getDimensions_r failed".to_owned())) - } else { - Ok(ret as _) - } + let ptr = GEOSBuildArea_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "build_area") } } - /// Return in which coordinate dimension the geometry is. + /// Description from [postgis](https://postgis.net/docs/ST_Polygonize.html): /// - /// # Example + /// > Creates a GeometryCollection containing possible polygons formed from the constituent + /// > linework of a set of geometries. + /// + /// # Example: /// /// ``` - /// use geos::{Dimensions, Geometry}; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); - /// assert!(point_geom.get_coordinate_dimension() == Ok(Dimensions::ThreeD)); + /// let geom1 = Geometry::new_from_wkt("POLYGON((-71.040878 42.285678,\ + /// -71.040943 42.2856,\ + /// -71.04096 42.285752,\ + /// -71.040878 42.285678))") + /// .expect("Failed to create geometry"); + /// let geom2 = Geometry::new_from_wkt("POLYGON((-71.17166 42.353675,\ + /// -71.172026 42.354044,\ + /// -71.17239 42.354358,\ + /// -71.171794 42.354971,\ + /// -71.170511 42.354855,\ + /// -71.17112 42.354238,\ + /// -71.17166 42.353675))") + /// .expect("Failed to create geometry"); /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 4.0)").expect("Invalid geometry"); - /// assert!(point_geom.get_coordinate_dimension() == Ok(Dimensions::TwoD)); + /// let polygonized = Geometry::polygonize(&[geom1, geom2]).expect("polygonize failed"); + /// assert_eq!(polygonized.to_wkt().unwrap(), + /// "GEOMETRYCOLLECTION (POLYGON ((-71.0408780000000064 42.2856779999999972, \ + /// -71.0409429999999986 42.2856000000000023, \ + /// -71.0409599999999983 42.2857520000000022, \ + /// -71.0408780000000064 42.2856779999999972)), \ + /// POLYGON ((-71.1716600000000028 42.3536750000000026, \ + /// -71.1720260000000025 42.3540440000000018, \ + /// -71.1723899999999929 42.3543579999999977, \ + /// -71.1717940000000056 42.3549709999999990, \ + /// -71.1705110000000047 42.3548550000000006, \ + /// -71.1711200000000019 42.3542380000000023, \ + /// -71.1716600000000028 42.3536750000000026)))"); /// ``` - pub fn get_coordinate_dimension(&self) -> GResult { + pub fn polygonize>>(geometries: &[T]) -> GResult> { unsafe { - let ret = GEOSGeom_getCoordinateDimension_r(self.get_raw_context(), self.as_raw()); - if ret != 2 && ret != 3 { - Err(Error::GenericError("GEOSGeom_getCoordinateDimension_r failed".to_owned())) - } else { - Ok(Dimensions::from(ret)) - } + let context = match geometries.get(0) { + Some(g) => g.borrow().clone_context(), + None => match ContextHandle::init_e(Some("Geometry::polygonize")) { + Ok(context) => Arc::new(context), + Err(e) => return Err(e), + }, + }; + let geoms = geometries + .iter() + .map(|g| g.borrow().as_raw() as *const _) + .collect::>(); + let ptr = GEOSPolygonize_r(context.as_raw(), geoms.as_ptr(), geoms.len() as _); + Geometry::new_from_raw(ptr, context, "polygonize") } } - /// This functions attempts to return a valid representation of `self`. - /// - /// Available using the `v3_8_0` feature. - #[cfg(any(feature = "v3_8_0", feature = "dox"))] - pub fn make_valid(&self) -> GResult> { + pub fn polygonizer_get_cut_edges>>( + &self, + geometries: &[T], + ) -> GResult> { unsafe { - let ptr = GEOSMakeValid_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "make_valid") + let context = match geometries.get(0) { + Some(g) => g.borrow().clone_context(), + None => match ContextHandle::init_e(Some("Geometry::polygonizer_get_cut_edges")) { + Ok(context) => Arc::new(context), + Err(e) => return Err(e), + }, + }; + let geoms = geometries + .iter() + .map(|g| g.borrow().as_raw() as *const _) + .collect::>(); + let ptr = + GEOSPolygonizer_getCutEdges_r(context.as_raw(), geoms.as_ptr(), geoms.len() as _); + Geometry::new_from_raw(ptr, context, "polygonizer_get_cut_edges") } } - /// Returns the number of geometries. + /// Merges `Multi Line String` geometry into a (set of) `Line String`. + /// + /// ### Warning + /// + /// If you use this function on something else than a `Multi Line String` or a + /// `Line String`, it'll return an empty `Geometry collection`. /// /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom = Geometry::new_from_wkt("LINESTRING(77.29 29.07,77.42 29.26,77.27 29.31,77.29 29.07)") - /// .expect("Invalid geometry"); - /// assert_eq!(geom.get_num_geometries(), Ok(1)); + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("GEOMETRYCOLLECTION(MULTIPOINT(-2 3 , -2 2),\ - /// LINESTRING(5 5 ,10 10),\ - /// POLYGON((-7 4.2,-7.1 5,-7.1 4.3,-7 4.2)))") - /// .expect("Invalid geometry"); - /// assert_eq!(geom.get_num_geometries(), Ok(3)); + /// let lines = Geometry::new_from_wkt("MULTILINESTRING((-29 -27,-30 -29.7,-36 -31,-45 -33),\ + /// (-45 -33,-46 -32))") + /// .expect("Invalid geometry"); + /// let lines_merged = lines.line_merge().expect("line merge failed"); + /// assert_eq!( + /// lines_merged.to_wkt_precision(1).unwrap(), + /// "LINESTRING (-29.0 -27.0, -30.0 -29.7, -36.0 -31.0, -45.0 -33.0, -46.0 -32.0)", + /// ); /// ``` - pub fn get_num_geometries(&self) -> GResult { + pub fn line_merge(&self) -> GResult> { unsafe { - let ret = GEOSGetNumGeometries_r(self.get_raw_context(), self.as_raw()); - if ret < 1 { - Err(Error::GenericError("GEOSGetNumGeometries_r failed".to_owned())) - } else { - Ok(ret as _) - } + let ptr = GEOSLineMerge_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "line_merge") } } - /// Returns the 1-based nth geometry. + /// Reverses the order of the vertexes. + /// + /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("MULTIPOINT(1 1, 2 2, 3 3, 4 4)") - /// .expect("Invalid geometry"); - /// let point_nb3 = geom.get_geometry_n(2).expect("failed to get third point"); - /// assert_eq!(point_nb3.to_wkt().unwrap(), "POINT (3.0000000000000000 3.0000000000000000)"); + /// let line = Geometry::new_from_wkt("LINESTRING(1 10,1 2)") + /// .expect("invalid geometry"); + /// let reversed_line = line.reverse().expect("reverse failed"); + /// + /// assert_eq!( + /// reversed_line.to_wkt_precision(1).unwrap(), + /// "LINESTRING (1.0 2.0, 1.0 10.0)", + /// ); /// ``` - pub fn get_geometry_n(&self, n: usize) -> GResult> { + #[cfg(any(feature = "v3_7_0", feature = "dox"))] + pub fn reverse(&self) -> GResult> { unsafe { - let ptr = GEOSGetGeometryN_r(self.get_raw_context(), self.as_raw(), n as _); - Geometry::new_from_raw(ptr, self.clone_context(), "get_geometry_n").map(|mut x| { - x.owned = false; - x - }) + let ptr = GEOSReverse_r(self.get_raw_context(), self.as_raw()); + Geometry::new_from_raw(ptr, self.clone_context(), "reverse") } } - /// Get SRID of `self`. - /// - /// # Example - /// - /// ``` - /// use geos::Geometry; - /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); - /// point_geom.set_srid(4326); - /// assert_eq!(point_geom.get_srid(), Ok(4326)); - /// ``` - pub fn get_srid(&self) -> GResult { + /// Returns a simplified version of the given geometry. + pub fn simplify(&self, tolerance: f64) -> GResult> { unsafe { - let ret = GEOSGetSRID_r(self.get_raw_context(), self.as_raw()); - if ret < 1 { - Err(Error::GenericError("GEOSGetSRID_r failed".to_owned())) + let ptr = GEOSSimplify_r(self.get_raw_context(), self.as_raw(), tolerance); + Geometry::new_from_raw(ptr, self.clone_context(), "simplify") + } + } + + /// Returns a simplified version of the given geometry. It will avoid creating invalid derived + /// geometries. + pub fn topology_preserve_simplify(&self, tolerance: f64) -> GResult> { + unsafe { + let ptr = + GEOSTopologyPreserveSimplify_r(self.get_raw_context(), self.as_raw(), tolerance); + Geometry::new_from_raw(ptr, self.clone_context(), "topology_preserve_simplify") + } + } + + pub(crate) unsafe fn new_from_raw( + ptr: *mut GEOSGeometry, + context: Arc>, + caller: &str, + ) -> GResult> { + if ptr.is_null() { + let extra = if let Some(x) = context.get_last_error() { + format!("\nLast error: {}", x) } else { - Ok(ret as _) - } + String::new() + }; + return Err(Error::NoConstructionFromNullPtr(format!( + "Geometry::{}{}", + caller, extra + ))); } + Ok(Geometry { + ptr: PtrWrap(ptr), + context, + }) } /// Set SRID of `self`. @@ -2283,449 +2531,420 @@ impl<'a> Geometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); + /// let mut point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)") + /// .expect("Invalid geometry"); /// point_geom.set_srid(4326); /// assert_eq!(point_geom.get_srid(), Ok(4326)); /// ``` - pub fn set_srid(&self, srid: usize) -> GResult<()> { - unsafe { - if GEOSSetSRID_r(self.get_raw_context(), self.as_raw(), srid as _) == 0 { - Err(Error::GenericError("GEOSSetSRID_r failed".to_owned())) - } else { - Ok(()) - } - } + pub fn set_srid(&mut self, srid: usize) { + unsafe { GEOSSetSRID_r(self.get_raw_context(), self.as_raw_mut(), srid as _) } } - /// Returns the precision of `self`. - /// - /// Available using the `v3_6_0` feature. + /// Normalizes `self` in its normalized/canonical form. May reorder vertices in polygon rings, + /// rings in a polygon, elements in a multi-geometry complex. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); - /// assert_eq!(point_geom.get_precision().map(|x| format!("{:.2}", x)).unwrap(), "0.00"); + /// let mut geom = Geometry::new_from_wkt( + /// "GEOMETRYCOLLECTION(POINT(2 3), MULTILINESTRING((0 0, 1 1),(2 2, 3 3)))", + /// ).expect("Invalid geometry"); + /// + /// geom.normalize().expect("normalize failed"); + /// + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), + /// "GEOMETRYCOLLECTION (MULTILINESTRING ((2.0 2.0, 3.0 3.0), (0.0 0.0, 1.0 1.0)), \ + /// POINT (2.0 3.0))"); /// ``` - #[cfg(any(feature = "v3_6_0", feature = "dox"))] - pub fn get_precision(&self) -> GResult { - unsafe { - let ret = GEOSGeom_getPrecision_r(self.get_raw_context(), self.as_raw()); - if ret == -1. { - Err(Error::GenericError("GEOSGeom_getPrecision_r failed".to_owned())) - } else { - Ok(ret) - } + pub fn normalize(&mut self) -> GResult<()> { + let ret_val = unsafe { GEOSNormalize_r(self.get_raw_context(), self.as_raw_mut()) }; + if ret_val == -1 { + Err(Error::GeosFunctionError(PredicateType::Normalize, ret_val)) + } else { + Ok(()) } } - /// Returns the precision of `self`. - /// - /// Available using the `v3_6_0` feature. + /// Creates an empty polygon geometry. /// /// # Example /// /// ``` - /// use geos::{Geometry, Precision}; + /// use geos::{Geom, Geometry}; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)").expect("Invalid geometry"); + /// let geom = Geometry::create_empty_polygon().expect("Failed to build empty polygon"); /// - /// point_geom.set_precision(1., Precision::KeepCollapsed); - /// assert_eq!(point_geom.get_precision().map(|x| format!("{:.2}", x)).unwrap(), "0.00"); + /// assert_eq!(geom.to_wkt().unwrap(), "POLYGON EMPTY"); /// ``` - #[cfg(any(feature = "v3_6_0", feature = "dox"))] - pub fn set_precision(&self, grid_size: f64, flags: Precision) -> GResult> { - unsafe { - let ptr = GEOSGeom_setPrecision_r(self.get_raw_context(), - self.as_raw(), - grid_size, - flags.into()); - Geometry::new_from_raw(ptr, self.clone_context(), "set_precision") + pub fn create_empty_polygon() -> GResult> { + match ContextHandle::init_e(Some("Geometry::create_empty_polygon")) { + Ok(context) => unsafe { + let ptr = GEOSGeom_createEmptyPolygon_r(context.as_raw()); + Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_polygon") + }, + Err(e) => Err(e), } } - /// Returns the biggest X of the geometry. - /// - /// Available using the `v3_7_0` feature. + /// Creates an empty point geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); - /// assert_eq!(line.get_x_max(), Ok(5.)); + /// let geom = Geometry::create_empty_point().expect("Failed to build empty point"); + /// + /// assert_eq!(geom.to_wkt().unwrap(), "POINT EMPTY"); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn get_x_max(&self) -> GResult { - unsafe { - let mut value = 0.; - if GEOSGeom_getXMax_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { - Err(Error::GenericError("GEOSGeom_getXMax_r failed".to_owned())) - } else { - Ok(value) - } + pub fn create_empty_point() -> GResult> { + match ContextHandle::init_e(Some("Geometry::create_empty_point")) { + Ok(context) => unsafe { + let ptr = GEOSGeom_createEmptyPoint_r(context.as_raw()); + Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_point") + }, + Err(e) => Err(e), } } - /// Returns the smallest X of the geometry. - /// - /// Available using the `v3_7_0` feature. + /// Creates an empty line string geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); - /// assert_eq!(line.get_x_min(), Ok(1.)); + /// let geom = Geometry::create_empty_line_string().expect("Failed to build empty line string"); + /// + /// assert_eq!(geom.to_wkt().unwrap(), "LINESTRING EMPTY"); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn get_x_min(&self) -> GResult { - unsafe { - let mut value = 0.; - if GEOSGeom_getXMin_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { - Err(Error::GenericError("GEOSGeom_getXMin_r failed".to_owned())) - } else { - Ok(value) - } + pub fn create_empty_line_string() -> GResult> { + match ContextHandle::init_e(Some("Geometry::create_empty_line_string")) { + Ok(context) => unsafe { + let ptr = GEOSGeom_createEmptyLineString_r(context.as_raw()); + Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_line_string") + }, + Err(e) => Err(e), } } - /// Returns the biggest Y of the geometry. + /// Creates an empty collection. /// - /// Available using the `v3_7_0` feature. + /// The `type_` must be one of: + /// + /// * [`GeometryTypes::GeometryCollection`] + /// * [`GeometryTypes::MultiPoint`] + /// * [`GeometryTypes::MultiLineString`] + /// * [`GeometryTypes::MultiPolygon`] /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry, GeometryTypes}; /// - /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); - /// assert_eq!(line.get_y_max(), Ok(6.)); + /// let geom = Geometry::create_empty_collection(GeometryTypes::MultiPolygon) + /// .expect("Failed to build empty collection"); + /// + /// assert_eq!(geom.to_wkt().unwrap(), "MULTIPOLYGON EMPTY"); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn get_y_max(&self) -> GResult { - unsafe { - let mut value = 0.; - if GEOSGeom_getYMax_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { - Err(Error::GenericError("GEOSGeom_getYMax_r failed".to_owned())) - } else { - Ok(value) - } + pub fn create_empty_collection(type_: GeometryTypes) -> GResult> { + match type_ { + GeometryTypes::GeometryCollection + | GeometryTypes::MultiPoint + | GeometryTypes::MultiLineString + | GeometryTypes::MultiPolygon => {} + _ => return Err(Error::GenericError("Invalid geometry type".to_owned())), + } + match ContextHandle::init_e(Some("Geometry::create_empty_collection")) { + Ok(context) => unsafe { + let ptr = GEOSGeom_createEmptyCollection_r(context.as_raw(), type_.into()); + Geometry::new_from_raw(ptr, Arc::new(context), "create_empty_collection") + }, + Err(e) => Err(e), } } - /// Returns the smallest Y of the geometry. + /// Creates a polygon formed by the given shell and array of holes. /// - /// Available using the `v3_7_0` feature. + /// ### Note + /// + /// `exterior` must be a `LinearRing`. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); - /// assert_eq!(line.get_y_min(), Ok(3.)); + /// let geom = Geometry::new_from_wkt("LINEARRING(75.15 29.53,77 29,77.6 29.5, 75.15 29.53)") + /// .expect("Invalid geometry"); + /// let polygon_geom = Geometry::create_polygon(geom, vec![]) + /// .expect("create_polygon failed"); + /// + /// assert_eq!( + /// polygon_geom.to_wkt_precision(1).unwrap(), + /// "POLYGON ((75.2 29.5, 77.0 29.0, 77.6 29.5, 75.2 29.5))", + /// ); /// ``` - #[cfg(any(feature = "v3_7_0", feature = "dox"))] - pub fn get_y_min(&self) -> GResult { - unsafe { - let mut value = 0.; - if GEOSGeom_getYMin_r(self.get_raw_context(), self.as_raw(), &mut value) == 0 { - Err(Error::GenericError("GEOSGeom_getYMin_r failed".to_owned())) - } else { - Ok(value) - } + pub fn create_polygon<'b>( + mut exterior: Geometry<'a>, + mut interiors: Vec>, + ) -> GResult> { + if exterior.geometry_type() != GeometryTypes::LinearRing { + return Err(Error::GenericError( + "exterior must be a LinearRing".to_owned(), + )); + } + let context_handle = exterior.clone_context(); + let nb_interiors = interiors.len(); + let res = unsafe { + let mut geoms: Vec<*mut GEOSGeometry> = + interiors.iter_mut().map(|g| g.as_raw_mut()).collect(); + let ptr = GEOSGeom_createPolygon_r( + context_handle.as_raw(), + exterior.as_raw_mut(), + geoms.as_mut_ptr() as *mut *mut GEOSGeometry, + nb_interiors as _, + ); + Geometry::new_from_raw(ptr, context_handle, "create_polygon") + }; + + // We transfered the ownership of the ptr to the new Geometry, + // so the old ones need to forget their c ptr to avoid double free. + exterior.ptr = PtrWrap(::std::ptr::null_mut()); + for i in interiors.iter_mut() { + i.ptr = PtrWrap(::std::ptr::null_mut()); } + + res } - /// Returns the smallest distance by which a vertex of `self` could be moved to produce an - /// invalid geometry. - /// - /// Available using the `v3_6_0` feature. + /// Create a geometry collection. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)").expect("Invalid WKT"); - /// assert_eq!(geom.minimum_clearance().map(|x| format!("{:.8}", x)).unwrap(), "5.00000000"); + /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (3.0 4.0)").expect("Invalid geometry"); + /// + /// let geom = Geometry::create_geometry_collection(vec![geom1, geom2]) + /// .expect("Failed to build multipolygon"); + /// + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), + /// "GEOMETRYCOLLECTION (POLYGON ((0.0 0.0, 10.0 0.0, 10.0 6.0, 0.0 6.0, 0.0 0.0)), \ + /// POINT (3.0 4.0))"); /// ``` - #[cfg(any(feature = "v3_6_0", feature = "dox"))] - pub fn minimum_clearance(&self) -> GResult { - unsafe { - let mut value = 0.; - if GEOSMinimumClearance_r(self.get_raw_context(), self.as_raw(), &mut value) != 0 { - Err(Error::GenericError("GEOSMinimumClearance_r failed".to_owned())) - } else { - Ok(value) - } - } + pub fn create_geometry_collection(geoms: Vec>) -> GResult> { + create_multi_geom(geoms, GeometryTypes::GeometryCollection) } - /// Returns the two-point LineString spanning of `self`'s minimum clearance. - /// - /// Available using the `v3_6_0` feature. + /// Create a multi polygon geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POLYGON ((0 0, 1 0, 1 1, 0.5 3.2e-4, 0 0))") - /// .expect("Invalid WKT"); - /// let line = geom.minimum_clearance_line().expect("minimum_clearance_line failed"); - /// assert_eq!(line.to_wkt_precision(1).unwrap(), "LINESTRING (0.5 0.0, 0.5 0.0)"); + /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POLYGON((3 3, 10 3, 10 6, 3 6, 3 3))") + /// .expect("Invalid geometry"); + /// + /// let geom = Geometry::create_multipolygon(vec![geom1, geom2]) + /// .expect("Failed to build multipolygon"); + /// + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), + /// "MULTIPOLYGON (((0.0 0.0, 10.0 0.0, 10.0 6.0, 0.0 6.0, 0.0 0.0)), \ + /// ((3.0 3.0, 10.0 3.0, 10.0 6.0, 3.0 6.0, 3.0 3.0)))"); /// ``` - #[cfg(any(feature = "v3_6_0", feature = "dox"))] - pub fn minimum_clearance_line(&self) -> GResult> { - unsafe { - let ptr = GEOSMinimumClearanceLine_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "minimum_clearance_line") + pub fn create_multipolygon(polygons: Vec>) -> GResult> { + if !check_same_geometry_type(&polygons, GeometryTypes::Polygon) { + return Err(Error::ImpossibleOperation( + "all the provided geometry have to be of type Polygon".to_owned(), + )); } + create_multi_geom(polygons, GeometryTypes::MultiPolygon) } - /// Returns the minimum rotated rectangle inside of `self`. + /// Create a multiline string geometry. /// - /// Available using the `v3_6_0` feature. - #[cfg(any(feature = "v3_6_0", feature = "dox"))] - pub fn minimum_rotated_rectangle(&self) -> GResult> { - unsafe { - let ptr = GEOSMinimumRotatedRectangle_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "minimum_rotated_rectangle") - } - } - - /// Returns the minimum width inside of `self`. + /// # Example /// - /// Available using the `v3_6_0` feature. - #[cfg(any(feature = "v3_6_0", feature = "dox"))] - pub fn minimum_width(&self) -> GResult> { - unsafe { - let ptr = GEOSMinimumWidth_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "minimum_width") + /// ``` + /// use geos::{Geom, Geometry}; + /// + /// let geom1 = Geometry::new_from_wkt("LINESTRING (1.0 2.0, 3.0 4.0)").expect("invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING (5.0 6.0, 7.0 8.0)").expect("invalid geometry"); + /// + /// let geom = Geometry::create_multiline_string(vec![geom1, geom2]) + /// .expect("Failed to build multiline string"); + /// + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), + /// "MULTILINESTRING ((1.0 2.0, 3.0 4.0), (5.0 6.0, 7.0 8.0))"); + /// ``` + pub fn create_multiline_string(linestrings: Vec>) -> GResult> { + if !check_same_geometry_type(&linestrings, GeometryTypes::LineString) { + return Err(Error::ImpossibleOperation( + "all the provided geometry have to be of type LineString".to_owned(), + )); } + create_multi_geom(linestrings, GeometryTypes::MultiLineString) } - /// Returns a [delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) - /// around the vertices of `self`. + /// Creates a multi point geometry. /// /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((175 150, 20 40, 50 60, 125 100, 175 150))") - /// .expect("Invalid WKT"); - /// let geom2 = Geometry::new_from_wkt("POINT(110 170)").expect("Invalid WKT"); - /// let geom2 = geom2.buffer(20., 8).expect("buffer failed"); + /// let geom1 = Geometry::new_from_wkt("POINT (1.0 2.0)").expect("Invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("POINT (3.0 4.0)").expect("Invalid geometry"); /// - /// let geom = geom1.union(&geom2).expect("union failed"); + /// let geom = Geometry::create_multipoint(vec![geom1, geom2]) + /// .expect("Failed to build multipoint"); /// - /// let final_geom = geom.delaunay_triangulation(0.001, false).expect("delaunay_triangulation failed"); + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), "MULTIPOINT (1.0 2.0, 3.0 4.0)"); /// ``` - pub fn delaunay_triangulation(&self, tolerance: f64, only_edges: bool) -> GResult> { - unsafe { - let ptr = GEOSDelaunayTriangulation_r( - self.get_raw_context(), - self.as_raw(), - tolerance, - only_edges as _, - ); - Geometry::new_from_raw(ptr, self.clone_context(), "delaunay_triangulation") - } - } - - pub fn interpolate(&self, d: f64) -> GResult> { - if self.geometry_type() != GeometryTypes::LineString { - return Err(Error::GenericError("Geometry must be a LineString".to_owned())); - } - unsafe { - let ptr = GEOSInterpolate_r(self.get_raw_context(), self.as_raw(), d); - Geometry::new_from_raw(ptr, self.clone_context(), "interpolate") - } - } - - pub fn interpolate_normalized(&self, d: f64) -> GResult> { - if self.geometry_type() != GeometryTypes::LineString { - return Err(Error::GenericError("Geometry must be a LineString".to_owned())); - } - unsafe { - let ptr = GEOSInterpolateNormalized_r(self.get_raw_context(), self.as_raw(), d); - Geometry::new_from_raw(ptr, self.clone_context(), "interpolate_normalized") - } - } - - pub fn project(&self, p: &Geometry<'_>) -> GResult { - if p.geometry_type() != GeometryTypes::Point { - return Err(Error::GenericError("Second geometry must be a Point".to_owned())); - } - unsafe { - let ret = GEOSProject_r(self.get_raw_context(), self.as_raw(), p.as_raw()); - if ret == -1. { - Err(Error::GenericError("GEOSProject_r failed".to_owned())) - } else { - Ok(ret) - } - } - } - - pub fn project_normalized(&self, p: &Geometry<'_>) -> GResult { - if p.geometry_type() != GeometryTypes::Point { - return Err(Error::GenericError("Second geometry must be a Point".to_owned())); - } - unsafe { - let ret = GEOSProjectNormalized_r(self.get_raw_context(), self.as_raw(), p.as_raw()); - if ret == -1. { - Err(Error::GenericError("GEOSProjectNormalized_r failed".to_owned())) - } else { - Ok(ret) - } + pub fn create_multipoint(points: Vec>) -> GResult> { + if !check_same_geometry_type(&points, GeometryTypes::Point) { + return Err(Error::ImpossibleOperation( + "all the provided geometry have to be of type Point".to_owned(), + )); } + create_multi_geom(points, GeometryTypes::MultiPoint) } - pub fn node(&self) -> GResult> { + /// Creates a point geometry. + /// + /// # Example + /// + /// ``` + /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; + /// + /// let coords = CoordSeq::new_from_vec(&[&[1., 2.]]) + /// .expect("failed to create CoordSeq"); + /// + /// let geom = Geometry::create_point(coords).expect("Failed to create a point"); + /// + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), "POINT (1.0 2.0)"); + /// ``` + pub fn create_point(mut s: CoordSeq<'a>) -> GResult> { unsafe { - let ptr = GEOSNode_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "node") + let ptr = GEOSGeom_createPoint_r(s.get_raw_context(), s.as_raw_mut()); + let res = Geometry::new_from_raw(ptr, s.clone_context(), "create_point"); + s.ptr = PtrWrap(::std::ptr::null_mut()); + res } } - /// Return an offset line at a given distance and side from an input line. All points of the - /// returned geometries are not further than the given distance from the input geometry. - /// - /// ### Parameters description: - /// - /// #### width + /// Creates a line string geometry. /// - /// * If `width` is positive, the offset will be at the left side of the input line and retain - /// the same direction. - /// * If `width` is negative, it'll be at the right side and in the opposite direction. + /// # Example /// - /// #### quadrant_segments + /// ``` + /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; /// - /// * If `quadrant_segments` is >= 1, joins are round, and `quadrant_segments` indicates the - /// number of segments to use to approximate a quarter-circle. - /// * If `quadrant_segments` == 0, joins are bevelled (flat). - /// * If `quadrant_segments` < 0, joins are mitred, and the value of `quadrant_segments` - /// indicates the mitre ration limit as `mitre_limit = |quadrant_segments|` + /// let coords = CoordSeq::new_from_vec(&[&[1., 2.], &[3., 4.]]) + /// .expect("failed to create CoordSeq"); /// - /// #### mitre_limit + /// let geom = Geometry::create_line_string(coords).expect("Failed to create a line string"); /// - /// The mitre ratio is the ratio of the distance from the corner to the end of the mitred offset - /// corner. When two line segments meet at a sharp angle, a miter join will extend far beyond - /// the original geometry (and in the extreme case will be infinitely far). To prevent - /// unreasonable geometry, the mitre limit allows controlling the maximum length of the join - /// corner. Corners with a ratio which exceed the limit will be beveled. - pub fn offset_curve( - &self, - width: f64, - quadrant_segments: i32, - join_style: JoinStyle, - mitre_limit: f64, - ) -> GResult> { - if self.geometry_type() != GeometryTypes::LineString { - return Err(Error::GenericError("Geometry must be a LineString".to_owned())); - } + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), "LINESTRING (1.0 2.0, 3.0 4.0)"); + /// ``` + pub fn create_line_string(mut s: CoordSeq<'a>) -> GResult> { unsafe { - let ptr = GEOSOffsetCurve_r(self.get_raw_context(), self.as_raw(), width, - quadrant_segments, join_style.into(), mitre_limit); - Geometry::new_from_raw(ptr, self.clone_context(), "offset_curve") + let ptr = GEOSGeom_createLineString_r(s.get_raw_context(), s.as_raw_mut()); + let res = Geometry::new_from_raw(ptr, s.clone_context(), "create_line_string"); + s.ptr = PtrWrap(::std::ptr::null_mut()); + res } } - pub fn point_on_surface(&self) -> GResult> { + /// Creates a linear ring geometry. + /// + /// # Example + /// + /// ``` + /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; + /// + /// let coords = CoordSeq::new_from_vec(&[&[75.15, 29.53], + /// &[77., 29.], + /// &[77.6, 29.5], + /// &[75.15, 29.53]]) + /// .expect("failed to create CoordSeq"); + /// + /// let geom = Geometry::create_linear_ring(coords) + /// .expect("Failed to create a linea ring"); + /// + /// assert_eq!(geom.to_wkt_precision(1).unwrap(), + /// "LINEARRING (75.2 29.5, 77.0 29.0, 77.6 29.5, 75.2 29.5)"); + /// ``` + pub fn create_linear_ring(mut s: CoordSeq<'a>) -> GResult> { unsafe { - let ptr = GEOSPointOnSurface_r(self.get_raw_context(), self.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "point_on_surface") + let ptr = GEOSGeom_createLinearRing_r(s.get_raw_context(), s.as_raw_mut()); + let res = Geometry::new_from_raw(ptr, s.clone_context(), "create_linear_ring"); + s.ptr = PtrWrap(::std::ptr::null_mut()); + res } } +} - /// Returns, in the tuple elements order: - /// - /// 1. The polygonized geometry. - /// 2. The cuts geometries collection. - /// 3. The dangles geometries collection. - /// 4. The invalid geometries collection. - pub fn polygonize_full( - &self, - ) -> GResult<(Geometry<'a>, Option>, Option>, Option>)> { - let mut cuts: *mut GEOSGeometry = ::std::ptr::null_mut(); - let mut dangles: *mut GEOSGeometry = ::std::ptr::null_mut(); - let mut invalids: *mut GEOSGeometry = ::std::ptr::null_mut(); - - unsafe { - let ptr = GEOSPolygonize_full_r( - self.get_raw_context(), - self.as_raw(), - &mut cuts, - &mut dangles, - &mut invalids, - ); - let cuts = if !cuts.is_null() { - Geometry::new_from_raw(cuts, self.clone_context(), "polygonize_full").ok() - } else { - None - }; - let dangles = if !dangles.is_null() { - Geometry::new_from_raw(dangles, self.clone_context(), "polygonize_full").ok() - } else { - None - }; - let invalids = if !invalids.is_null() { - Geometry::new_from_raw(invalids, self.clone_context(), "polygonize_full").ok() +impl<'a, 'b> ConstGeometry<'a, 'b> { + pub(crate) unsafe fn new_from_raw( + ptr: *const GEOSGeometry, + original: &'b Geometry<'a>, + caller: &str, + ) -> GResult> { + if ptr.is_null() { + let extra = if let Some(x) = original.context.get_last_error() { + format!("\nLast error: {}", x) } else { - None + String::new() }; - Geometry::new_from_raw(ptr, self.clone_context(), "polygonize_full") - .map(|x| (x, cuts, dangles, invalids)) - } - } - - pub fn shared_paths(&self, other: Geometry<'_>) -> GResult> { - unsafe { - let ptr = GEOSSharedPaths_r(self.get_raw_context(), self.as_raw(), other.as_raw()); - Geometry::new_from_raw(ptr, self.clone_context(), "shared_paths") + return Err(Error::NoConstructionFromNullPtr(format!( + "ConstGeometry::{}{}", + caller, extra + ))); } + Ok(ConstGeometry { + ptr: PtrWrap(ptr), + original, + }) } -} - -unsafe impl<'a> Send for Geometry<'a> {} -unsafe impl<'a> Sync for Geometry<'a> {} -impl<'a> Drop for Geometry<'a> { - fn drop(&mut self) { - if !self.ptr.is_null() && self.owned { - unsafe { GEOSGeom_destroy_r(self.get_raw_context(), self.as_raw()) } - } + /// Get the context handle of the geometry. + /// + /// ``` + /// use geos::{ContextInteractions, Geometry}; + /// + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let context = point_geom.get_context_handle(); + /// context.set_notice_message_handler(Some(Box::new(|s| println!("new message: {}", s)))); + /// ``` + pub fn get_context_handle(&self) -> &ContextHandle<'a> { + &self.original.context } } impl<'a> Clone for Geometry<'a> { /// Also passes the context to the newly created `Geometry`. fn clone(&self) -> Geometry<'a> { - let context = self.clone_context(); - let ptr = unsafe { GEOSGeom_clone_r(context.as_raw(), self.as_raw()) }; - if ptr.is_null() { - panic!("Couldn't clone geometry..."); - } - Geometry { - ptr: PtrWrap(ptr), - context, - owned: true, - } + Geom::clone(self) } } -impl<'a> PartialEq for Geometry<'a> { - fn eq<'b>(&self, other: &Geometry<'b>) -> bool { - self.equals(other).unwrap_or_else(|_| false) +impl<'a> Drop for Geometry<'a> { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { GEOSGeom_destroy_r(self.get_raw_context(), self.as_raw_mut()) } + } } } @@ -2759,9 +2978,17 @@ impl<'a> ContextInteractions<'a> for Geometry<'a> { } impl<'a> AsRaw for Geometry<'a> { - type RawType = *mut GEOSGeometry; + type RawType = GEOSGeometry; + + fn as_raw(&self) -> *const Self::RawType { + *self.ptr + } +} + +impl<'a> AsRawMut for Geometry<'a> { + type RawType = GEOSGeometry; - fn as_raw(&self) -> Self::RawType { + unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType { *self.ptr } } @@ -2777,3 +3004,23 @@ impl<'a> ContextHandling for Geometry<'a> { Arc::clone(&self.context) } } + +impl<'a, 'd> AsRaw for ConstGeometry<'a, 'd> { + type RawType = GEOSGeometry; + + fn as_raw(&self) -> *const Self::RawType { + *self.ptr + } +} + +impl<'a, 'd> ContextHandling for ConstGeometry<'a, 'd> { + type Context = Arc>; + + fn get_raw_context(&self) -> GEOSContextHandle_t { + self.original.context.as_raw() + } + + fn clone_context(&self) -> Arc> { + Arc::clone(&self.original.context) + } +} diff --git a/src/lib.rs b/src/lib.rs index b350d4f..056e05b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,15 +2,15 @@ #![crate_type = "lib"] extern crate c_vec; -extern crate libc; -extern crate num; #[cfg(any(feature = "geo", feature = "dox"))] extern crate geo_types; -#[cfg(any(feature = "geo", feature = "dox"))] -extern crate wkt; #[cfg(all(feature = "json"))] extern crate geojson; extern crate geos_sys; +extern crate libc; +extern crate num; +#[cfg(any(feature = "geo", feature = "dox"))] +extern crate wkt; #[cfg(all(feature = "geo", test))] #[macro_use] @@ -21,49 +21,21 @@ doctest!("../README.md"); pub(crate) mod functions; -pub use context_handle::{ - ContextHandle, -}; -pub use coord_seq::{ - CoordSeq, -}; -pub use enums::{ - ByteOrder, - CoordDimensions, - Dimensions, - GeometryTypes, - Ordinate, - Orientation, - OutputDimension, -}; +pub use context_handle::ContextHandle; +pub use coord_seq::CoordSeq; #[cfg(any(feature = "v3_6_0", feature = "dox"))] +pub use enums::Precision; pub use enums::{ - Precision, -}; -pub use functions::{ - orientation_index, - version, + ByteOrder, CoordDimensions, Dimensions, GeometryTypes, Ordinate, Orientation, OutputDimension, }; #[cfg(any(feature = "v3_7_0", feature = "dox"))] -pub use functions::{ - segment_intersection, -}; -pub use geometry::{ - Geometry, -}; -pub use prepared_geometry::{ - PreparedGeometry, -}; -pub use spatial_index:: { - SpatialIndex, - STRtree -}; -pub use wkb_writer::{ - WKBWriter, -}; -pub use wkt_writer::{ - WKTWriter, -}; +pub use functions::segment_intersection; +pub use functions::{orientation_index, version}; +pub use geometry::{ConstGeometry, Geom, Geometry}; +pub use prepared_geometry::PreparedGeometry; +pub use spatial_index::{STRtree, SpatialIndex}; +pub use wkb_writer::WKBWriter; +pub use wkt_writer::WKTWriter; mod context_handle; mod coord_seq; @@ -79,28 +51,18 @@ mod spatial_index; pub mod to_geo; #[cfg(all(feature = "json"))] pub mod to_geojson; -pub use error::{ - Error, - GResult, -}; +pub use error::{Error, GResult}; #[cfg(any(feature = "geo", feature = "dox"))] mod voronoi; #[cfg(any(feature = "geo", feature = "dox"))] -pub use voronoi::{ - compute_voronoi, -}; +pub use voronoi::compute_voronoi; mod enums; mod traits; mod wkb_writer; mod wkt_writer; -pub(crate) use traits::{ - AsRaw, -}; -pub use traits::{ - ContextInteractions, - ContextHandling -}; +pub(crate) use traits::{AsRaw, AsRawMut}; +pub use traits::{ContextHandling, ContextInteractions}; #[cfg(test)] mod test; diff --git a/src/prepared_geometry.rs b/src/prepared_geometry.rs index 4cf309d..2b7fd05 100644 --- a/src/prepared_geometry.rs +++ b/src/prepared_geometry.rs @@ -1,10 +1,10 @@ -use crate::{ContextHandle, Geometry, GResult, AsRaw, ContextHandling, ContextInteractions}; -use error::PredicateType; +use crate::{AsRaw, ContextHandle, ContextHandling, ContextInteractions, GResult, Geom}; use context_handle::PtrWrap; -use geos_sys::*; +use error::Error; +use error::PredicateType; use functions::*; +use geos_sys::*; use std::sync::Arc; -use error::Error; /// `PreparedGeometry` is an interface which prepares [`Geometry`] for greater performance /// on repeated calls. @@ -12,17 +12,19 @@ use error::Error; /// # Example /// /// ``` -/// use geos::Geometry; +/// use geos::{Geom, Geometry}; /// -/// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))").expect("Invalid geometry"); +/// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") +/// .expect("Invalid geometry"); /// let mut prepared_geom = geom1.to_prepared_geom() /// .expect("failed to create prepared geom"); -/// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); +/// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)") +/// .expect("Invalid geometry"); /// /// assert_eq!(prepared_geom.contains(&geom2), Ok(true)); /// ``` pub struct PreparedGeometry<'a> { - ptr: PtrWrap<*mut GEOSPreparedGeometry>, + ptr: PtrWrap<*const GEOSPreparedGeometry>, context: Arc>, } @@ -38,7 +40,7 @@ impl<'a> PreparedGeometry<'a> { /// .expect("Invalid geometry"); /// let prepared_geom = PreparedGeometry::new(&geom1); /// ``` - pub fn new(g: &Geometry<'a>) -> GResult> { + pub fn new>(g: &G) -> GResult> { unsafe { let ptr = GEOSPrepare_r(g.get_raw_context(), g.as_raw()); PreparedGeometry::new_from_raw(ptr, g.clone_context(), "new") @@ -46,7 +48,7 @@ impl<'a> PreparedGeometry<'a> { } pub(crate) unsafe fn new_from_raw( - ptr: *mut GEOSPreparedGeometry, + ptr: *const GEOSPreparedGeometry, context: Arc>, caller: &str, ) -> GResult> { @@ -56,11 +58,15 @@ impl<'a> PreparedGeometry<'a> { } else { String::new() }; - return Err(Error::NoConstructionFromNullPtr(format!("PreparedGeometry::{}{}", - caller, - extra))); + return Err(Error::NoConstructionFromNullPtr(format!( + "PreparedGeometry::{}{}", + caller, extra + ))); } - Ok(PreparedGeometry { ptr: PtrWrap(ptr), context }) + Ok(PreparedGeometry { + ptr: PtrWrap(ptr), + context, + }) } /// Returns `true` if no points of the other geometry is outside the exterior of `self`. @@ -68,16 +74,19 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))").expect("Invalid geometry"); - /// let mut prepared_geom = geom1.to_prepared_geom() - /// .expect("failed to create prepared geom"); - /// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); + /// let mut prepared_geom = geom1 + /// .to_prepared_geom() + /// .expect("failed to create prepared geom"); + /// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); /// /// assert_eq!(prepared_geom.contains(&geom2), Ok(true)); /// ``` - pub fn contains<'b>(&self, other: &Geometry<'b>) -> GResult { + pub fn contains<'b, G: Geom<'b>>(&self, other: &G) -> GResult { let ret_val = unsafe { GEOSPreparedContains_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; @@ -89,16 +98,19 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))").expect("Invalid geometry"); - /// let mut prepared_geom = geom1.to_prepared_geom() - /// .expect("failed to create prepared geom"); - /// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))") + /// .expect("Invalid geometry"); + /// let mut prepared_geom = geom1 + /// .to_prepared_geom() + /// .expect("failed to create prepared geom"); + /// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); /// /// assert_eq!(prepared_geom.contains_properly(&geom2), Ok(true)); /// ``` - pub fn contains_properly<'b>(&self, other: &Geometry<'b>) -> GResult { + pub fn contains_properly<'b, G: Geom<'b>>(&self, other: &G) -> GResult { let ret_val = unsafe { GEOSPreparedContainsProperly_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; @@ -110,19 +122,24 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let geom = Geometry::new_from_wkt("POINT (1 2)") + /// .expect("Invalid geometry"); /// let little_geom = geom.buffer(10., 8).expect("buffer failed"); /// let big_geom = geom.buffer(20., 8).expect("buffer failed"); /// - /// let prepared_little_geom = little_geom.to_prepared_geom().expect("to_prepared_geom failed"); - /// let prepared_big_geom = big_geom.to_prepared_geom().expect("to_prepared_geom failed"); + /// let prepared_little_geom = little_geom + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); + /// let prepared_big_geom = big_geom + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); /// /// assert_eq!(prepared_little_geom.covered_by(&big_geom), Ok(true)); /// assert_eq!(prepared_big_geom.covered_by(&little_geom), Ok(false)); /// ``` - pub fn covered_by<'b>(&self, other: &Geometry<'b>) -> GResult { + pub fn covered_by<'b, G: Geom<'b>>(&self, other: &G) -> GResult { let ret_val = unsafe { GEOSPreparedCoveredBy_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; @@ -134,22 +151,26 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT (1 2)").expect("Invalid geometry"); + /// let geom = Geometry::new_from_wkt("POINT (1 2)") + /// .expect("Invalid geometry"); /// let little_geom = geom.buffer(10., 8).expect("buffer failed"); /// let big_geom = geom.buffer(20., 8).expect("buffer failed"); /// - /// let prepared_little_geom = little_geom.to_prepared_geom().expect("to_prepared_geom failed"); - /// let prepared_big_geom = big_geom.to_prepared_geom().expect("to_prepared_geom failed"); + /// let prepared_little_geom = little_geom + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); + /// let prepared_big_geom = big_geom + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); /// /// assert_eq!(prepared_little_geom.covers(&big_geom), Ok(false)); /// assert_eq!(prepared_big_geom.covers(&little_geom), Ok(true)); /// ``` - pub fn covers<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSPreparedCovers_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; + pub fn covers<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = + unsafe { GEOSPreparedCovers_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; check_geos_predicate(ret_val, PredicateType::PreparedCovers) } @@ -158,18 +179,19 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING(1 1,2 2)").expect("invalid geometry"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 1,1 2)").expect("invalid geometry"); + /// let geom1 = Geometry::new_from_wkt("LINESTRING(1 1,2 2)") + /// .expect("invalid geometry"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 1,1 2)") + /// .expect("invalid geometry"); /// let prepared_geom = geom1.to_prepared_geom().expect("to_prepared_geom failed"); /// /// assert_eq!(prepared_geom.crosses(&geom2), Ok(true)); /// ``` - pub fn crosses<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSPreparedCrosses_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; + pub fn crosses<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = + unsafe { GEOSPreparedCrosses_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; check_geos_predicate(ret_val, PredicateType::PreparedCrosses) } @@ -182,17 +204,22 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("invalid geometry"); - /// let prepared_geom = geom1.to_prepared_geom().expect("to_prepared_geom failed"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("invalid geometry"); - /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("invalid geometry"); + /// use geos::{Geom, Geometry}; + /// + /// let geom1 = Geometry::new_from_wkt("POINT(0 0)") + /// .expect("invalid geometry"); + /// let prepared_geom = geom1 + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)") + /// .expect("invalid geometry"); + /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)") + /// .expect("invalid geometry"); /// /// assert_eq!(prepared_geom.disjoint(&geom2), Ok(true)); /// assert_eq!(prepared_geom.disjoint(&geom3), Ok(false)); /// ``` - pub fn disjoint<'b>(&self, other: &Geometry<'b>) -> GResult { + pub fn disjoint<'b, G: Geom<'b>>(&self, other: &G) -> GResult { let ret_val = unsafe { GEOSPreparedDisjoint_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; @@ -211,17 +238,22 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; - /// - /// let geom1 = Geometry::new_from_wkt("POINT(0 0)").expect("invalid geometry"); - /// let prepared_geom = geom1.to_prepared_geom().expect("to_prepared_geom failed"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)").expect("invalid geometry"); - /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)").expect("invalid geometry"); + /// use geos::{Geom, Geometry}; + /// + /// let geom1 = Geometry::new_from_wkt("POINT(0 0)") + /// .expect("invalid geometry"); + /// let prepared_geom = geom1 + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)") + /// .expect("invalid geometry"); + /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)") + /// .expect("invalid geometry"); /// /// assert_eq!(prepared_geom.intersects(&geom2), Ok(false)); /// assert_eq!(prepared_geom.intersects(&geom3), Ok(true)); /// ``` - pub fn intersects<'b>(&self, other: &Geometry<'b>) -> GResult { + pub fn intersects<'b, G: Geom<'b>>(&self, other: &G) -> GResult { let ret_val = unsafe { GEOSPreparedIntersects_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; @@ -233,21 +265,27 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("POINT(1 0.5)").expect("invalid geometry"); - /// let prepared_geom = geom1.to_prepared_geom().expect("to_prepared_geom failed"); - /// let geom2 = Geometry::new_from_wkt("LINESTRING(1 0, 1 1, 3 5)").expect("invalid geometry"); + /// let geom1 = Geometry::new_from_wkt("POINT(1 0.5)") + /// .expect("invalid geometry"); + /// let prepared_geom = geom1 + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); + /// let geom2 = Geometry::new_from_wkt("LINESTRING(1 0, 1 1, 3 5)") + /// .expect("invalid geometry"); /// /// assert_eq!(prepared_geom.overlaps(&geom2), Ok(false)); /// /// let geom1 = geom1.buffer(3., 8).expect("buffer failed"); - /// let prepared_geom = geom1.to_prepared_geom().expect("to_prepared_geom failed"); + /// let prepared_geom = geom1 + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); /// let geom2 = geom2.buffer(0.5, 8).expect("buffer failed"); /// /// assert_eq!(prepared_geom.overlaps(&geom2), Ok(true)); /// ``` - pub fn overlaps<'b>(&self, other: &Geometry<'b>) -> GResult { + pub fn overlaps<'b, G: Geom<'b>>(&self, other: &G) -> GResult { let ret_val = unsafe { GEOSPreparedOverlaps_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; @@ -260,10 +298,13 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom1 = Geometry::new_from_wkt("LINESTRING(0 0, 1 1, 0 2)").expect("invalid geometry"); - /// let prepared_geom = geom1.to_prepared_geom().expect("to_prepared_geom failed"); + /// let geom1 = Geometry::new_from_wkt("LINESTRING(0 0, 1 1, 0 2)") + /// .expect("invalid geometry"); + /// let prepared_geom = geom1 + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); /// let geom2 = Geometry::new_from_wkt("POINT(1 1)").expect("invalid geometry"); /// /// assert_eq!(prepared_geom.touches(&geom2), Ok(false)); @@ -272,10 +313,9 @@ impl<'a> PreparedGeometry<'a> { /// /// assert_eq!(prepared_geom.touches(&geom2), Ok(true)); /// ``` - pub fn touches<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSPreparedTouches_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; + pub fn touches<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = + unsafe { GEOSPreparedTouches_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; check_geos_predicate(ret_val, PredicateType::PreparedTouches) } @@ -284,23 +324,27 @@ impl<'a> PreparedGeometry<'a> { /// # Example /// /// ``` - /// use geos::Geometry; + /// use geos::{Geom, Geometry}; /// - /// let geom = Geometry::new_from_wkt("POINT(50 50)").expect("invalid geometry"); + /// let geom = Geometry::new_from_wkt("POINT(50 50)") + /// .expect("invalid geometry"); /// let small_geom = geom.buffer(20., 8).expect("buffer failed"); /// let big_geom = geom.buffer(40., 8).expect("buffer failed"); /// - /// let small_prepared_geom = small_geom.to_prepared_geom().expect("to_prepared_geom failed"); - /// let big_prepared_geom = big_geom.to_prepared_geom().expect("to_prepared_geom failed"); + /// let small_prepared_geom = small_geom + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); + /// let big_prepared_geom = big_geom + /// .to_prepared_geom() + /// .expect("to_prepared_geom failed"); /// /// assert_eq!(small_prepared_geom.within(&small_geom), Ok(true)); /// assert_eq!(small_prepared_geom.within(&big_geom), Ok(true)); /// assert_eq!(big_prepared_geom.within(&small_geom), Ok(false)); /// ``` - pub fn within<'b>(&self, other: &Geometry<'b>) -> GResult { - let ret_val = unsafe { - GEOSPreparedWithin_r(self.get_raw_context(), self.as_raw(), other.as_raw()) - }; + pub fn within<'b, G: Geom<'b>>(&self, other: &G) -> GResult { + let ret_val = + unsafe { GEOSPreparedWithin_r(self.get_raw_context(), self.as_raw(), other.as_raw()) }; check_geos_predicate(ret_val, PredicateType::PreparedWithin) } } @@ -318,13 +362,19 @@ impl<'a> ContextInteractions<'a> for PreparedGeometry<'a> { /// Set the context handle to the `PreparedGeometry`. /// /// ``` - /// use geos::{ContextInteractions, ContextHandle, Geometry, PreparedGeometry}; + /// use geos::{ + /// ContextInteractions, ContextHandle, Geom, Geometry, PreparedGeometry, + /// }; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); /// let context_handle = ContextHandle::init().expect("invalid init"); - /// let mut prepared_geom = point_geom.to_prepared_geom() - /// .expect("failed to create prepared geom"); - /// context_handle.set_notice_message_handler(Some(Box::new(|s| println!("new message: {}", s)))); + /// let mut prepared_geom = point_geom + /// .to_prepared_geom() + /// .expect("failed to create prepared geom"); + /// context_handle.set_notice_message_handler( + /// Some(Box::new(|s| println!("new message: {}", s))) + /// ); /// prepared_geom.set_context_handle(context_handle); /// ``` fn set_context_handle(&mut self, context: ContextHandle<'a>) { @@ -334,9 +384,13 @@ impl<'a> ContextInteractions<'a> for PreparedGeometry<'a> { /// Get the context handle of the `PreparedGeometry`. /// /// ``` - /// use geos::{ContextInteractions, CoordDimensions, Geometry, PreparedGeometry}; + /// use geos::{ + /// ContextInteractions, CoordDimensions, Geom, Geometry, + /// PreparedGeometry, + /// }; /// - /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); + /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)") + /// .expect("Invalid geometry"); /// let prepared_geom = point_geom.to_prepared_geom() /// .expect("failed to create prepared geom"); /// let context = prepared_geom.get_context_handle(); @@ -348,9 +402,9 @@ impl<'a> ContextInteractions<'a> for PreparedGeometry<'a> { } impl<'a> AsRaw for PreparedGeometry<'a> { - type RawType = *mut GEOSPreparedGeometry; + type RawType = GEOSPreparedGeometry; - fn as_raw(&self) -> Self::RawType { + fn as_raw(&self) -> *const Self::RawType { *self.ptr } } diff --git a/src/spatial_index.rs b/src/spatial_index.rs index 33ccb24..f32bb0d 100644 --- a/src/spatial_index.rs +++ b/src/spatial_index.rs @@ -4,15 +4,15 @@ use std::sync::Arc; use geos_sys::*; -use ::{ContextHandle, Geometry}; -use ::{AsRaw, GResult}; use context_handle::PtrWrap; use ContextHandling; +use {AsRaw, AsRawMut, GResult}; +use {ContextHandle, Geom}; pub trait SpatialIndex<'a, I> { - fn insert(&mut self, geometry: &Geometry<'a>, item: I); + fn insert<'b, G: Geom<'b>>(&mut self, geometry: &G, item: I); - fn query(&self, geometry: &Geometry, visitor: V); + fn query<'b, G: Geom<'b>, V: FnMut(&I)>(&self, geometry: &G, visitor: V); } pub struct STRtree<'a, I> { @@ -24,55 +24,48 @@ pub struct STRtree<'a, I> { impl<'a, I> STRtree<'a, I> { pub fn with_capacity(node_capacity: usize) -> GResult> { match ContextHandle::init_e(Some("STRtree::with_capacity")) { - Ok(context_handle) => { - unsafe { - let ptr = GEOSSTRtree_create_r( - context_handle.as_raw(), - node_capacity, - ); - Ok(STRtree { - ptr: PtrWrap(ptr), - context: Arc::new(context_handle), - item_type: PhantomData, - }) - } - } + Ok(context_handle) => unsafe { + let ptr = GEOSSTRtree_create_r(context_handle.as_raw(), node_capacity); + Ok(STRtree { + ptr: PtrWrap(ptr), + context: Arc::new(context_handle), + item_type: PhantomData, + }) + }, Err(e) => Err(e), } } - pub fn iterate(&self, visitor: V) where V: FnMut(&I) { + pub fn iterate(&self, visitor: V) + where + V: FnMut(&I), + { unsafe { let (closure, callback) = unpack_closure(&visitor); - GEOSSTRtree_iterate_r( - self.get_raw_context(), - *self.ptr, - Some(callback), - closure, - ); + GEOSSTRtree_iterate_r(self.get_raw_context(), *self.ptr, Some(callback), closure); } } } impl<'a, I> SpatialIndex<'a, I> for STRtree<'a, I> { - fn insert(&mut self, geometry: &Geometry<'a>, item: I) { + fn insert<'b, G: Geom<'b>>(&mut self, geometry: &G, item: I) { unsafe { GEOSSTRtree_insert_r( self.get_raw_context(), *self.ptr, - *geometry.ptr, + geometry.as_raw(), Box::into_raw(Box::new(item)) as *mut c_void, ); } } - fn query(&self, geometry: &Geometry, visitor: V) where V: FnMut(&I) { + fn query<'b, G: Geom<'b>, V: FnMut(&I)>(&self, geometry: &G, visitor: V) { unsafe { let (closure, callback) = unpack_closure(&visitor); GEOSSTRtree_query_r( self.get_raw_context(), *self.ptr, - *geometry.ptr, + geometry.as_raw(), Some(callback), closure, ); @@ -81,9 +74,17 @@ impl<'a, I> SpatialIndex<'a, I> for STRtree<'a, I> { } impl<'a, I> AsRaw for STRtree<'a, I> { - type RawType = *mut GEOSSTRtree; + type RawType = GEOSSTRtree; - fn as_raw(&self) -> Self::RawType { + fn as_raw(&self) -> *const Self::RawType { + *self.ptr + } +} + +impl<'a, I> AsRawMut for STRtree<'a, I> { + type RawType = GEOSSTRtree; + + unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType { *self.ptr } } @@ -118,10 +119,16 @@ impl Drop for STRtree<'_, I> { } } -unsafe fn unpack_closure(closure: &F) -> (*mut c_void, extern "C" fn(*mut c_void, *mut c_void)) - where F: FnMut(&I) { +unsafe fn unpack_closure( + closure: &F, +) -> (*mut c_void, extern "C" fn(*mut c_void, *mut c_void)) +where + F: FnMut(&I), +{ extern "C" fn trampoline(item: *mut c_void, data: *mut c_void) - where F: FnMut(&I) { + where + F: FnMut(&I), + { unsafe { let closure: &mut F = &mut *(data as *mut F); (*closure)(&mut *(item as *mut I)); @@ -135,7 +142,7 @@ unsafe fn unpack_closure(closure: &F) -> (*mut c_void, extern "C" fn(*mut mod test { use std::collections::HashSet; - use ::{Geometry, SpatialIndex, STRtree}; + use {Geometry, STRtree, SpatialIndex}; #[test] fn test_strtree() { @@ -149,7 +156,6 @@ mod test { tree.insert(&line, "Line"); tree.insert(&polygon, "Polygon"); - // Test iterate let mut items = HashSet::<&str>::new(); @@ -157,8 +163,10 @@ mod test { items.insert(*item); }); - assert_eq!(items, vec!["Line", "Point", "Polygon"].into_iter().collect()); - + assert_eq!( + items, + vec!["Line", "Point", "Polygon"].into_iter().collect() + ); // Test query diff --git a/src/test.rs b/src/test.rs index 23ce18d..900ad42 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod test { - use crate::{Geometry, PreparedGeometry}; + use crate::{Geom, Geometry, PreparedGeometry}; use enums::GeometryTypes; #[test] @@ -34,11 +34,23 @@ mod test { fn test_geom_creation_from_geoms() { let polygon_geom = Geometry::new_from_wkt("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))").unwrap(); let new_geom = polygon_geom.buffer(100.0, 12).expect("buffer failed"); - let g1 = new_geom.difference(&polygon_geom).expect("difference failed"); - let g2 = polygon_geom.sym_difference(&new_geom).expect("sym difference failed"); - let g3 = new_geom.sym_difference(&polygon_geom).expect("sym difference 2 faileed"); - assert_almost_eq(g1.area().expect("area 1.1 failed"), g2.area().expect("area 1.2 failed")); - assert_almost_eq(g2.area().expect("area 2.1 failed"), g3.area().expect("area 2.2 failed")); + let g1 = new_geom + .difference(&polygon_geom) + .expect("difference failed"); + let g2 = polygon_geom + .sym_difference(&new_geom) + .expect("sym difference failed"); + let g3 = new_geom + .sym_difference(&polygon_geom) + .expect("sym difference 2 faileed"); + assert_almost_eq( + g1.area().expect("area 1.1 failed"), + g2.area().expect("area 1.2 failed"), + ); + assert_almost_eq( + g2.area().expect("area 2.1 failed"), + g3.area().expect("area 2.2 failed"), + ); let g4 = g3.get_centroid().expect("get_centroid failed"); assert_eq!(GeometryTypes::Point, g4.geometry_type()); let g5 = g4.buffer(200.0, 12).expect("buffer 2 failed"); @@ -61,13 +73,14 @@ mod test { Geometry::new_from_wkt("POINT (0.4 4.1)").unwrap(), ]; for geom in &vec_geoms { - assert_eq!(true, pg1.intersects(&geom).unwrap()); + assert_eq!(true, pg1.intersects(geom).unwrap()); } } #[test] fn test_wkt_rounding_precision() { - let g = Geometry::new_from_wkt("LINESTRING(0.0 0.0, 7.0 7.0, 45.0 50.5, 100.0 100.0)").unwrap(); + let g = + Geometry::new_from_wkt("LINESTRING(0.0 0.0, 7.0 7.0, 45.0 50.5, 100.0 100.0)").unwrap(); let wkt = g.to_wkt_precision(0); assert_eq!(wkt, Ok("LINESTRING (0 0, 7 7, 45 50, 100 100)".to_owned())); let wkt2 = g.to_wkt(); @@ -111,7 +124,10 @@ mod test { let multi_polygon = Geometry::create_multipolygon(vec_geoms).unwrap(); assert_eq!( multi_polygon.to_wkt_precision(0), - Ok("MULTIPOLYGON (((0 0, 0 5, 5 5, 5 0, 0 0)), ((1 1, 1 3, 5 5, 5 0, 1 1)))".to_owned()), + Ok( + "MULTIPOLYGON (((0 0, 0 5, 5 5, 5 0, 0 0)), ((1 1, 1 3, 5 5, 5 0, 1 1)))" + .to_owned() + ), ); } @@ -146,7 +162,10 @@ mod test { #[test] fn test_get_geometry_n() { - let multilinestring = Geometry::new_from_wkt("MULTILINESTRING ((1 1, 10 50, 20 25), (0 0, 7 7, 45 50, 100 100))").unwrap(); + let multilinestring = Geometry::new_from_wkt( + "MULTILINESTRING ((1 1, 10 50, 20 25), (0 0, 7 7, 45 50, 100 100))", + ) + .unwrap(); let l0 = multilinestring.get_geometry_n(0).unwrap(); let l1 = multilinestring.get_geometry_n(1).unwrap(); diff --git a/src/to_geo.rs b/src/to_geo.rs index 663f0b2..df52dcb 100644 --- a/src/to_geo.rs +++ b/src/to_geo.rs @@ -1,11 +1,13 @@ -use crate::Geometry as GGeom; +use crate::{ConstGeometry, Geom, Geometry as GGeometry}; use error::Error; use from_geo::TryInto; use geo_types::Geometry; use wkt; use wkt::conversion::try_into_geometry; -impl<'a> TryInto> for GGeom<'a> { +macro_rules! impl_try_into { + ($ty_name:ident $(,$lt:lifetime)?) => ( +impl<'a$(,$lt)?> TryInto> for $ty_name<'a$(,$lt)?> { type Err = Error; fn try_into(self) -> Result, Self::Err> { @@ -26,10 +28,15 @@ impl<'a> TryInto> for GGeom<'a> { .map_err(|e| Error::ConversionError(format!("impossible to built from wkt: {}", e))) } } + ); +} + +impl_try_into!(GGeometry); +impl_try_into!(ConstGeometry, 'c); #[cfg(test)] mod test { - use super::GGeom; + use crate::Geometry as GGeometry; use from_geo::TryInto; use geo_types::{Coordinate, Geometry, LineString, MultiPolygon, Polygon}; @@ -40,7 +47,7 @@ mod test { #[test] fn geom_to_geo_polygon() { let poly = "MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))"; - let poly = GGeom::new_from_wkt(poly).unwrap(); + let poly = GGeometry::new_from_wkt(poly).unwrap(); let geo_polygon: Geometry = poly.try_into().unwrap(); diff --git a/src/to_geojson.rs b/src/to_geojson.rs index a3a00e9..1ae2982 100644 --- a/src/to_geojson.rs +++ b/src/to_geojson.rs @@ -1,8 +1,7 @@ -use crate::{CoordSeq, Geometry as GGeom, GeometryTypes}; +use crate::{ConstGeometry, CoordSeq, Geom, Geometry as GGeometry, GeometryTypes}; use error::{Error, GResult}; use geojson::{Geometry, Value}; - pub trait TryInto { type Err; fn try_into(self) -> Result; @@ -12,15 +11,14 @@ fn coords_seq_to_vec_position(cs: &CoordSeq) -> GResult>> { let n_coords = cs.size()?; let mut coords = Vec::with_capacity(n_coords); for i in 0..n_coords { - coords.push(vec![ - cs.get_x(i)?, - cs.get_y(i)?, - ]); + coords.push(vec![cs.get_x(i)?, cs.get_y(i)?]); } Ok(coords) } -impl<'a> TryInto for GGeom<'a> { +macro_rules! impl_try_into { + ($ty_name:ident $(,$lt:lifetime)?) => ( +impl<'a$(,$lt)?> TryInto for $ty_name<'a$(,$lt)?> { type Err = Error; fn try_into(self) -> Result { @@ -111,17 +109,22 @@ impl<'a> TryInto for GGeom<'a> { } } } + ); +} + +impl_try_into!(GGeometry); +impl_try_into!(ConstGeometry, 'c); #[cfg(test)] mod test { - use super::GGeom; use crate::to_geojson::TryInto; + use crate::Geometry as GGeometry; use geojson::{Geometry, Value}; #[test] fn geom_to_geojson_point() { let pt = "POINT(1 1)"; - let pt = GGeom::new_from_wkt(pt).unwrap(); + let pt = GGeometry::new_from_wkt(pt).unwrap(); let geojson_pt: Geometry = pt.try_into().unwrap(); @@ -132,35 +135,29 @@ mod test { #[test] fn geom_to_geojson_multipoint() { let pts = "MULTIPOINT((1 1), (2 2))"; - let pts = GGeom::new_from_wkt(pts).unwrap(); + let pts = GGeometry::new_from_wkt(pts).unwrap(); let geojson_pts: Geometry = pts.try_into().unwrap(); - let expected_pts = Geometry::new(Value::MultiPoint(vec![ - vec![1., 1.], - vec![2., 2.], - ])); + let expected_pts = Geometry::new(Value::MultiPoint(vec![vec![1., 1.], vec![2., 2.]])); assert_eq!(geojson_pts, expected_pts); } #[test] fn geom_to_geojson_line() { let line = "LINESTRING(1 1, 2 2)"; - let line = GGeom::new_from_wkt(line).unwrap(); + let line = GGeometry::new_from_wkt(line).unwrap(); let geojson_line: Geometry = line.try_into().unwrap(); - let expected_line = Geometry::new(Value::LineString(vec![ - vec![1., 1.], - vec![2., 2.], - ])); + let expected_line = Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]])); assert_eq!(geojson_line, expected_line); } #[test] fn geom_to_geojson_linearring() { let line = "LINEARRING(1 1, 2 1, 2 2, 1 1)"; - let line = GGeom::new_from_wkt(line).unwrap(); + let line = GGeometry::new_from_wkt(line).unwrap(); let geojson_line: Geometry = line.try_into().unwrap(); @@ -176,85 +173,71 @@ mod test { #[test] fn geom_to_geojson_multiline() { let line = "MULTILINESTRING((1 1, 2 2), (3 3, 4 4))"; - let line = GGeom::new_from_wkt(line).unwrap(); + let line = GGeometry::new_from_wkt(line).unwrap(); let geojson_line: Geometry = line.try_into().unwrap(); let expected_line = Geometry::new(Value::MultiLineString(vec![ - vec![ - vec![1., 1.], - vec![2., 2.], - ], - vec![ - vec![3., 3.], - vec![4., 4.], - ], + vec![vec![1., 1.], vec![2., 2.]], + vec![vec![3., 3.], vec![4., 4.]], ])); assert_eq!(geojson_line, expected_line); } - #[test] fn geom_to_geojson_polygon() { let poly = "POLYGON((0 0, 0 3, 3 3, 3 0, 0 0) ,(0.2 0.2, 0.2 2, 2 2, 2 0.2, 0.2 0.2))"; - let poly = GGeom::new_from_wkt(poly).unwrap(); + let poly = GGeometry::new_from_wkt(poly).unwrap(); let geojson_polygon: Geometry = poly.try_into().unwrap(); - let expected_polygon = Geometry::new(Value::Polygon( + let expected_polygon = Geometry::new(Value::Polygon(vec![ + vec![ + vec![0., 0.], + vec![0., 3.], + vec![3., 3.], + vec![3., 0.], + vec![0., 0.], + ], vec![ - vec![ - vec![0., 0.], - vec![0., 3.], - vec![3., 3.], - vec![3., 0.], - vec![0., 0.], - ], - vec![ - vec![0.2, 0.2], - vec![0.2, 2.], - vec![2., 2.], - vec![2., 0.2], - vec![0.2, 0.2], - ], - ] - )); + vec![0.2, 0.2], + vec![0.2, 2.], + vec![2., 2.], + vec![2., 0.2], + vec![0.2, 0.2], + ], + ])); assert_eq!(geojson_polygon, expected_polygon); } #[test] fn geom_to_geojson_multipolygon() { let poly = "MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))"; - let poly = GGeom::new_from_wkt(poly).unwrap(); + let poly = GGeometry::new_from_wkt(poly).unwrap(); let geojson_polygon: Geometry = poly.try_into().unwrap(); - let expected_polygon = Geometry::new(Value::MultiPolygon( - vec![vec![vec![ - vec![0., 0.], - vec![0., 1.], - vec![1., 1.], - vec![1., 0.], - vec![0., 0.], - ]]] - )); + let expected_polygon = Geometry::new(Value::MultiPolygon(vec![vec![vec![ + vec![0., 0.], + vec![0., 1.], + vec![1., 1.], + vec![1., 0.], + vec![0., 0.], + ]]])); assert_eq!(geojson_polygon, expected_polygon); } #[test] fn geom_to_geojson_geometry_collection() { let gc = "GEOMETRYCOLLECTION(POINT(1 1), LINESTRING(1 1, 2 2))"; - let gc = GGeom::new_from_wkt(gc).unwrap(); + let gc = GGeometry::new_from_wkt(gc).unwrap(); let geojson_gc: Geometry = gc.try_into().unwrap(); - let expected_gc = Geometry::new(Value::GeometryCollection( - vec![ - Geometry::new(Value::Point(vec![1., 1.])), - Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]])), - ] - )); + let expected_gc = Geometry::new(Value::GeometryCollection(vec![ + Geometry::new(Value::Point(vec![1., 1.])), + Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]])), + ])); assert_eq!(geojson_gc, expected_gc); } - } diff --git a/src/traits.rs b/src/traits.rs index ed24dc0..109d11d 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,10 +1,23 @@ -use geos_sys::GEOSContextHandle_t; use crate::ContextHandle; +use geos_sys::GEOSContextHandle_t; pub trait AsRaw { type RawType; - fn as_raw(&self) -> Self::RawType; + fn as_raw(&self) -> *const Self::RawType; +} + +pub trait AsRawMut { + type RawType; + + fn as_raw_mut(&mut self) -> *mut Self::RawType { + unsafe { self.as_raw_mut_override() } + } + /// This method exists because in certain case, even though you don't run any mutable operation + /// on the object, it still requires a mutable access. + /// + /// A good example is `GEOSWKTWriter_getOutputDimension_r` (which is very likely a bug). + unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType; } pub trait ContextHandling { diff --git a/src/voronoi.rs b/src/voronoi.rs index e2eb50e..adccd22 100644 --- a/src/voronoi.rs +++ b/src/voronoi.rs @@ -1,4 +1,4 @@ -use crate::Geometry as GGeom; +use crate::{Geom, Geometry as GGeometry}; use error::Error; use from_geo::TryInto; use geo_types::{Geometry, GeometryCollection, Point, Polygon}; @@ -7,11 +7,11 @@ use std::borrow::Borrow; /// Available using the `geo` feature. pub fn compute_voronoi>>( points: &[T], - envelope: Option<&GGeom>, + envelope: Option<&GGeometry>, tolerance: f64, only_edges: bool, ) -> Result>, Error> { - let geom_points: GGeom = points.try_into()?; + let geom_points: GGeometry = points.try_into()?; geom_points .voronoi(envelope, tolerance, only_edges)? @@ -32,23 +32,23 @@ pub fn compute_voronoi>>( #[cfg(test)] mod test { - use super::GGeom; + use crate::{Geom, Geometry as GGeometry}; use geo_types::{Coordinate, LineString, Point, Polygon}; // create a voronoi diagram. Same unit test as // https://github.com/libgeos/geos/blob/master/tests/unit/triangulate/VoronoiTest.cpp#L118 #[test] fn simple_voronoi() { let points = "MULTIPOINT ((150 200), (180 270), (275 163))"; - let input = GGeom::new_from_wkt(points).unwrap(); + let input = GGeometry::new_from_wkt(points).unwrap(); - let mut voronoi = input.voronoi(None, 0., false).unwrap(); + let mut voronoi = input.voronoi(None::<&GGeometry>, 0., false).unwrap(); let expected_output = "GEOMETRYCOLLECTION ( POLYGON ((25 38, 25 295, 221.20588235294116 210.91176470588235, 170.024 38, 25 38)), POLYGON ((400 369.6542056074766, 400 38, 170.024 38, 221.20588235294116 210.91176470588235, 400 369.6542056074766)), POLYGON ((25 295, 25 395, 400 395, 400 369.6542056074766, 221.20588235294116 210.91176470588235, 25 295)))"; - let mut expected_output = GGeom::new_from_wkt(expected_output).unwrap(); + let mut expected_output = GGeometry::new_from_wkt(expected_output).unwrap(); expected_output.normalize().unwrap(); voronoi.normalize().unwrap(); @@ -63,9 +63,9 @@ mod test { fn wkt_voronoi_precision() { let points = "MULTIPOINT ((100 200), (105 202), (110 200), (140 230), (210 240), (220 190), (170 170), (170 260), (213 245), (220 190))"; - let input = GGeom::new_from_wkt(points).unwrap(); + let input = GGeometry::new_from_wkt(points).unwrap(); - let mut voronoi = input.voronoi(None, 6., false).unwrap(); + let mut voronoi = input.voronoi(None::<&GGeometry>, 6., false).unwrap(); let expected_output = "GEOMETRYCOLLECTION ( POLYGON ((-20 50, -20 380, -3.75 380, 105 235, 105 115, 77.14285714285714 50, -20 50)), @@ -76,7 +76,7 @@ mod test { POLYGON ((255 380, 340 380, 340 240, 183.51851851851853 208.7037037037037, 178.33333333333334 211.66666666666666, 176.66666666666666 223.33333333333334, 255 380)), POLYGON ((340 240, 340 50, 247 50, 183.51851851851853 208.7037037037037, 340 240)))"; - let mut expected_output = GGeom::new_from_wkt(expected_output).unwrap(); + let mut expected_output = GGeometry::new_from_wkt(expected_output).unwrap(); expected_output.normalize().unwrap(); voronoi.normalize().unwrap(); @@ -87,7 +87,7 @@ mod test { // #[test] // fn check() { - // let geom = GGeom::new_from_wkt("MULTIPOLYGON ((( + // let geom = GGeometry::new_from_wkt("MULTIPOLYGON ((( // 4.5687299000000001 7.6963754000000000, 4.5687299000000001 7.6957645999999995, // 4.5687299000000001 7.6954739999999999, 4.5687299000000001 7.6950833999999997, // 4.5687299000000001 7.6906570999999992, 4.5717796999999996 7.6765523000000000, @@ -126,7 +126,7 @@ mod test { // 4.5492917999999998 7.6979505999999995, 4.5496692999999997 7.6979002999999997, // 4.5584372999999996 7.6959944000000000, 4.5637740999999998 7.6952318999999996, // 4.5687299000000001 7.6963754000000000)))").expect("new_from_wkt failed"); - // let points = GGeom::new_from_wkt("MULTIPOINT( + // let points = GGeometry::new_from_wkt("MULTIPOINT( // (4.4333330000000002 7.6666669999999996), // (4.4500000000000002 7.6166669999999996), // (4.4666670000000002 7.6333329999999995), diff --git a/src/wkb_writer.rs b/src/wkb_writer.rs index 05bb100..76f41b2 100644 --- a/src/wkb_writer.rs +++ b/src/wkb_writer.rs @@ -1,18 +1,18 @@ -use enums::{ByteOrder, OutputDimension}; -use crate::{ContextHandle, Geometry, GResult, AsRaw, ContextHandling, ContextInteractions}; +use crate::{AsRaw, AsRawMut, ContextHandle, ContextHandling, ContextInteractions, GResult, Geom}; +use c_vec::CVec; use context_handle::PtrWrap; +use enums::TryFrom; +use enums::{ByteOrder, OutputDimension}; +use error::Error; use geos_sys::*; use std::sync::Arc; -use error::Error; -use enums::TryFrom; -use c_vec::CVec; /// The `WKBWriter` type is used to generate `HEX` or `WKB` formatted output from [`Geometry`]. /// /// # Example /// /// ``` -/// use geos::{ContextHandling, Geometry, WKBWriter}; +/// use geos::{ContextHandling, Geom, Geometry, WKBWriter}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); /// let mut writer = WKBWriter::new_with_context(point_geom.clone_context()) @@ -39,7 +39,7 @@ impl<'a> WKBWriter<'a> { /// # Example /// /// ``` - /// use geos::{Geometry, WKBWriter}; + /// use geos::{Geom, Geometry, WKBWriter}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter"); @@ -60,7 +60,7 @@ impl<'a> WKBWriter<'a> { /// # Example /// /// ``` - /// use geos::{ContextHandling, Geometry, WKBWriter}; + /// use geos::{ContextHandling, Geom, Geometry, WKBWriter}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry"); /// let mut writer = WKBWriter::new_with_context(point_geom.clone_context()) @@ -88,9 +88,15 @@ impl<'a> WKBWriter<'a> { } else { String::new() }; - return Err(Error::NoConstructionFromNullPtr(format!("WKBWriter::{}{}", caller, extra))); + return Err(Error::NoConstructionFromNullPtr(format!( + "WKBWriter::{}{}", + caller, extra + ))); } - Ok(WKBWriter { ptr: PtrWrap(ptr), context }) + Ok(WKBWriter { + ptr: PtrWrap(ptr), + context, + }) } /// Writes out the given `geometry` as WKB format. @@ -107,15 +113,20 @@ impl<'a> WKBWriter<'a> { /// let expected = vec![1u8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 64, 0, 0, 0, 0, 0, 0, 4, 64]; /// assert_eq!(v, expected); /// ``` - pub fn write_wkb(&self, geometry: &Geometry<'_>) -> GResult> { + pub fn write_wkb<'b, G: Geom<'b>>(&mut self, geometry: &G) -> GResult> { let mut size = 0; unsafe { - let ptr = GEOSWKBWriter_write_r(self.get_raw_context(), self.as_raw(), - geometry.as_raw(), &mut size); + let ptr = GEOSWKBWriter_write_r( + self.get_raw_context(), + self.as_raw_mut(), + geometry.as_raw(), + &mut size, + ); if ptr.is_null() { Err(Error::NoConstructionFromNullPtr( - "WKBWriter::write_wkb failed: GEOSWKBWriter_writeHEX_r returned null pointer".to_owned()) - ) + "WKBWriter::write_wkb failed: GEOSWKBWriter_writeHEX_r returned null pointer" + .to_owned(), + )) } else { Ok(CVec::new(ptr, size as _)) } @@ -137,15 +148,20 @@ impl<'a> WKBWriter<'a> { /// 52,52,48,48,48,48,48,48,48,48,48,48,48,48,48,48,52,52,48]; /// assert_eq!(v, expected); /// ``` - pub fn write_hex(&self, geometry: &Geometry<'_>) -> GResult> { + pub fn write_hex<'b, G: Geom<'b>>(&mut self, geometry: &G) -> GResult> { let mut size = 0; unsafe { - let ptr = GEOSWKBWriter_writeHEX_r(self.get_raw_context(), self.as_raw(), - geometry.as_raw(), &mut size); + let ptr = GEOSWKBWriter_writeHEX_r( + self.get_raw_context(), + self.as_raw_mut(), + geometry.as_raw(), + &mut size, + ); if ptr.is_null() { Err(Error::NoConstructionFromNullPtr( - "WKBWriter::write_hex failed: GEOSWKBWriter_writeHEX_r returned null pointer".to_owned()) - ) + "WKBWriter::write_hex failed: GEOSWKBWriter_writeHEX_r returned null pointer" + .to_owned(), + )) } else { Ok(CVec::new(ptr, size as _)) } @@ -178,8 +194,11 @@ impl<'a> WKBWriter<'a> { /// ``` pub fn set_output_dimension(&mut self, dimension: OutputDimension) { unsafe { - GEOSWKBWriter_setOutputDimension_r(self.get_raw_context(), self.as_raw(), - dimension.into()) + GEOSWKBWriter_setOutputDimension_r( + self.get_raw_context(), + self.as_raw_mut(), + dimension.into(), + ) } } @@ -237,7 +256,11 @@ impl<'a> WKBWriter<'a> { /// ``` pub fn set_wkb_byte_order(&mut self, byte_order: ByteOrder) { unsafe { - GEOSWKBWriter_setByteOrder_r(self.get_raw_context(), self.as_raw(), byte_order.into()) + GEOSWKBWriter_setByteOrder_r( + self.get_raw_context(), + self.as_raw_mut(), + byte_order.into(), + ) } } @@ -258,7 +281,9 @@ impl<'a> WKBWriter<'a> { unsafe { let out = GEOSWKBWriter_getIncludeSRID_r(self.get_raw_context(), self.as_raw()); if out < 0 { - Err(Error::GenericError("GEOSWKBWriter_getIncludeSRID_r failed".to_owned())) + Err(Error::GenericError( + "GEOSWKBWriter_getIncludeSRID_r failed".to_owned(), + )) } else { Ok(out != 0) } @@ -278,9 +303,13 @@ impl<'a> WKBWriter<'a> { /// assert_eq!(writer.get_include_SRID(), Ok(true)); /// ``` #[allow(non_snake_case)] - pub fn set_include_SRID(&self, include_SRID: bool) { + pub fn set_include_SRID(&mut self, include_SRID: bool) { unsafe { - GEOSWKBWriter_setIncludeSRID_r(self.get_raw_context(), self.as_raw(), include_SRID as _) + GEOSWKBWriter_setIncludeSRID_r( + self.get_raw_context(), + self.as_raw_mut(), + include_SRID as _, + ) } } } @@ -290,7 +319,7 @@ unsafe impl<'a> Sync for WKBWriter<'a> {} impl<'a> Drop for WKBWriter<'a> { fn drop(&mut self) { - unsafe { GEOSWKBWriter_destroy_r(self.get_raw_context(), self.as_raw()) }; + unsafe { GEOSWKBWriter_destroy_r(self.get_raw_context(), self.as_raw_mut()) }; } } @@ -324,9 +353,17 @@ impl<'a> ContextInteractions<'a> for WKBWriter<'a> { } impl<'a> AsRaw for WKBWriter<'a> { - type RawType = *mut GEOSWKBWriter; + type RawType = GEOSWKBWriter; + + fn as_raw(&self) -> *const Self::RawType { + *self.ptr + } +} + +impl<'a> AsRawMut for WKBWriter<'a> { + type RawType = GEOSWKBWriter; - fn as_raw(&self) -> Self::RawType { + unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType { *self.ptr } } diff --git a/src/wkt_writer.rs b/src/wkt_writer.rs index bc6c82e..ed9ae59 100644 --- a/src/wkt_writer.rs +++ b/src/wkt_writer.rs @@ -1,10 +1,13 @@ -use crate::{ContextHandle, Geometry, GResult, AsRaw, ContextHandling, ContextInteractions, OutputDimension}; +use crate::{ + AsRaw, AsRawMut, ContextHandle, ContextHandling, ContextInteractions, GResult, Geom, + OutputDimension, +}; use context_handle::PtrWrap; -use geos_sys::*; +use enums::TryFrom; +use error::Error; use functions::*; +use geos_sys::*; use std::sync::Arc; -use error::Error; -use enums::TryFrom; /// The `WKTWriter` type is used to generate `WKT` formatted output from [`Geometry`]. /// @@ -74,9 +77,15 @@ impl<'a> WKTWriter<'a> { } else { String::new() }; - return Err(Error::NoConstructionFromNullPtr(format!("WKTWriter::{}{}", caller, extra))); + return Err(Error::NoConstructionFromNullPtr(format!( + "WKTWriter::{}{}", + caller, extra + ))); } - Ok(WKTWriter { ptr: PtrWrap(ptr), context }) + Ok(WKTWriter { + ptr: PtrWrap(ptr), + context, + }) } /// Writes out the given `geometry` as WKT format. @@ -91,10 +100,10 @@ impl<'a> WKTWriter<'a> { /// /// assert_eq!(writer.write(&point_geom).unwrap(), "POINT (2.5000000000000000 2.5000000000000000)"); /// ``` - pub fn write(&self, geometry: &Geometry<'_>) -> GResult { + pub fn write<'b, G: Geom<'b>>(&mut self, geometry: &G) -> GResult { unsafe { - let ptr = GEOSWKTWriter_write_r(self.get_raw_context(), self.as_raw(), - geometry.as_raw()); + let ptr = + GEOSWKTWriter_write_r(self.get_raw_context(), self.as_raw_mut(), geometry.as_raw()); managed_string(ptr, self.get_context_handle(), "WKTWriter::write") } } @@ -118,8 +127,11 @@ impl<'a> WKTWriter<'a> { /// ``` pub fn set_rounding_precision(&mut self, precision: u32) { unsafe { - GEOSWKTWriter_setRoundingPrecision_r(self.get_raw_context(), self.as_raw(), - precision as _) + GEOSWKTWriter_setRoundingPrecision_r( + self.get_raw_context(), + self.as_raw_mut(), + precision as _, + ) } } @@ -142,13 +154,16 @@ impl<'a> WKTWriter<'a> { /// ``` pub fn set_output_dimension(&mut self, dimension: OutputDimension) { unsafe { - GEOSWKTWriter_setOutputDimension_r(self.get_raw_context(), self.as_raw(), - dimension.into()) + GEOSWKTWriter_setOutputDimension_r( + self.get_raw_context(), + self.as_raw_mut(), + dimension.into(), + ) } } - /// Returns the number of dimensions to be used when calling [`WKTWriter::write`]. By default, it - /// is 2. + /// Returns the number of dimensions to be used when calling [`WKTWriter::write`]. By default, + /// it is 2. /// /// # Example /// @@ -163,7 +178,10 @@ impl<'a> WKTWriter<'a> { /// ``` pub fn get_out_dimension(&self) -> GResult { unsafe { - let out = GEOSWKTWriter_getOutputDimension_r(self.get_raw_context(), self.as_raw()); + let out = GEOSWKTWriter_getOutputDimension_r( + self.get_raw_context(), + self.as_raw_mut_override(), + ); OutputDimension::try_from(out).map_err(|e| Error::GenericError(e.to_owned())) } } @@ -184,9 +202,7 @@ impl<'a> WKTWriter<'a> { /// assert_eq!(writer.write(&point_geom).unwrap(), "POINT (2.5 2.5)"); /// ``` pub fn set_trim(&mut self, trim: bool) { - unsafe { - GEOSWKTWriter_setTrim_r(self.get_raw_context(), self.as_raw(), trim as _) - } + unsafe { GEOSWKTWriter_setTrim_r(self.get_raw_context(), self.as_raw_mut(), trim as _) } } /// Enables/disables old 3D/4D WKT style generation. @@ -210,7 +226,7 @@ impl<'a> WKTWriter<'a> { #[allow(non_snake_case)] pub fn set_old_3D(&mut self, use_old_3D: bool) { unsafe { - GEOSWKTWriter_setOld3D_r(self.get_raw_context(), self.as_raw(), use_old_3D as _) + GEOSWKTWriter_setOld3D_r(self.get_raw_context(), self.as_raw_mut(), use_old_3D as _) } } } @@ -220,7 +236,7 @@ unsafe impl<'a> Sync for WKTWriter<'a> {} impl<'a> Drop for WKTWriter<'a> { fn drop(&mut self) { - unsafe { GEOSWKTWriter_destroy_r(self.get_raw_context(), self.as_raw()) }; + unsafe { GEOSWKTWriter_destroy_r(self.get_raw_context(), self.as_raw_mut()) }; } } @@ -254,9 +270,17 @@ impl<'a> ContextInteractions<'a> for WKTWriter<'a> { } impl<'a> AsRaw for WKTWriter<'a> { - type RawType = *mut GEOSWKTWriter; + type RawType = GEOSWKTWriter; + + fn as_raw(&self) -> *const Self::RawType { + *self.ptr + } +} + +impl<'a> AsRawMut for WKTWriter<'a> { + type RawType = GEOSWKTWriter; - fn as_raw(&self) -> Self::RawType { + unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType { *self.ptr } }