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::{CoordBufferBuilder, OffsetsBuilder};
14use crate::capacity::MultiPolygonCapacity;
15use crate::trait_::{GeoArrowArrayAccessor, GeoArrowArrayBuilder};
16use crate::util::GeometryTypeName;
17
18#[derive(Debug)]
22pub struct MultiPolygonBuilder {
23 data_type: MultiPolygonType,
24
25 pub(crate) coords: CoordBufferBuilder,
26
27 pub(crate) geom_offsets: OffsetsBuilder<i32>,
29
30 pub(crate) polygon_offsets: OffsetsBuilder<i32>,
32
33 pub(crate) ring_offsets: OffsetsBuilder<i32>,
35
36 pub(crate) validity: NullBufferBuilder,
38}
39
40impl MultiPolygonBuilder {
41 pub fn new(typ: MultiPolygonType) -> Self {
43 Self::with_capacity(typ, Default::default())
44 }
45
46 pub fn with_capacity(typ: MultiPolygonType, capacity: MultiPolygonCapacity) -> Self {
48 let coords = CoordBufferBuilder::with_capacity(
49 capacity.coord_capacity,
50 typ.coord_type(),
51 typ.dimension(),
52 );
53 Self {
54 coords,
55 geom_offsets: OffsetsBuilder::with_capacity(capacity.geom_capacity),
56 polygon_offsets: OffsetsBuilder::with_capacity(capacity.polygon_capacity),
57 ring_offsets: OffsetsBuilder::with_capacity(capacity.ring_capacity),
58 validity: NullBufferBuilder::new(capacity.geom_capacity),
59 data_type: typ,
60 }
61 }
62
63 pub fn reserve(&mut self, additional: MultiPolygonCapacity) {
69 self.coords.reserve(additional.coord_capacity);
70 self.ring_offsets.reserve(additional.ring_capacity);
71 self.polygon_offsets.reserve(additional.polygon_capacity);
72 self.geom_offsets.reserve(additional.geom_capacity);
73 }
74
75 pub fn reserve_exact(&mut self, additional: MultiPolygonCapacity) {
87 self.coords.reserve_exact(additional.coord_capacity);
88 self.ring_offsets.reserve_exact(additional.ring_capacity);
89 self.polygon_offsets
90 .reserve_exact(additional.polygon_capacity);
91 self.geom_offsets.reserve_exact(additional.geom_capacity);
92 }
93
94 pub fn finish(mut self) -> MultiPolygonArray {
96 let validity = self.validity.finish();
97
98 MultiPolygonArray::new(
99 self.coords.finish(),
100 self.geom_offsets.finish(),
101 self.polygon_offsets.finish(),
102 self.ring_offsets.finish(),
103 validity,
104 self.data_type.metadata().clone(),
105 )
106 }
107
108 #[inline]
114 pub fn push_polygon(
115 &mut self,
116 value: Option<&impl PolygonTrait<T = f64>>,
117 ) -> GeoArrowResult<()> {
118 if let Some(polygon) = value {
119 let exterior_ring = polygon.exterior();
120 if exterior_ring.is_none() {
121 self.push_empty();
122 return Ok(());
123 }
124
125 let num_polygons = 1;
127 self.geom_offsets.try_push_usize(num_polygons).unwrap();
128
129 let ext_ring = polygon.exterior().unwrap();
131 for coord in ext_ring.coords() {
132 self.coords.push_coord(&coord);
133 }
134
135 self.polygon_offsets
137 .try_push_usize(polygon.num_interiors() + 1)
138 .unwrap();
139
140 self.ring_offsets
142 .try_push_usize(ext_ring.num_coords())
143 .unwrap();
144
145 for int_ring in polygon.interiors() {
146 self.ring_offsets
147 .try_push_usize(int_ring.num_coords())
148 .unwrap();
149
150 for coord in int_ring.coords() {
151 self.coords.push_coord(&coord);
152 }
153 }
154 } else {
155 self.push_null();
156 };
157 Ok(())
158 }
159
160 #[inline]
166 pub fn push_multi_polygon(
167 &mut self,
168 value: Option<&impl MultiPolygonTrait<T = f64>>,
169 ) -> GeoArrowResult<()> {
170 if let Some(multi_polygon) = value {
171 let num_polygons = multi_polygon.num_polygons();
173 self.try_push_geom_offset(num_polygons)?;
174
175 for polygon in multi_polygon.polygons() {
177 let ext_ring = polygon.exterior().unwrap();
180 for coord in ext_ring.coords() {
181 self.coords.push_coord(&coord);
182 }
183
184 self.polygon_offsets
186 .try_push_usize(polygon.num_interiors() + 1)
187 .unwrap();
188
189 self.ring_offsets
191 .try_push_usize(ext_ring.num_coords())
192 .unwrap();
193
194 for int_ring in polygon.interiors() {
195 self.ring_offsets
196 .try_push_usize(int_ring.num_coords())
197 .unwrap();
198
199 for coord in int_ring.coords() {
200 self.coords.push_coord(&coord);
201 }
202 }
203 }
204 } else {
205 self.push_null();
206 };
207 Ok(())
208 }
209
210 #[inline]
214 pub fn push_geometry(
215 &mut self,
216 value: Option<&impl GeometryTrait<T = f64>>,
217 ) -> GeoArrowResult<()> {
218 if let Some(value) = value {
219 match value.as_type() {
220 GeometryType::Polygon(g) => self.push_polygon(Some(g))?,
221 GeometryType::MultiPolygon(g) => self.push_multi_polygon(Some(g))?,
222 gt => {
224 return Err(GeoArrowError::IncorrectGeometryType(format!(
225 "Expected MultiPolygon compatible geometry, got {}",
226 gt.name()
227 )));
228 }
229 }
230 } else {
231 self.push_null();
232 };
233 Ok(())
234 }
235
236 pub fn extend_from_iter<'a>(
238 &mut self,
239 geoms: impl Iterator<Item = Option<&'a (impl MultiPolygonTrait<T = f64> + 'a)>>,
240 ) {
241 geoms
242 .into_iter()
243 .try_for_each(|maybe_multi_polygon| self.push_multi_polygon(maybe_multi_polygon))
244 .unwrap();
245 }
246
247 pub fn extend_from_geometry_iter<'a>(
249 &mut self,
250 geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait<T = f64> + 'a)>>,
251 ) -> GeoArrowResult<()> {
252 geoms.into_iter().try_for_each(|g| self.push_geometry(g))?;
253 Ok(())
254 }
255
256 #[inline]
263 pub(crate) fn try_push_geom_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
264 self.geom_offsets.try_push_usize(offsets_length)?;
265 self.validity.append(true);
266 Ok(())
267 }
268
269 #[inline]
276 pub(crate) fn try_push_polygon_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
277 self.polygon_offsets.try_push_usize(offsets_length)?;
278 Ok(())
279 }
280
281 #[inline]
288 pub(crate) fn try_push_ring_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
289 self.ring_offsets.try_push_usize(offsets_length)?;
290 Ok(())
291 }
292
293 #[inline]
300 pub unsafe fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
301 self.coords.push_coord(coord);
302 Ok(())
303 }
304
305 #[inline]
306 pub(crate) fn push_empty(&mut self) {
307 self.geom_offsets.try_push_usize(0).unwrap();
308 self.validity.append(true);
309 }
310
311 #[inline]
312 pub(crate) fn push_null(&mut self) {
313 self.geom_offsets.extend_constant(1);
317 self.validity.append(false);
318 }
319
320 pub fn from_multi_polygons(
322 geoms: &[impl MultiPolygonTrait<T = f64>],
323 typ: MultiPolygonType,
324 ) -> Self {
325 let capacity = MultiPolygonCapacity::from_multi_polygons(geoms.iter().map(Some));
326 let mut array = Self::with_capacity(typ, capacity);
327 array.extend_from_iter(geoms.iter().map(Some));
328 array
329 }
330
331 pub fn from_nullable_multi_polygons(
333 geoms: &[Option<impl MultiPolygonTrait<T = f64>>],
334 typ: MultiPolygonType,
335 ) -> Self {
336 let capacity = MultiPolygonCapacity::from_multi_polygons(geoms.iter().map(|x| x.as_ref()));
337 let mut array = Self::with_capacity(typ, capacity);
338 array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
339 array
340 }
341
342 pub fn from_nullable_geometries(
344 geoms: &[Option<impl GeometryTrait<T = f64>>],
345 typ: MultiPolygonType,
346 ) -> GeoArrowResult<Self> {
347 let capacity = MultiPolygonCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
348 let mut array = Self::with_capacity(typ, capacity);
349 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
350 Ok(array)
351 }
352}
353
354impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, MultiPolygonType)> for MultiPolygonBuilder {
355 type Error = GeoArrowError;
356
357 fn try_from((value, typ): (GenericWkbArray<O>, MultiPolygonType)) -> GeoArrowResult<Self> {
358 let wkb_objects = value
359 .iter()
360 .map(|x| x.transpose())
361 .collect::<GeoArrowResult<Vec<_>>>()?;
362 Self::from_nullable_geometries(&wkb_objects, typ)
363 }
364}
365
366impl GeoArrowArrayBuilder for MultiPolygonBuilder {
367 fn len(&self) -> usize {
368 self.geom_offsets.len_proxy()
369 }
370
371 fn push_null(&mut self) {
372 self.push_null();
373 }
374
375 fn push_geometry(
376 &mut self,
377 geometry: Option<&impl GeometryTrait<T = f64>>,
378 ) -> GeoArrowResult<()> {
379 self.push_geometry(geometry)
380 }
381
382 fn finish(self) -> Arc<dyn GeoArrowArray> {
383 Arc::new(self.finish())
384 }
385}