geoarrow_expr_geo/
area.rs1use arrow_array::Float64Array;
2use arrow_array::builder::Float64Builder;
3use arrow_buffer::NullBuffer;
4use geo::Area;
5use geoarrow_array::{GeoArrowArray, GeoArrowArrayAccessor, downcast_geoarrow_array};
6use geoarrow_schema::GeoArrowType;
7use geoarrow_schema::error::GeoArrowResult;
8
9use crate::util::to_geo::geometry_to_geo;
10
11pub fn unsigned_area(array: &dyn GeoArrowArray) -> GeoArrowResult<Float64Array> {
12 downcast_geoarrow_array!(array, _unsigned_area_impl)
13}
14
15pub fn signed_area(array: &dyn GeoArrowArray) -> GeoArrowResult<Float64Array> {
16 downcast_geoarrow_array!(array, _signed_area_impl)
17}
18
19fn _zeros(len: usize, nulls: Option<NullBuffer>) -> Float64Array {
20 let values = vec![0.0f64; len];
21 Float64Array::new(values.into(), nulls)
22}
23
24fn _unsigned_area_impl<'a>(
25 array: &'a impl GeoArrowArrayAccessor<'a>,
26) -> GeoArrowResult<Float64Array> {
27 use GeoArrowType::*;
28 match array.data_type() {
29 Point(_) | LineString(_) | MultiPoint(_) | MultiLineString(_) => {
30 Ok(_zeros(array.len(), array.logical_nulls()))
31 }
32 _ => _area_impl(array, Area::unsigned_area),
33 }
34}
35
36fn _signed_area_impl<'a>(
37 array: &'a impl GeoArrowArrayAccessor<'a>,
38) -> GeoArrowResult<Float64Array> {
39 use GeoArrowType::*;
40 match array.data_type() {
41 Point(_) | LineString(_) | MultiPoint(_) | MultiLineString(_) => {
42 Ok(_zeros(array.len(), array.logical_nulls()))
43 }
44 _ => _area_impl(array, Area::signed_area),
45 }
46}
47
48fn _area_impl<'a, F: Fn(&geo::Geometry) -> f64>(
49 array: &'a impl GeoArrowArrayAccessor<'a>,
50 area_fn: F,
51) -> GeoArrowResult<Float64Array> {
52 let mut builder = Float64Builder::with_capacity(array.len());
53
54 for item in array.iter() {
55 if let Some(geom) = item {
56 let geo_geom = geometry_to_geo(&geom?)?;
57 builder.append_value(area_fn(&geo_geom));
58 } else {
59 builder.append_null();
60 }
61 }
62
63 Ok(builder.finish())
64}
65
66#[cfg(test)]
67mod test {
68 use arrow_array::create_array;
69 use geoarrow_schema::{CoordType, Dimension};
70
71 use super::*;
72
73 #[test]
74 fn area_zero() {
75 let geo_arr = geoarrow_array::test::point::array(CoordType::Interleaved, Dimension::XY);
76 let signed = signed_area(&geo_arr).unwrap();
77 let unsigned = unsigned_area(&geo_arr).unwrap();
78
79 let expected = create_array!(Float64, [Some(0.0), Some(0.0), None, Some(0.0)]);
80 assert_eq!(&signed, expected.as_ref());
81 assert_eq!(&unsigned, expected.as_ref());
82 }
83
84 #[test]
85 fn area_polygon() {
86 let geo_arr = geoarrow_array::test::polygon::array(CoordType::Separated, Dimension::XY);
87 let signed = signed_area(&geo_arr).unwrap();
88 let unsigned = unsigned_area(&geo_arr).unwrap();
89
90 let expected = create_array!(Float64, [Some(550.0), Some(675.0), None, Some(0.0)]);
91 assert_eq!(&signed, expected.as_ref());
92 assert_eq!(&unsigned, expected.as_ref());
93 }
94}