diff --git a/src/algorithm/geo/affine_ops.rs b/src/algorithm/geo/affine_ops.rs index d7842a702..8eb4392f8 100644 --- a/src/algorithm/geo/affine_ops.rs +++ b/src/algorithm/geo/affine_ops.rs @@ -46,9 +46,9 @@ pub trait AffineOps { // fn affine_transform_mut(&mut self, transform: &AffineTransform); } -// ┌────────────────────────────────┐ -// │ Implementations for RHS arrays │ -// └────────────────────────────────┘ +// ┌─────────────────────────────────┐ +// │ Implementations for RHS scalars │ +// └─────────────────────────────────┘ // Note: this can't (easily) be parameterized in the macro because PointArray is not generic over O impl AffineOps for PointArray { @@ -103,9 +103,9 @@ impl AffineOps for GeometryArray { } } -// ┌─────────────────────────────────┐ -// │ Implementations for RHS scalars │ -// └─────────────────────────────────┘ +// ┌────────────────────────────────┐ +// │ Implementations for RHS arrays │ +// └────────────────────────────────┘ // Note: this can't (easily) be parameterized in the macro because PointArray is not generic over O impl AffineOps> for PointArray { diff --git a/src/algorithm/geo/contains.rs b/src/algorithm/geo/contains.rs new file mode 100644 index 000000000..ea0772e50 --- /dev/null +++ b/src/algorithm/geo/contains.rs @@ -0,0 +1,221 @@ +use crate::array::*; +use crate::scalar::*; +use crate::trait_::GeometryScalarTrait; +use crate::GeometryArrayTrait; +use arrow2::array::{BooleanArray, MutableBooleanArray}; +use arrow2::types::Offset; +use geo::Contains as _Contains; + +/// Checks if `rhs` is completely contained within `self`. +/// More formally, the interior of `rhs` has non-empty +/// (set-theoretic) intersection but neither the interior, +/// nor the boundary of `rhs` intersects the exterior of +/// `self`. In other words, the [DE-9IM] intersection matrix +/// of `(rhs, self)` is `T*F**F***`. +/// +/// [DE-9IM]: https://en.wikipedia.org/wiki/DE-9IM +/// +/// # Examples +/// +/// ``` +/// use geo::Contains; +/// use geo::{line_string, point, Polygon}; +/// +/// let line_string = line_string![ +/// (x: 0., y: 0.), +/// (x: 2., y: 0.), +/// (x: 2., y: 2.), +/// (x: 0., y: 2.), +/// (x: 0., y: 0.), +/// ]; +/// +/// let polygon = Polygon::new(line_string.clone(), vec![]); +/// +/// // Point in Point +/// assert!(point!(x: 2., y: 0.).contains(&point!(x: 2., y: 0.))); +/// +/// // Point in Linestring +/// assert!(line_string.contains(&point!(x: 2., y: 0.))); +/// +/// // Point in Polygon +/// assert!(polygon.contains(&point!(x: 1., y: 1.))); +/// ``` +pub trait Contains { + fn contains(&self, rhs: &Rhs) -> BooleanArray; +} + +// ┌────────────────────────────────┐ +// │ Implementations for RHS arrays │ +// └────────────────────────────────┘ + +// Note: this implementation is outside the macro because it is not generic over O +impl Contains for PointArray { + fn contains(&self, rhs: &Self) -> BooleanArray { + assert_eq!(self.len(), rhs.len()); + + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo() + .zip(rhs.iter_geo()) + .for_each(|(first, second)| match (first, second) { + (Some(first), Some(second)) => output_array.push(Some(first.contains(&second))), + _ => output_array.push(None), + }); + + output_array.into() + } +} + +// Implementation that iterates over geo objects +macro_rules! iter_geo_impl { + ($first:ty, $second:ty) => { + impl<'a, O: Offset> Contains<$second> for $first { + fn contains(&self, rhs: &$second) -> BooleanArray { + assert_eq!(self.len(), rhs.len()); + + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo() + .zip(rhs.iter_geo()) + .for_each(|(first, second)| match (first, second) { + (Some(first), Some(second)) => { + output_array.push(Some(first.contains(&second))) + } + _ => output_array.push(None), + }); + + output_array.into() + } + } + }; +} + +// Implementations on PointArray +iter_geo_impl!(PointArray, LineStringArray); +iter_geo_impl!(PointArray, PolygonArray); +iter_geo_impl!(PointArray, MultiPointArray); +iter_geo_impl!(PointArray, MultiLineStringArray); +iter_geo_impl!(PointArray, MultiPolygonArray); + +// Implementations on LineStringArray +iter_geo_impl!(LineStringArray, PointArray); +iter_geo_impl!(LineStringArray, LineStringArray); +iter_geo_impl!(LineStringArray, PolygonArray); +iter_geo_impl!(LineStringArray, MultiPointArray); +iter_geo_impl!(LineStringArray, MultiLineStringArray); +iter_geo_impl!(LineStringArray, MultiPolygonArray); + +// Implementations on PolygonArray +iter_geo_impl!(PolygonArray, PointArray); +iter_geo_impl!(PolygonArray, LineStringArray); +iter_geo_impl!(PolygonArray, PolygonArray); +iter_geo_impl!(PolygonArray, MultiPointArray); +iter_geo_impl!(PolygonArray, MultiLineStringArray); +iter_geo_impl!(PolygonArray, MultiPolygonArray); + +// Implementations on MultiPointArray +iter_geo_impl!(MultiPointArray, PointArray); +iter_geo_impl!(MultiPointArray, LineStringArray); +iter_geo_impl!(MultiPointArray, PolygonArray); +iter_geo_impl!(MultiPointArray, MultiPointArray); +iter_geo_impl!(MultiPointArray, MultiLineStringArray); +iter_geo_impl!(MultiPointArray, MultiPolygonArray); + +// Implementations on MultiLineStringArray +iter_geo_impl!(MultiLineStringArray, PointArray); +iter_geo_impl!(MultiLineStringArray, LineStringArray); +iter_geo_impl!(MultiLineStringArray, PolygonArray); +iter_geo_impl!(MultiLineStringArray, MultiPointArray); +iter_geo_impl!(MultiLineStringArray, MultiLineStringArray); +iter_geo_impl!(MultiLineStringArray, MultiPolygonArray); + +// Implementations on MultiPolygonArray +iter_geo_impl!(MultiPolygonArray, PointArray); +iter_geo_impl!(MultiPolygonArray, LineStringArray); +iter_geo_impl!(MultiPolygonArray, PolygonArray); +iter_geo_impl!(MultiPolygonArray, MultiPointArray); +iter_geo_impl!(MultiPolygonArray, MultiLineStringArray); +iter_geo_impl!(MultiPolygonArray, MultiPolygonArray); + +// ┌─────────────────────────────────┐ +// │ Implementations for RHS scalars │ +// └─────────────────────────────────┘ + +// Note: this implementation is outside the macro because it is not generic over O +impl<'a> Contains> for PointArray { + fn contains(&self, rhs: &Point<'a>) -> BooleanArray { + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo().for_each(|maybe_point| { + let output = maybe_point.map(|point| point.contains(&rhs.to_geo())); + output_array.push(output) + }); + + output_array.into() + } +} + +/// Implementation that iterates over geo objects +macro_rules! iter_geo_impl_scalar { + ($first:ty, $second:ty) => { + impl<'a, O: Offset> Contains<$second> for $first { + fn contains(&self, rhs: &$second) -> BooleanArray { + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo().for_each(|maybe_geom| { + let output = maybe_geom.map(|geom| geom.contains(&rhs.to_geo())); + output_array.push(output) + }); + + output_array.into() + } + } + }; +} + +// Implementations on PointArray +iter_geo_impl_scalar!(PointArray, LineString<'a, O>); +iter_geo_impl_scalar!(PointArray, Polygon<'a, O>); +iter_geo_impl_scalar!(PointArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(PointArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(PointArray, MultiPolygon<'a, O>); + +// Implementations on LineStringArray +iter_geo_impl_scalar!(LineStringArray, Point<'a>); +iter_geo_impl_scalar!(LineStringArray, LineString<'a, O>); +iter_geo_impl_scalar!(LineStringArray, Polygon<'a, O>); +iter_geo_impl_scalar!(LineStringArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(LineStringArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(LineStringArray, MultiPolygon<'a, O>); + +// Implementations on PolygonArray +iter_geo_impl_scalar!(PolygonArray, Point<'a>); +iter_geo_impl_scalar!(PolygonArray, LineString<'a, O>); +iter_geo_impl_scalar!(PolygonArray, Polygon<'a, O>); +iter_geo_impl_scalar!(PolygonArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(PolygonArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(PolygonArray, MultiPolygon<'a, O>); + +// Implementations on MultiPointArray +iter_geo_impl_scalar!(MultiPointArray, Point<'a>); +iter_geo_impl_scalar!(MultiPointArray, LineString<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, Polygon<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, MultiPolygon<'a, O>); + +// Implementations on MultiLineStringArray +iter_geo_impl_scalar!(MultiLineStringArray, Point<'a>); +iter_geo_impl_scalar!(MultiLineStringArray, LineString<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, Polygon<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, MultiPolygon<'a, O>); + +// Implementations on MultiPolygonArray +iter_geo_impl_scalar!(MultiPolygonArray, Point<'a>); +iter_geo_impl_scalar!(MultiPolygonArray, LineString<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, Polygon<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, MultiPolygon<'a, O>); diff --git a/src/algorithm/geo/intersects.rs b/src/algorithm/geo/intersects.rs new file mode 100644 index 000000000..0e2a66ca6 --- /dev/null +++ b/src/algorithm/geo/intersects.rs @@ -0,0 +1,218 @@ +use crate::array::*; +use crate::scalar::*; +use crate::trait_::GeometryScalarTrait; +use crate::GeometryArrayTrait; +use arrow2::array::{BooleanArray, MutableBooleanArray}; +use arrow2::types::Offset; +use geo::Intersects as _Intersects; + +/// Checks if the geometry Self intersects the geometry Rhs. +/// More formally, either boundary or interior of Self has +/// non-empty (set-theoretic) intersection with the boundary +/// or interior of Rhs. In other words, the [DE-9IM] +/// intersection matrix for (Self, Rhs) is _not_ `FF*FF****`. +/// +/// This predicate is symmetric: `a.intersects(b)` iff +/// `b.intersects(a)`. +/// +/// [DE-9IM]: https://en.wikipedia.org/wiki/DE-9IM +/// +/// # Examples +/// +/// ``` +/// use geo::Intersects; +/// use geo::line_string; +/// +/// let line_string_a = line_string![ +/// (x: 3., y: 2.), +/// (x: 7., y: 6.), +/// ]; +/// +/// let line_string_b = line_string![ +/// (x: 3., y: 4.), +/// (x: 8., y: 4.), +/// ]; +/// +/// let line_string_c = line_string![ +/// (x: 9., y: 2.), +/// (x: 11., y: 5.), +/// ]; +/// +/// assert!(line_string_a.intersects(&line_string_b)); +/// assert!(!line_string_a.intersects(&line_string_c)); +/// ``` +pub trait Intersects { + fn intersects(&self, rhs: &Rhs) -> BooleanArray; +} + +// Note: this implementation is outside the macro because it is not generic over O +impl Intersects for PointArray { + fn intersects(&self, rhs: &Self) -> BooleanArray { + assert_eq!(self.len(), rhs.len()); + + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo() + .zip(rhs.iter_geo()) + .for_each(|(first, second)| match (first, second) { + (Some(first), Some(second)) => output_array.push(Some(first.intersects(&second))), + _ => output_array.push(None), + }); + + output_array.into() + } +} + +// Implementation that iterates over geo objects +macro_rules! iter_geo_impl { + ($first:ty, $second:ty) => { + impl<'a, O: Offset> Intersects<$second> for $first { + fn intersects(&self, rhs: &$second) -> BooleanArray { + assert_eq!(self.len(), rhs.len()); + + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo() + .zip(rhs.iter_geo()) + .for_each(|(first, second)| match (first, second) { + (Some(first), Some(second)) => { + output_array.push(Some(first.intersects(&second))) + } + _ => output_array.push(None), + }); + + output_array.into() + } + } + }; +} + +// Implementations on PointArray +iter_geo_impl!(PointArray, LineStringArray); +iter_geo_impl!(PointArray, PolygonArray); +iter_geo_impl!(PointArray, MultiPointArray); +iter_geo_impl!(PointArray, MultiLineStringArray); +iter_geo_impl!(PointArray, MultiPolygonArray); + +// Implementations on LineStringArray +iter_geo_impl!(LineStringArray, PointArray); +iter_geo_impl!(LineStringArray, LineStringArray); +iter_geo_impl!(LineStringArray, PolygonArray); +iter_geo_impl!(LineStringArray, MultiPointArray); +iter_geo_impl!(LineStringArray, MultiLineStringArray); +iter_geo_impl!(LineStringArray, MultiPolygonArray); + +// Implementations on PolygonArray +iter_geo_impl!(PolygonArray, PointArray); +iter_geo_impl!(PolygonArray, LineStringArray); +iter_geo_impl!(PolygonArray, PolygonArray); +iter_geo_impl!(PolygonArray, MultiPointArray); +iter_geo_impl!(PolygonArray, MultiLineStringArray); +iter_geo_impl!(PolygonArray, MultiPolygonArray); + +// Implementations on MultiPointArray +iter_geo_impl!(MultiPointArray, PointArray); +iter_geo_impl!(MultiPointArray, LineStringArray); +iter_geo_impl!(MultiPointArray, PolygonArray); +iter_geo_impl!(MultiPointArray, MultiPointArray); +iter_geo_impl!(MultiPointArray, MultiLineStringArray); +iter_geo_impl!(MultiPointArray, MultiPolygonArray); + +// Implementations on MultiLineStringArray +iter_geo_impl!(MultiLineStringArray, PointArray); +iter_geo_impl!(MultiLineStringArray, LineStringArray); +iter_geo_impl!(MultiLineStringArray, PolygonArray); +iter_geo_impl!(MultiLineStringArray, MultiPointArray); +iter_geo_impl!(MultiLineStringArray, MultiLineStringArray); +iter_geo_impl!(MultiLineStringArray, MultiPolygonArray); + +// Implementations on MultiPolygonArray +iter_geo_impl!(MultiPolygonArray, PointArray); +iter_geo_impl!(MultiPolygonArray, LineStringArray); +iter_geo_impl!(MultiPolygonArray, PolygonArray); +iter_geo_impl!(MultiPolygonArray, MultiPointArray); +iter_geo_impl!(MultiPolygonArray, MultiLineStringArray); +iter_geo_impl!(MultiPolygonArray, MultiPolygonArray); + +// ┌─────────────────────────────────┐ +// │ Implementations for RHS scalars │ +// └─────────────────────────────────┘ + +// Note: this implementation is outside the macro because it is not generic over O +impl<'a> Intersects> for PointArray { + fn intersects(&self, rhs: &Point<'a>) -> BooleanArray { + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo().for_each(|maybe_point| { + let output = maybe_point.map(|point| point.intersects(&rhs.to_geo())); + output_array.push(output) + }); + + output_array.into() + } +} + +/// Implementation that iterates over geo objects +macro_rules! iter_geo_impl_scalar { + ($first:ty, $second:ty) => { + impl<'a, O: Offset> Intersects<$second> for $first { + fn intersects(&self, rhs: &$second) -> BooleanArray { + let mut output_array = MutableBooleanArray::with_capacity(self.len()); + + self.iter_geo().for_each(|maybe_geom| { + let output = maybe_geom.map(|geom| geom.intersects(&rhs.to_geo())); + output_array.push(output) + }); + + output_array.into() + } + } + }; +} + +// Implementations on PointArray +iter_geo_impl_scalar!(PointArray, LineString<'a, O>); +iter_geo_impl_scalar!(PointArray, Polygon<'a, O>); +iter_geo_impl_scalar!(PointArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(PointArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(PointArray, MultiPolygon<'a, O>); + +// Implementations on LineStringArray +iter_geo_impl_scalar!(LineStringArray, Point<'a>); +iter_geo_impl_scalar!(LineStringArray, LineString<'a, O>); +iter_geo_impl_scalar!(LineStringArray, Polygon<'a, O>); +iter_geo_impl_scalar!(LineStringArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(LineStringArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(LineStringArray, MultiPolygon<'a, O>); + +// Implementations on PolygonArray +iter_geo_impl_scalar!(PolygonArray, Point<'a>); +iter_geo_impl_scalar!(PolygonArray, LineString<'a, O>); +iter_geo_impl_scalar!(PolygonArray, Polygon<'a, O>); +iter_geo_impl_scalar!(PolygonArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(PolygonArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(PolygonArray, MultiPolygon<'a, O>); + +// Implementations on MultiPointArray +iter_geo_impl_scalar!(MultiPointArray, Point<'a>); +iter_geo_impl_scalar!(MultiPointArray, LineString<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, Polygon<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(MultiPointArray, MultiPolygon<'a, O>); + +// Implementations on MultiLineStringArray +iter_geo_impl_scalar!(MultiLineStringArray, Point<'a>); +iter_geo_impl_scalar!(MultiLineStringArray, LineString<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, Polygon<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(MultiLineStringArray, MultiPolygon<'a, O>); + +// Implementations on MultiPolygonArray +iter_geo_impl_scalar!(MultiPolygonArray, Point<'a>); +iter_geo_impl_scalar!(MultiPolygonArray, LineString<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, Polygon<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, MultiPoint<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, MultiLineString<'a, O>); +iter_geo_impl_scalar!(MultiPolygonArray, MultiPolygon<'a, O>); diff --git a/src/algorithm/geo/mod.rs b/src/algorithm/geo/mod.rs index f9c343d05..e767214ab 100644 --- a/src/algorithm/geo/mod.rs +++ b/src/algorithm/geo/mod.rs @@ -34,6 +34,10 @@ pub use chaikin_smoothing::ChaikinSmoothing; pub mod chamberlain_duquette_area; pub use chamberlain_duquette_area::ChamberlainDuquetteArea; +/// Determine whether `Geometry` `A` completely encloses `Geometry` `B`. +pub mod contains; +pub use contains::Contains; + /// Calculate the convex hull of geometries. pub mod convex_hull; pub use convex_hull::ConvexHull; @@ -67,6 +71,10 @@ pub use geodesic_length::GeodesicLength; pub mod haversine_length; pub use haversine_length::HaversineLength; +/// Determine whether `Geometry` `A` intersects `Geometry` `B`. +pub mod intersects; +pub use intersects::Intersects; + /// Rotate geometries by an angle given in degrees. pub mod rotate; pub use rotate::Rotate;