Skip to content

Commit 80e905b

Browse files
committed
Add downcast_geometry_array macro
1 parent c013a7f commit 80e905b

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

rust/geoarrow-array/src/cast.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)