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