geoarrow_array/geozero/import/
multipolygon.rs

1use geoarrow_schema::MultiPolygonType;
2use geozero::{GeomProcessor, GeozeroGeometry};
3
4use crate::array::MultiPolygonArray;
5use crate::builder::MultiPolygonBuilder;
6use crate::capacity::MultiPolygonCapacity;
7use crate::geozero::import::util::{from_xy, from_xyzm};
8
9/// GeoZero trait to convert to GeoArrow MultiPolygonArray.
10pub trait ToMultiPolygonArray {
11    /// Convert to GeoArrow MultiPolygonArray
12    fn to_multi_polygon_array(
13        &self,
14        typ: MultiPolygonType,
15    ) -> geozero::error::Result<MultiPolygonArray>;
16
17    /// Convert to a GeoArrow MultiPolygonBuilder
18    fn to_multi_polygon_builder(
19        &self,
20        typ: MultiPolygonType,
21    ) -> geozero::error::Result<MultiPolygonBuilder>;
22}
23
24impl<T: GeozeroGeometry> ToMultiPolygonArray for T {
25    fn to_multi_polygon_array(
26        &self,
27        typ: MultiPolygonType,
28    ) -> geozero::error::Result<MultiPolygonArray> {
29        Ok(self.to_multi_polygon_builder(typ)?.finish())
30    }
31
32    fn to_multi_polygon_builder(
33        &self,
34        typ: MultiPolygonType,
35    ) -> geozero::error::Result<MultiPolygonBuilder> {
36        let mut mutable_array = MultiPolygonBuilder::new(typ);
37        self.process_geom(&mut mutable_array)?;
38        Ok(mutable_array)
39    }
40}
41
42#[allow(unused_variables)]
43impl GeomProcessor for MultiPolygonBuilder {
44    fn geometrycollection_begin(&mut self, size: usize, idx: usize) -> geozero::error::Result<()> {
45        // reserve `size` geometries
46        let capacity = MultiPolygonCapacity::new(0, 0, 0, size);
47        self.reserve(capacity);
48        Ok(())
49    }
50
51    fn geometrycollection_end(&mut self, idx: usize) -> geozero::error::Result<()> {
52        // self.shrink_to_fit()
53        Ok(())
54    }
55
56    fn xy(&mut self, x: f64, y: f64, idx: usize) -> geozero::error::Result<()> {
57        // # Safety:
58        // This upholds invariants because we call try_push_length in multipoint_begin to ensure
59        // offset arrays are correct.
60        unsafe { self.push_coord(&from_xy(x, y).expect("valid coord")) }.unwrap();
61        Ok(())
62    }
63
64    fn coordinate(
65        &mut self,
66        x: f64,
67        y: f64,
68        z: Option<f64>,
69        m: Option<f64>,
70        t: Option<f64>,
71        tm: Option<u64>,
72        idx: usize,
73    ) -> geozero::error::Result<()> {
74        // # Safety:
75        // This upholds invariants because we call try_push_length in multipoint_begin to ensure
76        // offset arrays are correct.
77        unsafe { self.push_coord(&from_xyzm(x, y, z, m).expect("valid coord")) }.unwrap();
78        Ok(())
79    }
80
81    fn multipolygon_begin(&mut self, size: usize, idx: usize) -> geozero::error::Result<()> {
82        // reserve `size` polygons
83        let capacity = MultiPolygonCapacity::new(0, 0, size, 0);
84        self.reserve(capacity);
85
86        // # Safety:
87        // This upholds invariants because we separately update the ring offsets in
88        // linestring_begin
89        self.try_push_geom_offset(size).unwrap();
90        Ok(())
91    }
92
93    fn polygon_begin(
94        &mut self,
95        tagged: bool,
96        size: usize,
97        idx: usize,
98    ) -> geozero::error::Result<()> {
99        // > An untagged Polygon is part of a MultiPolygon
100        if tagged {
101            // reserve 1 polygon
102            let capacity = MultiPolygonCapacity::new(0, 0, 1, 0);
103            self.reserve(capacity);
104
105            // # Safety:
106            // This upholds invariants because we separately update the ring offsets in
107            // linestring_begin
108            self.try_push_geom_offset(1).unwrap();
109        }
110
111        // reserve `size` rings
112        let capacity = MultiPolygonCapacity::new(0, size, 0, 0);
113        self.reserve(capacity);
114
115        // # Safety:
116        // This upholds invariants because we separately update the geometry offsets in
117        // polygon_begin
118        self.try_push_polygon_offset(size).unwrap();
119        Ok(())
120    }
121
122    fn linestring_begin(
123        &mut self,
124        tagged: bool,
125        size: usize,
126        idx: usize,
127    ) -> geozero::error::Result<()> {
128        assert!(!tagged);
129
130        // reserve `size` coordinates
131        let capacity = MultiPolygonCapacity::new(size, 0, 0, 0);
132        self.reserve(capacity);
133
134        // # Safety:
135        // This upholds invariants because we separately update the ring offsets in
136        // linestring_begin
137        self.try_push_ring_offset(size).unwrap();
138        Ok(())
139    }
140}
141
142#[cfg(test)]
143mod test {
144    use geo_types::Geometry;
145    use geoarrow_schema::Dimension;
146    use geozero::error::Result;
147
148    use super::*;
149    use crate::test::multipolygon::{mp0, mp1};
150
151    #[test]
152    fn from_geozero() -> Result<()> {
153        let geo_geoms = vec![mp0(), mp1()];
154
155        let geo = Geometry::GeometryCollection(
156            geo_geoms
157                .clone()
158                .into_iter()
159                .map(Geometry::MultiPolygon)
160                .collect(),
161        );
162        let typ = MultiPolygonType::new(Dimension::XY, Default::default());
163        let geo_arr = geo.to_multi_polygon_array(typ.clone()).unwrap();
164
165        let geo_arr2 = MultiPolygonBuilder::from_multi_polygons(&geo_geoms, typ).finish();
166
167        // These are constructed with two different code paths
168        assert_eq!(geo_arr, geo_arr2);
169        Ok(())
170    }
171}