use crate::context_handle::with_context; use crate::enums::*; use crate::error::{Error, GResult}; use crate::functions::*; use crate::traits::{as_raw_impl, as_raw_mut_impl}; #[cfg(feature = "v3_10_0")] use crate::GeoJSONWriter; #[cfg(feature = "v3_10_0")] use crate::MakeValidParams; #[cfg(feature = "v3_6_0")] use crate::Precision; use crate::{AsRaw, AsRawMut, BufferParams, CoordSeq, PreparedGeometry, WKTWriter}; use geos_sys::*; use std::borrow::Borrow; use std::ffi::CString; use std::marker::PhantomData; use std::ptr::NonNull; use std::{self, str}; /// Representation of a GEOS geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 3.5)")?; /// assert_eq!(point_geom.get_x()?, 2.5); /// assert_eq!(point_geom.get_y()?, 3.5); /// # Ok::<(), geos::Error>(()) /// ``` pub struct Geometry { pub(crate) ptr: NonNull, } // 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))", /// )?; /// let point_geom = geom.get_interior_ring_n(0)?; /// # Ok::<(), geos::Error>(()) /// ``` pub struct ConstGeometry<'a> { pub(crate) ptr: NonNull, phantom: PhantomData<&'a Geometry>, } unsafe impl Send for Geometry {} unsafe impl Sync for Geometry {} unsafe impl Send for ConstGeometry<'_> {} unsafe impl Sync for ConstGeometry<'_> {} impl Geom for Geometry {} impl Geom for ConstGeometry<'_> {} pub trait Geom: AsRaw + Sized + Send + Sync { /// Returns the type of the geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))")?; /// assert_eq!(geom.get_type()?, "Polygon"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_type(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomType_r(ctx.as_raw(), self.as_raw()))?; managed_string(ptr, ctx) }) } fn geometry_type(&self) -> GResult { with_context(|ctx| unsafe { let geom_type = errcheck!(-1, GEOSGeomTypeId_r(ctx.as_raw(), self.as_raw()))?; GeometryTypes::try_from(geom_type) }) } /// Checks if the geometry is valid. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))")?; /// assert_eq!(geom.is_valid()?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn is_valid(&self) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSisValid_r(ctx.as_raw(), self.as_raw())) }) } /// Returns an explanation on why the geometry is invalid. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// // Bowtie polygon with self-intersection /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 2 2, 2 0, 0 2, 0 0))")?; /// assert_eq!(geom.is_valid_reason()?, "Self-intersection[1 1]".to_owned(),); /// # Ok::<(), geos::Error>(()) /// ``` fn is_valid_reason(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSisValidReason_r(ctx.as_raw(), self.as_raw()))?; managed_string(ptr, ctx) }) } /// 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::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT (2 3)")?; /// let coord_seq = geom.get_coord_seq()?; /// /// assert_eq!(coord_seq.get_x(0)?, 2.); /// assert_eq!(coord_seq.get_y(0)?, 3.); /// # Ok::<(), geos::Error>(()) /// ``` fn get_coord_seq(&self) -> GResult { with_context(|ctx| unsafe { let coords = nullcheck!(GEOSGeom_getCoordSeq_r(ctx.as_raw(), self.as_raw()))?; let coords = nullcheck!(GEOSCoordSeq_clone_r(ctx.as_raw(), coords.as_ptr()))?; let mut size = 0; errcheck!(GEOSCoordSeq_getSize_r( ctx.as_raw(), coords.as_ptr(), &mut size ))?; #[cfg(not(feature = "v3_14_0"))] { let mut dims = 0; errcheck!(GEOSCoordSeq_getDimensions_r( ctx.as_raw(), coords.as_ptr(), &mut dims ))?; Ok(CoordSeq::new_from_raw(coords, size, dims.try_into()?)) } #[cfg(feature = "v3_14_0")] { let has_z = predicate!(GEOSCoordSeq_hasZ_r(ctx.as_raw(), coords.as_ptr()))?; let has_m = predicate!(GEOSCoordSeq_hasM_r(ctx.as_raw(), coords.as_ptr()))?; Ok(CoordSeq::new_from_raw( coords, size, CoordType::try_from((has_z, has_m))?, )) } }) } /// Returns the area of the geometry. Units are specified by the SRID of the given geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))")?; /// assert_eq!(geom1.area()?, 60.); /// # Ok::<(), geos::Error>(()) /// ``` fn area(&self) -> GResult { with_context(|ctx| unsafe { let mut n = 0.; errcheck!(GEOSArea_r(ctx.as_raw(), self.as_raw(), &mut n))?; Ok(n) }) } /// 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, WKTWriter}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// assert_eq!(point_geom.to_wkt()?, "POINT (2.5 2.5)"); /// /// let point_geom = Geometry::new_from_wkt("POINT Z (2.5 2.5 3)")?; /// assert_eq!(point_geom.to_wkt()?, "POINT Z (2.5 2.5 3)"); /// # Ok::<(), geos::Error>(()) /// ``` fn to_wkt(&self) -> GResult { WKTWriter::new()?.write(self) } /// Returns a WKT representation of the geometry with the given `precision`. It is a wrapper /// around [`WKTWriter::set_rounding_precision`]. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry, WKTWriter}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.43 2.56)")?; /// assert_eq!(point_geom.to_wkt_precision(1)?, "POINT (2.4 2.6)"); /// /// // It is a wrapper around: /// let mut writer = WKTWriter::new()?; /// writer.set_rounding_precision(1); /// assert_eq!(writer.write(&point_geom)?, "POINT (2.4 2.6)"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(not(all(doctest, feature = "tests", not(feature = "v3_10_0"))))] fn to_wkt_precision(&self, precision: u32) -> GResult { let mut writer = WKTWriter::new()?; writer.set_rounding_precision(precision); writer.write(self) } /// Returns `true` if the geometry is a ring. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let circle = Geometry::new_from_wkt("LINESTRING(0 0, 0 1, 1 1, 0 0)")?; /// assert_eq!(circle.is_ring()?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn is_ring(&self) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSisRing_r(ctx.as_raw(), self.as_raw())) }) } /// Returns `true` if `self` shares any portion of space with `other`. So if any of this is /// `true`: /// /// * `self` overlaps `other` /// * `self` touches `other` /// * `self` is within `other` /// /// Then `intersects` will return `true` as well. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT(0 0)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)")?; /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)")?; /// /// assert_eq!(geom1.intersects(&geom2)?, false); /// assert_eq!(geom1.intersects(&geom3)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn intersects(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSIntersects_r( ctx.as_raw(), self.as_raw(), other.as_raw() )) }) } /// Returns `true` if `self` and `other` have at least one interior into each other. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING(1 1,2 2)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 1,1 2)")?; /// /// assert_eq!(geom1.crosses(&geom2)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn crosses(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSCrosses_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// Returns `true` if `self` doesn't: /// /// * Overlap `other` /// * Touch `other` /// * Is within `other` /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT(0 0)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)")?; /// let geom3 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)")?; /// /// assert_eq!(geom1.disjoint(&geom2)?, true); /// assert_eq!(geom1.disjoint(&geom3)?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn disjoint(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSDisjoint_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// 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::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING(0 0, 1 1, 0 2)")?; /// let geom2 = Geometry::new_from_wkt("POINT(1 1)")?; /// /// assert_eq!(geom1.touches(&geom2)?, false); /// /// let geom2 = Geometry::new_from_wkt("POINT(0 2)")?; /// /// assert_eq!(geom1.touches(&geom2)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn touches(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSTouches_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// Returns `true` if `self` spatially overlaps `other`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT(1 0.5)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(1 0, 1 1, 3 5)")?; /// /// assert_eq!(geom1.overlaps(&geom2)?, false); /// /// let geom1 = geom1.buffer(3., 8)?; /// let geom2 = geom2.buffer(0.5, 8)?; /// /// assert_eq!(geom1.overlaps(&geom2)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn overlaps(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSOverlaps_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// Returns `true` if `self` is completely inside `other`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT(50 50)")?; /// let small_geom = geom.buffer(20., 8)?; /// let big_geom = geom.buffer(40., 8)?; /// /// assert_eq!(small_geom.within(&small_geom)?, true); /// assert_eq!(small_geom.within(&big_geom)?, true); /// assert_eq!(big_geom.within(&small_geom)?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn within(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSWithin_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// Checks if the two [`Geometry`] objects are equal. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)")?; /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// /// assert_eq!(geom1.equals(&geom2)?, false); /// assert_eq!(geom1.equals(&geom3)?, true); /// # Ok::<(), geos::Error>(()) /// ``` /// /// Note that you can also use method through the `PartialEq` trait: /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)")?; /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// /// assert!(geom1 != geom2); /// assert!(geom1 == geom3); /// # Ok::<(), geos::Error>(()) /// ``` fn equals(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSEquals_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// Checks if the two [`Geometry`] objects are exactly equal. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let geom2 = Geometry::new_from_wkt("POINT (3.8 3.8)")?; /// let geom3 = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// /// assert_eq!(geom1.equals_exact(&geom2, 0.1)?, false); /// assert_eq!(geom1.equals_exact(&geom3, 0.1)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn equals_exact(&self, other: &G, precision: f64) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSEqualsExact_r( ctx.as_raw(), self.as_raw(), other.as_raw(), precision )) }) } #[cfg(feature = "v3_12_0")] fn equals_identical(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSEqualsIdentical_r( ctx.as_raw(), self.as_raw(), other.as_raw() )) }) } /// Returns `true` if no point of `other` is outside of `self`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT (1 2)")?; /// let little_geom = geom.buffer(10., 8)?; /// let big_geom = geom.buffer(20., 8)?; /// /// assert_eq!(little_geom.covers(&big_geom)?, false); /// assert_eq!(big_geom.covers(&little_geom)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn covers(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSCovers_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// Returns `true` if no point of `self` is outside of `other`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT (1 2)")?; /// let little_geom = geom.buffer(10., 8)?; /// let big_geom = geom.buffer(20., 8)?; /// /// assert_eq!(little_geom.covered_by(&big_geom)?, true); /// assert_eq!(big_geom.covered_by(&little_geom)?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn covered_by(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSCoveredBy_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } /// Returns `true` if no points of the `other` geometry is outside the exterior of `self`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))")?; /// let geom2 = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// /// assert_eq!(geom1.contains(&geom2)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn contains(&self, other: &G) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSContains_r(ctx.as_raw(), self.as_raw(), other.as_raw())) }) } fn relate(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSRelate_r(ctx.as_raw(), self.as_raw(), other.as_raw()))?; managed_string(ptr, ctx) }) } fn relate_pattern(&self, other: &G, pattern: &str) -> GResult { with_context(|ctx| unsafe { let pattern = CString::new(pattern) .map_err(|e| Error::GenericError(format!("Conversion to CString failed: {e}")))?; predicate!(GEOSRelatePattern_r( ctx.as_raw(), self.as_raw(), other.as_raw(), pattern.as_ptr() )) }) } /// Returns a geometry which represents all points whose distance from `self` is less than or /// equal to distance. /// /// You can find nice examples about this in [postgis](https://postgis.net/docs/ST_Buffer.html) /// documentation. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT(1 3)")?; /// let buffer_geom = geom.buffer(50., 2)?; /// /// assert_eq!( /// buffer_geom.to_wkt_precision(1)?, /// "POLYGON ((51 3, 36.4 -32.4, 1 -47, -34.4 -32.4, -49 3, -34.4 38.4, 1 53, 36.4 \ /// 38.4, 51 3))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(not(all(doctest, feature = "tests", not(feature = "v3_10_0"))))] fn buffer(&self, width: f64, quadsegs: i32) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSBuffer_r( ctx.as_raw(), self.as_raw(), width, quadsegs as _ ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns a geometry which represents all points whose distance from `self` is less than or /// equal to distance. /// /// The explicit `buffer_params` argument passing is more efficient than /// the otherwise identical (except for the `single_sided` option is missing) /// [`buffer_with_style`](crate::Geom::buffer_with_style) method when the same [`BufferParams`] /// is reused. /// /// You can find nice examples and details about the [`BufferParams`] options in this /// [postgis](https://postgis.net/docs/ST_Buffer.html) documentation. /// /// # Example /// /// ``` /// use geos::{BufferParams, CapStyle, Geom, Geometry, JoinStyle}; /// /// let geom = Geometry::new_from_wkt("POINT(1 3)")?; /// let params = BufferParams::builder() /// .end_cap_style(CapStyle::Round) /// .join_style(JoinStyle::Round) /// .mitre_limit(5.0) /// .quadrant_segments(8) /// .single_sided(false) /// .build()?; /// let buffer_geom = geom.buffer_with_params(2., ¶ms)?; /// /// assert_eq!( /// buffer_geom.to_wkt_precision(1)?, /// "POLYGON ((3 3, 3 2.6, 2.8 2.2, 2.7 1.9, 2.4 1.6, 2.1 1.3, 1.8 1.2, 1.4 1, 1 1, \ /// 0.6 1, 0.2 1.2, -0.1 1.3, -0.4 1.6, -0.7 1.9, -0.8 2.2, -1 2.6, -1 3, -1 3.4, \ /// -0.8 3.8, -0.7 4.1, -0.4 4.4, -0.1 4.7, 0.2 4.8, 0.6 5, 1 5, 1.4 5, 1.8 4.8, \ /// 2.1 4.7, 2.4 4.4, 2.7 4.1, 2.8 3.8, 3 3.4, 3 3))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(not(all(doctest, feature = "tests", not(feature = "v3_10_0"))))] fn buffer_with_params(&self, width: f64, buffer_params: &BufferParams) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSBufferWithParams_r( ctx.as_raw(), self.as_raw(), buffer_params.as_raw(), width ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns a geometry which represents all points whose distance from `self` is less than or /// equal to distance. /// /// If the same parameters are used many times it's more efficient to use the /// [`buffer_with_params`](crate::Geom::buffer_with_params) operation. /// /// You can find nice examples and details about the options in this /// [postgis](https://postgis.net/docs/ST_Buffer.html) documentation. /// /// # Example /// /// ``` /// use geos::{CapStyle, Geom, Geometry, JoinStyle}; /// /// let geom = Geometry::new_from_wkt("POINT(1 3)")?; /// let buffer_geom = geom.buffer_with_style(2., 8, CapStyle::Round, JoinStyle::Round, 5.)?; /// assert_eq!( /// buffer_geom.to_wkt_precision(1)?, /// "POLYGON ((3 3, 3 2.6, 2.8 2.2, 2.7 1.9, 2.4 1.6, 2.1 1.3, 1.8 1.2, 1.4 1, 1 1, \ /// 0.6 1, 0.2 1.2, -0.1 1.3, -0.4 1.6, -0.7 1.9, -0.8 2.2, -1 2.6, -1 3, -1 3.4, \ /// -0.8 3.8, -0.7 4.1, -0.4 4.4, -0.1 4.7, 0.2 4.8, 0.6 5, 1 5, 1.4 5, 1.8 4.8, \ /// 2.1 4.7, 2.4 4.4, 2.7 4.1, 2.8 3.8, 3 3.4, 3 3))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(not(all(doctest, feature = "tests", not(feature = "v3_10_0"))))] fn buffer_with_style( &self, width: f64, quadsegs: i32, end_cap_style: CapStyle, join_style: JoinStyle, mitre_limit: f64, ) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSBufferWithStyle_r( ctx.as_raw(), self.as_raw(), width, quadsegs, end_cap_style.into(), join_style.into(), mitre_limit ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns `true` if the given geometry is empty. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_empty_polygon()?; /// assert_eq!(geom.is_empty()?, true); /// /// let geom = Geometry::new_from_wkt("POLYGON EMPTY")?; /// assert_eq!(geom.is_empty()?, true); /// /// let geom = Geometry::new_from_wkt("POINT(1 3)")?; /// assert_eq!(geom.is_empty()?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn is_empty(&self) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSisEmpty_r(ctx.as_raw(), self.as_raw())) }) } /// Returns true if the given geometry has no anomalous geometric points, such as self /// intersection or self tangency. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// assert_eq!(geom.is_simple()?, true); /// /// let geom = Geometry::new_from_wkt("LINESTRING(1 1,2 2,2 3.5,1 3,1 2,2 1)")?; /// assert_eq!(geom.is_simple()?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn is_simple(&self) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSisSimple_r(ctx.as_raw(), self.as_raw())) }) } /// Returns a geometry which represents part of `self` that doesn't intersect with `other`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING(50 100, 50 200)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(50 50, 50 150)")?; /// /// let difference_geom = geom1.difference(&geom2)?; /// /// assert_eq!(difference_geom.to_wkt()?, "LINESTRING (50 150, 50 200)"); /// # Ok::<(), geos::Error>(()) /// ``` fn difference(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSDifference_r( ctx.as_raw(), self.as_raw(), other.as_raw() ))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_9_0")] fn difference_prec(&self, other: &G, grid_size: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSDifferencePrec_r( ctx.as_raw(), self.as_raw(), other.as_raw(), grid_size ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the minimum bounding box of the given geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT(1 3)")?; /// let envelope_geom = geom.envelope()?; /// /// assert_eq!(envelope_geom.to_wkt()?, "POINT (1 3)"); /// /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 1 3)")?; /// let envelope_geom = geom.envelope()?; /// /// assert_eq!( /// envelope_geom.to_wkt()?, /// "POLYGON ((0 0, 1 0, 1 3, 0 3, 0 0))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn envelope(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSEnvelope_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns a geometry which represents the parts of `self` and `other` that don't intersect. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING(50 100, 50 200)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(50 50, 50 150)")?; /// /// let sym_diff_geom = geom1.sym_difference(&geom2)?; /// /// assert_eq!( /// sym_diff_geom.to_wkt()?, /// "MULTILINESTRING ((50 150, 50 200), (50 50, 50 100))", /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn sym_difference(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSSymDifference_r( ctx.as_raw(), self.as_raw(), other.as_raw() ))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_9_0")] fn sym_difference_prec(&self, other: &G, grid_size: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSSymDifferencePrec_r( ctx.as_raw(), self.as_raw(), other.as_raw(), grid_size ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Aggregates the given geometry with another one. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING(1 2, 3 4)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(5 6, 7 8)")?; /// /// let union_geom = geom1.union(&geom2)?; /// assert_eq!( /// union_geom.to_wkt()?, /// "MULTILINESTRING ((1 2, 3 4), (5 6, 7 8))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn union(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSUnion_r(ctx.as_raw(), self.as_raw(), other.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_9_0")] fn union_prec(&self, other: &G, grid_size: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSUnionPrec_r( ctx.as_raw(), self.as_raw(), other.as_raw(), grid_size ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the geometric center or (equivalently) the center of mass of the given geometry as /// a point. /// /// # Example /// /// ``` /// 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)")?; /// let centroid = geom.get_centroid()?; /// /// assert_eq!(centroid.to_wkt()?, "POINT (-0.5 2.625)"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_centroid(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGetCentroid_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// 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::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))")?; /// let geom2 = Geometry::new_from_wkt("POLYGON((1 1, 2 1, 2 5, 1 5, 1 1))")?; /// /// let geom = Geometry::create_multipolygon(vec![geom1, geom2])?; /// /// let mut union_geom = geom.unary_union()?; /// union_geom.normalize()?; /// /// assert_eq!( /// union_geom.to_wkt()?, /// "POLYGON ((0 0, 0 6, 10 6, 10 0, 0 0))", /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn unary_union(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSUnaryUnion_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_9_0")] fn unary_union_prec(&self, grid_size: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSUnaryUnionPrec_r(ctx.as_raw(), self.as_raw(), grid_size))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_8_0")] fn coverage_union(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSCoverageUnion_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_12_0")] fn disjoint_subset_union(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSDisjointSubsetUnion_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Create a voronoi diagram. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let input = Geometry::new_from_wkt("MULTIPOINT(2 2, 4 2)")?; /// let mut expected = Geometry::new_from_wkt( /// "GEOMETRYCOLLECTION(POLYGON((0 0, 0 4, 3 4, 3 0, 0 0)), POLYGON((6 4, 6 0, 3 0, 3 4, 6 4)))")?; /// /// let mut voronoi = input.voronoi(None::<&Geometry>, 0., false)?; /// /// expected.normalize()?; /// voronoi.normalize()?; /// /// assert_eq!(expected.equals(&voronoi)?, true); /// # Ok::<(), geos::Error>(()) /// ``` fn voronoi( &self, envelope: Option<&G>, tolerance: f64, only_edges: bool, ) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSVoronoiDiagram_r( ctx.as_raw(), self.as_raw(), envelope.map_or(std::ptr::null_mut(), AsRaw::as_raw), tolerance, only_edges.into(), ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns a geometry representing the intersection between `self` and `other`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let mut geom1 = Geometry::new_from_wkt("POINT(0 0)")?; /// let mut geom2 = Geometry::new_from_wkt("LINESTRING(2 0, 0 2)")?; /// /// let intersection_geom = geom1.intersection(&geom2)?; /// /// // No intersection. /// assert_eq!(intersection_geom.is_empty()?, true); /// /// // We slightly change the linestring so we have an intersection: /// let mut geom2 = Geometry::new_from_wkt("LINESTRING(0 0, 0 2)")?; /// /// let intersection_geom = geom1.intersection(&geom2)?; /// /// assert_eq!(intersection_geom.to_wkt()?, "POINT (0 0)"); /// # Ok::<(), geos::Error>(()) /// ``` fn intersection(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSIntersection_r( ctx.as_raw(), self.as_raw(), other.as_raw() ))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_9_0")] fn intersection_prec(&self, other: &G, grid_size: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSIntersectionPrec_r( ctx.as_raw(), self.as_raw(), other.as_raw(), grid_size ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Documentation from [postgis](https://postgis.net/docs/ST_ConvexHull.html): /// /// > 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::{Geom, Geometry}; /// /// let mut geom1 = Geometry::new_from_wkt("MULTILINESTRING((100 190,10 8), (150 10, 20 30))")?; /// let mut geom2 = Geometry::new_from_wkt("MULTIPOINT(50 5, 150 30, 50 10, 10 10)")?; /// /// let geom = geom1.union(&geom2)?; /// let convex_hull_geom = geom.convex_hull()?; /// /// assert_eq!( /// convex_hull_geom.to_wkt()?, /// "POLYGON ((50 5, 10 8, 10 10, 100 190, 150 30, 150 10, 50 5))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn convex_hull(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSConvexHull_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the closure of the combinatorial boundary of `self`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry, GeometryTypes}; /// /// let geom = Geometry::new_from_wkt("LINESTRING(1 1,0 0, -1 1)")?; /// let boundary_geom = geom.boundary()?; /// assert_eq!(boundary_geom.geometry_type()?, GeometryTypes::MultiPoint); /// assert_eq!(boundary_geom.get_num_geometries()?, 2); /// assert_eq!(boundary_geom.get_geometry_n(0)?.to_wkt()?, "POINT (1 1)"); /// assert_eq!(boundary_geom.get_geometry_n(1)?.to_wkt()?, "POINT (-1 1)"); /// # Ok::<(), geos::Error>(()) /// ``` fn boundary(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSBoundary_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns `true` if `self` has a Z coordinate. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT(1 2 3)")?; /// assert_eq!(geom.has_z()?, true); /// /// let geom = Geometry::new_from_wkt("POINT(1 2)")?; /// assert_eq!(geom.has_z()?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn has_z(&self) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSHasZ_r(ctx.as_raw(), self.as_raw())) }) } /// Returns `true` if `self` has a M coordinate. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT(1 2 3 4)")?; /// assert_eq!(geom.has_m()?, true); /// /// let geom = Geometry::new_from_wkt("POINT(1 2 3)")?; /// assert_eq!(geom.has_m()?, false); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_12_0")] fn has_m(&self) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSHasM_r(ctx.as_raw(), self.as_raw())) }) } /// Returns `true` if start and end point are coincident. /// /// Only works on `LineString`, `LinearRing`, `CircularString`, `MultiLineString` and `MultiCurve`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 1 1)")?; /// assert_eq!(geom.is_closed()?, false); /// /// let geom = Geometry::new_from_wkt("LINESTRING(0 0, 0 1, 1 1, 0 0)")?; /// assert_eq!(geom.is_closed()?, true); /// /// let geom = Geometry::new_from_wkt("MULTILINESTRING((0 0, 0 1, 1 1, 0 0),(0 0, 1 1))")?; /// assert_eq!(geom.is_closed()?, false); /// # Ok::<(), geos::Error>(()) /// ``` fn is_closed(&self) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSisClosed_r(ctx.as_raw(), self.as_raw())) }) } /// Returns the length of `self`. The unit depends on the SRID. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING(743238 2967416,743238 2967450)")?; /// /// assert_eq!(geom.length().map(|x| format!("{:.2}", x))?, "34.00",); /// # Ok::<(), geos::Error>(()) /// ``` fn length(&self) -> GResult { with_context(|ctx| unsafe { let mut length = 0.0; errcheck!(GEOSLength_r(ctx.as_raw(), self.as_raw(), &mut length))?; Ok(length) }) } /// Returns the distance between `self` and `other`. The unit depends on the SRID. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (1 2)")?; /// let geom2 = Geometry::new_from_wkt("POINT (2 2)")?; /// /// assert_eq!(geom1.distance(&geom2).map(|x| format!("{:.2}", x))?, "1.00"); /// # Ok::<(), geos::Error>(()) /// ``` fn distance(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let mut distance = 0.0; errcheck!(GEOSDistance_r( ctx.as_raw(), self.as_raw(), other.as_raw(), &mut distance ))?; Ok(distance) }) } /// Returns `true` if the distance between `self` and `other` is shorter than `distance`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (1 2)")?; /// let geom2 = Geometry::new_from_wkt("POINT (2 2)")?; /// let geom3 = Geometry::new_from_wkt("POINT (3 2)")?; /// /// assert_eq!(geom1.dwithin(&geom2, 1.0)?, true); /// assert_eq!(geom1.dwithin(&geom3, 1.0)?, false); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_10_0")] fn dwithin(&self, other: &G, distance: f64) -> GResult { with_context(|ctx| unsafe { predicate!(GEOSDistanceWithin_r( ctx.as_raw(), self.as_raw(), other.as_raw(), distance )) }) } /// Returns the indexed distance between `self` and `other`. The unit depends on the SRID. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (1 2)")?; /// let geom2 = Geometry::new_from_wkt("POINT (2 2)")?; /// /// assert_eq!( /// geom1 /// .distance_indexed(&geom2) /// .map(|x| format!("{:.2}", x))?, /// "1.00" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn distance_indexed(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let mut distance = 0.0; errcheck!(GEOSDistanceIndexed_r( ctx.as_raw(), self.as_raw(), other.as_raw(), &mut distance ))?; Ok(distance) }) } /// Returns the hausdorff distance between `self` and `other`. The unit depends on the SRID. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (1 2)")?; /// let geom2 = Geometry::new_from_wkt("POINT (2 2)")?; /// /// assert_eq!( /// geom1 /// .hausdorff_distance(&geom2) /// .map(|x| format!("{:.2}", x))?, /// "1.00" /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn hausdorff_distance(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let mut distance = 0.0; errcheck!(GEOSHausdorffDistance_r( ctx.as_raw(), self.as_raw(), other.as_raw(), &mut distance ))?; Ok(distance) }) } /// Returns the hausdorff distance between `self` and `other`. The unit depends on the SRID. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POINT (1 2)")?; /// let geom2 = Geometry::new_from_wkt("POINT (2 2)")?; /// /// assert_eq!( /// geom1 /// .hausdorff_distance_densify(&geom2, 1.) /// .map(|x| format!("{:.2}", x))?, /// "1.00" /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn hausdorff_distance_densify(&self, other: &G, distance_frac: f64) -> GResult { with_context(|ctx| unsafe { let mut distance = 0.0; errcheck!(GEOSHausdorffDistanceDensify_r( ctx.as_raw(), self.as_raw(), other.as_raw(), distance_frac, &mut distance ))?; Ok(distance) }) } /// Returns the frechet distance between `self` and `other`. The unit depends on the SRID. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING (0 0, 100 0)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING (0 0, 50 50, 100 0)")?; /// /// assert_eq!( /// geom1 /// .frechet_distance(&geom2) /// .map(|x| format!("{:.2}", x))?, /// "70.71" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn frechet_distance(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let mut distance = 0.0; errcheck!(GEOSFrechetDistance_r( ctx.as_raw(), self.as_raw(), other.as_raw(), &mut distance ))?; Ok(distance) }) } /// Returns the frechet distance between `self` and `other`. The unit depends on the SRID. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING (0 0, 100 0)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING (0 0, 50 50, 100 0)")?; /// /// assert_eq!( /// geom1 /// .frechet_distance_densify(&geom2, 1.) /// .map(|x| format!("{:.2}", x))?, /// "70.71" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn frechet_distance_densify(&self, other: &G, distance_frac: f64) -> GResult { with_context(|ctx| unsafe { let mut distance = 0.0; errcheck!(GEOSFrechetDistanceDensify_r( ctx.as_raw(), self.as_raw(), other.as_raw(), distance_frac, &mut distance, ))?; Ok(distance) }) } /// Returns the length of the given geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4, 5 6)")?; /// /// assert_eq!(geom.get_length().map(|x| format!("{:.2}", x))?, "5.66"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_length(&self) -> GResult { with_context(|ctx| unsafe { let mut length = 0.0; errcheck!(GEOSGeomGetLength_r( ctx.as_raw(), self.as_raw(), &mut length ))?; Ok(length) }) } /// Documentation from [postgis](https://postgis.net/docs/ST_Snap.html): /// /// > 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::{Geom, 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)))", /// )?; /// let geom2 = Geometry::new_from_wkt("LINESTRING(5 107, 54 84, 101 100)")?; /// /// let distance = geom1.distance(&geom2)?; /// let snap_geom = geom1.snap(&geom2, distance * 1.25)?; /// /// assert_eq!( /// snap_geom.to_wkt()?, /// "MULTIPOLYGON (((5 107, 26 200, 126 200, 126 125, 101 100, 54 84, 5 107), \ /// (51 150, 101 150, 76 175, 51 150)), \ /// ((151 100, 151 200, 176 175, 151 100)))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn snap(&self, other: &G, tolerance: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSSnap_r( ctx.as_raw(), self.as_raw(), other.as_raw(), tolerance, ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns unique points of `self`. fn extract_unique_points(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_extractUniquePoints_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } fn nearest_points(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSNearestPoints_r( ctx.as_raw(), self.as_raw(), other.as_raw(), ))?; let mut size = 0; errcheck!(GEOSCoordSeq_getSize_r( ctx.as_raw(), ptr.as_ptr(), &mut size ))?; #[cfg(not(feature = "v3_14_0"))] { let mut dims = 0; errcheck!(GEOSCoordSeq_getDimensions_r( ctx.as_raw(), ptr.as_ptr(), &mut dims ))?; Ok(CoordSeq::new_from_raw(ptr, size, dims.try_into()?)) } #[cfg(feature = "v3_14_0")] { let has_z = predicate!(GEOSCoordSeq_hasZ_r(ctx.as_raw(), ptr.as_ptr()))?; let has_m = predicate!(GEOSCoordSeq_hasM_r(ctx.as_raw(), ptr.as_ptr()))?; Ok(CoordSeq::new_from_raw( ptr, size, CoordType::try_from((has_z, has_m))?, )) } }) } /// Returns the X position. The given `Geometry` must be a `Point`, otherwise it'll fail. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (1.5 2.5 3.5)")?; /// assert_eq!(point_geom.get_x()?, 1.5); /// # Ok::<(), geos::Error>(()) /// ``` fn get_x(&self) -> GResult { with_context(|ctx| unsafe { let mut x = 0.; errcheck!(GEOSGeomGetX_r(ctx.as_raw(), self.as_raw(), &mut x))?; Ok(x) }) } /// Returns the Y position. The given `Geometry` must be a `Point`, otherwise it'll fail. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (1.5 2.5 3.5)")?; /// assert_eq!(point_geom.get_y()?, 2.5); /// # Ok::<(), geos::Error>(()) /// ``` fn get_y(&self) -> GResult { with_context(|ctx| unsafe { let mut x = 0.; errcheck!(GEOSGeomGetY_r(ctx.as_raw(), self.as_raw(), &mut x))?; Ok(x) }) } /// 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::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)")?; /// assert_eq!(point_geom.get_z()?, 4.0); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn get_z(&self) -> GResult { with_context(|ctx| unsafe { let mut x = 0.; errcheck!(GEOSGeomGetZ_r(ctx.as_raw(), self.as_raw(), &mut x))?; Ok(x) }) } /// Returns the M position. The given `Geometry` must be a `Point`, otherwise it'll fail. /// /// Available using the `v3_12_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0 5.0)")?; /// assert_eq!(point_geom.get_m()?, 5.0); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_12_0")] fn get_m(&self) -> GResult { with_context(|ctx| unsafe { let mut m = 0.; errcheck!(GEOSGeomGetM_r(ctx.as_raw(), self.as_raw(), &mut m))?; Ok(m) }) } /// Returns the nth point of the given geometry. /// /// The given `Geometry` must be a `LineString`, `LinearRing` or `CircularString` otherwise it'll fail. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4, 5 6)")?; /// let nth_point = geom.get_point_n(1)?; /// /// assert_eq!(nth_point.to_wkt()?, "POINT (3 4)"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_point_n(&self, n: usize) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomGetPointN_r(ctx.as_raw(), self.as_raw(), n as _))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the start point of `self`. /// /// The given `Geometry` must be a `LineString`, `LinearRing` or `CircularString` otherwise it'll fail. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)")?; /// let start_point = geom.get_start_point()?; /// /// assert_eq!(start_point.to_wkt()?, "POINT (1 2)"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_start_point(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomGetStartPoint_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the end point of `self`. /// /// The given `Geometry` must be a `LineString`, `LinearRing` or `CircularString` otherwise it'll fail. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)")?; /// let end_point = geom.get_end_point()?; /// /// assert_eq!(end_point.to_wkt()?, "POINT (3 4)"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_end_point(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomGetEndPoint_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the number of points of `self`. /// /// The given `Geometry` must be a `LineString`, `LinearRing` or `CircularString` otherwise it'll fail. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING (1 2, 3 4)")?; /// /// assert_eq!(geom.get_num_points()?, 2); /// # Ok::<(), geos::Error>(()) /// ``` fn get_num_points(&self) -> GResult { with_context(|ctx| unsafe { let ret = errcheck!(-1, GEOSGeomGetNumPoints_r(ctx.as_raw(), self.as_raw()))?; Ok(ret as _) }) } /// Returns the number of interior rings. /// /// # 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))", /// )?; /// /// assert_eq!(geom.get_num_interior_rings()?, 2); /// # Ok::<(), geos::Error>(()) /// ``` fn get_num_interior_rings(&self) -> GResult { with_context(|ctx| unsafe { let ret = errcheck!(-1, GEOSGetNumInteriorRings_r(ctx.as_raw(), self.as_raw()))?; Ok(ret as _) }) } /// Returns the number of coordinates inside `self`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))")?; /// /// assert_eq!(geom.get_num_coordinates()?, 5); /// # Ok::<(), geos::Error>(()) /// ``` fn get_num_coordinates(&self) -> GResult { with_context(|ctx| unsafe { let ret = errcheck!(-1, GEOSGetNumCoordinates_r(ctx.as_raw(), self.as_raw()))?; Ok(ret as _) }) } /// Returns the number of dimensions used in `self`. /// /// # Example /// /// ``` /// use geos::{DimensionType, Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))")?; /// /// assert_eq!(geom.get_dimension()?, DimensionType::Surface); /// # Ok::<(), geos::Error>(()) /// ``` fn get_dimension(&self) -> GResult { with_context(|ctx| unsafe { // Need to skip errcheck as 0 is a valid return values // TODO: file a bug report to GEOS let ret = GEOSGeom_getDimensions_r(ctx.as_raw(), self.as_raw()); DimensionType::try_from(ret) }) } /// Return in which coordinate dimension the geometry is. /// /// # Example /// /// ``` /// use geos::{CoordDimensions, Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)")?; /// assert_eq!( /// point_geom.get_coordinate_dimension()?, /// CoordDimensions::ThreeD /// ); /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 4.0)")?; /// assert_eq!( /// point_geom.get_coordinate_dimension()?, /// CoordDimensions::TwoD /// ); /// # Ok::<(), geos::Error>(()) /// ``` fn get_coordinate_dimension(&self) -> GResult { with_context(|ctx| unsafe { let ret = errcheck!(GEOSGeom_getCoordinateDimension_r( ctx.as_raw(), self.as_raw(), ))?; CoordDimensions::try_from(ret) }) } /// Return the coordinate type of the geometry. /// /// # Example /// /// ``` /// use geos::{CoordType, Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT M (2.5 2.5 4.0)")?; /// assert_eq!(point_geom.get_coordinate_type()?, CoordType::XYM); /// /// let point_geom = Geometry::new_from_wkt("POINT Z (2.5 2.5 4.0)")?; /// assert_eq!(point_geom.get_coordinate_type()?, CoordType::XYZ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(any(feature = "v3_12_0", feature = "dox"))] fn get_coordinate_type(&self) -> GResult { with_context(|ctx| unsafe { let has_z = predicate!(GEOSHasZ_r(ctx.as_raw(), self.as_raw()))?; let has_m = predicate!(GEOSHasM_r(ctx.as_raw(), self.as_raw()))?; (has_z, has_m).try_into() }) } /// This function attempts to return a valid representation of `self`. /// /// Available using the `v3_8_0` feature. #[cfg(feature = "v3_8_0")] fn make_valid(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSMakeValid_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Attempts to return a valid representation of `self` using the provided parameters. /// /// This allows control over the validation method and whether to preserve collapsed geometries. /// /// Available using the `v3_10_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry, MakeValidMethod, MakeValidParams}; /// /// // Bow-tie polygon (self-intersecting, invalid) /// let geom = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 0 1, 1 0, 0 0))")?; /// /// assert_eq!(geom.is_valid()?, false); /// /// let params = MakeValidParams::builder() /// .method(MakeValidMethod::Structure) /// .keep_collapsed(false) /// .build()?; /// /// let valid_geom = geom.make_valid_with_params(¶ms)?; /// /// assert_eq!(valid_geom.is_valid()?, true); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_10_0")] fn make_valid_with_params(&self, params: &MakeValidParams) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSMakeValidWithParams_r( ctx.as_raw(), self.as_raw(), params.as_raw(), ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the number of geometries. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = /// Geometry::new_from_wkt("LINESTRING(77.29 29.07,77.42 29.26,77.27 29.31,77.29 29.07)")?; /// assert_eq!(geom.get_num_geometries()?, 1); /// /// 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)))", /// )?; /// assert_eq!(geom.get_num_geometries()?, 3); /// # Ok::<(), geos::Error>(()) /// ``` fn get_num_geometries(&self) -> GResult { with_context(|ctx| unsafe { let ret = errcheck!(-1, GEOSGetNumGeometries_r(ctx.as_raw(), self.as_raw()))?; Ok(ret as _) }) } /// Get SRID of `self`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let mut point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)")?; /// point_geom.set_srid(4326); /// assert_eq!(point_geom.get_srid()?, 4326); /// # Ok::<(), geos::Error>(()) /// ``` fn get_srid(&self) -> GResult { with_context(|ctx| unsafe { // No need to wrap this one, as 0 is a valid SRID let ret = GEOSGetSRID_r(ctx.as_raw(), self.as_raw()); Ok(ret as _) }) } /// Returns the precision of `self`. /// /// Available using the `v3_6_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)")?; /// assert_eq!( /// point_geom.get_precision().map(|x| format!("{:.2}", x))?, /// "0.00" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_6_0")] fn get_precision(&self) -> GResult { with_context(|ctx| unsafe { errcheck!(-1.0, GEOSGeom_getPrecision_r(ctx.as_raw(), self.as_raw())) }) } /// Returns the precision of `self`. /// /// Available using the `v3_6_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry, Precision}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)")?; /// /// point_geom.set_precision(1., Precision::KeepCollapsed); /// assert_eq!( /// point_geom.get_precision().map(|x| format!("{:.2}", x))?, /// "0.00" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_6_0")] fn set_precision(&self, grid_size: f64, flags: Precision) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_setPrecision_r( ctx.as_raw(), self.as_raw(), grid_size, flags.into(), ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the biggest X of the geometry. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)")?; /// assert_eq!(line.get_x_max()?, 5.); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn get_x_max(&self) -> GResult { with_context(|ctx| unsafe { let mut value = 0.0; errcheck!(GEOSGeom_getXMax_r(ctx.as_raw(), self.as_raw(), &mut value))?; Ok(value) }) } /// Returns the smallest X of the geometry. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)")?; /// assert_eq!(line.get_x_min()?, 1.); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn get_x_min(&self) -> GResult { with_context(|ctx| unsafe { let mut value = 0.0; errcheck!(GEOSGeom_getXMin_r(ctx.as_raw(), self.as_raw(), &mut value))?; Ok(value) }) } /// Returns the biggest Y of the geometry. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)")?; /// assert_eq!(line.get_y_max()?, 6.); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn get_y_max(&self) -> GResult { with_context(|ctx| unsafe { let mut value = 0.0; errcheck!(GEOSGeom_getYMax_r(ctx.as_raw(), self.as_raw(), &mut value))?; Ok(value) }) } /// Returns the smallest Y of the geometry. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let line = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)")?; /// assert_eq!(line.get_y_min()?, 3.); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] fn get_y_min(&self) -> GResult { with_context(|ctx| unsafe { let mut value = 0.0; errcheck!(GEOSGeom_getYMin_r(ctx.as_raw(), self.as_raw(), &mut value))?; Ok(value) }) } /// 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. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("LINESTRING(1 3 4, 5 6 7)")?; /// assert_eq!( /// geom.minimum_clearance().map(|x| format!("{:.8}", x))?, /// "5.00000000" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_6_0")] fn minimum_clearance(&self) -> GResult { with_context(|ctx| unsafe { let mut value = 0.0; predicate!(GEOSMinimumClearance_r( ctx.as_raw(), self.as_raw(), &mut value ))?; Ok(value) }) } /// Returns the two-point `LineString` spanning of `self`'s minimum clearance. /// /// Available using the `v3_6_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POLYGON ((0 0, 1 0, 1 1, 0.5 3.2e-4, 0 0))")?; /// let line = geom.minimum_clearance_line()?; /// assert_eq!(line.to_wkt()?, "LINESTRING (0.5 0.00032, 0.5 0)"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_6_0")] fn minimum_clearance_line(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSMinimumClearanceLine_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the minimum rotated rectangle inside of `self`. /// /// Available using the `v3_6_0` feature. #[cfg(feature = "v3_6_0")] fn minimum_rotated_rectangle(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSMinimumRotatedRectangle_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the minimum width inside of `self`. /// /// Available using the `v3_6_0` feature. #[cfg(feature = "v3_6_0")] fn minimum_width(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSMinimumWidth_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns a [delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) /// around the vertices of `self`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POLYGON((175 150, 20 40, 50 60, 125 100, 175 150))")?; /// let geom2 = Geometry::new_from_wkt("POINT(110 170)")?; /// let geom2 = geom2.buffer(20., 8)?; /// /// let geom = geom1.union(&geom2)?; /// /// let final_geom = geom.delaunay_triangulation(0.001, false)?; /// # Ok::<(), geos::Error>(()) /// ``` fn delaunay_triangulation(&self, tolerance: f64, only_edges: bool) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSDelaunayTriangulation_r( ctx.as_raw(), self.as_raw(), tolerance, only_edges.into(), ))?; Ok(Geometry::new_from_raw(ptr)) }) } fn interpolate(&self, d: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSInterpolate_r(ctx.as_raw(), self.as_raw(), d))?; Ok(Geometry::new_from_raw(ptr)) }) } fn interpolate_normalized(&self, d: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSInterpolateNormalized_r(ctx.as_raw(), self.as_raw(), d))?; Ok(Geometry::new_from_raw(ptr)) }) } fn project(&self, p: &G) -> GResult { with_context(|ctx| unsafe { errcheck!(-1.0, GEOSProject_r(ctx.as_raw(), self.as_raw(), p.as_raw())) }) } fn project_normalized(&self, p: &G) -> GResult { with_context(|ctx| unsafe { errcheck!( -1.0, GEOSProjectNormalized_r(ctx.as_raw(), self.as_raw(), p.as_raw()) ) }) } fn node(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSNode_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// 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: /// /// - `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 { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSOffsetCurve_r( ctx.as_raw(), self.as_raw(), width, quadrant_segments, join_style.into(), mitre_limit, ))?; Ok(Geometry::new_from_raw(ptr)) }) } fn point_on_surface(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSPointOnSurface_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// 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. #[allow(clippy::type_complexity)] fn polygonize_full( &self, ) -> GResult<( Geometry, 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(); with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSPolygonize_full_r( ctx.as_raw(), self.as_raw(), &mut cuts, &mut dangles, &mut invalids, ))?; let geom = Geometry::new_from_raw(ptr); let cuts = NonNull::new(cuts).map(Geometry::new_from_raw); let dangles = NonNull::new(dangles).map(Geometry::new_from_raw); let invalids = NonNull::new(invalids).map(Geometry::new_from_raw); Ok((geom, cuts, dangles, invalids)) }) } fn shared_paths(&self, other: &G) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSSharedPaths_r( ctx.as_raw(), self.as_raw(), other.as_raw() ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Converts a [`Geometry`] to the HEX format. For more control over the generated output, /// use the [`WKBWriter`](crate::WKBWriter) type. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let hex_buf = point_geom.to_hex()?; /// # Ok::<(), geos::Error>(()) /// ``` fn to_hex(&self) -> GResult> { let mut size = 0; with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomToHEX_buf_r(ctx.as_raw(), self.as_raw(), &mut size))?; Ok(managed_vec(ptr, size, ctx)) }) } /// Converts a [`Geometry`] to the WKB format. For more control over the generated output, /// use the [`WKBWriter`](crate::WKBWriter) type. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let wkb_buf = point_geom.to_wkb()?; /// # Ok::<(), geos::Error>(()) /// ``` fn to_wkb(&self) -> GResult> { let mut size = 0; with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomToWKB_buf_r(ctx.as_raw(), self.as_raw(), &mut size))?; Ok(managed_vec(ptr, size, ctx)) }) } /// Converts a [`Geometry`] to the `GeoJSON` format. For more control over the generated output, /// use the [`GeoJSONWriter`](crate::GeoJSONWriter) type. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// assert_eq!( /// point_geom.to_geojson()?, /// r#"{"type":"Point","coordinates":[2.5,2.5]}"#, /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_10_0")] fn to_geojson(&self) -> GResult { GeoJSONWriter::new()?.write(self) } #[cfg(feature = "v3_10_0")] fn to_geojson_formatted(&self, indent: i32) -> GResult { GeoJSONWriter::new()?.write_formatted(self, indent) } /// Creates a new [`PreparedGeometry`] from the current `Geometry`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let prepared_geom = point_geom.to_prepared_geom()?; /// # Ok::<(), geos::Error>(()) /// ``` fn to_prepared_geom(&self) -> GResult> { PreparedGeometry::new(self) } fn clone(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_clone_r(ctx.as_raw(), self.as_raw()))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Returns the 1-based nth geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("MULTIPOINT(1 1, 2 2, 3 3, 4 4)")?; /// let point_nb3 = geom.get_geometry_n(2)?; /// assert_eq!(point_nb3.to_wkt()?, "POINT (3 3)"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_geometry_n(&self, n: usize) -> GResult> { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGetGeometryN_r(ctx.as_raw(), self.as_raw(), n as _))?; Ok(ConstGeometry::new_from_raw(ptr)) }) } /// Returns the nth interior ring. /// /// # 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))", /// )?; /// let interior = geom.get_interior_ring_n(0)?; /// assert_eq!(interior.to_wkt()?, "LINEARRING (1 1, 2 1, 2 5, 1 5, 1 1)"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_interior_ring_n(&self, n: usize) -> GResult> { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGetInteriorRingN_r(ctx.as_raw(), self.as_raw(), n as _))?; Ok(ConstGeometry::new_from_raw(ptr)) }) } /// Returns the exterior ring. /// /// # Example /// /// ``` /// 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))")?; /// /// let exterior = point_geom.get_exterior_ring()?; /// assert_eq!(exterior.to_wkt()?, "LINEARRING (0 0, 10 0, 10 6, 0 6, 0 0)"); /// # Ok::<(), geos::Error>(()) /// ``` fn get_exterior_ring(&self) -> GResult> { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGetExteriorRing_r(ctx.as_raw(), self.as_raw()))?; Ok(ConstGeometry::new_from_raw(ptr)) }) } /// Apply XY coordinate transform callback to all coordinates in a copy of input geometry. /// If the callback returns an error, the function will return an Err. /// Z and M values, if present, are not modified by this function. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt("POINT (1.5 2.5)")?; /// let transformed = geom.transform_xy(|x, y| Ok::<_, geos::Error>((x + 1.0, y + 2.0)))?; /// assert_eq!(transformed.to_wkt()?, "POINT (2.5 4.5)"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_11_0")] fn transform_xy Result<(f64, f64), E>, E: From>( &self, on_transform_point: F, ) -> Result { let mut trampoline = Trampoline::new(on_transform_point); with_context(|ctx| unsafe { let ptr = GEOSGeom_transformXY_r( ctx.as_raw(), self.as_raw(), trampoline.get_xy_callback(), trampoline.as_mut_void(), ); if let Some(ptr) = NonNull::new(ptr) { Ok(Geometry::new_from_raw(ptr)) } else if let Some(err) = trampoline.err { Err(err) } else { Err(Error::GeosError(("GEOSGeom_transformXY_r", ctx.get_last_error())).into()) } }) } #[cfg(feature = "v3_14_0")] fn transform_xyz Result<(f64, f64, f64), E>, E: From>( &self, on_transform_point: F, ) -> Result { let mut trampoline = Trampoline::new(on_transform_point); with_context(|ctx| unsafe { let ptr = GEOSGeom_transformXYZ_r( ctx.as_raw(), self.as_raw(), trampoline.get_xyz_callback(), trampoline.as_mut_void(), ); if let Some(ptr) = NonNull::new(ptr) { Ok(Geometry::new_from_raw(ptr)) } else if let Some(err) = trampoline.err { Err(err) } else { Err(Error::GeosError(("GEOSGeom_transformXY_r", ctx.get_last_error())).into()) } }) } fn clip_by_rect(&self, xmin: f64, ymin: f64, xmax: f64, ymax: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSClipByRect_r( ctx.as_raw(), self.as_raw(), xmin, ymin, xmax, ymax ))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_10_0")] fn densify(&self, tolerance: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSDensify_r(ctx.as_raw(), self.as_raw(), tolerance))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_11_0")] fn remove_repeated_points(&self, tolerance: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSRemoveRepeatedPoints_r( ctx.as_raw(), self.as_raw(), tolerance ))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_11_0")] fn concave_hull(&self, ratio: f64, allow_holes: bool) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSConcaveHull_r( ctx.as_raw(), self.as_raw(), ratio, allow_holes.into() ))?; Ok(Geometry::new_from_raw(ptr)) }) } #[cfg(feature = "v3_11_0")] fn get_extent(&self) -> GResult> { with_context(|ctx| unsafe { let mut xmin: f64 = 0.; let mut ymin: f64 = 0.; let mut xmax: f64 = 0.; let mut ymax: f64 = 0.; errcheck!(GEOSGeom_getExtent_r( ctx.as_raw(), self.as_raw(), &mut xmin, &mut ymin, &mut xmax, &mut ymax ))?; Ok(vec![xmin, ymin, xmax, ymax]) }) } #[cfg(feature = "v3_12_0")] fn line_substring(&self, start_fraction: f64, end_fraction: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSLineSubstring_r( ctx.as_raw(), self.as_raw(), start_fraction, end_fraction as _, ))?; Ok(Geometry::new_from_raw(ptr)) }) } /// Compute the Maximum Inscribed Circle (MIC) of a polygonal geometry, /// which is the largest circle that fits inside the geometry. /// This is useful to find the most "central" point of a geometry (also called /// the "pole of inaccessibility"), and it is sometimes used /// in cartography for label placement. /// The algorithm is stopped when the search area is smaller than the given tolerance. /// /// Returns a two-point linestring, where the first point is the center of the /// inscribed circle and the second point is on the boundary of the inscribed circle. /// /// # Example /// ``` /// use geos::{Geom, Geometry}; /// /// let poly = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))")?; /// let mic = poly.maximum_inscribed_circle(1.)?; /// /// let center = mic.get_start_point()?; /// assert_eq!( /// center.to_wkt_precision(0)?, /// "POINT (5 5)" /// ); /// /// let radius = mic.length()?; /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_9_0")] fn maximum_inscribed_circle(&self, tolerance: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSMaximumInscribedCircle_r( ctx.as_raw(), self.as_raw(), tolerance ))?; Ok(Geometry::new_from_raw(ptr)) }) } } /// Trampoline function helper function to get the trampoline function from the closure. #[cfg(feature = "v3_11_0")] struct Trampoline> { closure: F, err: Option, } #[cfg(feature = "v3_11_0")] impl> Trampoline { const fn new(closure: F) -> Self { Self { closure, err: None } } fn as_mut_void(&mut self) -> *mut libc::c_void { self as *mut _ as *mut _ } } #[cfg(feature = "v3_11_0")] impl Result<(f64, f64), E>, E: From> Trampoline { fn get_xy_callback(&self) -> GEOSTransformXYCallback { unsafe extern "C" fn transform_trampoline( x: *mut libc::c_double, y: *mut libc::c_double, user_data: *mut libc::c_void, ) -> libc::c_int where F: FnMut(f64, f64) -> Result<(f64, f64), E>, E: From, { let trampoline = &mut *user_data.cast::>(); let closure = &mut trampoline.closure; match closure(*x, *y) { Ok((new_x, new_y)) => { *x = new_x; *y = new_y; 1 } Err(error) => { trampoline.err = Some(error); 0 } } } Some(transform_trampoline::) } } #[cfg(feature = "v3_14_0")] impl Result<(f64, f64, f64), E>, E: From> Trampoline { fn get_xyz_callback(&self) -> GEOSTransformXYZCallback { unsafe extern "C" fn transform_trampoline( x: *mut libc::c_double, y: *mut libc::c_double, z: *mut libc::c_double, user_data: *mut libc::c_void, ) -> libc::c_int where F: FnMut(f64, f64, f64) -> Result<(f64, f64, f64), E>, E: From, { let trampoline = &mut *user_data.cast::>(); let closure = &mut trampoline.closure; match closure(*x, *y, *z) { Ok((new_x, new_y, new_z)) => { *x = new_x; *y = new_y; *z = new_z; 1 } Err(error) => { trampoline.err = Some(error); 0 } } } Some(transform_trampoline::) } } impl ConstGeometry<'_> { pub(crate) const fn new_from_raw(ptr: NonNull) -> Self { ConstGeometry { ptr, phantom: PhantomData, } } } impl Geometry { pub(crate) const fn new_from_raw(ptr: NonNull) -> Self { Self { ptr } } /// Creates a `Geometry` from the WKT format. /// /// # Example /// /// ``` /// use geos::Geometry; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// # Ok::<(), geos::Error>(()) /// ``` pub fn new_from_wkt(wkt: &str) -> GResult { with_context(|ctx| match CString::new(wkt) { Ok(c_str) => unsafe { let reader = nullcheck!(GEOSWKTReader_create_r(ctx.as_raw()))?.as_ptr(); let ptr = nullcheck!(GEOSWKTReader_read_r(ctx.as_raw(), reader, c_str.as_ptr()))?; GEOSWKTReader_destroy_r(ctx.as_raw(), reader); Ok(Self::new_from_raw(ptr)) }, Err(e) => Err(Error::GenericError(format!( "Conversion to CString failed with {e}", ))), }) } /// Create a new [`Geometry`] from the HEX format. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let hex_buf = point_geom.to_hex()?; /// /// // The interesting part is here: /// let new_geom = Geometry::new_from_hex(hex_buf.as_ref())?; /// assert_eq!(point_geom.equals(&new_geom)?, true); /// # Ok::<(), geos::Error>(()) /// ``` pub fn new_from_hex(hex: &[u8]) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomFromHEX_buf_r(ctx.as_raw(), hex.as_ptr(), hex.len()))?; Ok(Self::new_from_raw(ptr)) }) } /// Create a new [`Geometry`] from the WKB format. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)")?; /// let wkb_buf = point_geom.to_wkb()?; /// /// // The interesting part is here: /// let new_geom = Geometry::new_from_wkb(wkb_buf.as_ref())?; /// assert_eq!(point_geom.equals(&new_geom)?, true); /// # Ok::<(), geos::Error>(()) /// ``` pub fn new_from_wkb(wkb: &[u8]) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeomFromWKB_buf_r(ctx.as_raw(), wkb.as_ptr(), wkb.len()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates a `Geometry` from the `GeoJSON` format. /// /// # Example /// /// ``` /// use geos::Geometry; /// /// let point_geom = Geometry::new_from_geojson(r#"{"type": "Point", "coordinates": [2.5, 2.5]}"#)?; /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_10_0")] pub fn new_from_geojson(wkt: &str) -> GResult { with_context(|ctx| match CString::new(wkt) { Ok(c_str) => unsafe { let reader = nullcheck!(GEOSGeoJSONReader_create_r(ctx.as_raw()))?; let ptr = nullcheck!(GEOSGeoJSONReader_readGeometry_r( ctx.as_raw(), reader.as_ptr(), c_str.as_ptr() ))?; GEOSGeoJSONReader_destroy_r(ctx.as_raw(), reader.as_ptr()); Ok(Self::new_from_raw(ptr)) }, Err(e) => Err(Error::GenericError(format!( "Conversion to CString failed: {e}", ))), }) } /// 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::{Geom, Geometry}; /// /// let geom = Geometry::new_from_wkt( /// "GEOMETRYCOLLECTION(POLYGON((0 0, 4 0, 4 4, 0 4, 0 0)), POLYGON((1 1, 1 3, 3 3, 3 1, 1 1)))", /// )?; /// /// let build_area_geom = geom.build_area()?; /// // Square polygon with square hole. /// assert_eq!( /// build_area_geom.to_wkt()?, /// "POLYGON ((0 0, 0 4, 4 4, 4 0, 0 0), (1 1, 3 1, 3 3, 1 3, 1 1))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_8_0")] pub fn build_area(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSBuildArea_r(ctx.as_raw(), self.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Description from [postgis](https://postgis.net/docs/ST_Polygonize.html): /// /// > Creates a GeometryCollection containing possible polygons formed from the constituent /// > linework of a set of geometries. /// /// # Example: /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt( /// "POLYGON((-71.040888 42.285678,\ /// -71.040943 42.2856,\ /// -71.04096 42.285752,\ /// -71.040888 42.285678))", /// )?; /// let geom2 = Geometry::new_from_wkt( /// "POLYGON((-71.17166 42.353675,\ /// -71.172026 42.354044,\ /// -71.17238 42.354358,\ /// -71.17179 42.354971,\ /// -71.170511 42.354855,\ /// -71.17112 42.354238,\ /// -71.17166 42.353675))", /// )?; /// /// let polygonized = Geometry::polygonize(&[geom1, geom2])?; /// assert_eq!( /// polygonized.to_wkt()?, /// "GEOMETRYCOLLECTION (POLYGON ((-71.040888 42.285678, \ /// -71.040943 42.2856, \ /// -71.04096 42.285752, \ /// -71.040888 42.285678)), \ /// POLYGON ((-71.17166 42.353675, \ /// -71.172026 42.354044, \ /// -71.17238 42.354358, \ /// -71.17179 42.354971, \ /// -71.170511 42.354855, \ /// -71.17112 42.354238, \ /// -71.17166 42.353675)))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` pub fn polygonize>(geometries: &[T]) -> GResult { with_context(|ctx| unsafe { let geoms = geometries .iter() .map(|g| g.borrow().as_raw().cast()) .collect::>(); let ptr = nullcheck!(GEOSPolygonize_r( ctx.as_raw(), geoms.as_ptr(), geoms.len() as _ ))?; Ok(Self::new_from_raw(ptr)) }) } pub fn polygonizer_get_cut_edges>(&self, geometries: &[T]) -> GResult { with_context(|ctx| unsafe { let geoms = geometries .iter() .map(|g| g.borrow().as_raw().cast()) .collect::>(); let ptr = nullcheck!(GEOSPolygonizer_getCutEdges_r( ctx.as_raw(), geoms.as_ptr(), geoms.len() as _ ))?; Ok(Self::new_from_raw(ptr)) }) } /// 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::{Geom, Geometry}; /// /// let lines = Geometry::new_from_wkt( /// "MULTILINESTRING((-29 -27,-30 -29.7,-36 -31,-45 -33), (-45 -33,-46 -32))", /// )?; /// let lines_merged = lines.line_merge()?; /// assert_eq!( /// lines_merged.to_wkt()?, /// "LINESTRING (-29 -27, -30 -29.7, -36 -31, -45 -33, -46 -32)", /// ); /// # Ok::<(), geos::Error>(()) /// ``` pub fn line_merge(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSLineMerge_r(ctx.as_raw(), self.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } #[cfg(feature = "v3_11_0")] pub fn line_merge_directed(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSLineMergeDirected_r(ctx.as_raw(), self.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Reverses the order of the vertexes. /// /// Available using the `v3_7_0` feature. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let line = Geometry::new_from_wkt("LINESTRING(1 10,1 2)")?; /// let reversed_line = line.reverse()?; /// /// assert_eq!(reversed_line.to_wkt()?, "LINESTRING (1 2, 1 10)",); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_7_0")] pub fn reverse(&self) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSReverse_r(ctx.as_raw(), self.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Returns a simplified version of the given geometry. pub fn simplify(&self, tolerance: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSSimplify_r(ctx.as_raw(), self.as_raw(), tolerance))?; Ok(Self::new_from_raw(ptr)) }) } /// Returns a simplified version of the given geometry. It will avoid creating invalid derived /// geometries. pub fn topology_preserve_simplify(&self, tolerance: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSTopologyPreserveSimplify_r( ctx.as_raw(), self.as_raw(), tolerance ))?; Ok(Self::new_from_raw(ptr)) }) } /// Set SRID of `self`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let mut point_geom = Geometry::new_from_wkt("POINT (2.5 2.5 4.0)")?; /// point_geom.set_srid(4326); /// assert_eq!(point_geom.get_srid()?, 4326); /// # Ok::<(), geos::Error>(()) /// ``` pub fn set_srid(&mut self, srid: libc::c_int) { with_context(|ctx| unsafe { GEOSSetSRID_r(ctx.as_raw(), self.as_raw_mut(), srid) }); } /// 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::{Geom, Geometry}; /// /// let mut geom = Geometry::new_from_wkt( /// "GEOMETRYCOLLECTION(POINT(2 3), MULTILINESTRING((0 0, 1 1),(2 2, 3 3)))", /// )?; /// /// geom.normalize()?; /// /// assert_eq!( /// geom.to_wkt()?, /// "GEOMETRYCOLLECTION (MULTILINESTRING ((2 2, 3 3), (0 0, 1 1)), POINT (2 3))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` pub fn normalize(&mut self) -> GResult<()> { with_context(|ctx| unsafe { errcheck!(-1, GEOSNormalize_r(ctx.as_raw(), self.as_raw_mut()))?; Ok(()) }) } /// Creates an empty polygon geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_empty_polygon()?; /// /// assert_eq!(geom.to_wkt()?, "POLYGON EMPTY"); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_empty_polygon() -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createEmptyPolygon_r(ctx.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates an empty point geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_empty_point()?; /// /// assert_eq!(geom.to_wkt()?, "POINT EMPTY"); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_empty_point() -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createEmptyPoint_r(ctx.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates an empty line string geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_empty_line_string()?; /// /// assert_eq!(geom.to_wkt()?, "LINESTRING EMPTY"); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_empty_line_string() -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createEmptyLineString_r(ctx.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates an empty circular string geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_empty_circular_string()?; /// /// assert_eq!(geom.to_wkt()?, "CIRCULARSTRING EMPTY"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_13_0")] pub fn create_empty_circular_string() -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createEmptyCircularString_r(ctx.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates an empty compound curve geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_empty_compound_curve()?; /// /// assert_eq!(geom.to_wkt()?, "COMPOUNDCURVE EMPTY"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_13_0")] pub fn create_empty_compound_curve() -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createEmptyCompoundCurve_r(ctx.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates an empty curve polygon geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_empty_curve_polygon()?; /// /// assert_eq!(geom.to_wkt()?, "CURVEPOLYGON EMPTY"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_13_0")] pub fn create_empty_curve_polygon() -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createEmptyCurvePolygon_r(ctx.as_raw()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates an empty collection. /// /// The `type_` must be one of: /// /// * [`GeometryTypes::GeometryCollection`] /// * [`GeometryTypes::MultiPoint`] /// * [`GeometryTypes::MultiLineString`] /// * [`GeometryTypes::MultiPolygon`] /// * [`GeometryTypes::MultiCurve`] /// * [`GeometryTypes::MultiSurface`] /// /// # Example /// /// ``` /// use geos::{Geom, Geometry, GeometryTypes}; /// /// let geom = Geometry::create_empty_collection(GeometryTypes::MultiPolygon)?; /// /// assert_eq!(geom.to_wkt()?, "MULTIPOLYGON EMPTY"); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_empty_collection(type_: GeometryTypes) -> GResult { if !type_.is_collection() { return Err(Error::GenericError(format!( "invalid collection type: {type_:?}" ))); } with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createEmptyCollection_r(ctx.as_raw(), type_.into()))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates a polygon formed by the given shell and array of holes. /// /// ### Note /// /// `exterior` must be a `LinearRing`. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = /// Geometry::new_from_wkt("LINEARRING(75.16 29.53, 78.2 29.2, 77.7 29.5, 75.16 29.53)")?; /// let polygon_geom = Geometry::create_polygon(geom, vec![])?; /// /// assert_eq!( /// polygon_geom.to_wkt()?, /// "POLYGON ((75.16 29.53, 78.2 29.2, 77.7 29.5, 75.16 29.53))", /// ); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_polygon(mut exterior: Self, mut interiors: Vec) -> GResult { let nb_interiors = interiors.len(); let polygon = with_context(|ctx| unsafe { let mut geoms: Vec<*mut GEOSGeometry> = interiors.iter_mut().map(AsRawMut::as_raw_mut).collect(); let ptr = nullcheck!(GEOSGeom_createPolygon_r( ctx.as_raw(), exterior.as_raw_mut(), geoms.as_mut_ptr().cast(), nb_interiors as _, ))?; Ok(Self::new_from_raw(ptr)) }); // We transferred the ownership of the ptr to the new Geometry, // so the old ones need to forget their c ptr to avoid double free. std::mem::forget(exterior); for interior in interiors { std::mem::forget(interior); } polygon } /// Create a geometry collection. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))")?; /// let geom2 = Geometry::new_from_wkt("POINT (3.0 4.0)")?; /// /// let geom = Geometry::create_geometry_collection(vec![geom1, geom2])?; /// /// assert_eq!( /// geom.to_wkt()?, /// "GEOMETRYCOLLECTION (POLYGON ((0 0, 10 0, 10 6, 0 6, 0 0)), POINT (3 4))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_geometry_collection(geoms: Vec) -> GResult { create_multi_geom(geoms, GeometryTypes::GeometryCollection) } /// Create a multi polygon geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))")?; /// let geom2 = Geometry::new_from_wkt("POLYGON((3 3, 10 3, 10 6, 3 6, 3 3))")?; /// /// let geom = Geometry::create_multipolygon(vec![geom1, geom2])?; /// /// assert_eq!( /// geom.to_wkt()?, /// "MULTIPOLYGON (((0 0, 10 0, 10 6, 0 6, 0 0)), \ /// ((3 3, 10 3, 10 6, 3 6, 3 3)))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` 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. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING (1.0 2.0, 3.0 4.0)")?; /// let geom2 = Geometry::new_from_wkt("LINESTRING (5.0 6.0, 7.0 8.0)")?; /// /// let geom = Geometry::create_multiline_string(vec![geom1, geom2])?; /// /// assert_eq!(geom.to_wkt()?, "MULTILINESTRING ((1 2, 3 4), (5 6, 7 8))"); /// # Ok::<(), geos::Error>(()) /// ``` 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. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry, GeometryTypes}; /// /// let geom1 = Geometry::new_from_wkt("POINT (1 2)")?; /// let geom2 = Geometry::new_from_wkt("POINT (3 4)")?; /// /// let geom = Geometry::create_multipoint(vec![geom1, geom2])?; /// /// assert_eq!(geom.geometry_type()?, GeometryTypes::MultiPoint); /// assert_eq!(geom.get_num_geometries()?, 2); /// assert_eq!(geom.get_geometry_n(0)?.to_wkt()?, "POINT (1 2)"); /// assert_eq!(geom.get_geometry_n(1)?.to_wkt()?, "POINT (3 4)"); /// # Ok::<(), geos::Error>(()) /// ``` 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) } /// Create a multicurve geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("LINESTRING (1.0 2.0, 3.0 4.0)")?; /// let geom2 = Geometry::new_from_wkt("CIRCULARSTRING (5.0 6.0, 7.0 8.0, 9.0 10.0)")?; /// /// let geom = Geometry::create_multicurve(vec![geom1, geom2])?; /// /// assert_eq!( /// geom.to_wkt()?, /// "MULTICURVE ((1 2, 3 4), CIRCULARSTRING (5 6, 7 8, 9 10))" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_13_0")] pub fn create_multicurve(curves: Vec) -> GResult { if !curves .iter() .all(|g| g.geometry_type().is_ok_and(GeometryTypes::is_curve)) { return Err(Error::ImpossibleOperation( "all the provided geometry have to be of type LineString or CircularString" .to_owned(), )); } create_multi_geom(curves, GeometryTypes::MultiCurve) } /// Create a multisurface geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom1 = Geometry::new_from_wkt("POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))")?; /// let geom2 = Geometry::new_from_wkt("CURVEPOLYGON(CIRCULARSTRING(1 3, 3 5, 4 7, 7 3, 1 3))")?; /// /// let geom = Geometry::create_multisurface(vec![geom1, geom2])?; /// /// assert_eq!(geom.to_wkt()?, /// "MULTISURFACE (((0 0, 1 1, 1 2, 1 1, 0 0)), CURVEPOLYGON (CIRCULARSTRING (1 3, 3 5, 4 7, 7 3, 1 3)))"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_13_0")] pub fn create_multisurface(surfaces: Vec) -> GResult { if !surfaces .iter() .all(|g| g.geometry_type().is_ok_and(GeometryTypes::is_surface)) { return Err(Error::ImpossibleOperation( "all the provided geometry have to be of type Polygon or CurvePolygon".to_owned(), )); } create_multi_geom(surfaces, GeometryTypes::MultiSurface) } /// Creates a point geometry. /// /// # Example /// /// ``` /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; /// /// let coords = CoordSeq::new_from_vec(&[&[1., 2.]])?; /// /// let geom = Geometry::create_point(coords)?; /// /// assert_eq!(geom.to_wkt()?, "POINT (1 2)"); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_point(mut s: CoordSeq) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createPoint_r(ctx.as_raw(), s.as_raw_mut())); std::mem::forget(s); Ok(Self::new_from_raw(ptr?)) }) } /// Creates a line string geometry. /// /// # Example /// /// ``` /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; /// /// let coords = CoordSeq::new_from_vec(&[&[1., 2.], &[3., 4.]])?; /// /// let geom = Geometry::create_line_string(coords)?; /// /// assert_eq!(geom.to_wkt()?, "LINESTRING (1 2, 3 4)"); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_line_string(mut s: CoordSeq) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createLineString_r(ctx.as_raw(), s.as_raw_mut())); std::mem::forget(s); Ok(Self::new_from_raw(ptr?)) }) } /// Creates a linear ring geometry. /// /// # Example /// /// ``` /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; /// /// let coords = /// CoordSeq::new_from_vec(&[&[75.2, 29.6], &[77.2, 29.2], &[77.7, 29.5], &[75.2, 29.6]])?; /// /// let geom = Geometry::create_linear_ring(coords)?; /// /// assert_eq!( /// geom.to_wkt()?, /// "LINEARRING (75.2 29.6, 77.2 29.2, 77.7 29.5, 75.2 29.6)" /// ); /// # Ok::<(), geos::Error>(()) /// ``` pub fn create_linear_ring(mut s: CoordSeq) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createLinearRing_r(ctx.as_raw(), s.as_raw_mut())); std::mem::forget(s); Ok(Self::new_from_raw(ptr?)) }) } /// Creates a rectangular polygon geometry. /// /// # Example /// /// ``` /// use geos::{Geom, Geometry}; /// /// let geom = Geometry::create_rectangle(0., 0., 1., 1.)?; /// /// assert_eq!(geom.to_wkt()?, "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_11_0")] pub fn create_rectangle(xmin: f64, ymin: f64, xmax: f64, ymax: f64) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createRectangle_r( ctx.as_raw(), xmin, ymin, xmax, ymax ))?; Ok(Self::new_from_raw(ptr)) }) } /// Creates a circular string geometry. /// /// # Example /// /// ``` /// use geos::{CoordDimensions, CoordSeq, Geom, Geometry}; /// /// let coords = CoordSeq::new_from_vec(&[ /// &[75.15, 29.53], /// &[77.2, 29.1], /// &[77.6, 29.5], /// &[75.15, 29.53], /// ])?; /// /// let geom = Geometry::create_circular_string(coords)?; /// /// assert_eq!( /// geom.to_wkt_precision(1)?, /// "CIRCULARSTRING (75.2 29.5, 77.2 29.1, 77.6 29.5, 75.2 29.5)" /// ); /// # Ok::<(), geos::Error>(()) /// ``` #[cfg(feature = "v3_13_0")] pub fn create_circular_string(mut s: CoordSeq) -> GResult { with_context(|ctx| unsafe { let ptr = nullcheck!(GEOSGeom_createCircularString_r( ctx.as_raw(), s.as_raw_mut() )); std::mem::forget(s); Ok(Self::new_from_raw(ptr?)) }) } } impl PartialEq for ConstGeometry<'_> { fn eq(&self, other: &G) -> bool { self.equals(other).unwrap_or(false) } } impl PartialEq for Geometry { fn eq(&self, other: &G) -> bool { self.equals(other).unwrap_or(false) } } impl Clone for Geometry { fn clone(&self) -> Self { Geom::clone(self).expect("cloning failed") } } impl Drop for Geometry { fn drop(&mut self) { with_context(|ctx| unsafe { GEOSGeom_destroy_r(ctx.as_raw(), self.as_raw_mut()) }); } } as_raw_mut_impl!(Geometry, GEOSGeometry); as_raw_impl!(ConstGeometry<'_>, GEOSGeometry); #[cfg(test)] mod test { #[cfg(feature = "v3_11_0")] use super::*; #[test] #[cfg(feature = "v3_11_0")] fn transform_point_geometry() { let geom = Geometry::new_from_wkt("POINT (1.5 2.5)").unwrap(); let transformed = geom .transform_xy(|x, y| { assert_eq!(x, 1.5); assert_eq!(y, 2.5); Ok::<_, Error>((3.5, 4.5)) }) .unwrap(); let expected_geom = Geometry::new_from_wkt("POINT (3.5 4.5)").unwrap(); assert_eq!(expected_geom.equals(&transformed).unwrap(), true); } #[test] #[cfg(feature = "v3_11_0")] fn transform_point_geometry_closure_error() { let geom = Geometry::new_from_wkt("POINT (1.5 2.5)").unwrap(); match geom.transform_xy(|_x, _y| Err(Error::ImpossibleOperation("foo".into()))) { Ok(_) => panic!("transform_xy should have failed"), Err(e) => assert_eq!(e, Error::ImpossibleOperation("foo".into())), }; } #[test] #[cfg(feature = "v3_11_0")] fn transform_polygon_geometry() { let geom = Geometry::new_from_wkt("POLYGON((0 0, 10 0, 10 6, 0 6, 0 0))").unwrap(); let transformed = geom .transform_xy(|x, y| Ok::<_, crate::Error>((x + 1.0, y + 2.0))) .unwrap(); let expected_geom = Geometry::new_from_wkt("POLYGON((1 2, 11 2, 11 8, 1 8, 1 2))").unwrap(); assert_eq!(expected_geom.equals(&transformed).unwrap(), true); } }