geoarrow_array/builder/
multipolygon.rs1use std::sync::Arc;
2
3use arrow_array::OffsetSizeTrait;
4use arrow_buffer::NullBufferBuilder;
5use geo_traits::{
6 CoordTrait, GeometryTrait, GeometryType, LineStringTrait, MultiPolygonTrait, PolygonTrait,
7};
8use geoarrow_schema::MultiPolygonType;
9use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
10
11use crate::GeoArrowArray;
12use crate::array::{GenericWkbArray, MultiPolygonArray};
13use crate::builder::geo_trait_wrappers::RectWrapper;
14use crate::builder::{CoordBufferBuilder, OffsetsBuilder};
15use crate::capacity::MultiPolygonCapacity;
16use crate::trait_::{GeoArrowArrayAccessor, GeoArrowArrayBuilder};
17use crate::util::GeometryTypeName;
18
19#[derive(Debug)]
23pub struct MultiPolygonBuilder {
24 data_type: MultiPolygonType,
25
26 pub(crate) coords: CoordBufferBuilder,
27
28 pub(crate) geom_offsets: OffsetsBuilder<i32>,
30
31 pub(crate) polygon_offsets: OffsetsBuilder<i32>,
33
34 pub(crate) ring_offsets: OffsetsBuilder<i32>,
36
37 pub(crate) validity: NullBufferBuilder,
39}
40
41impl MultiPolygonBuilder {
42 pub fn new(typ: MultiPolygonType) -> Self {
44 Self::with_capacity(typ, Default::default())
45 }
46
47 pub fn with_capacity(typ: MultiPolygonType, capacity: MultiPolygonCapacity) -> Self {
49 let coords = CoordBufferBuilder::with_capacity(
50 capacity.coord_capacity,
51 typ.coord_type(),
52 typ.dimension(),
53 );
54 Self {
55 coords,
56 geom_offsets: OffsetsBuilder::with_capacity(capacity.geom_capacity),
57 polygon_offsets: OffsetsBuilder::with_capacity(capacity.polygon_capacity),
58 ring_offsets: OffsetsBuilder::with_capacity(capacity.ring_capacity),
59 validity: NullBufferBuilder::new(capacity.geom_capacity),
60 data_type: typ,
61 }
62 }
63
64 pub fn reserve(&mut self, additional: MultiPolygonCapacity) {
70 self.coords.reserve(additional.coord_capacity);
71 self.ring_offsets.reserve(additional.ring_capacity);
72 self.polygon_offsets.reserve(additional.polygon_capacity);
73 self.geom_offsets.reserve(additional.geom_capacity);
74 }
75
76 pub fn reserve_exact(&mut self, additional: MultiPolygonCapacity) {
88 self.coords.reserve_exact(additional.coord_capacity);
89 self.ring_offsets.reserve_exact(additional.ring_capacity);
90 self.polygon_offsets
91 .reserve_exact(additional.polygon_capacity);
92 self.geom_offsets.reserve_exact(additional.geom_capacity);
93 }
94
95 pub fn shrink_to_fit(&mut self) {
97 self.coords.shrink_to_fit();
98 self.ring_offsets.shrink_to_fit();
99 self.polygon_offsets.shrink_to_fit();
100 self.geom_offsets.shrink_to_fit();
101 }
103
104 pub fn finish(mut self) -> MultiPolygonArray {
106 let validity = self.validity.finish();
107
108 MultiPolygonArray::new(
109 self.coords.finish(),
110 self.geom_offsets.finish(),
111 self.polygon_offsets.finish(),
112 self.ring_offsets.finish(),
113 validity,
114 self.data_type.metadata().clone(),
115 )
116 }
117
118 #[inline]
124 pub fn push_polygon(
125 &mut self,
126 value: Option<&impl PolygonTrait<T = f64>>,
127 ) -> GeoArrowResult<()> {
128 if let Some(polygon) = value {
129 let exterior_ring = polygon.exterior();
130 if exterior_ring.is_none() {
131 self.push_empty()?;
132 return Ok(());
133 }
134
135 if let Some(ext_ring) = polygon.exterior() {
136 let num_polygons = 1;
138 self.geom_offsets.try_push_usize(num_polygons)?;
139
140 for coord in ext_ring.coords() {
141 self.coords.push_coord(&coord);
142 }
143
144 self.polygon_offsets
146 .try_push_usize(polygon.num_interiors() + 1)?;
147
148 self.ring_offsets.try_push_usize(ext_ring.num_coords())?;
150
151 for int_ring in polygon.interiors() {
152 self.ring_offsets.try_push_usize(int_ring.num_coords())?;
153
154 for coord in int_ring.coords() {
155 self.coords.push_coord(&coord);
156 }
157 }
158 } else {
159 let num_polygons = 0;
160 self.geom_offsets.try_push_usize(num_polygons)?;
161 }
162 } else {
163 self.push_null();
164 };
165 Ok(())
166 }
167
168 #[inline]
174 pub fn push_multi_polygon(
175 &mut self,
176 value: Option<&impl MultiPolygonTrait<T = f64>>,
177 ) -> GeoArrowResult<()> {
178 if let Some(multi_polygon) = value {
179 let num_polygons = multi_polygon.num_polygons();
181 self.try_push_geom_offset(num_polygons)?;
182
183 for polygon in multi_polygon.polygons() {
185 let ext_ring = polygon.exterior().unwrap();
188 for coord in ext_ring.coords() {
189 self.coords.push_coord(&coord);
190 }
191
192 self.polygon_offsets
194 .try_push_usize(polygon.num_interiors() + 1)?;
195
196 self.ring_offsets.try_push_usize(ext_ring.num_coords())?;
198
199 for int_ring in polygon.interiors() {
200 self.ring_offsets.try_push_usize(int_ring.num_coords())?;
201
202 for coord in int_ring.coords() {
203 self.coords.push_coord(&coord);
204 }
205 }
206 }
207 } else {
208 self.push_null();
209 };
210 Ok(())
211 }
212
213 #[inline]
217 pub fn push_geometry(
218 &mut self,
219 value: Option<&impl GeometryTrait<T = f64>>,
220 ) -> GeoArrowResult<()> {
221 if let Some(value) = value {
222 match value.as_type() {
223 GeometryType::Polygon(g) => self.push_polygon(Some(g))?,
224 GeometryType::MultiPolygon(g) => self.push_multi_polygon(Some(g))?,
225 GeometryType::Rect(g) => self.push_polygon(Some(&RectWrapper::try_new(g)?))?,
226 gt => {
227 return Err(GeoArrowError::IncorrectGeometryType(format!(
228 "Expected MultiPolygon compatible geometry, got {}",
229 gt.name()
230 )));
231 }
232 }
233 } else {
234 self.push_null();
235 };
236 Ok(())
237 }
238
239 pub fn extend_from_iter<'a>(
241 &mut self,
242 geoms: impl Iterator<Item = Option<&'a (impl MultiPolygonTrait<T = f64> + 'a)>>,
243 ) {
244 geoms
245 .into_iter()
246 .try_for_each(|maybe_multi_polygon| self.push_multi_polygon(maybe_multi_polygon))
247 .unwrap();
248 }
249
250 pub fn extend_from_geometry_iter<'a>(
252 &mut self,
253 geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait<T = f64> + 'a)>>,
254 ) -> GeoArrowResult<()> {
255 geoms.into_iter().try_for_each(|g| self.push_geometry(g))?;
256 Ok(())
257 }
258
259 #[inline]
266 pub(crate) fn try_push_geom_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
267 self.geom_offsets.try_push_usize(offsets_length)?;
268 self.validity.append(true);
269 Ok(())
270 }
271
272 #[inline]
279 #[allow(dead_code)]
280 pub(crate) fn try_push_polygon_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
281 self.polygon_offsets.try_push_usize(offsets_length)?;
282 Ok(())
283 }
284
285 #[inline]
292 #[allow(dead_code)]
293 pub(crate) fn try_push_ring_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
294 self.ring_offsets.try_push_usize(offsets_length)?;
295 Ok(())
296 }
297
298 #[inline]
305 pub unsafe fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
306 self.coords.push_coord(coord);
307 Ok(())
308 }
309
310 #[inline]
311 pub(crate) fn push_empty(&mut self) -> GeoArrowResult<()> {
312 self.geom_offsets.try_push_usize(0)?;
313 self.validity.append(true);
314 Ok(())
315 }
316
317 #[inline]
318 pub(crate) fn push_null(&mut self) {
319 self.geom_offsets.extend_constant(1);
323 self.validity.append(false);
324 }
325
326 pub fn from_multi_polygons(
328 geoms: &[impl MultiPolygonTrait<T = f64>],
329 typ: MultiPolygonType,
330 ) -> Self {
331 let capacity = MultiPolygonCapacity::from_multi_polygons(geoms.iter().map(Some));
332 let mut array = Self::with_capacity(typ, capacity);
333 array.extend_from_iter(geoms.iter().map(Some));
334 array
335 }
336
337 pub fn from_nullable_multi_polygons(
339 geoms: &[Option<impl MultiPolygonTrait<T = f64>>],
340 typ: MultiPolygonType,
341 ) -> Self {
342 let capacity = MultiPolygonCapacity::from_multi_polygons(geoms.iter().map(|x| x.as_ref()));
343 let mut array = Self::with_capacity(typ, capacity);
344 array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
345 array
346 }
347
348 pub fn from_nullable_geometries(
350 geoms: &[Option<impl GeometryTrait<T = f64>>],
351 typ: MultiPolygonType,
352 ) -> GeoArrowResult<Self> {
353 let capacity = MultiPolygonCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
354 let mut array = Self::with_capacity(typ, capacity);
355 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
356 Ok(array)
357 }
358}
359
360impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, MultiPolygonType)> for MultiPolygonBuilder {
361 type Error = GeoArrowError;
362
363 fn try_from((value, typ): (GenericWkbArray<O>, MultiPolygonType)) -> GeoArrowResult<Self> {
364 let wkb_objects = value
365 .iter()
366 .map(|x| x.transpose())
367 .collect::<GeoArrowResult<Vec<_>>>()?;
368 Self::from_nullable_geometries(&wkb_objects, typ)
369 }
370}
371
372impl GeoArrowArrayBuilder for MultiPolygonBuilder {
373 fn len(&self) -> usize {
374 self.geom_offsets.len_proxy()
375 }
376
377 fn push_null(&mut self) {
378 self.push_null();
379 }
380
381 fn push_geometry(
382 &mut self,
383 geometry: Option<&impl GeometryTrait<T = f64>>,
384 ) -> GeoArrowResult<()> {
385 self.push_geometry(geometry)
386 }
387
388 fn finish(self) -> Arc<dyn GeoArrowArray> {
389 Arc::new(self.finish())
390 }
391}