geoarrow_expr_geo/
area.rs

1use 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}