From f3dcfd409bf234cd447f37ea87b51a8cd4c5e7a4 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Mon, 22 Jul 2024 11:38:38 -0400 Subject: [PATCH] update euclidean distance --- src/algorithm/geo/euclidean_distance.rs | 309 +++++++++++++----------- 1 file changed, 171 insertions(+), 138 deletions(-) diff --git a/src/algorithm/geo/euclidean_distance.rs b/src/algorithm/geo/euclidean_distance.rs index e164f4575..36aaa5864 100644 --- a/src/algorithm/geo/euclidean_distance.rs +++ b/src/algorithm/geo/euclidean_distance.rs @@ -1,12 +1,18 @@ +use crate::algorithm::native::Binary; use crate::array::*; -use crate::scalar::*; -use crate::trait_::GeometryArrayAccessor; +use crate::datatypes::{Dimension, GeoDataType}; +use crate::error::Result; use crate::trait_::{GeometryArrayTrait, GeometryScalarTrait}; -use arrow_array::builder::Float64Builder; use arrow_array::{Float64Array, OffsetSizeTrait}; use geo::EuclideanDistance as _EuclideanDistance; -pub trait EuclideanDistance { +// ┌────────────────────────────────┐ +// │ Implementations for RHS arrays │ +// └────────────────────────────────┘ + +pub trait EuclideanDistance { + type Output; + /// Returns the distance between two geometries /// /// If a `Point` is contained by a `Polygon`, the distance is `0.0` @@ -84,7 +90,7 @@ pub trait EuclideanDistance { /// /// assert_relative_eq!(distance, 1.1313708498984762); /// ``` - fn euclidean_distance(&self, rhs: &Rhs) -> Float64Array; + fn euclidean_distance(&self, rhs: &Rhs) -> Self::Output; } // ┌────────────────────────────────┐ @@ -93,21 +99,13 @@ pub trait EuclideanDistance { // Note: this implementation is outside the macro because it is not generic over O impl EuclideanDistance> for PointArray<2> { - /// Minimum distance between two Points - fn euclidean_distance(&self, other: &PointArray<2>) -> Float64Array { - assert_eq!(self.len(), other.len()); - let mut output_array = Float64Builder::with_capacity(self.len()); - - self.iter_geo() - .zip(other.iter_geo()) - .for_each(|(first, second)| match (first, second) { - (Some(first), Some(second)) => { - output_array.append_value(first.euclidean_distance(&second)) - } - _ => output_array.append_null(), - }); + type Output = Result; - output_array.finish() + /// Minimum distance between two Points + fn euclidean_distance(&self, rhs: &PointArray<2>) -> Self::Output { + self.try_binary_primitive(rhs, |left, right| { + Ok(left.to_geo().euclidean_distance(&right.to_geo())) + }) } } @@ -115,153 +113,188 @@ impl EuclideanDistance> for PointArray<2> { macro_rules! iter_geo_impl { ($first:ty, $second:ty) => { impl<'a, O: OffsetSizeTrait> EuclideanDistance<$second> for $first { - fn euclidean_distance(&self, other: &$second) -> Float64Array { - assert_eq!(self.len(), other.len()); - let mut output_array = Float64Builder::with_capacity(self.len()); - - self.iter_geo() - .zip(other.iter_geo()) - .for_each(|(first, second)| match (first, second) { - (Some(first), Some(second)) => { - output_array.append_value(first.euclidean_distance(&second)) - } - _ => output_array.append_null(), - }); + type Output = Result; - output_array.finish() + fn euclidean_distance(&self, rhs: &$second) -> Self::Output { + self.try_binary_primitive(rhs, |left, right| { + Ok(left.to_geo().euclidean_distance(&right.to_geo())) + }) } } }; } // Implementations on PointArray -iter_geo_impl!(PointArray<2>, LineStringArray); -iter_geo_impl!(PointArray<2>, PolygonArray); -iter_geo_impl!(PointArray<2>, MultiPointArray); -iter_geo_impl!(PointArray<2>, MultiLineStringArray); -iter_geo_impl!(PointArray<2>, MultiPolygonArray); +iter_geo_impl!(PointArray<2>, LineStringArray); +iter_geo_impl!(PointArray<2>, PolygonArray); +iter_geo_impl!(PointArray<2>, MultiPointArray); +iter_geo_impl!(PointArray<2>, MultiLineStringArray); +iter_geo_impl!(PointArray<2>, MultiPolygonArray); +iter_geo_impl!(PointArray<2>, MixedGeometryArray); +iter_geo_impl!(PointArray<2>, GeometryCollectionArray); // Implementations on LineStringArray -iter_geo_impl!(LineStringArray, PointArray<2>); -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); +iter_geo_impl!(LineStringArray, PointArray<2>); +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); +iter_geo_impl!(LineStringArray, MixedGeometryArray); +iter_geo_impl!(LineStringArray, GeometryCollectionArray); // Implementations on PolygonArray -iter_geo_impl!(PolygonArray, PointArray<2>); -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); +iter_geo_impl!(PolygonArray, PointArray<2>); +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<2>); -// 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); +iter_geo_impl!(MultiPointArray, PointArray<2>); +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<2>); -// 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); +iter_geo_impl!(MultiLineStringArray, PointArray<2>); +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<2>); -// 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); +iter_geo_impl!(MultiPolygonArray, PointArray<2>); +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 │ -// └─────────────────────────────────┘ +impl EuclideanDistance for &dyn GeometryArrayTrait { + type Output = Result; -// Note: this implementation is outside the macro because it is not generic over O -impl<'a> EuclideanDistance> for PointArray<2> { - /// Minimum distance between two Points - fn euclidean_distance(&self, other: &Point<'a, 2>) -> Float64Array { - let mut output_array = Float64Builder::with_capacity(self.len()); - - self.iter_geo().for_each(|maybe_point| { - let output = maybe_point.map(|point| point.euclidean_distance(&other.to_geo())); - output_array.append_option(output) - }); + fn euclidean_distance(&self, rhs: &Self) -> Self::Output { + use GeoDataType::*; + match (self.data_type(), rhs.data_type()) { + (Point(_, Dimension::XY), Point(_, Dimension::XY)) => { + self.as_point_2d().euclidean_distance(rhs.as_point_2d()) + } + (Point(_, Dimension::XY), LineString(_, Dimension::XY)) => self + .as_point_2d() + .euclidean_distance(rhs.as_line_string_2d()), + (Point(_, Dimension::XY), LargeLineString(_, Dimension::XY)) => self + .as_point_2d() + .euclidean_distance(rhs.as_line_string_2d()), + (Point(_, Dimension::XY), Polygon(_, Dimension::XY)) => { + self.as_point_2d().euclidean_distance(rhs.as_polygon_2d()) + } + (Point(_, Dimension::XY), MultiPoint(_, Dimension::XY)) => self + .as_point_2d() + .euclidean_distance(rhs.as_multi_point_2d()), + (Point(_, Dimension::XY), MultiLineString(_, Dimension::XY)) => self + .as_point_2d() + .euclidean_distance(rhs.as_multi_line_string_2d()), + (Point(_, Dimension::XY), MultiPolygon(_, Dimension::XY)) => self + .as_point_2d() + .euclidean_distance(rhs.as_multi_polygon_2d()), + (Point(_, Dimension::XY), Mixed(_, Dimension::XY)) => { + self.as_point_2d().euclidean_distance(rhs.as_mixed_2d()) + } + (Point(_, Dimension::XY), GeometryCollection(_, Dimension::XY)) => self + .as_point_2d() + .euclidean_distance(rhs.as_geometry_collection_2d()), - output_array.finish() + _ => todo!(), + } } } -/// Implementation that iterates over geo objects -macro_rules! iter_geo_impl_scalar { - ($first:ty, $second:ty) => { - impl<'a, O: OffsetSizeTrait> EuclideanDistance<$second> for $first { - fn euclidean_distance(&self, other: &$second) -> Float64Array { - let mut output_array = Float64Builder::with_capacity(self.len()); - let other_geo = other.to_geo(); +// // ┌─────────────────────────────────┐ +// // │ Implementations for RHS scalars │ +// // └─────────────────────────────────┘ - self.iter_geo().for_each(|maybe_geom| { - let output = maybe_geom.map(|geom| geom.euclidean_distance(&other_geo)); - output_array.append_option(output) - }); +// // Note: this implementation is outside the macro because it is not generic over O +// impl<'a> EuclideanDistance> for PointArray { +// /// Minimum distance between two Points +// fn euclidean_distance(&self, other: &Point<'a>) -> Float64Array { +// let mut output_array = Float64Builder::with_capacity(self.len()); - output_array.finish() - } - } - }; -} +// self.iter_geo().for_each(|maybe_point| { +// let output = maybe_point.map(|point| point.euclidean_distance(&other.to_geo())); +// output_array.append_option(output) +// }); -// Implementations on PointArray -iter_geo_impl_scalar!(PointArray<2>, LineString<'a, O, 2>); -iter_geo_impl_scalar!(PointArray<2>, Polygon<'a, O, 2>); -iter_geo_impl_scalar!(PointArray<2>, MultiPoint<'a, O, 2>); -iter_geo_impl_scalar!(PointArray<2>, MultiLineString<'a, O, 2>); -iter_geo_impl_scalar!(PointArray<2>, MultiPolygon<'a, O, 2>); +// output_array.finish() +// } +// } -// Implementations on LineStringArray -iter_geo_impl_scalar!(LineStringArray, Point<'a, 2>); -iter_geo_impl_scalar!(LineStringArray, LineString<'a, O, 2>); -iter_geo_impl_scalar!(LineStringArray, Polygon<'a, O, 2>); -// iter_geo_impl_scalar!(LineStringArray, MultiPoint<'a, O, 2>); -// iter_geo_impl_scalar!(LineStringArray, MultiLineString<'a, O, 2>); -// iter_geo_impl_scalar!(LineStringArray, MultiPolygon<'a, O, 2>); +// /// Implementation that iterates over geo objects +// macro_rules! iter_geo_impl_scalar { +// ($first:ty, $second:ty) => { +// impl<'a, O: OffsetSizeTrait> EuclideanDistance<$second> for $first { +// fn euclidean_distance(&self, other: &$second) -> Float64Array { +// let mut output_array = Float64Builder::with_capacity(self.len()); +// let other_geo = other.to_geo(); -// Implementations on PolygonArray -iter_geo_impl_scalar!(PolygonArray, Point<'a, 2>); -iter_geo_impl_scalar!(PolygonArray, LineString<'a, O, 2>); -iter_geo_impl_scalar!(PolygonArray, Polygon<'a, O, 2>); -// iter_geo_impl_scalar!(PolygonArray, MultiPoint<'a, O, 2>); -// iter_geo_impl_scalar!(PolygonArray, MultiLineString<'a, O, 2>); -// iter_geo_impl_scalar!(PolygonArray, MultiPolygon<'a, O, 2>); +// self.iter_geo().for_each(|maybe_geom| { +// let output = maybe_geom.map(|geom| geom.euclidean_distance(&other_geo)); +// output_array.append_option(output) +// }); -// Implementations on MultiPointArray -iter_geo_impl_scalar!(MultiPointArray, Point<'a, 2>); -// iter_geo_impl_scalar!(MultiPointArray, LineString<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPointArray, Polygon<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPointArray, MultiPoint<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPointArray, MultiLineString<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPointArray, MultiPolygon<'a, O, 2>); +// output_array.finish() +// } +// } +// }; +// } -// Implementations on MultiLineStringArray -iter_geo_impl_scalar!(MultiLineStringArray, Point<'a, 2>); -// iter_geo_impl_scalar!(MultiLineStringArray, LineString<'a, O, 2>); -// iter_geo_impl_scalar!(MultiLineStringArray, Polygon<'a, O, 2>); -// iter_geo_impl_scalar!(MultiLineStringArray, MultiPoint<'a, O, 2>); -// iter_geo_impl_scalar!(MultiLineStringArray, MultiLineString<'a, O, 2>); -// iter_geo_impl_scalar!(MultiLineStringArray, MultiPolygon<'a, O, 2>); +// // 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 MultiPolygonArray -iter_geo_impl_scalar!(MultiPolygonArray, Point<'a, 2>); -// iter_geo_impl_scalar!(MultiPolygonArray, LineString<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPolygonArray, Polygon<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPolygonArray, MultiPoint<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPolygonArray, MultiLineString<'a, O, 2>); -// iter_geo_impl_scalar!(MultiPolygonArray, MultiPolygon<'a, O, 2>); +// // 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>);