diff --git a/rust/geoarrow/src/algorithm/broadcasting/iterator.rs b/rust/geoarrow/src/algorithm/broadcasting/iterator.rs new file mode 100644 index 00000000..41fb1bf7 --- /dev/null +++ b/rust/geoarrow/src/algorithm/broadcasting/iterator.rs @@ -0,0 +1,108 @@ +//! Idiomatic iterators for [`Array`](crate::Array) + +use arrow_buffer::NullBuffer; + +use crate::array::PointArray; +use crate::scalar::Point; +use crate::trait_::ArrayAccessor; +use crate::ArrayBase; + +/// An iterator that returns Some(T) or None, that can be used on any [`ArrayAccessor`] +/// +/// # Performance +/// +/// [`ArrayIter`] provides an idiomatic way to iterate over an array, however, this +/// comes at the cost of performance. In particular the interleaved handling of +/// the null mask is often sub-optimal. +/// +/// If performing an infallible operation, it is typically faster to perform the operation +/// on every index of the array, and handle the null mask separately. For [`PrimitiveArray`] +/// this functionality is provided by [`compute::unary`] +/// +/// If performing a fallible operation, it isn't possible to perform the operation independently +/// of the null mask, as this might result in a spurious failure on a null index. However, +/// there are more efficient ways to iterate over just the non-null indices, this functionality +/// is provided by [`compute::try_unary`] +/// +/// [`PrimitiveArray`]: crate::PrimitiveArray +/// [`compute::unary`]: https://docs.rs/arrow/latest/arrow/compute/fn.unary.html +/// [`compute::try_unary`]: https://docs.rs/arrow/latest/arrow/compute/fn.try_unary.html +#[derive(Debug)] +pub struct PointArrayIter<'a> { + array: &'a PointArray, + logical_nulls: Option, + current: usize, + current_end: usize, +} + +impl<'a> PointArrayIter<'a> { + /// create a new iterator + pub fn new(array: &'a PointArray) -> Self { + let len = array.len(); + let logical_nulls = array.nulls().cloned(); + PointArrayIter { + array, + logical_nulls, + current: 0, + current_end: len, + } + } + + #[inline] + fn is_null(&self, idx: usize) -> bool { + self.logical_nulls + .as_ref() + .map(|x| x.is_null(idx)) + .unwrap_or_default() + } +} + +impl<'a> Iterator for PointArrayIter<'a> { + type Item = Option>; + + #[inline] + fn next(&mut self) -> Option { + if self.current == self.current_end { + None + } else if self.is_null(self.current) { + self.current += 1; + Some(None) + } else { + let old = self.current; + self.current += 1; + // Safety: + // we just checked bounds in `self.current_end == self.current` + // this is safe on the premise that this struct is initialized with + // current = array.len() + // and that current_end is ever only decremented + unsafe { Some(Some(self.array.value_unchecked(old))) } + } + } + + fn size_hint(&self) -> (usize, Option) { + ( + self.array.len() - self.current, + Some(self.array.len() - self.current), + ) + } +} + +impl DoubleEndedIterator for PointArrayIter<'_> { + fn next_back(&mut self) -> Option { + if self.current_end == self.current { + None + } else { + self.current_end -= 1; + Some(if self.is_null(self.current_end) { + None + } else { + // Safety: + // we just checked bounds in `self.current_end == self.current` + // this is safe on the premise that this struct is initialized with + // current = array.len() + // and that current_end is ever only decremented + unsafe { Some(self.array.value_unchecked(self.current_end)) } + }) + } + } +} diff --git a/rust/geoarrow/src/algorithm/broadcasting/mod.rs b/rust/geoarrow/src/algorithm/broadcasting/mod.rs index f55f4b8c..19878966 100644 --- a/rust/geoarrow/src/algorithm/broadcasting/mod.rs +++ b/rust/geoarrow/src/algorithm/broadcasting/mod.rs @@ -18,7 +18,8 @@ // mod multilinestring; // mod multipoint; // mod multipolygon; -// mod point; +mod iterator; +mod point; // mod polygon; mod primitive; mod vec; @@ -28,7 +29,7 @@ mod vec; // pub use multilinestring::BroadcastableMultiLineString; // pub use multipoint::BroadcastableMultiPoint; // pub use multipolygon::BroadcastableMultiPolygon; -// pub use point::BroadcastablePoint; +pub use point::BroadcastablePoint; // pub use polygon::BroadcastablePolygon; pub use primitive::BroadcastablePrimitive; pub use vec::BroadcastableVec; diff --git a/rust/geoarrow/src/algorithm/broadcasting/point.rs b/rust/geoarrow/src/algorithm/broadcasting/point.rs index 24ea4eb6..da1bd842 100644 --- a/rust/geoarrow/src/algorithm/broadcasting/point.rs +++ b/rust/geoarrow/src/algorithm/broadcasting/point.rs @@ -1,23 +1,39 @@ +use geo_traits::to_geo::ToGeoPoint; +use geo_traits::PointTrait; + +use crate::algorithm::broadcasting::iterator::PointArrayIter; use crate::array::PointArray; -use crate::scalar::Point; +use crate::trait_::NativeScalar; /// An enum over a [`Point`] scalar and [`PointArray`] array. /// /// [`IntoIterator`] is implemented for this, where it will iterate over the `Array` variant /// normally but will iterate over the `Scalar` variant forever. #[derive(Debug)] -pub enum BroadcastablePoint<'a> { - Scalar(Point<'a>), +pub enum BroadcastablePoint { + Scalar(geo::Point), Array(PointArray), } +impl> From for BroadcastablePoint { + fn from(value: G) -> Self { + Self::Scalar(value.to_point()) + } +} + +impl From for BroadcastablePoint { + fn from(value: PointArray) -> Self { + Self::Array(value) + } +} + pub enum BroadcastPointIter<'a> { - Scalar(Point<'a>), + Scalar(geo::Point), Array(PointArrayIter<'a>), } -impl<'a> IntoIterator for &'a BroadcastablePoint<'a> { - type Item = Option>; +impl<'a> IntoIterator for &'a BroadcastablePoint { + type Item = Option; type IntoIter = BroadcastPointIter<'a>; fn into_iter(self) -> Self::IntoIter { @@ -28,12 +44,12 @@ impl<'a> IntoIterator for &'a BroadcastablePoint<'a> { } } -impl<'a> Iterator for BroadcastPointIter<'a> { - type Item = Option>; +impl Iterator for BroadcastPointIter<'_> { + type Item = Option; fn next(&mut self) -> Option { match self { - BroadcastPointIter::Array(arr) => arr.next(), + BroadcastPointIter::Array(arr) => arr.next().map(|x| x.map(|y| y.to_geo())), BroadcastPointIter::Scalar(val) => Some(Some(val.to_owned())), } } diff --git a/rust/geoarrow/src/algorithm/geo/rotate.rs b/rust/geoarrow/src/algorithm/geo/rotate.rs index 27bb8a6e..89009462 100644 --- a/rust/geoarrow/src/algorithm/geo/rotate.rs +++ b/rust/geoarrow/src/algorithm/geo/rotate.rs @@ -1,14 +1,14 @@ use std::sync::Arc; -use crate::algorithm::geo::{AffineOps, Center, Centroid}; +use crate::algorithm::broadcasting::{BroadcastablePoint, BroadcastablePrimitive}; use crate::array::MultiPointArray; use crate::array::*; use crate::datatypes::{Dimension, NativeType}; -use crate::error::Result; +use crate::error::{GeoArrowError, Result}; use crate::trait_::ArrayAccessor; use crate::NativeArray; -use arrow_array::Float64Array; -use geo::AffineTransform; +use arrow::datatypes::Float64Type; +use geo::Rotate as _; /// Rotate geometries around a point by an angle, in degrees. /// @@ -21,7 +21,7 @@ use geo::AffineTransform; /// [`Translate`](crate::algorithm::geo::Translate), or [`Rotate`](crate::algorithm::geo::Rotate), /// it is more efficient to compose the transformations and apply them as a single operation using /// the [`AffineOps`](crate::algorithm::geo::AffineOps) trait. -pub trait Rotate { +pub trait Rotate { type Output; /// Rotate a geometry around its [centroid](Centroid) by an angle, in degrees @@ -52,7 +52,8 @@ pub trait Rotate { /// assert_relative_eq!(expected, rotated); /// ``` #[must_use] - fn rotate_around_centroid(&self, degrees: &DegreesT) -> Self::Output; + fn rotate_around_centroid(&self, degrees: &BroadcastablePrimitive) + -> Self::Output; /// Rotate a geometry around the center of its [bounding /// box](crate::algorithm::geo::BoundingRect) by an angle, in degrees. @@ -60,7 +61,7 @@ pub trait Rotate { /// Positive angles are counter-clockwise, and negative angles are clockwise rotations. /// #[must_use] - fn rotate_around_center(&self, degrees: &DegreesT) -> Self::Output; + fn rotate_around_center(&self, degrees: &BroadcastablePrimitive) -> Self::Output; /// Rotate a Geometry around an arbitrary point by an angle, given in degrees /// @@ -90,285 +91,336 @@ pub trait Rotate { /// ]); /// ``` #[must_use] - fn rotate_around_point(&self, degrees: &DegreesT, point: geo::Point) -> Self::Output; + fn rotate_around_point( + &self, + degrees: &BroadcastablePrimitive, + point: &BroadcastablePoint, + ) -> Self::Output; } -// ┌────────────────────────────────┐ -// │ Implementations for RHS arrays │ -// └────────────────────────────────┘ - -/// Implementation that iterates over geo objects -macro_rules! iter_geo_impl { - ($type:ty) => { - impl Rotate for $type { - type Output = Self; +impl Rotate for PointArray { + type Output = Self; - fn rotate_around_centroid(&self, degrees: &Float64Array) -> $type { - 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_centroid(&self, degrees: &BroadcastablePrimitive) -> PointArray { + let mut builder = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); + + self.iter_geo().zip(degrees).for_each(|(maybe_g, degrees)| { + if let (Some(mut geom), Some(degrees)) = (maybe_g, degrees) { + geom.rotate_around_centroid_mut(degrees); + builder.push_point(Some(&geom)); + } else { + builder.push_null(); } + }); - 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()) - } + builder.finish() + } - 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()) + fn rotate_around_center(&self, degrees: &BroadcastablePrimitive) -> Self { + let mut builder = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); + + self.iter_geo().zip(degrees).for_each(|(maybe_g, degrees)| { + if let (Some(mut geom), Some(degrees)) = (maybe_g, degrees) { + geom.rotate_around_center_mut(degrees); + builder.push_point(Some(&geom)); + } else { + builder.push_null(); } - } - }; -} - -iter_geo_impl!(PointArray); -iter_geo_impl!(LineStringArray); -iter_geo_impl!(PolygonArray); -iter_geo_impl!(MultiPointArray); -iter_geo_impl!(MultiLineStringArray); -iter_geo_impl!(MultiPolygonArray); - -// ┌─────────────────────────────────┐ -// │ Implementations for RHS scalars │ -// └─────────────────────────────────┘ - -// 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: &f64) -> Self { - let centroids = self.centroid(); - let transforms: Vec = centroids - .iter_geo_values() - .map(|point| AffineTransform::rotate(*degrees, point)) - .collect(); - self.affine_transform(transforms.as_slice()) - } + }); - fn rotate_around_center(&self, degrees: &f64) -> Self { - let centers = self.center(); - let transforms: Vec = centers - .iter_geo_values() - .map(|point| AffineTransform::rotate(*degrees, point)) - .collect(); - self.affine_transform(transforms.as_slice()) + builder.finish() } - fn rotate_around_point(&self, degrees: &f64, point: geo::Point) -> Self { - let transform = AffineTransform::rotate(*degrees, point); - self.affine_transform(&transform) + fn rotate_around_point( + &self, + degrees: &BroadcastablePrimitive, + point: &BroadcastablePoint, + ) -> Self { + let mut builder = PointBuilder::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); + + self.iter_geo() + .zip(degrees) + .zip(point) + .for_each(|((maybe_g, degrees), point)| { + if let (Some(mut geom), Some(degrees), Some(point)) = (maybe_g, degrees, point) { + geom.rotate_around_point_mut(degrees, point); + builder.push_point(Some(&geom)); + } else { + builder.push_null(); + } + }); + + builder.finish() } } /// Implementation that iterates over geo objects -macro_rules! iter_geo_impl_scalar { - ($type:ty) => { - impl Rotate for $type { +macro_rules! iter_geo_impl { + ($type:ty, $builder_type:ty, $push_func:ident) => { + impl Rotate for $type { type Output = Self; - fn rotate_around_centroid(&self, degrees: &f64) -> $type { - let centroids = self.centroid(); - let transforms: Vec = centroids - .iter_geo_values() - .map(|point| AffineTransform::rotate(*degrees, point)) - .collect(); - self.affine_transform(transforms.as_slice()) + fn rotate_around_centroid( + &self, + degrees: &BroadcastablePrimitive, + ) -> Self { + let mut builder = <$builder_type>::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); + + self.iter_geo().zip(degrees).for_each(|(maybe_g, degrees)| { + if let (Some(mut geom), Some(degrees)) = (maybe_g, degrees) { + geom.rotate_around_centroid_mut(degrees); + builder.$push_func(Some(&geom)).unwrap(); + } else { + builder.push_null(); + } + }); + + builder.finish() } - fn rotate_around_center(&self, degrees: &f64) -> Self { - let centers = self.center(); - let transforms: Vec = centers - .iter_geo_values() - .map(|point| AffineTransform::rotate(*degrees, point)) - .collect(); - self.affine_transform(transforms.as_slice()) + fn rotate_around_center(&self, degrees: &BroadcastablePrimitive) -> Self { + let mut builder = <$builder_type>::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); + + self.iter_geo().zip(degrees).for_each(|(maybe_g, degrees)| { + if let (Some(mut geom), Some(degrees)) = (maybe_g, degrees) { + geom.rotate_around_center_mut(degrees); + builder.$push_func(Some(&geom)).unwrap(); + } else { + builder.push_null(); + } + }); + + builder.finish() } - fn rotate_around_point(&self, degrees: &f64, point: geo::Point) -> Self { - let transform = AffineTransform::rotate(*degrees, point); - self.affine_transform(&transform) + fn rotate_around_point( + &self, + degrees: &BroadcastablePrimitive, + point: &BroadcastablePoint, + ) -> Self { + let mut builder = <$builder_type>::with_capacity_and_options( + Dimension::XY, + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + ); + + self.iter_geo() + .zip(degrees) + .zip(point) + .for_each(|((maybe_g, degrees), point)| { + if let (Some(mut geom), Some(degrees), Some(point)) = + (maybe_g, degrees, point) + { + geom.rotate_around_point_mut(degrees, point); + builder.$push_func(Some(&geom)).unwrap(); + } else { + builder.push_null(); + } + }); + + builder.finish() } } }; } -iter_geo_impl_scalar!(LineStringArray); -iter_geo_impl_scalar!(PolygonArray); -iter_geo_impl_scalar!(MultiPointArray); -iter_geo_impl_scalar!(MultiLineStringArray); -iter_geo_impl_scalar!(MultiPolygonArray); - -impl Rotate for &dyn NativeArray { - type Output = Result>; - - fn rotate_around_centroid(&self, degrees: &f64) -> Self::Output { - macro_rules! impl_method { - ($method:ident) => {{ - Arc::new(self.$method().rotate_around_centroid(degrees)) - }}; - } - - 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), - _ => todo!("unsupported data type"), - }; - - Ok(result) +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!(GeometryArray, GeometryBuilder, push_geometry); + +impl Rotate for GeometryArray { + type Output = Result; + + fn rotate_around_centroid( + &self, + degrees: &BroadcastablePrimitive, + ) -> Self::Output { + let mut builder = GeometryBuilder::with_capacity_and_options( + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + false, + ); + + self.iter_geo() + .zip(degrees) + .try_for_each(|(maybe_g, degrees)| { + if let (Some(mut geom), Some(degrees)) = (maybe_g, degrees) { + geom.rotate_around_centroid_mut(degrees); + builder.push_geometry(Some(&geom))?; + } else { + builder.push_null(); + } + Ok::<_, GeoArrowError>(()) + })?; + + Ok(builder.finish()) } - fn rotate_around_center(&self, degrees: &f64) -> Self::Output { - macro_rules! impl_method { - ($method:ident) => {{ - Arc::new(self.$method().rotate_around_center(degrees)) - }}; - } - - 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), - _ => todo!("unsupported data type"), - }; - - Ok(result) + fn rotate_around_center(&self, degrees: &BroadcastablePrimitive) -> Self::Output { + let mut builder = GeometryBuilder::with_capacity_and_options( + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + false, + ); + + self.iter_geo() + .zip(degrees) + .try_for_each(|(maybe_g, degrees)| { + if let (Some(mut geom), Some(degrees)) = (maybe_g, degrees) { + geom.rotate_around_center_mut(degrees); + builder.push_geometry(Some(&geom))?; + } else { + builder.push_null(); + } + Ok::<_, GeoArrowError>(()) + })?; + + Ok(builder.finish()) } - fn rotate_around_point(&self, degrees: &f64, point: geo::Point) -> Self::Output { - macro_rules! impl_method { - ($method:ident) => {{ - Arc::new(self.$method().rotate_around_point(degrees, point)) - }}; - } - - 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), - _ => todo!("unsupported data type"), - }; - - Ok(result) + fn rotate_around_point( + &self, + degrees: &BroadcastablePrimitive, + point: &BroadcastablePoint, + ) -> Self::Output { + let mut builder = GeometryBuilder::with_capacity_and_options( + self.buffer_lengths(), + self.coord_type(), + self.metadata().clone(), + false, + ); + + self.iter_geo() + .zip(degrees) + .zip(point) + .try_for_each(|((maybe_g, degrees), point)| { + if let (Some(mut geom), Some(degrees), Some(point)) = (maybe_g, degrees, point) { + geom.rotate_around_point_mut(degrees, point); + builder.push_geometry(Some(&geom))?; + } else { + builder.push_null(); + } + Ok::<_, GeoArrowError>(()) + })?; + + Ok(builder.finish()) } } -impl Rotate for &dyn NativeArray { +impl Rotate for &dyn NativeArray { type Output = Result>; - fn rotate_around_centroid(&self, degrees: &Float64Array) -> Self::Output { + fn rotate_around_centroid( + &self, + degrees: &BroadcastablePrimitive, + ) -> Self::Output { macro_rules! impl_method { ($method:ident) => {{ Arc::new(self.$method().rotate_around_centroid(degrees)) }}; } - 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().rotate_around_centroid(degrees)?), + // GeometryCollection(_, _) => impl_method!(as_geometry_collection), + // Rect(_) => impl_method!(as_rect), _ => todo!("unsupported data type"), }; Ok(result) } - fn rotate_around_center(&self, degrees: &Float64Array) -> Self::Output { + fn rotate_around_center(&self, degrees: &BroadcastablePrimitive) -> Self::Output { macro_rules! impl_method { ($method:ident) => {{ Arc::new(self.$method().rotate_around_center(degrees)) }}; } - 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().rotate_around_centroid(degrees)?), + // GeometryCollection(_, _) => impl_method!(as_geometry_collection), + // Rect(_) => impl_method!(as_rect), _ => todo!("unsupported data type"), }; Ok(result) } - fn rotate_around_point(&self, degrees: &Float64Array, point: geo::Point) -> Self::Output { + fn rotate_around_point( + &self, + degrees: &BroadcastablePrimitive, + point: &BroadcastablePoint, + ) -> Self::Output { macro_rules! impl_method { ($method:ident) => {{ Arc::new(self.$method().rotate_around_point(degrees, point)) }}; } - 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().rotate_around_centroid(degrees)?), + // GeometryCollection(_, _) => impl_method!(as_geometry_collection), + // Rect(_) => impl_method!(as_rect), _ => todo!("unsupported data type"), };