diff --git a/Cargo.lock b/Cargo.lock index f9c4d34f..58033938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1317,6 +1317,7 @@ dependencies = [ "http-range-client", "indexmap", "lexical-core 0.8.5", + "num-traits", "object_store", "parquet", "phf", diff --git a/python/Cargo.lock b/python/Cargo.lock index 3322c936..b72ef0be 100644 --- a/python/Cargo.lock +++ b/python/Cargo.lock @@ -1125,6 +1125,7 @@ dependencies = [ "http-range-client", "indexmap", "lexical-core 0.8.5", + "num-traits", "object_store", "parquet", "phf", diff --git a/rust/geoarrow/Cargo.toml b/rust/geoarrow/Cargo.toml index 1b0ba804..47d1890d 100644 --- a/rust/geoarrow/Cargo.toml +++ b/rust/geoarrow/Cargo.toml @@ -73,6 +73,7 @@ half = { version = "2.4.1" } http-range-client = { version = "0.8", optional = true } indexmap = { version = "2" } lexical-core = { version = "0.8.5" } +num-traits = "0.2.19" object_store = { version = "0.11", optional = true } parquet = { version = "53", optional = true, default-features = false, features = [ "arrow", diff --git a/rust/geoarrow/src/algorithm/geo/affine_ops.rs b/rust/geoarrow/src/algorithm/geo/affine_ops.rs index a8bee5aa..cf19296b 100644 --- a/rust/geoarrow/src/algorithm/geo/affine_ops.rs +++ b/rust/geoarrow/src/algorithm/geo/affine_ops.rs @@ -6,7 +6,8 @@ use crate::datatypes::{Dimension, NativeType}; use crate::error::{GeoArrowError, Result}; use crate::trait_::ArrayAccessor; use crate::NativeArray; -use geo::{AffineTransform, MapCoords}; +use geo::AffineOps as _AffineOps; +use geo::AffineTransform; /// Apply an [`AffineTransform`] like [`scale`](AffineTransform::scale), /// [`skew`](AffineTransform::skew), or [`rotate`](AffineTransform::rotate) to geometries. @@ -30,67 +31,203 @@ pub trait AffineOps { // │ Implementations for RHS scalars │ // └─────────────────────────────────┘ -// Note: this can't (easily) be parameterized in the macro because PointArray is not generic over O impl AffineOps<&AffineTransform> for PointArray { type Output = Self; fn affine_transform(&self, transform: &AffineTransform) -> Self::Output { - let mut output_array = PointBuilder::with_capacity(Dimension::XY, self.buffer_lengths()); + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + PointBuilder::from_nullable_points( + output_geoms.iter().map(|x| x.as_ref()), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + ) + .finish() + } +} + +impl AffineOps<&AffineTransform> for RectArray { + type Output = Self; + + fn affine_transform(&self, transform: &AffineTransform) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + RectBuilder::from_nullable_rects( + output_geoms.iter().map(|x| x.as_ref()), + Dimension::XY, + self.metadata().clone(), + ) + .finish() + } +} + +impl AffineOps<&AffineTransform> for GeometryArray { + type Output = Result; + + fn affine_transform(&self, transform: &AffineTransform) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + Ok(GeometryBuilder::from_nullable_geometries( + output_geoms.as_slice(), + self.coord_type(), + self.metadata().clone(), + false, + )? + .finish()) + } +} + +impl AffineOps<&AffineTransform> for GeometryCollectionArray { + type Output = Result; + + fn affine_transform(&self, transform: &AffineTransform) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + Ok(GeometryCollectionBuilder::from_nullable_geometries( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + false, + )? + .finish()) + } +} - self.iter_geo().for_each(|maybe_g| { - output_array.push_point( - maybe_g - .map(|geom| geom.map_coords(|coord| transform.apply(coord))) - .as_ref(), - ) - }); +impl AffineOps<&AffineTransform> for MixedGeometryArray { + type Output = Result; - output_array.finish() + fn affine_transform(&self, transform: &AffineTransform) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + Ok(MixedGeometryBuilder::from_nullable_geometries( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + false, + )? + .finish()) } } /// Implementation that iterates over geo objects macro_rules! iter_geo_impl { - ($type:ty, $builder_type:ty, $push_func:ident) => { + ($type:ty, $builder_type:ty, $method:ident, $geo_type:ty) => { impl AffineOps<&AffineTransform> for $type { type Output = Self; fn affine_transform(&self, transform: &AffineTransform) -> Self::Output { - let mut output_array = - <$builder_type>::with_capacity(Dimension::XY, self.buffer_lengths()); - - self.iter_geo().for_each(|maybe_g| { - output_array - .$push_func( - maybe_g - .map(|geom| geom.map_coords(|coord| transform.apply(coord))) - .as_ref(), - ) - .unwrap() - }); - - output_array.finish() + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + <$builder_type>::$method( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + ) + .finish() } } }; } -iter_geo_impl!(LineStringArray, LineStringBuilder, push_line_string); -iter_geo_impl!(PolygonArray, PolygonBuilder, push_polygon); -iter_geo_impl!(MultiPointArray, MultiPointBuilder, push_multi_point); +iter_geo_impl!( + LineStringArray, + LineStringBuilder, + from_nullable_line_strings, + geo::LineString +); +iter_geo_impl!( + PolygonArray, + PolygonBuilder, + from_nullable_polygons, + geo::Polygon +); +iter_geo_impl!( + MultiPointArray, + MultiPointBuilder, + from_nullable_multi_points, + geo::MultiPoint +); iter_geo_impl!( MultiLineStringArray, MultiLineStringBuilder, - push_multi_line_string + from_nullable_multi_line_strings, + geo::MultiLineString ); -iter_geo_impl!(MultiPolygonArray, MultiPolygonBuilder, push_multi_polygon); -iter_geo_impl!(MixedGeometryArray, MixedGeometryBuilder, push_geometry); iter_geo_impl!( - GeometryCollectionArray, - GeometryCollectionBuilder, - push_geometry_collection + MultiPolygonArray, + MultiPolygonBuilder, + from_nullable_multi_polygons, + geo::MultiPolygon ); +// iter_geo_impl!(LineStringArray, LineStringBuilder, push_line_string); +// iter_geo_impl!(PolygonArray, PolygonBuilder, push_polygon); +// iter_geo_impl!(MultiPointArray, MultiPointBuilder, push_multi_point); +// iter_geo_impl!( +// MultiLineStringArray, +// MultiLineStringBuilder, +// push_multi_line_string +// ); +// iter_geo_impl!(MultiPolygonArray, MultiPolygonBuilder, push_multi_polygon); +// iter_geo_impl!(MixedGeometryArray, MixedGeometryBuilder, push_geometry); +// iter_geo_impl!( +// GeometryCollectionArray, +// GeometryCollectionBuilder, +// push_geometry_collection +// ); + impl AffineOps<&AffineTransform> for &dyn NativeArray { type Output = Result>; @@ -100,20 +237,21 @@ impl AffineOps<&AffineTransform> for &dyn NativeArray { Arc::new(self.$method().affine_transform(transform)) }; } - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - Point(_, XY) => impl_downcast!(as_point), - LineString(_, XY) => impl_downcast!(as_line_string), - Polygon(_, XY) => impl_downcast!(as_polygon), - MultiPoint(_, XY) => impl_downcast!(as_multi_point), - MultiLineString(_, XY) => impl_downcast!(as_multi_line_string), - MultiPolygon(_, XY) => impl_downcast!(as_multi_polygon), - Mixed(_, XY) => impl_downcast!(as_mixed), - GeometryCollection(_, XY) => impl_downcast!(as_geometry_collection), - // Rect => impl_downcast!(as_rect), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => impl_downcast!(as_point), + LineString(_, _) => impl_downcast!(as_line_string), + Polygon(_, _) => impl_downcast!(as_polygon), + MultiPoint(_, _) => impl_downcast!(as_multi_point), + MultiLineString(_, _) => impl_downcast!(as_multi_line_string), + MultiPolygon(_, _) => impl_downcast!(as_multi_polygon), + Mixed(_, _) => Arc::new(self.as_mixed().affine_transform(transform)?), + GeometryCollection(_, _) => { + Arc::new(self.as_geometry_collection().affine_transform(transform)?) + } + Rect(_) => impl_downcast!(as_rect), + Geometry(_) => Arc::new(self.as_geometry().affine_transform(transform)?), }; Ok(result) } @@ -148,8 +286,25 @@ impl_chunked!(ChunkedPolygonArray); impl_chunked!(ChunkedMultiPointArray); impl_chunked!(ChunkedMultiLineStringArray); impl_chunked!(ChunkedMultiPolygonArray); -impl_chunked!(ChunkedMixedGeometryArray); -impl_chunked!(ChunkedGeometryCollectionArray); +impl_chunked!(ChunkedRectArray); + +macro_rules! impl_try_chunked { + ($struct_name:ty) => { + impl AffineOps<&AffineTransform> for $struct_name { + type Output = Result; + + fn affine_transform(&self, transform: &AffineTransform) -> Self::Output { + Ok(self + .try_map(|chunk| chunk.affine_transform(transform))? + .try_into() + .unwrap()) + } + } + }; +} + +impl_try_chunked!(ChunkedMixedGeometryArray); +impl_try_chunked!(ChunkedGeometryCollectionArray); impl AffineOps<&AffineTransform> for &dyn ChunkedNativeArray { type Output = Result>; @@ -160,19 +315,20 @@ impl AffineOps<&AffineTransform> for &dyn ChunkedNativeArray { Arc::new(self.$method().affine_transform(transform)) }; } - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - Point(_, XY) => impl_downcast!(as_point), - LineString(_, XY) => impl_downcast!(as_line_string), - Polygon(_, XY) => impl_downcast!(as_polygon), - MultiPoint(_, XY) => impl_downcast!(as_multi_point), - MultiLineString(_, XY) => impl_downcast!(as_multi_line_string), - MultiPolygon(_, XY) => impl_downcast!(as_multi_polygon), - Mixed(_, XY) => impl_downcast!(as_mixed), - GeometryCollection(_, XY) => impl_downcast!(as_geometry_collection), - // Rect => impl_downcast!(as_rect), + Point(_, _) => impl_downcast!(as_point), + LineString(_, _) => impl_downcast!(as_line_string), + Polygon(_, _) => impl_downcast!(as_polygon), + MultiPoint(_, _) => impl_downcast!(as_multi_point), + MultiLineString(_, _) => impl_downcast!(as_multi_line_string), + MultiPolygon(_, _) => impl_downcast!(as_multi_polygon), + Mixed(_, _) => Arc::new(self.as_mixed().affine_transform(transform)?), + GeometryCollection(_, _) => { + Arc::new(self.as_geometry_collection().affine_transform(transform)?) + } + Rect(_) => impl_downcast!(as_rect), _ => return Err(GeoArrowError::IncorrectType("".into())), }; Ok(result) @@ -188,66 +344,140 @@ impl AffineOps<&[AffineTransform]> for PointArray { type Output = Self; fn affine_transform(&self, transform: &[AffineTransform]) -> Self::Output { - let mut output_array = PointBuilder::with_capacity(Dimension::XY, self.buffer_lengths()); - - self.iter_geo() + let output_geoms: Vec> = self + .iter_geo() .zip(transform.iter()) - .for_each(|(maybe_g, transform)| { - output_array.push_point( - maybe_g - .map(|geom| geom.map_coords(|coord| transform.apply(coord))) - .as_ref(), - ) - }); - - output_array.finish() + .map(|(maybe_g, transform)| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + PointBuilder::from_nullable_points( + output_geoms.iter().map(|x| x.as_ref()), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + ) + .finish() } } /// Implementation that iterates over geo objects macro_rules! iter_geo_impl2 { - ($type:ty, $builder_type:ty, $push_func:ident) => { + ($type:ty, $builder_type:ty, $method:ident, $geo_type:ty) => { impl AffineOps<&[AffineTransform]> for $type { type Output = Self; fn affine_transform(&self, transform: &[AffineTransform]) -> Self::Output { - let mut output_array = - <$builder_type>::with_capacity(Dimension::XY, self.buffer_lengths()); - - self.iter_geo() + let output_geoms: Vec> = self + .iter_geo() .zip(transform.iter()) - .for_each(|(maybe_g, transform)| { - output_array - .$push_func( - maybe_g - .map(|geom| geom.map_coords(|coord| transform.apply(coord))) - .as_ref(), - ) - .unwrap() - }); - - output_array.finish() + .map(|(maybe_g, transform)| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + <$builder_type>::$method( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + ) + .finish() } } }; } -iter_geo_impl2!(LineStringArray, LineStringBuilder, push_line_string); -iter_geo_impl2!(PolygonArray, PolygonBuilder, push_polygon); -iter_geo_impl2!(MultiPointArray, MultiPointBuilder, push_multi_point); +iter_geo_impl2!( + LineStringArray, + LineStringBuilder, + from_nullable_line_strings, + geo::LineString +); +iter_geo_impl2!( + PolygonArray, + PolygonBuilder, + from_nullable_polygons, + geo::Polygon +); +iter_geo_impl2!( + MultiPointArray, + MultiPointBuilder, + from_nullable_multi_points, + geo::MultiPoint +); iter_geo_impl2!( MultiLineStringArray, MultiLineStringBuilder, - push_multi_line_string + from_nullable_multi_line_strings, + geo::MultiLineString ); -iter_geo_impl2!(MultiPolygonArray, MultiPolygonBuilder, push_multi_polygon); -iter_geo_impl2!(MixedGeometryArray, MixedGeometryBuilder, push_geometry); iter_geo_impl2!( - GeometryCollectionArray, - GeometryCollectionBuilder, - push_geometry_collection + MultiPolygonArray, + MultiPolygonBuilder, + from_nullable_multi_polygons, + geo::MultiPolygon ); +impl AffineOps<&[AffineTransform]> for GeometryCollectionArray { + type Output = Result; + + fn affine_transform(&self, transform: &[AffineTransform]) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .zip(transform.iter()) + .map(|(maybe_g, transform)| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + Ok(GeometryCollectionBuilder::from_nullable_geometries( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + false, + )? + .finish()) + } +} + +impl AffineOps<&[AffineTransform]> for MixedGeometryArray { + type Output = Result; + + fn affine_transform(&self, transform: &[AffineTransform]) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .zip(transform.iter()) + .map(|(maybe_g, transform)| { + maybe_g.map(|mut geom| { + geom.affine_transform_mut(transform); + geom + }) + }) + .collect(); + + Ok(MixedGeometryBuilder::from_nullable_geometries( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + false, + )? + .finish()) + } +} + impl AffineOps<&[AffineTransform]> for &dyn NativeArray { type Output = Result>; @@ -264,9 +494,9 @@ impl AffineOps<&[AffineTransform]> for &dyn NativeArray { Arc::new(self.as_multi_line_string().affine_transform(transform)) } MultiPolygon(_, XY) => Arc::new(self.as_multi_polygon().affine_transform(transform)), - Mixed(_, XY) => Arc::new(self.as_mixed().affine_transform(transform)), + Mixed(_, XY) => Arc::new(self.as_mixed().affine_transform(transform)?), GeometryCollection(_, XY) => { - Arc::new(self.as_geometry_collection().affine_transform(transform)) + Arc::new(self.as_geometry_collection().affine_transform(transform)?) } _ => return Err(GeoArrowError::IncorrectType("".into())), }; diff --git a/rust/geoarrow/src/algorithm/geo/area.rs b/rust/geoarrow/src/algorithm/geo/area.rs index 59a763ba..fbc2e42c 100644 --- a/rust/geoarrow/src/algorithm/geo/area.rs +++ b/rust/geoarrow/src/algorithm/geo/area.rs @@ -2,8 +2,8 @@ use crate::algorithm::geo::utils::zeroes; use crate::algorithm::native::Unary; use crate::array::*; use crate::chunked_array::{ChunkedArray, ChunkedGeometryArray, ChunkedNativeArray}; -use crate::datatypes::{Dimension, NativeType}; -use crate::error::{GeoArrowError, Result}; +use crate::datatypes::NativeType; +use crate::error::Result; use crate::trait_::NativeScalar; use crate::NativeArray; use arrow_array::Float64Array; @@ -93,44 +93,43 @@ iter_geo_impl!(MultiPolygonArray); iter_geo_impl!(MixedGeometryArray); iter_geo_impl!(GeometryCollectionArray); iter_geo_impl!(RectArray); +iter_geo_impl!(GeometryArray); impl Area for &dyn NativeArray { type Output = Result; fn signed_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().signed_area(), - LineString(_, XY) => self.as_line_string().signed_area(), - Polygon(_, XY) => self.as_polygon().signed_area(), - MultiPoint(_, XY) => self.as_multi_point().signed_area(), - MultiLineString(_, XY) => self.as_multi_line_string().signed_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().signed_area(), - Mixed(_, XY) => self.as_mixed().signed_area(), - GeometryCollection(_, XY) => self.as_geometry_collection().signed_area(), - Rect(XY) => self.as_rect().signed_area(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().signed_area(), + LineString(_, _) => self.as_line_string().signed_area(), + Polygon(_, _) => self.as_polygon().signed_area(), + MultiPoint(_, _) => self.as_multi_point().signed_area(), + MultiLineString(_, _) => self.as_multi_line_string().signed_area(), + MultiPolygon(_, _) => self.as_multi_polygon().signed_area(), + Mixed(_, _) => self.as_mixed().signed_area(), + GeometryCollection(_, _) => self.as_geometry_collection().signed_area(), + Rect(_) => self.as_rect().signed_area(), + Geometry(_) => self.as_geometry().signed_area(), }; Ok(result) } fn unsigned_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().unsigned_area(), - LineString(_, XY) => self.as_line_string().unsigned_area(), - Polygon(_, XY) => self.as_polygon().unsigned_area(), - MultiPoint(_, XY) => self.as_multi_point().unsigned_area(), - MultiLineString(_, XY) => self.as_multi_line_string().unsigned_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().unsigned_area(), - Mixed(_, XY) => self.as_mixed().unsigned_area(), - GeometryCollection(_, XY) => self.as_geometry_collection().unsigned_area(), - Rect(XY) => self.as_rect().unsigned_area(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().unsigned_area(), + LineString(_, _) => self.as_line_string().unsigned_area(), + Polygon(_, _) => self.as_polygon().unsigned_area(), + MultiPoint(_, _) => self.as_multi_point().unsigned_area(), + MultiLineString(_, _) => self.as_multi_line_string().unsigned_area(), + MultiPolygon(_, _) => self.as_multi_polygon().unsigned_area(), + Mixed(_, _) => self.as_mixed().unsigned_area(), + GeometryCollection(_, _) => self.as_geometry_collection().unsigned_area(), + Rect(_) => self.as_rect().unsigned_area(), + Geometry(_) => self.as_geometry().unsigned_area(), }; Ok(result) } @@ -154,38 +153,36 @@ impl Area for &dyn ChunkedNativeArray { type Output = Result>; fn signed_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().signed_area(), - LineString(_, XY) => self.as_line_string().signed_area(), - Polygon(_, XY) => self.as_polygon().signed_area(), - MultiPoint(_, XY) => self.as_multi_point().signed_area(), - MultiLineString(_, XY) => self.as_multi_line_string().signed_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().signed_area(), - Mixed(_, XY) => self.as_mixed().signed_area(), - GeometryCollection(_, XY) => self.as_geometry_collection().signed_area(), - Rect(XY) => self.as_rect().signed_area(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().signed_area(), + LineString(_, _) => self.as_line_string().signed_area(), + Polygon(_, _) => self.as_polygon().signed_area(), + MultiPoint(_, _) => self.as_multi_point().signed_area(), + MultiLineString(_, _) => self.as_multi_line_string().signed_area(), + MultiPolygon(_, _) => self.as_multi_polygon().signed_area(), + Mixed(_, _) => self.as_mixed().signed_area(), + GeometryCollection(_, _) => self.as_geometry_collection().signed_area(), + Rect(_) => self.as_rect().signed_area(), + Geometry(_) => self.as_geometry().unsigned_area(), } } fn unsigned_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().unsigned_area(), - LineString(_, XY) => self.as_line_string().unsigned_area(), - Polygon(_, XY) => self.as_polygon().unsigned_area(), - MultiPoint(_, XY) => self.as_multi_point().unsigned_area(), - MultiLineString(_, XY) => self.as_multi_line_string().unsigned_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().unsigned_area(), - Mixed(_, XY) => self.as_mixed().unsigned_area(), - GeometryCollection(_, XY) => self.as_geometry_collection().unsigned_area(), - Rect(XY) => self.as_rect().unsigned_area(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().unsigned_area(), + LineString(_, _) => self.as_line_string().unsigned_area(), + Polygon(_, _) => self.as_polygon().unsigned_area(), + MultiPoint(_, _) => self.as_multi_point().unsigned_area(), + MultiLineString(_, _) => self.as_multi_line_string().unsigned_area(), + MultiPolygon(_, _) => self.as_multi_polygon().unsigned_area(), + Mixed(_, _) => self.as_mixed().unsigned_area(), + GeometryCollection(_, _) => self.as_geometry_collection().unsigned_area(), + Rect(_) => self.as_rect().unsigned_area(), + Geometry(_) => self.as_geometry().unsigned_area(), } } } diff --git a/rust/geoarrow/src/algorithm/geo/bounding_rect.rs b/rust/geoarrow/src/algorithm/geo/bounding_rect.rs index 509b5b68..c50af085 100644 --- a/rust/geoarrow/src/algorithm/geo/bounding_rect.rs +++ b/rust/geoarrow/src/algorithm/geo/bounding_rect.rs @@ -1,7 +1,7 @@ use crate::array::*; use crate::chunked_array::{ChunkedGeometryArray, ChunkedNativeArray}; use crate::datatypes::{Dimension, NativeType}; -use crate::error::{GeoArrowError, Result}; +use crate::error::Result; use crate::trait_::ArrayAccessor; use crate::NativeArray; use geo::algorithm::bounding_rect::BoundingRect as GeoBoundingRect; @@ -44,7 +44,20 @@ impl BoundingRect for PointArray { .map(|maybe_g| maybe_g.map(|geom| geom.bounding_rect())) .collect(); - (output_geoms, Dimension::XY).into() + RectBuilder::from_nullable_rects( + output_geoms.iter().map(|x| x.as_ref()), + Dimension::XY, + self.metadata().clone(), + ) + .finish() + } +} + +impl BoundingRect for RectArray { + type Output = RectArray; + + fn bounding_rect(&self) -> Self::Output { + self.clone() } } @@ -60,7 +73,12 @@ macro_rules! iter_geo_impl { .map(|maybe_g| maybe_g.and_then(|geom| geom.bounding_rect())) .collect(); - (output_geoms, Dimension::XY).into() + RectBuilder::from_nullable_rects( + output_geoms.iter().map(|x| x.as_ref()), + Dimension::XY, + self.metadata().clone(), + ) + .finish() } } }; @@ -73,24 +91,25 @@ iter_geo_impl!(MultiLineStringArray); iter_geo_impl!(MultiPolygonArray); iter_geo_impl!(MixedGeometryArray); iter_geo_impl!(GeometryCollectionArray); +iter_geo_impl!(GeometryArray); impl BoundingRect for &dyn NativeArray { type Output = Result; fn bounding_rect(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().bounding_rect(), - LineString(_, XY) => self.as_line_string().bounding_rect(), - Polygon(_, XY) => self.as_polygon().bounding_rect(), - MultiPoint(_, XY) => self.as_multi_point().bounding_rect(), - MultiLineString(_, XY) => self.as_multi_line_string().bounding_rect(), - MultiPolygon(_, XY) => self.as_multi_polygon().bounding_rect(), - Mixed(_, XY) => self.as_mixed().bounding_rect(), - GeometryCollection(_, XY) => self.as_geometry_collection().bounding_rect(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().bounding_rect(), + LineString(_, _) => self.as_line_string().bounding_rect(), + Polygon(_, _) => self.as_polygon().bounding_rect(), + MultiPoint(_, _) => self.as_multi_point().bounding_rect(), + MultiLineString(_, _) => self.as_multi_line_string().bounding_rect(), + MultiPolygon(_, _) => self.as_multi_polygon().bounding_rect(), + Mixed(_, _) => self.as_mixed().bounding_rect(), + GeometryCollection(_, _) => self.as_geometry_collection().bounding_rect(), + Geometry(_) => self.as_geometry().bounding_rect(), + Rect(_) => self.as_rect().bounding_rect(), }; Ok(result) } @@ -109,19 +128,19 @@ impl BoundingRect for &dyn ChunkedNativeArray { type Output = Result>; fn bounding_rect(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().bounding_rect(), - LineString(_, XY) => self.as_line_string().bounding_rect(), - Polygon(_, XY) => self.as_polygon().bounding_rect(), - MultiPoint(_, XY) => self.as_multi_point().bounding_rect(), - MultiLineString(_, XY) => self.as_multi_line_string().bounding_rect(), - MultiPolygon(_, XY) => self.as_multi_polygon().bounding_rect(), - Mixed(_, XY) => self.as_mixed().bounding_rect(), - GeometryCollection(_, XY) => self.as_geometry_collection().bounding_rect(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().bounding_rect(), + LineString(_, _) => self.as_line_string().bounding_rect(), + Polygon(_, _) => self.as_polygon().bounding_rect(), + MultiPoint(_, _) => self.as_multi_point().bounding_rect(), + MultiLineString(_, _) => self.as_multi_line_string().bounding_rect(), + MultiPolygon(_, _) => self.as_multi_polygon().bounding_rect(), + Mixed(_, _) => self.as_mixed().bounding_rect(), + GeometryCollection(_, _) => self.as_geometry_collection().bounding_rect(), + Geometry(_) => self.as_geometry().bounding_rect(), + Rect(_) => self.as_rect().bounding_rect(), } } } diff --git a/rust/geoarrow/src/algorithm/geo/center.rs b/rust/geoarrow/src/algorithm/geo/center.rs index a2fffd53..61a03b67 100644 --- a/rust/geoarrow/src/algorithm/geo/center.rs +++ b/rust/geoarrow/src/algorithm/geo/center.rs @@ -1,7 +1,7 @@ use crate::array::*; use crate::chunked_array::{ChunkedGeometryArray, ChunkedNativeArray, ChunkedPointArray}; use crate::datatypes::{Dimension, NativeType}; -use crate::error::{GeoArrowError, Result}; +use crate::error::Result; use crate::trait_::ArrayAccessor; use crate::NativeArray; use geo::BoundingRect; @@ -23,6 +23,23 @@ impl Center for PointArray { } } +impl Center for RectArray { + type Output = PointArray; + + fn center(&self) -> Self::Output { + let mut output_array = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.len(), + self.coord_type(), + self.metadata().clone(), + ); + self.iter_geo().for_each(|maybe_g| { + output_array.push_coord(maybe_g.map(|g| g.bounding_rect().center()).as_ref()) + }); + output_array.into() + } +} + /// Implementation that iterates over geo objects macro_rules! iter_geo_impl { ($type:ty) => { @@ -30,7 +47,12 @@ macro_rules! iter_geo_impl { type Output = PointArray; fn center(&self) -> Self::Output { - let mut output_array = PointBuilder::with_capacity(Dimension::XY, self.len()); + let mut output_array = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.len(), + self.coord_type(), + self.metadata().clone(), + ); self.iter_geo().for_each(|maybe_g| { output_array.push_coord( maybe_g @@ -51,24 +73,25 @@ iter_geo_impl!(MultiLineStringArray); iter_geo_impl!(MultiPolygonArray); iter_geo_impl!(MixedGeometryArray); iter_geo_impl!(GeometryCollectionArray); +iter_geo_impl!(GeometryArray); impl Center for &dyn NativeArray { type Output = Result; fn center(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().center(), - LineString(_, XY) => self.as_line_string().center(), - Polygon(_, XY) => self.as_polygon().center(), - MultiPoint(_, XY) => self.as_multi_point().center(), - MultiLineString(_, XY) => self.as_multi_line_string().center(), - MultiPolygon(_, XY) => self.as_multi_polygon().center(), - Mixed(_, XY) => self.as_mixed().center(), - GeometryCollection(_, XY) => self.as_geometry_collection().center(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().center(), + LineString(_, _) => self.as_line_string().center(), + Polygon(_, _) => self.as_polygon().center(), + MultiPoint(_, _) => self.as_multi_point().center(), + MultiLineString(_, _) => self.as_multi_line_string().center(), + MultiPolygon(_, _) => self.as_multi_polygon().center(), + Mixed(_, _) => self.as_mixed().center(), + GeometryCollection(_, _) => self.as_geometry_collection().center(), + Rect(_) => self.as_rect().center(), + Geometry(_) => self.as_geometry().center(), }; Ok(result) } @@ -86,19 +109,19 @@ impl Center for &dyn ChunkedNativeArray { type Output = Result; fn center(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().center(), - LineString(_, XY) => self.as_line_string().center(), - Polygon(_, XY) => self.as_polygon().center(), - MultiPoint(_, XY) => self.as_multi_point().center(), - MultiLineString(_, XY) => self.as_multi_line_string().center(), - MultiPolygon(_, XY) => self.as_multi_polygon().center(), - Mixed(_, XY) => self.as_mixed().center(), - GeometryCollection(_, XY) => self.as_geometry_collection().center(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().center(), + LineString(_, _) => self.as_line_string().center(), + Polygon(_, _) => self.as_polygon().center(), + MultiPoint(_, _) => self.as_multi_point().center(), + MultiLineString(_, _) => self.as_multi_line_string().center(), + MultiPolygon(_, _) => self.as_multi_polygon().center(), + Mixed(_, _) => self.as_mixed().center(), + GeometryCollection(_, _) => self.as_geometry_collection().center(), + Rect(_) => self.as_rect().center(), + Geometry(_) => self.as_geometry().center(), } } } diff --git a/rust/geoarrow/src/algorithm/geo/centroid.rs b/rust/geoarrow/src/algorithm/geo/centroid.rs index 41c4c72f..fdb77e59 100644 --- a/rust/geoarrow/src/algorithm/geo/centroid.rs +++ b/rust/geoarrow/src/algorithm/geo/centroid.rs @@ -1,7 +1,7 @@ use crate::array::*; use crate::chunked_array::{ChunkedGeometryArray, ChunkedNativeArray, ChunkedPointArray}; use crate::datatypes::{Dimension, NativeType}; -use crate::error::{GeoArrowError, Result}; +use crate::error::Result; use crate::trait_::ArrayAccessor; use crate::NativeArray; use geo::algorithm::centroid::Centroid as GeoCentroid; @@ -74,6 +74,22 @@ impl Centroid for PointArray { } } +impl Centroid for RectArray { + type Output = PointArray; + + fn centroid(&self) -> Self::Output { + let mut output_array = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.len(), + self.coord_type(), + self.metadata().clone(), + ); + self.iter_geo() + .for_each(|maybe_g| output_array.push_point(maybe_g.map(|g| g.centroid()).as_ref())); + output_array.into() + } +} + /// Implementation that iterates over geo objects macro_rules! iter_geo_impl { ($type:ty) => { @@ -81,7 +97,12 @@ macro_rules! iter_geo_impl { type Output = PointArray; fn centroid(&self) -> Self::Output { - let mut output_array = PointBuilder::with_capacity(Dimension::XY, self.len()); + let mut output_array = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.len(), + self.coord_type(), + self.metadata().clone(), + ); self.iter_geo().for_each(|maybe_g| { output_array.push_point(maybe_g.and_then(|g| g.centroid()).as_ref()) }); @@ -98,24 +119,25 @@ iter_geo_impl!(MultiLineStringArray); iter_geo_impl!(MultiPolygonArray); iter_geo_impl!(MixedGeometryArray); iter_geo_impl!(GeometryCollectionArray); +iter_geo_impl!(GeometryArray); impl Centroid for &dyn NativeArray { type Output = Result; fn centroid(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().centroid(), - LineString(_, XY) => self.as_line_string().centroid(), - Polygon(_, XY) => self.as_polygon().centroid(), - MultiPoint(_, XY) => self.as_multi_point().centroid(), - MultiLineString(_, XY) => self.as_multi_line_string().centroid(), - MultiPolygon(_, XY) => self.as_multi_polygon().centroid(), - Mixed(_, XY) => self.as_mixed().centroid(), - GeometryCollection(_, XY) => self.as_geometry_collection().centroid(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().centroid(), + LineString(_, _) => self.as_line_string().centroid(), + Polygon(_, _) => self.as_polygon().centroid(), + MultiPoint(_, _) => self.as_multi_point().centroid(), + MultiLineString(_, _) => self.as_multi_line_string().centroid(), + MultiPolygon(_, _) => self.as_multi_polygon().centroid(), + Mixed(_, _) => self.as_mixed().centroid(), + GeometryCollection(_, _) => self.as_geometry_collection().centroid(), + Rect(_) => self.as_rect().centroid(), + Geometry(_) => self.as_geometry().centroid(), }; Ok(result) } @@ -133,19 +155,19 @@ impl Centroid for &dyn ChunkedNativeArray { type Output = Result; fn centroid(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().centroid(), - LineString(_, XY) => self.as_line_string().centroid(), - Polygon(_, XY) => self.as_polygon().centroid(), - MultiPoint(_, XY) => self.as_multi_point().centroid(), - MultiLineString(_, XY) => self.as_multi_line_string().centroid(), - MultiPolygon(_, XY) => self.as_multi_polygon().centroid(), - Mixed(_, XY) => self.as_mixed().centroid(), - GeometryCollection(_, XY) => self.as_geometry_collection().centroid(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().centroid(), + LineString(_, _) => self.as_line_string().centroid(), + Polygon(_, _) => self.as_polygon().centroid(), + MultiPoint(_, _) => self.as_multi_point().centroid(), + MultiLineString(_, _) => self.as_multi_line_string().centroid(), + MultiPolygon(_, _) => self.as_multi_polygon().centroid(), + Mixed(_, _) => self.as_mixed().centroid(), + GeometryCollection(_, _) => self.as_geometry_collection().centroid(), + Rect(_) => self.as_rect().centroid(), + Geometry(_) => self.as_geometry().centroid(), } } } diff --git a/rust/geoarrow/src/algorithm/geo/chaikin_smoothing.rs b/rust/geoarrow/src/algorithm/geo/chaikin_smoothing.rs index 2a97a629..b62b3e30 100644 --- a/rust/geoarrow/src/algorithm/geo/chaikin_smoothing.rs +++ b/rust/geoarrow/src/algorithm/geo/chaikin_smoothing.rs @@ -28,7 +28,7 @@ pub trait ChaikinSmoothing { /// Implementation that iterates over geo objects macro_rules! iter_geo_impl { - ($type:ty, $geo_type:ty) => { + ($type:ty, $builder_type:ty, $method:ident, $geo_type:ty) => { impl ChaikinSmoothing for $type { type Output = Self; @@ -40,33 +40,78 @@ macro_rules! iter_geo_impl { }) .collect(); - (output_geoms, Dimension::XY).into() + <$builder_type>::$method( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata.clone(), + ) + .finish() } } }; } -iter_geo_impl!(LineStringArray, geo::LineString); -iter_geo_impl!(PolygonArray, geo::Polygon); -iter_geo_impl!(MultiLineStringArray, geo::MultiLineString); -iter_geo_impl!(MultiPolygonArray, geo::MultiPolygon); +iter_geo_impl!( + LineStringArray, + LineStringBuilder, + from_nullable_line_strings, + geo::LineString +); +iter_geo_impl!( + PolygonArray, + PolygonBuilder, + from_nullable_polygons, + geo::Polygon +); +iter_geo_impl!( + MultiLineStringArray, + MultiLineStringBuilder, + from_nullable_multi_line_strings, + geo::MultiLineString +); +iter_geo_impl!( + MultiPolygonArray, + MultiPolygonBuilder, + from_nullable_multi_polygons, + geo::MultiPolygon +); + +impl ChaikinSmoothing for GeometryArray { + type Output = Result; + + fn chaikin_smoothing(&self, n_iterations: u32) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|geom| geom.chaikin_smoothing(n_iterations.try_into().unwrap())) + }) + .collect(); + + Ok(GeometryBuilder::from_nullable_geometries( + output_geoms.as_slice(), + self.coord_type(), + self.metadata.clone(), + false, + )? + .finish()) + } +} impl ChaikinSmoothing for &dyn NativeArray { type Output = Result>; fn chaikin_smoothing(&self, n_iterations: u32) -> Self::Output { - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - LineString(_, XY) => Arc::new(self.as_line_string().chaikin_smoothing(n_iterations)), - Polygon(_, XY) => Arc::new(self.as_polygon().chaikin_smoothing(n_iterations)), - MultiLineString(_, XY) => { + LineString(_, _) => Arc::new(self.as_line_string().chaikin_smoothing(n_iterations)), + Polygon(_, _) => Arc::new(self.as_polygon().chaikin_smoothing(n_iterations)), + MultiLineString(_, _) => { Arc::new(self.as_multi_line_string().chaikin_smoothing(n_iterations)) } - MultiPolygon(_, XY) => { - Arc::new(self.as_multi_polygon().chaikin_smoothing(n_iterations)) - } + MultiPolygon(_, _) => Arc::new(self.as_multi_polygon().chaikin_smoothing(n_iterations)), + Geometry(_) => Arc::new(self.as_geometry().chaikin_smoothing(n_iterations)?), _ => return Err(GeoArrowError::IncorrectType("".into())), }; Ok(result) @@ -96,18 +141,15 @@ impl ChaikinSmoothing for &dyn ChunkedNativeArray { type Output = Result>; fn chaikin_smoothing(&self, n_iterations: u32) -> Self::Output { - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - LineString(_, XY) => Arc::new(self.as_line_string().chaikin_smoothing(n_iterations)), - Polygon(_, XY) => Arc::new(self.as_polygon().chaikin_smoothing(n_iterations)), - MultiLineString(_, XY) => { + LineString(_, _) => Arc::new(self.as_line_string().chaikin_smoothing(n_iterations)), + Polygon(_, _) => Arc::new(self.as_polygon().chaikin_smoothing(n_iterations)), + MultiLineString(_, _) => { Arc::new(self.as_multi_line_string().chaikin_smoothing(n_iterations)) } - MultiPolygon(_, XY) => { - Arc::new(self.as_multi_polygon().chaikin_smoothing(n_iterations)) - } + MultiPolygon(_, _) => Arc::new(self.as_multi_polygon().chaikin_smoothing(n_iterations)), _ => return Err(GeoArrowError::IncorrectType("".into())), }; Ok(result) diff --git a/rust/geoarrow/src/algorithm/geo/chamberlain_duquette_area.rs b/rust/geoarrow/src/algorithm/geo/chamberlain_duquette_area.rs index 99aeb07c..6ea74446 100644 --- a/rust/geoarrow/src/algorithm/geo/chamberlain_duquette_area.rs +++ b/rust/geoarrow/src/algorithm/geo/chamberlain_duquette_area.rs @@ -1,8 +1,8 @@ use crate::algorithm::geo::utils::zeroes; use crate::array::*; use crate::chunked_array::{ChunkedArray, ChunkedGeometryArray, ChunkedNativeArray}; -use crate::datatypes::{Dimension, NativeType}; -use crate::error::{GeoArrowError, Result}; +use crate::datatypes::NativeType; +use crate::error::Result; use crate::trait_::ArrayAccessor; use crate::NativeArray; use arrow_array::builder::Float64Builder; @@ -119,50 +119,52 @@ iter_geo_impl!(PolygonArray); iter_geo_impl!(MultiPolygonArray); iter_geo_impl!(MixedGeometryArray); iter_geo_impl!(GeometryCollectionArray); +iter_geo_impl!(GeometryArray); +iter_geo_impl!(RectArray); impl ChamberlainDuquetteArea for &dyn NativeArray { type Output = Result; fn chamberlain_duquette_signed_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().chamberlain_duquette_signed_area(), - LineString(_, XY) => self.as_line_string().chamberlain_duquette_signed_area(), - Polygon(_, XY) => self.as_polygon().chamberlain_duquette_signed_area(), - MultiPoint(_, XY) => self.as_multi_point().chamberlain_duquette_signed_area(), - MultiLineString(_, XY) => self + Point(_, _) => self.as_point().chamberlain_duquette_signed_area(), + LineString(_, _) => self.as_line_string().chamberlain_duquette_signed_area(), + Polygon(_, _) => self.as_polygon().chamberlain_duquette_signed_area(), + MultiPoint(_, _) => self.as_multi_point().chamberlain_duquette_signed_area(), + MultiLineString(_, _) => self .as_multi_line_string() .chamberlain_duquette_signed_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().chamberlain_duquette_signed_area(), - Mixed(_, XY) => self.as_mixed().chamberlain_duquette_signed_area(), - GeometryCollection(_, XY) => self + MultiPolygon(_, _) => self.as_multi_polygon().chamberlain_duquette_signed_area(), + Mixed(_, _) => self.as_mixed().chamberlain_duquette_signed_area(), + GeometryCollection(_, _) => self .as_geometry_collection() .chamberlain_duquette_signed_area(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Rect(_) => self.as_rect().chamberlain_duquette_signed_area(), + Geometry(_) => self.as_geometry().chamberlain_duquette_signed_area(), }; Ok(result) } fn chamberlain_duquette_unsigned_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().chamberlain_duquette_unsigned_area(), - LineString(_, XY) => self.as_line_string().chamberlain_duquette_unsigned_area(), - Polygon(_, XY) => self.as_polygon().chamberlain_duquette_unsigned_area(), - MultiPoint(_, XY) => self.as_multi_point().chamberlain_duquette_unsigned_area(), - MultiLineString(_, XY) => self + Point(_, _) => self.as_point().chamberlain_duquette_unsigned_area(), + LineString(_, _) => self.as_line_string().chamberlain_duquette_unsigned_area(), + Polygon(_, _) => self.as_polygon().chamberlain_duquette_unsigned_area(), + MultiPoint(_, _) => self.as_multi_point().chamberlain_duquette_unsigned_area(), + MultiLineString(_, _) => self .as_multi_line_string() .chamberlain_duquette_unsigned_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().chamberlain_duquette_unsigned_area(), - Mixed(_, XY) => self.as_mixed().chamberlain_duquette_unsigned_area(), - GeometryCollection(_, XY) => self + MultiPolygon(_, _) => self.as_multi_polygon().chamberlain_duquette_unsigned_area(), + Mixed(_, _) => self.as_mixed().chamberlain_duquette_unsigned_area(), + GeometryCollection(_, _) => self .as_geometry_collection() .chamberlain_duquette_unsigned_area(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Rect(_) => self.as_rect().chamberlain_duquette_unsigned_area(), + Geometry(_) => self.as_geometry().chamberlain_duquette_unsigned_area(), }; Ok(result) } @@ -194,44 +196,44 @@ impl ChamberlainDuquetteArea for &dyn ChunkedNativeArray { type Output = Result>; fn chamberlain_duquette_signed_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().chamberlain_duquette_signed_area(), - LineString(_, XY) => self.as_line_string().chamberlain_duquette_signed_area(), - Polygon(_, XY) => self.as_polygon().chamberlain_duquette_signed_area(), - MultiPoint(_, XY) => self.as_multi_point().chamberlain_duquette_signed_area(), - MultiLineString(_, XY) => self + Point(_, _) => self.as_point().chamberlain_duquette_signed_area(), + LineString(_, _) => self.as_line_string().chamberlain_duquette_signed_area(), + Polygon(_, _) => self.as_polygon().chamberlain_duquette_signed_area(), + MultiPoint(_, _) => self.as_multi_point().chamberlain_duquette_signed_area(), + MultiLineString(_, _) => self .as_multi_line_string() .chamberlain_duquette_signed_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().chamberlain_duquette_signed_area(), - Mixed(_, XY) => self.as_mixed().chamberlain_duquette_signed_area(), - GeometryCollection(_, XY) => self + MultiPolygon(_, _) => self.as_multi_polygon().chamberlain_duquette_signed_area(), + Mixed(_, _) => self.as_mixed().chamberlain_duquette_signed_area(), + GeometryCollection(_, _) => self .as_geometry_collection() .chamberlain_duquette_signed_area(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Rect(_) => self.as_rect().chamberlain_duquette_unsigned_area(), + Geometry(_) => self.as_geometry().chamberlain_duquette_unsigned_area(), } } fn chamberlain_duquette_unsigned_area(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().chamberlain_duquette_unsigned_area(), - LineString(_, XY) => self.as_line_string().chamberlain_duquette_unsigned_area(), - Polygon(_, XY) => self.as_polygon().chamberlain_duquette_unsigned_area(), - MultiPoint(_, XY) => self.as_multi_point().chamberlain_duquette_unsigned_area(), - MultiLineString(_, XY) => self + Point(_, _) => self.as_point().chamberlain_duquette_unsigned_area(), + LineString(_, _) => self.as_line_string().chamberlain_duquette_unsigned_area(), + Polygon(_, _) => self.as_polygon().chamberlain_duquette_unsigned_area(), + MultiPoint(_, _) => self.as_multi_point().chamberlain_duquette_unsigned_area(), + MultiLineString(_, _) => self .as_multi_line_string() .chamberlain_duquette_unsigned_area(), - MultiPolygon(_, XY) => self.as_multi_polygon().chamberlain_duquette_unsigned_area(), - Mixed(_, XY) => self.as_mixed().chamberlain_duquette_unsigned_area(), - GeometryCollection(_, XY) => self + MultiPolygon(_, _) => self.as_multi_polygon().chamberlain_duquette_unsigned_area(), + Mixed(_, _) => self.as_mixed().chamberlain_duquette_unsigned_area(), + GeometryCollection(_, _) => self .as_geometry_collection() .chamberlain_duquette_unsigned_area(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Rect(_) => self.as_rect().chamberlain_duquette_unsigned_area(), + Geometry(_) => self.as_geometry().chamberlain_duquette_unsigned_area(), } } } diff --git a/rust/geoarrow/src/algorithm/geo/contains.rs b/rust/geoarrow/src/algorithm/geo/contains.rs index 15e37ba5..2114b738 100644 --- a/rust/geoarrow/src/algorithm/geo/contains.rs +++ b/rust/geoarrow/src/algorithm/geo/contains.rs @@ -1,17 +1,13 @@ use crate::algorithm::native::{Binary, Unary}; use crate::array::*; -use crate::datatypes::{Dimension, NativeType}; +use crate::datatypes::NativeType; use crate::error::GeoArrowError; -use crate::trait_::{ArrayAccessor, NativeScalar}; +use crate::trait_::NativeScalar; use crate::NativeArray; -use arrow_array::builder::BooleanBuilder; use arrow_array::BooleanArray; use geo::Contains as _Contains; use geo_traits::to_geo::*; -use geo_traits::{ - GeometryCollectionTrait, GeometryTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait, - MultiPolygonTrait, PointTrait, PolygonTrait, -}; +use geo_traits::GeometryTrait; /// Checks if `rhs` is completely contained within `self`. /// More formally, the interior of `rhs` has non-empty @@ -55,23 +51,13 @@ pub trait Contains { // │ 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 { - self.try_binary_boolean(rhs, |left, right| { - Ok(left.to_geo().contains(&right.to_geo())) - }) - .unwrap() - } -} - // Implementation that iterates over geo objects macro_rules! iter_geo_impl { ($first:ty, $second:ty) => { impl<'a> Contains<$second> for $first { fn contains(&self, rhs: &$second) -> BooleanArray { self.try_binary_boolean(rhs, |left, right| { - Ok(left.to_geo().contains(&right.to_geo())) + Ok(left.to_geo_geometry().contains(&right.to_geo_geometry())) }) .unwrap() } @@ -80,11 +66,14 @@ macro_rules! iter_geo_impl { } // Implementations on PointArray +iter_geo_impl!(PointArray, 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); +iter_geo_impl!(PointArray, RectArray); +iter_geo_impl!(PointArray, GeometryArray); // Implementations on LineStringArray iter_geo_impl!(LineStringArray, PointArray); @@ -93,6 +82,8 @@ iter_geo_impl!(LineStringArray, PolygonArray); iter_geo_impl!(LineStringArray, MultiPointArray); iter_geo_impl!(LineStringArray, MultiLineStringArray); iter_geo_impl!(LineStringArray, MultiPolygonArray); +iter_geo_impl!(LineStringArray, RectArray); +iter_geo_impl!(LineStringArray, GeometryArray); // Implementations on PolygonArray iter_geo_impl!(PolygonArray, PointArray); @@ -101,6 +92,8 @@ iter_geo_impl!(PolygonArray, PolygonArray); iter_geo_impl!(PolygonArray, MultiPointArray); iter_geo_impl!(PolygonArray, MultiLineStringArray); iter_geo_impl!(PolygonArray, MultiPolygonArray); +iter_geo_impl!(PolygonArray, RectArray); +iter_geo_impl!(PolygonArray, GeometryArray); // Implementations on MultiPointArray iter_geo_impl!(MultiPointArray, PointArray); @@ -109,6 +102,8 @@ iter_geo_impl!(MultiPointArray, PolygonArray); iter_geo_impl!(MultiPointArray, MultiPointArray); iter_geo_impl!(MultiPointArray, MultiLineStringArray); iter_geo_impl!(MultiPointArray, MultiPolygonArray); +iter_geo_impl!(MultiPointArray, RectArray); +iter_geo_impl!(MultiPointArray, GeometryArray); // Implementations on MultiLineStringArray iter_geo_impl!(MultiLineStringArray, PointArray); @@ -117,6 +112,8 @@ iter_geo_impl!(MultiLineStringArray, PolygonArray); iter_geo_impl!(MultiLineStringArray, MultiPointArray); iter_geo_impl!(MultiLineStringArray, MultiLineStringArray); iter_geo_impl!(MultiLineStringArray, MultiPolygonArray); +iter_geo_impl!(MultiLineStringArray, RectArray); +iter_geo_impl!(MultiLineStringArray, GeometryArray); // Implementations on MultiPolygonArray iter_geo_impl!(MultiPolygonArray, PointArray); @@ -125,18 +122,20 @@ iter_geo_impl!(MultiPolygonArray, PolygonArray); iter_geo_impl!(MultiPolygonArray, MultiPointArray); iter_geo_impl!(MultiPolygonArray, MultiLineStringArray); iter_geo_impl!(MultiPolygonArray, MultiPolygonArray); +iter_geo_impl!(MultiPolygonArray, RectArray); +iter_geo_impl!(MultiPolygonArray, GeometryArray); // ┌─────────────────────────────────┐ // │ Implementations for RHS scalars │ // └─────────────────────────────────┘ -pub trait ContainsPoint { +pub trait ContainsGeometry { fn contains(&self, rhs: &Rhs) -> BooleanArray; } -impl> ContainsPoint for PointArray { +impl> ContainsGeometry for PointArray { fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_point(); + let rhs = rhs.to_geometry(); self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) .unwrap() } @@ -144,11 +143,13 @@ impl> ContainsPoint for PointArray { macro_rules! impl_contains_point { ($array:ty) => { - impl> ContainsPoint for $array { + impl> ContainsGeometry for $array { fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_point(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() + let rhs = rhs.to_geometry(); + self.try_unary_boolean::<_, GeoArrowError>(|geom| { + Ok(geom.to_geo_geometry().contains(&rhs)) + }) + .unwrap() } } }; @@ -162,412 +163,23 @@ impl_contains_point!(MultiPolygonArray); impl_contains_point!(MixedGeometryArray); impl_contains_point!(GeometryCollectionArray); -impl> ContainsPoint for &dyn NativeArray { - fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; - use NativeType::*; - - match self.data_type() { - Point(_, XY) => ContainsPoint::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsPoint::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsPoint::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => ContainsPoint::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => ContainsPoint::contains(self.as_multi_line_string(), rhs), - MultiPolygon(_, XY) => ContainsPoint::contains(self.as_multi_polygon(), rhs), - Mixed(_, XY) => ContainsPoint::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { - ContainsPoint::contains(self.as_geometry_collection(), rhs) - } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), - } - } -} - -pub trait ContainsLineString { - fn contains(&self, rhs: &Rhs) -> BooleanArray; -} - -impl> ContainsLineString for PointArray { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_line_string(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } -} - -macro_rules! impl_contains_line_string { - ($array:ty) => { - impl> ContainsLineString for $array { - fn contains(&self, rhs: &G) -> BooleanArray { - let mut output_array = BooleanBuilder::with_capacity(self.len()); - let rhs = rhs.to_line_string(); - - self.iter_geo().for_each(|maybe_point| { - let output = maybe_point.map(|point| point.contains(&rhs)); - output_array.append_option(output) - }); - - output_array.finish() - } - } - }; -} - -impl_contains_line_string!(LineStringArray); -impl_contains_line_string!(PolygonArray); -impl_contains_line_string!(MultiPointArray); -impl_contains_line_string!(MultiLineStringArray); -impl_contains_line_string!(MultiPolygonArray); -impl_contains_line_string!(MixedGeometryArray); -impl_contains_line_string!(GeometryCollectionArray); - -impl> ContainsLineString for &dyn NativeArray { - fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; - use NativeType::*; - - match self.data_type() { - Point(_, XY) => ContainsLineString::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsLineString::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsLineString::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => ContainsLineString::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => { - ContainsLineString::contains(self.as_multi_line_string(), rhs) - } - MultiPolygon(_, XY) => ContainsLineString::contains(self.as_multi_polygon(), rhs), - Mixed(_, XY) => ContainsLineString::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { - ContainsLineString::contains(self.as_geometry_collection(), rhs) - } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), - } - } -} - -pub trait ContainsPolygon { - fn contains(&self, rhs: &Rhs) -> BooleanArray; -} - -impl> ContainsPolygon for PointArray { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_polygon(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } -} - -macro_rules! impl_contains_polygon { - ($array:ty) => { - impl> ContainsPolygon for $array { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_polygon(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } - } - }; -} - -impl_contains_polygon!(LineStringArray); -impl_contains_polygon!(PolygonArray); -impl_contains_polygon!(MultiPointArray); -impl_contains_polygon!(MultiLineStringArray); -impl_contains_polygon!(MultiPolygonArray); -impl_contains_polygon!(MixedGeometryArray); -impl_contains_polygon!(GeometryCollectionArray); - -impl> ContainsPolygon for &dyn NativeArray { - fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; - use NativeType::*; - - match self.data_type() { - Point(_, XY) => ContainsPolygon::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsPolygon::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsPolygon::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => ContainsPolygon::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => ContainsPolygon::contains(self.as_multi_line_string(), rhs), - MultiPolygon(_, XY) => ContainsPolygon::contains(self.as_multi_polygon(), rhs), - Mixed(_, XY) => ContainsPolygon::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { - ContainsPolygon::contains(self.as_geometry_collection(), rhs) - } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), - } - } -} - -pub trait ContainsMultiPoint { - fn contains(&self, rhs: &Rhs) -> BooleanArray; -} - -impl> ContainsMultiPoint for PointArray { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_multi_point(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } -} - -macro_rules! impl_contains_multi_point { - ($array:ty) => { - impl> ContainsMultiPoint for $array { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_multi_point(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } - } - }; -} - -impl_contains_multi_point!(LineStringArray); -impl_contains_multi_point!(PolygonArray); -impl_contains_multi_point!(MultiPointArray); -impl_contains_multi_point!(MultiLineStringArray); -impl_contains_multi_point!(MultiPolygonArray); -impl_contains_multi_point!(MixedGeometryArray); -impl_contains_multi_point!(GeometryCollectionArray); - -impl> ContainsMultiPoint for &dyn NativeArray { - fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; - use NativeType::*; - - match self.data_type() { - Point(_, XY) => ContainsMultiPoint::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsMultiPoint::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsMultiPoint::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => ContainsMultiPoint::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => { - ContainsMultiPoint::contains(self.as_multi_line_string(), rhs) - } - MultiPolygon(_, XY) => ContainsMultiPoint::contains(self.as_multi_polygon(), rhs), - Mixed(_, XY) => ContainsMultiPoint::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { - ContainsMultiPoint::contains(self.as_geometry_collection(), rhs) - } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), - } - } -} - -pub trait ContainsMultiLineString { - fn contains(&self, rhs: &Rhs) -> BooleanArray; -} - -impl> ContainsMultiLineString for PointArray { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_multi_line_string(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } -} - -macro_rules! impl_contains_multi_line_string { - ($array:ty) => { - impl> ContainsMultiLineString for $array { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_multi_line_string(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } - } - }; -} - -impl_contains_multi_line_string!(LineStringArray); -impl_contains_multi_line_string!(PolygonArray); -impl_contains_multi_line_string!(MultiPointArray); -impl_contains_multi_line_string!(MultiLineStringArray); -impl_contains_multi_line_string!(MultiPolygonArray); -impl_contains_multi_line_string!(MixedGeometryArray); -impl_contains_multi_line_string!(GeometryCollectionArray); - -impl> ContainsMultiLineString for &dyn NativeArray { - fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; - use NativeType::*; - - match self.data_type() { - Point(_, XY) => ContainsMultiLineString::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsMultiLineString::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsMultiLineString::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => ContainsMultiLineString::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => { - ContainsMultiLineString::contains(self.as_multi_line_string(), rhs) - } - MultiPolygon(_, XY) => ContainsMultiLineString::contains(self.as_multi_polygon(), rhs), - Mixed(_, XY) => ContainsMultiLineString::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { - ContainsMultiLineString::contains(self.as_geometry_collection(), rhs) - } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), - } - } -} - -pub trait ContainsMultiPolygon { - fn contains(&self, rhs: &Rhs) -> BooleanArray; -} - -impl> ContainsMultiPolygon for PointArray { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_multi_polygon(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } -} - -macro_rules! impl_contains_multi_polygon { - ($array:ty) => { - impl> ContainsMultiPolygon for $array { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_multi_polygon(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } - } - }; -} - -impl_contains_multi_polygon!(LineStringArray); -impl_contains_multi_polygon!(PolygonArray); -impl_contains_multi_polygon!(MultiPointArray); -impl_contains_multi_polygon!(MultiLineStringArray); -impl_contains_multi_polygon!(MultiPolygonArray); -impl_contains_multi_polygon!(MixedGeometryArray); -impl_contains_multi_polygon!(GeometryCollectionArray); - -impl> ContainsMultiPolygon for &dyn NativeArray { - fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; - use NativeType::*; - - match self.data_type() { - Point(_, XY) => ContainsMultiPolygon::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsMultiPolygon::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsMultiPolygon::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => ContainsMultiPolygon::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => { - ContainsMultiPolygon::contains(self.as_multi_line_string(), rhs) - } - MultiPolygon(_, XY) => ContainsMultiPolygon::contains(self.as_multi_polygon(), rhs), - Mixed(_, XY) => ContainsMultiPolygon::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { - ContainsMultiPolygon::contains(self.as_geometry_collection(), rhs) - } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), - } - } -} - -pub trait ContainsGeometry { - fn contains(&self, rhs: &Rhs) -> BooleanArray; -} - -impl> ContainsGeometry for PointArray { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_geometry(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } -} - -macro_rules! impl_contains_geometry { - ($array:ty) => { - impl> ContainsGeometry for $array { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_geometry(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } - } - }; -} - -impl_contains_geometry!(LineStringArray); -impl_contains_geometry!(PolygonArray); -// impl_contains_geometry!(MultiPointArray); // Not implemented in geo -impl_contains_geometry!(MultiLineStringArray); -// impl_contains_geometry!(MultiPolygonArray); // Not implemented in geo -impl_contains_geometry!(MixedGeometryArray); -impl_contains_geometry!(GeometryCollectionArray); - impl> ContainsGeometry for &dyn NativeArray { fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => ContainsGeometry::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsGeometry::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsGeometry::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => todo!(), // ContainsGeometry::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => ContainsGeometry::contains(self.as_multi_line_string(), rhs), - MultiPolygon(_, XY) => todo!(), // ContainsGeometry::contains(self.as_multi_polygon(), rhs), - Mixed(_, XY) => ContainsGeometry::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { + Point(_, _) => ContainsGeometry::contains(self.as_point(), rhs), + LineString(_, _) => ContainsGeometry::contains(self.as_line_string(), rhs), + Polygon(_, _) => ContainsGeometry::contains(self.as_polygon(), rhs), + MultiPoint(_, _) => ContainsGeometry::contains(self.as_multi_point(), rhs), + MultiLineString(_, _) => ContainsGeometry::contains(self.as_multi_line_string(), rhs), + MultiPolygon(_, _) => ContainsGeometry::contains(self.as_multi_polygon(), rhs), + Mixed(_, _) => ContainsGeometry::contains(self.as_mixed(), rhs), + GeometryCollection(_, _) => { ContainsGeometry::contains(self.as_geometry_collection(), rhs) } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), - } - } -} - -pub trait ContainsGeometryCollection { - fn contains(&self, rhs: &Rhs) -> BooleanArray; -} - -impl> ContainsGeometryCollection for PointArray { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_geometry_collection(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } -} - -macro_rules! impl_contains_geometry_collection { - ($array:ty) => { - impl> ContainsGeometryCollection for $array { - fn contains(&self, rhs: &G) -> BooleanArray { - let rhs = rhs.to_geometry_collection(); - self.try_unary_boolean::<_, GeoArrowError>(|geom| Ok(geom.to_geo().contains(&rhs))) - .unwrap() - } - } - }; -} - -impl_contains_geometry_collection!(LineStringArray); -impl_contains_geometry_collection!(PolygonArray); -impl_contains_geometry_collection!(MultiPointArray); -impl_contains_geometry_collection!(MultiLineStringArray); -impl_contains_geometry_collection!(MultiPolygonArray); -impl_contains_geometry_collection!(MixedGeometryArray); -impl_contains_geometry_collection!(GeometryCollectionArray); - -impl> ContainsGeometryCollection for &dyn NativeArray { - fn contains(&self, rhs: &G) -> BooleanArray { - use Dimension::*; - use NativeType::*; - - match self.data_type() { - Point(_, XY) => ContainsGeometryCollection::contains(self.as_point(), rhs), - LineString(_, XY) => ContainsGeometryCollection::contains(self.as_line_string(), rhs), - Polygon(_, XY) => ContainsGeometryCollection::contains(self.as_polygon(), rhs), - MultiPoint(_, XY) => ContainsGeometryCollection::contains(self.as_multi_point(), rhs), - MultiLineString(_, XY) => { - ContainsGeometryCollection::contains(self.as_multi_line_string(), rhs) - } - MultiPolygon(_, XY) => { - ContainsGeometryCollection::contains(self.as_multi_polygon(), rhs) - } - Mixed(_, XY) => ContainsGeometryCollection::contains(self.as_mixed(), rhs), - GeometryCollection(_, XY) => { - ContainsGeometryCollection::contains(self.as_geometry_collection(), rhs) - } - _ => panic!("incorrect type"), // _ => return Err(GeoArrowError::IncorrectType("".into())), + Rect(_) => ContainsGeometry::contains(self.as_mixed(), rhs), + Geometry(_) => ContainsGeometry::contains(self.as_mixed(), rhs), } } } diff --git a/rust/geoarrow/src/algorithm/geo/convex_hull.rs b/rust/geoarrow/src/algorithm/geo/convex_hull.rs index 6185d72b..bf8d6788 100644 --- a/rust/geoarrow/src/algorithm/geo/convex_hull.rs +++ b/rust/geoarrow/src/algorithm/geo/convex_hull.rs @@ -1,7 +1,7 @@ use crate::array::*; use crate::chunked_array::{ChunkedGeometryArray, ChunkedNativeArray, ChunkedPolygonArray}; use crate::datatypes::{Dimension, NativeType}; -use crate::error::{GeoArrowError, Result}; +use crate::error::Result; use crate::trait_::ArrayAccessor; use crate::NativeArray; use geo::algorithm::convex_hull::ConvexHull as GeoConvexHull; @@ -61,7 +61,13 @@ macro_rules! iter_geo_impl { .map(|maybe_g| maybe_g.map(|geom| geom.convex_hull())) .collect(); - (output_geoms, Dimension::XY).into() + PolygonBuilder::from_nullable_polygons( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata().clone(), + ) + .finish() } } }; @@ -76,25 +82,25 @@ iter_geo_impl!(MultiPolygonArray); iter_geo_impl!(MixedGeometryArray); iter_geo_impl!(GeometryCollectionArray); iter_geo_impl!(RectArray); +iter_geo_impl!(GeometryArray); impl ConvexHull for &dyn NativeArray { type Output = Result; fn convex_hull(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => self.as_point().convex_hull(), - LineString(_, XY) => self.as_line_string().convex_hull(), - Polygon(_, XY) => self.as_polygon().convex_hull(), - MultiPoint(_, XY) => self.as_multi_point().convex_hull(), - MultiLineString(_, XY) => self.as_multi_line_string().convex_hull(), - MultiPolygon(_, XY) => self.as_multi_polygon().convex_hull(), - Mixed(_, XY) => self.as_mixed().convex_hull(), - GeometryCollection(_, XY) => self.as_geometry_collection().convex_hull(), - Rect(XY) => self.as_rect().convex_hull(), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().convex_hull(), + LineString(_, _) => self.as_line_string().convex_hull(), + Polygon(_, _) => self.as_polygon().convex_hull(), + MultiPoint(_, _) => self.as_multi_point().convex_hull(), + MultiLineString(_, _) => self.as_multi_line_string().convex_hull(), + MultiPolygon(_, _) => self.as_multi_polygon().convex_hull(), + Mixed(_, _) => self.as_mixed().convex_hull(), + GeometryCollection(_, _) => self.as_geometry_collection().convex_hull(), + Rect(_) => self.as_rect().convex_hull(), + Geometry(_) => self.as_geometry().convex_hull(), }; Ok(result) } @@ -113,20 +119,19 @@ impl ConvexHull for &dyn ChunkedNativeArray { type Output = Result; fn convex_hull(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().convex_hull(), - LineString(_, XY) => self.as_line_string().convex_hull(), - Polygon(_, XY) => self.as_polygon().convex_hull(), - MultiPoint(_, XY) => self.as_multi_point().convex_hull(), - MultiLineString(_, XY) => self.as_multi_line_string().convex_hull(), - MultiPolygon(_, XY) => self.as_multi_polygon().convex_hull(), - Mixed(_, XY) => self.as_mixed().convex_hull(), - GeometryCollection(_, XY) => self.as_geometry_collection().convex_hull(), - Rect(XY) => self.as_rect().convex_hull(), - _ => Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => self.as_point().convex_hull(), + LineString(_, _) => self.as_line_string().convex_hull(), + Polygon(_, _) => self.as_polygon().convex_hull(), + MultiPoint(_, _) => self.as_multi_point().convex_hull(), + MultiLineString(_, _) => self.as_multi_line_string().convex_hull(), + MultiPolygon(_, _) => self.as_multi_polygon().convex_hull(), + Mixed(_, _) => self.as_mixed().convex_hull(), + GeometryCollection(_, _) => self.as_geometry_collection().convex_hull(), + Rect(_) => self.as_rect().convex_hull(), + Geometry(_) => self.as_geometry().convex_hull(), } } } diff --git a/rust/geoarrow/src/algorithm/geo/densify.rs b/rust/geoarrow/src/algorithm/geo/densify.rs index 877b7a9d..5f6d15b5 100644 --- a/rust/geoarrow/src/algorithm/geo/densify.rs +++ b/rust/geoarrow/src/algorithm/geo/densify.rs @@ -6,8 +6,9 @@ use crate::datatypes::{Dimension, NativeType}; use crate::error::{GeoArrowError, Result}; use crate::trait_::ArrayAccessor; use crate::NativeArray; -use geo::Densify as _Densify; use geo::Euclidean; +use geo::{CoordFloat, Densify as _Densify}; +use num_traits::FromPrimitive; /// Return a new linear geometry containing both existing and new interpolated coordinates with /// a maximum distance of `max_distance` between them. @@ -21,7 +22,7 @@ pub trait Densify { /// Implementation that iterates over geo objects macro_rules! iter_geo_impl { - ($type:ty, $geo_type:ty) => { + ($type:ty, $builder_type:ty, $method:ident, $geo_type:ty) => { impl Densify for $type { type Output = $type; @@ -31,29 +32,124 @@ macro_rules! iter_geo_impl { .map(|maybe_g| maybe_g.map(|geom| geom.densify::(max_distance))) .collect(); - (output_geoms, Dimension::XY).into() + <$builder_type>::$method( + output_geoms.as_slice(), + Dimension::XY, + self.coord_type(), + self.metadata.clone(), + ) + .finish() } } }; } -iter_geo_impl!(LineStringArray, geo::LineString); -iter_geo_impl!(PolygonArray, geo::Polygon); -iter_geo_impl!(MultiLineStringArray, geo::MultiLineString); -iter_geo_impl!(MultiPolygonArray, geo::MultiPolygon); +iter_geo_impl!( + LineStringArray, + LineStringBuilder, + from_nullable_line_strings, + geo::LineString +); +iter_geo_impl!( + PolygonArray, + PolygonBuilder, + from_nullable_polygons, + geo::Polygon +); +iter_geo_impl!( + MultiLineStringArray, + MultiLineStringBuilder, + from_nullable_multi_line_strings, + geo::MultiLineString +); +iter_geo_impl!( + MultiPolygonArray, + MultiPolygonBuilder, + from_nullable_multi_polygons, + geo::MultiPolygon +); + +#[repr(transparent)] +struct GeometryDensifyWrapper<'a, T: CoordFloat>(&'a geo::Geometry); + +impl geo::Densify for GeometryDensifyWrapper<'_, F> { + type Output = geo::Geometry; + + fn densify(&self, max_segment_length: F) -> Self::Output + where + MetricSpace: geo::Distance, geo::Point> + geo::InterpolatePoint, + { + match &self.0 { + geo::Geometry::Point(g) => geo::Geometry::Point(*g), + geo::Geometry::LineString(g) => { + geo::Geometry::LineString(g.densify::(max_segment_length)) + } + geo::Geometry::Polygon(g) => { + geo::Geometry::Polygon(g.densify::(max_segment_length)) + } + geo::Geometry::MultiPoint(g) => geo::Geometry::MultiPoint(g.clone()), + geo::Geometry::MultiLineString(g) => { + geo::Geometry::MultiLineString(g.densify::(max_segment_length)) + } + geo::Geometry::MultiPolygon(g) => { + geo::Geometry::MultiPolygon(g.densify::(max_segment_length)) + } + geo::Geometry::Triangle(g) => { + geo::Geometry::Polygon(g.densify::(max_segment_length)) + } + geo::Geometry::Rect(g) => { + geo::Geometry::Polygon(g.densify::(max_segment_length)) + } + geo::Geometry::Line(g) => { + geo::Geometry::LineString(g.densify::(max_segment_length)) + } + geo::Geometry::GeometryCollection(g) => { + let mut output = Vec::with_capacity(g.len()); + for inner_geom in g.iter() { + output.push( + GeometryDensifyWrapper(inner_geom) + .densify::(max_segment_length), + ); + } + geo::Geometry::GeometryCollection(geo::GeometryCollection::new_from(output)) + } + } + } +} + +impl Densify for GeometryArray { + type Output = Result; + + fn densify(&self, max_distance: f64) -> Self::Output { + let output_geoms: Vec> = self + .iter_geo() + .map(|maybe_g| { + maybe_g.map(|geom| GeometryDensifyWrapper(&geom).densify::(max_distance)) + }) + .collect(); + + Ok(GeometryBuilder::from_nullable_geometries( + output_geoms.as_slice(), + self.coord_type(), + self.metadata.clone(), + false, + )? + .finish()) + } +} impl Densify for &dyn NativeArray { type Output = Result>; fn densify(&self, max_distance: f64) -> Self::Output { - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - LineString(_, XY) => Arc::new(self.as_line_string().densify(max_distance)), - Polygon(_, XY) => Arc::new(self.as_polygon().densify(max_distance)), - MultiLineString(_, XY) => Arc::new(self.as_multi_line_string().densify(max_distance)), - MultiPolygon(_, XY) => Arc::new(self.as_multi_polygon().densify(max_distance)), + LineString(_, _) => Arc::new(self.as_line_string().densify(max_distance)), + Polygon(_, _) => Arc::new(self.as_polygon().densify(max_distance)), + MultiLineString(_, _) => Arc::new(self.as_multi_line_string().densify(max_distance)), + MultiPolygon(_, _) => Arc::new(self.as_multi_polygon().densify(max_distance)), + Geometry(_) => Arc::new(self.as_geometry().densify(max_distance)?), _ => return Err(GeoArrowError::IncorrectType("".into())), }; Ok(result) @@ -83,14 +179,13 @@ impl Densify for &dyn ChunkedNativeArray { type Output = Result>; fn densify(&self, max_distance: f64) -> Self::Output { - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - LineString(_, XY) => Arc::new(self.as_line_string().densify(max_distance)), - Polygon(_, XY) => Arc::new(self.as_polygon().densify(max_distance)), - MultiLineString(_, XY) => Arc::new(self.as_multi_line_string().densify(max_distance)), - MultiPolygon(_, XY) => Arc::new(self.as_multi_polygon().densify(max_distance)), + LineString(_, _) => Arc::new(self.as_line_string().densify(max_distance)), + Polygon(_, _) => Arc::new(self.as_polygon().densify(max_distance)), + MultiLineString(_, _) => Arc::new(self.as_multi_line_string().densify(max_distance)), + MultiPolygon(_, _) => Arc::new(self.as_multi_polygon().densify(max_distance)), _ => return Err(GeoArrowError::IncorrectType("".into())), }; Ok(result) diff --git a/rust/geoarrow/src/algorithm/geo/dimensions.rs b/rust/geoarrow/src/algorithm/geo/dimensions.rs index 0332f3d8..26565f6d 100644 --- a/rust/geoarrow/src/algorithm/geo/dimensions.rs +++ b/rust/geoarrow/src/algorithm/geo/dimensions.rs @@ -1,7 +1,7 @@ use crate::array::*; use crate::chunked_array::{ChunkedArray, ChunkedGeometryArray, ChunkedNativeArray}; -use crate::datatypes::{Dimension, NativeType}; -use crate::error::{GeoArrowError, Result}; +use crate::datatypes::NativeType; +use crate::error::Result; use crate::trait_::ArrayAccessor; use crate::NativeArray; use arrow_array::builder::BooleanBuilder; @@ -59,24 +59,26 @@ iter_geo_impl!(MultiLineStringArray); iter_geo_impl!(MultiPolygonArray); iter_geo_impl!(MixedGeometryArray); iter_geo_impl!(GeometryCollectionArray); +iter_geo_impl!(RectArray); +iter_geo_impl!(GeometryArray); impl HasDimensions for &dyn NativeArray { type Output = Result; fn is_empty(&self) -> Self::Output { - use Dimension::*; use NativeType::*; let result = match self.data_type() { - Point(_, XY) => HasDimensions::is_empty(self.as_point()), - LineString(_, XY) => HasDimensions::is_empty(self.as_line_string()), - Polygon(_, XY) => HasDimensions::is_empty(self.as_polygon()), - MultiPoint(_, XY) => HasDimensions::is_empty(self.as_multi_point()), - MultiLineString(_, XY) => HasDimensions::is_empty(self.as_multi_line_string()), - MultiPolygon(_, XY) => HasDimensions::is_empty(self.as_multi_polygon()), - Mixed(_, XY) => HasDimensions::is_empty(self.as_mixed()), - GeometryCollection(_, XY) => HasDimensions::is_empty(self.as_geometry_collection()), - _ => return Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => HasDimensions::is_empty(self.as_point()), + LineString(_, _) => HasDimensions::is_empty(self.as_line_string()), + Polygon(_, _) => HasDimensions::is_empty(self.as_polygon()), + MultiPoint(_, _) => HasDimensions::is_empty(self.as_multi_point()), + MultiLineString(_, _) => HasDimensions::is_empty(self.as_multi_line_string()), + MultiPolygon(_, _) => HasDimensions::is_empty(self.as_multi_polygon()), + Mixed(_, _) => HasDimensions::is_empty(self.as_mixed()), + GeometryCollection(_, _) => HasDimensions::is_empty(self.as_geometry_collection()), + Rect(_) => HasDimensions::is_empty(self.as_rect()), + Geometry(_) => HasDimensions::is_empty(self.as_geometry()), }; Ok(result) } @@ -95,19 +97,19 @@ impl HasDimensions for &dyn ChunkedNativeArray { type Output = Result>; fn is_empty(&self) -> Self::Output { - use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => HasDimensions::is_empty(self.as_point()), - LineString(_, XY) => HasDimensions::is_empty(self.as_line_string()), - Polygon(_, XY) => HasDimensions::is_empty(self.as_polygon()), - MultiPoint(_, XY) => HasDimensions::is_empty(self.as_multi_point()), - MultiLineString(_, XY) => HasDimensions::is_empty(self.as_multi_line_string()), - MultiPolygon(_, XY) => HasDimensions::is_empty(self.as_multi_polygon()), - Mixed(_, XY) => HasDimensions::is_empty(self.as_mixed()), - GeometryCollection(_, XY) => HasDimensions::is_empty(self.as_geometry_collection()), - _ => Err(GeoArrowError::IncorrectType("".into())), + Point(_, _) => HasDimensions::is_empty(self.as_point()), + LineString(_, _) => HasDimensions::is_empty(self.as_line_string()), + Polygon(_, _) => HasDimensions::is_empty(self.as_polygon()), + MultiPoint(_, _) => HasDimensions::is_empty(self.as_multi_point()), + MultiLineString(_, _) => HasDimensions::is_empty(self.as_multi_line_string()), + MultiPolygon(_, _) => HasDimensions::is_empty(self.as_multi_polygon()), + Mixed(_, _) => HasDimensions::is_empty(self.as_mixed()), + GeometryCollection(_, _) => HasDimensions::is_empty(self.as_geometry_collection()), + Rect(_) => HasDimensions::is_empty(self.as_rect()), + Geometry(_) => HasDimensions::is_empty(self.as_geometry()), } } } diff --git a/rust/geoarrow/src/algorithm/geo/rotate.rs b/rust/geoarrow/src/algorithm/geo/rotate.rs index 9aca1547..27bb8a6e 100644 --- a/rust/geoarrow/src/algorithm/geo/rotate.rs +++ b/rust/geoarrow/src/algorithm/geo/rotate.rs @@ -97,40 +97,6 @@ pub trait Rotate { // │ Implementations for RHS arrays │ // └────────────────────────────────┘ -// Note: this can't (easily) be parameterized in the macro because PointArray is not generic over O -impl Rotate for PointArray { - type Output = Self; - - fn rotate_around_centroid(&self, degrees: &Float64Array) -> Self { - let centroids = self.centroid(); - let transforms: Vec = centroids - .iter_geo_values() - .zip(degrees.values().iter()) - .map(|(point, angle)| AffineTransform::rotate(*angle, point)) - .collect(); - self.affine_transform(transforms.as_slice()) - } - - fn rotate_around_center(&self, degrees: &Float64Array) -> Self { - let centers = self.center(); - let transforms: Vec = centers - .iter_geo_values() - .zip(degrees.values().iter()) - .map(|(point, angle)| AffineTransform::rotate(*angle, point)) - .collect(); - self.affine_transform(transforms.as_slice()) - } - - fn rotate_around_point(&self, degrees: &Float64Array, point: geo::Point) -> Self { - let transforms: Vec = degrees - .values() - .iter() - .map(|degrees| AffineTransform::rotate(*degrees, point)) - .collect(); - self.affine_transform(transforms.as_slice()) - } -} - /// Implementation that iterates over geo objects macro_rules! iter_geo_impl { ($type:ty) => { @@ -169,6 +135,7 @@ macro_rules! iter_geo_impl { }; } +iter_geo_impl!(PointArray); iter_geo_impl!(LineStringArray); iter_geo_impl!(PolygonArray); iter_geo_impl!(MultiPointArray); diff --git a/rust/geoarrow/src/algorithm/geo/scale.rs b/rust/geoarrow/src/algorithm/geo/scale.rs index 22730533..ee0ea4e1 100644 --- a/rust/geoarrow/src/algorithm/geo/scale.rs +++ b/rust/geoarrow/src/algorithm/geo/scale.rs @@ -99,7 +99,6 @@ pub trait Scale: Sized { ) -> Self::Output; } -// Note: this can't (easily) be parameterized in the macro because PointArray is not generic over O impl Scale for PointArray { type Output = Self; @@ -108,7 +107,12 @@ impl Scale for PointArray { x_factor: &BroadcastablePrimitive, y_factor: &BroadcastablePrimitive, ) -> Self { - let mut output_array = PointBuilder::with_capacity(Dimension::XY, self.buffer_lengths()); + let mut output_array = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); self.iter_geo() .zip(x_factor) @@ -130,7 +134,12 @@ impl Scale for PointArray { y_factor: &BroadcastablePrimitive, origin: geo::Point, ) -> Self { - let mut output_array = PointBuilder::with_capacity(Dimension::XY, self.buffer_lengths()); + let mut output_array = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); self.iter_geo() .zip(x_factor) @@ -160,8 +169,12 @@ macro_rules! iter_geo_impl { x_factor: &BroadcastablePrimitive, y_factor: &BroadcastablePrimitive, ) -> Self { - let mut output_array = - <$builder_type>::with_capacity(Dimension::XY, self.buffer_lengths()); + let mut output_array = <$builder_type>::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); self.iter_geo().zip(x_factor).zip(y_factor).for_each( |((maybe_g, x_factor), y_factor)| { @@ -184,8 +197,12 @@ macro_rules! iter_geo_impl { y_factor: &BroadcastablePrimitive, origin: geo::Point, ) -> Self { - let mut output_array = - <$builder_type>::with_capacity(Dimension::XY, self.buffer_lengths()); + let mut output_array = <$builder_type>::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); self.iter_geo().zip(x_factor).zip(y_factor).for_each( |((maybe_g, x_factor), y_factor)| { @@ -221,6 +238,63 @@ iter_geo_impl!( ); iter_geo_impl!(MultiPolygonArray, MultiPolygonBuilder, push_multi_polygon); +impl Scale for GeometryArray { + type Output = Result; + + fn scale_xy( + &self, + x_factor: &BroadcastablePrimitive, + y_factor: &BroadcastablePrimitive, + ) -> Self::Output { + let mut output_array = GeometryBuilder::with_capacity_and_options( + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + false, + ); + + self.iter_geo().zip(x_factor).zip(y_factor).try_for_each( + |((maybe_g, x_factor), y_factor)| { + output_array.push_geometry( + maybe_g + .map(|geom| geom.scale_xy(x_factor.unwrap(), y_factor.unwrap())) + .as_ref(), + ) + }, + )?; + + Ok(output_array.finish()) + } + + fn scale_around_point( + &self, + x_factor: &BroadcastablePrimitive, + y_factor: &BroadcastablePrimitive, + origin: geo::Point, + ) -> Self::Output { + let mut output_array = GeometryBuilder::with_capacity_and_options( + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + false, + ); + + self.iter_geo().zip(x_factor).zip(y_factor).try_for_each( + |((maybe_g, x_factor), y_factor)| { + output_array.push_geometry( + maybe_g + .map(|geom| { + geom.scale_around_point(x_factor.unwrap(), y_factor.unwrap(), origin) + }) + .as_ref(), + ) + }, + )?; + + Ok(output_array.finish()) + } +} + impl Scale for &dyn NativeArray { type Output = Result>; @@ -235,19 +309,19 @@ impl Scale for &dyn NativeArray { }}; } - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - Point(_, XY) => impl_method!(as_point), - LineString(_, XY) => impl_method!(as_line_string), - Polygon(_, XY) => impl_method!(as_polygon), - MultiPoint(_, XY) => impl_method!(as_multi_point), - MultiLineString(_, XY) => impl_method!(as_multi_line_string), - MultiPolygon(_, XY) => impl_method!(as_multi_polygon), - // Mixed(_, XY) => impl_method!(as_mixed), - // GeometryCollection(_, XY) => impl_method!(as_geometry_collection), - // Rect(XY) => impl_method!(as_rect), + Point(_, _) => impl_method!(as_point), + LineString(_, _) => impl_method!(as_line_string), + Polygon(_, _) => impl_method!(as_polygon), + MultiPoint(_, _) => impl_method!(as_multi_point), + MultiLineString(_, _) => impl_method!(as_multi_line_string), + MultiPolygon(_, _) => impl_method!(as_multi_polygon), + Geometry(_) => Arc::new(self.as_geometry().scale_xy(x_factor, y_factor)?), + // Mixed(_, _) => impl_method!(as_mixed), + // GeometryCollection(_, _) => impl_method!(as_geometry_collection), + // Rect(_) => impl_method!(as_rect), _ => todo!("unsupported data type"), }; @@ -269,19 +343,22 @@ impl Scale for &dyn NativeArray { }}; } - use Dimension::*; use NativeType::*; let result: Arc = match self.data_type() { - Point(_, XY) => impl_method!(as_point), - LineString(_, XY) => impl_method!(as_line_string), - Polygon(_, XY) => impl_method!(as_polygon), - MultiPoint(_, XY) => impl_method!(as_multi_point), - MultiLineString(_, XY) => impl_method!(as_multi_line_string), - MultiPolygon(_, XY) => impl_method!(as_multi_polygon), - // Mixed(_, XY) => impl_method!(as_mixed), - // GeometryCollection(_, XY) => impl_method!(as_geometry_collection), - // Rect(XY) => impl_method!(as_rect), + Point(_, _) => impl_method!(as_point), + LineString(_, _) => impl_method!(as_line_string), + Polygon(_, _) => impl_method!(as_polygon), + MultiPoint(_, _) => impl_method!(as_multi_point), + MultiLineString(_, _) => impl_method!(as_multi_line_string), + MultiPolygon(_, _) => impl_method!(as_multi_polygon), + Geometry(_) => Arc::new( + self.as_geometry() + .scale_around_point(x_factor, y_factor, origin)?, + ), + // Mixed(_, _) => impl_method!(as_mixed), + // GeometryCollection(_, _) => impl_method!(as_geometry_collection), + // Rect(_) => impl_method!(as_rect), _ => todo!("unsupported data type"), }; diff --git a/rust/geoarrow/src/algorithm/geo_index/rtree.rs b/rust/geoarrow/src/algorithm/geo_index/rtree.rs index 8286e4d5..a3e4458e 100644 --- a/rust/geoarrow/src/algorithm/geo_index/rtree.rs +++ b/rust/geoarrow/src/algorithm/geo_index/rtree.rs @@ -110,7 +110,7 @@ impl RTree for &dyn ChunkedNativeArray { Mixed(_, _) => impl_method!(as_mixed), GeometryCollection(_, _) => impl_method!(as_geometry_collection), Rect(_) => impl_method!(as_rect), - Geometry(_) => todo!("Chunked unknown array"), // impl_method!(as_unknown), + Geometry(_) => impl_method!(as_geometry), }; Ok(result) } diff --git a/rust/geoarrow/src/algorithm/geos/buffer.rs b/rust/geoarrow/src/algorithm/geos/buffer.rs index 1b64241a..56b0e54e 100644 --- a/rust/geoarrow/src/algorithm/geos/buffer.rs +++ b/rust/geoarrow/src/algorithm/geos/buffer.rs @@ -1,5 +1,5 @@ -use crate::algorithm::geos::util::try_unary_polygon; -use crate::array::{PointArray, PolygonArray}; +use crate::algorithm::geos::util::{try_unary_geometry, try_unary_polygon}; +use crate::array::{GeometryArray, PointArray, PolygonArray}; use crate::error::Result; use crate::NativeArray; use geos::{BufferParams, Geom}; @@ -28,6 +28,18 @@ impl Buffer for PointArray { } } +impl Buffer for GeometryArray { + type Output = Result; + + fn buffer(&self, width: f64, quadsegs: i32) -> Self::Output { + try_unary_geometry(self, |g| g.buffer(width, quadsegs)) + } + + fn buffer_with_params(&self, width: f64, buffer_params: &BufferParams) -> Self::Output { + try_unary_geometry(self, |g| g.buffer_with_params(width, buffer_params)) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/rust/geoarrow/src/algorithm/geos/util.rs b/rust/geoarrow/src/algorithm/geos/util.rs index 84e12d9e..e72ddcf0 100644 --- a/rust/geoarrow/src/algorithm/geos/util.rs +++ b/rust/geoarrow/src/algorithm/geos/util.rs @@ -1,10 +1,10 @@ use arrow_array::{ArrowPrimitiveType, PrimitiveArray}; use arrow_buffer::BufferBuilder; -use crate::array::PolygonArray; +use crate::array::{GeometryArray, GeometryBuilder, PolygonArray}; use crate::datatypes::Dimension; use crate::error::GeoArrowError; -use crate::io::geos::scalar::GEOSPolygon; +use crate::io::geos::scalar::{GEOSGeometry, GEOSPolygon}; use crate::trait_::NativeGEOSGeometryAccessor; // Note: This is derived from arrow-rs here: @@ -69,3 +69,37 @@ where Ok(PolygonArray::from((buffer, output_dim))) } + +pub(super) fn try_unary_geometry<'a, F>( + array: &'a dyn NativeGEOSGeometryAccessor<'a>, + op: F, +) -> std::result::Result +where + F: Fn(geos::Geometry) -> std::result::Result, +{ + let len = array.len(); + + let mut buffer = vec![None; len]; + + let f = |idx| { + unsafe { + buffer[idx] = Some(GEOSGeometry::new(op( + array.value_as_geometry_unchecked(idx)? + )?)) + }; + Ok::<_, geos::Error>(()) + }; + + match array.nulls() { + Some(nulls) => nulls.try_for_each_valid_idx(f)?, + None => (0..len).try_for_each(f)?, + } + + Ok(GeometryBuilder::from_nullable_geometries( + buffer.as_slice(), + array.coord_type(), + array.metadata().clone(), + true, + )? + .finish()) +}