@@ -253,3 +253,140 @@ impl AsGeoArrowArray for Arc<dyn GeoArrowArray> {
253253 self . as_any ( ) . downcast_ref :: < WKTArray < O > > ( )
254254 }
255255}
256+
257+ /// Re-export symbols needed for downcast macros
258+ ///
259+ /// Name follows `serde` convention
260+ #[ doc( hidden) ]
261+ pub mod __private {
262+ pub use crate :: GeoArrowType ;
263+ }
264+
265+ /// Downcast a [GeoArrowArray] to a concrete-typed array based on its [`GeoArrowType`].
266+ ///
267+ /// For example: computing unsigned area:
268+ ///
269+ /// ```
270+ /// use arrow_array::Float64Array;
271+ /// use arrow_array::builder::Float64Builder;
272+ /// use geo::Area;
273+ /// use geo_traits::to_geo::ToGeoGeometry;
274+ /// use geoarrow_array::error::Result;
275+ /// use geoarrow_array::{ArrayAccessor, GeoArrowArray, downcast_geoarrow_array};
276+ ///
277+ /// pub fn unsigned_area(array: &dyn GeoArrowArray) -> Result<Float64Array> {
278+ /// downcast_geoarrow_array!(array, impl_unsigned_area)
279+ /// }
280+ ///
281+ /// fn impl_unsigned_area<'a>(array: &'a impl ArrayAccessor<'a>) -> Result<Float64Array> {
282+ /// let mut builder = Float64Builder::with_capacity(array.len());
283+ ///
284+ /// for item in array.iter() {
285+ /// if let Some(geom) = item {
286+ /// builder.append_value(geom?.to_geometry().unsigned_area());
287+ /// } else {
288+ /// builder.append_null();
289+ /// }
290+ /// }
291+ ///
292+ /// Ok(builder.finish())
293+ /// }
294+ /// ```
295+ ///
296+ /// You can also override the behavior of specific data types to specialize or provide a fast path.
297+ /// For example, we know that points and lines will always have an area of 0, and don't need to
298+ /// iterate over the input values to compute that.
299+ ///
300+ /// ```
301+ /// fn impl_unsigned_area_specialized<'a>(array: &'a impl ArrayAccessor<'a>) -> Result<Float64Array> {
302+ /// use GeoArrowType::*;
303+ /// match array.data_type() {
304+ /// Point(_) | LineString(_) | MultiPoint(_) | MultiLineString(_) => {
305+ /// let values = vec![0.0f64; array.len()];
306+ /// Ok(Float64Array::new(values.into(), array.nulls().cloned()))
307+ /// }
308+ /// _ => impl_unsigned_area(array),
309+ /// }
310+ /// }
311+ /// ```
312+ ///
313+ /// This is a simplified version of the upstream
314+ /// [downcast_primitive_array][arrow_array::downcast_primitive_array].
315+ ///
316+ /// If you would like to help in updating this `downcast_geoarrow_array` to support the full range
317+ /// of functionality of the upstream `downcast_primitive_array`, please create an issue or submit a
318+ /// PR.
319+ #[ macro_export]
320+ macro_rules! downcast_geoarrow_array {
321+ ( $array: ident, $fn: expr) => {
322+ match $array. data_type( ) {
323+ $crate:: cast:: __private:: GeoArrowType :: Point ( _) => {
324+ $fn( $crate:: cast:: AsGeoArrowArray :: as_point( $array) )
325+ }
326+ $crate:: cast:: __private:: GeoArrowType :: LineString ( _) => {
327+ $fn( $crate:: cast:: AsGeoArrowArray :: as_line_string( $array) )
328+ }
329+ $crate:: cast:: __private:: GeoArrowType :: Polygon ( _) => {
330+ $fn( $crate:: cast:: AsGeoArrowArray :: as_polygon( $array) )
331+ }
332+ $crate:: cast:: __private:: GeoArrowType :: MultiPoint ( _) => {
333+ $fn( $crate:: cast:: AsGeoArrowArray :: as_multi_point( $array) )
334+ }
335+ $crate:: cast:: __private:: GeoArrowType :: MultiLineString ( _) => {
336+ $fn( $crate:: cast:: AsGeoArrowArray :: as_multi_line_string( $array) )
337+ }
338+ $crate:: cast:: __private:: GeoArrowType :: MultiPolygon ( _) => {
339+ $fn( $crate:: cast:: AsGeoArrowArray :: as_multi_polygon( $array) )
340+ }
341+ $crate:: cast:: __private:: GeoArrowType :: Geometry ( _) => {
342+ $fn( $crate:: cast:: AsGeoArrowArray :: as_geometry( $array) )
343+ }
344+ $crate:: cast:: __private:: GeoArrowType :: GeometryCollection ( _) => $fn(
345+ $crate:: cast:: AsGeoArrowArray :: as_geometry_collection( $array) ,
346+ ) ,
347+ $crate:: cast:: __private:: GeoArrowType :: Rect ( _) => {
348+ $fn( $crate:: cast:: AsGeoArrowArray :: as_rect( $array) )
349+ }
350+ $crate:: cast:: __private:: GeoArrowType :: WKB ( _) => {
351+ $fn( $crate:: cast:: AsGeoArrowArray :: as_wkb:: <i32 >( $array) )
352+ }
353+ $crate:: cast:: __private:: GeoArrowType :: LargeWKB ( _) => {
354+ $fn( $crate:: cast:: AsGeoArrowArray :: as_wkb:: <i64 >( $array) )
355+ }
356+ $crate:: cast:: __private:: GeoArrowType :: WKT ( _) => {
357+ $fn( $crate:: cast:: AsGeoArrowArray :: as_wkt:: <i32 >( $array) )
358+ }
359+ $crate:: cast:: __private:: GeoArrowType :: LargeWKT ( _) => {
360+ $fn( $crate:: cast:: AsGeoArrowArray :: as_wkt:: <i64 >( $array) )
361+ }
362+ }
363+ } ;
364+ }
365+
366+ #[ cfg( test) ]
367+ mod test {
368+ use geoarrow_schema:: WkbType ;
369+
370+ use crate :: array:: WKBArray ;
371+ use crate :: builder:: WKBBuilder ;
372+ use crate :: error:: Result ;
373+ use crate :: { ArrayAccessor , GeoArrowArray } ;
374+
375+ // Verify that this compiles with the macro
376+ #[ allow( dead_code) ]
377+ fn to_wkb ( arr : & dyn GeoArrowArray ) -> Result < WKBArray < i32 > > {
378+ downcast_geoarrow_array ! ( arr, impl_to_wkb)
379+ }
380+
381+ fn impl_to_wkb < ' a > ( geo_arr : & ' a impl ArrayAccessor < ' a > ) -> Result < WKBArray < i32 > > {
382+ // let metadata = geo_arr.metadata().clone();
383+
384+ let geoms = geo_arr
385+ . iter ( )
386+ . map ( |x| x. transpose ( ) )
387+ . collect :: < std:: result:: Result < Vec < _ > , _ > > ( )
388+ . unwrap ( ) ;
389+ let wkb_type = WkbType :: new ( Default :: default ( ) ) ;
390+ Ok ( WKBBuilder :: from_nullable_geometries ( geoms. as_slice ( ) , wkb_type) . finish ( ) )
391+ }
392+ }
0 commit comments