geoarrow_array/builder/
polygon.rs1use std::sync::Arc;
2
3use arrow_array::OffsetSizeTrait;
4use arrow_buffer::NullBufferBuilder;
5use geo_traits::{
6 CoordTrait, GeometryTrait, GeometryType, LineStringTrait, MultiPolygonTrait, PolygonTrait,
7 RectTrait,
8};
9use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
10use geoarrow_schema::type_id::GeometryTypeId;
11use geoarrow_schema::{Dimension, PolygonType};
12
13use crate::GeoArrowArray;
14use crate::array::{GenericWkbArray, PolygonArray};
15use crate::builder::geo_trait_wrappers::{RectWrapper, TriangleWrapper};
16use crate::builder::{CoordBufferBuilder, OffsetsBuilder};
17use crate::capacity::PolygonCapacity;
18use crate::trait_::{GeoArrowArrayAccessor, GeoArrowArrayBuilder};
19use crate::util::GeometryTypeName;
20
21#[derive(Debug)]
25pub struct PolygonBuilder {
26 data_type: PolygonType,
27
28 pub(crate) coords: CoordBufferBuilder,
29
30 pub(crate) geom_offsets: OffsetsBuilder<i32>,
32
33 pub(crate) ring_offsets: OffsetsBuilder<i32>,
35
36 pub(crate) validity: NullBufferBuilder,
38}
39
40impl PolygonBuilder {
41 pub fn new(typ: PolygonType) -> Self {
43 Self::with_capacity(typ, Default::default())
44 }
45
46 pub fn with_capacity(typ: PolygonType, capacity: PolygonCapacity) -> 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 ring_offsets: OffsetsBuilder::with_capacity(capacity.ring_capacity),
57 validity: NullBufferBuilder::new(capacity.geom_capacity),
58 data_type: typ,
59 }
60 }
61
62 pub fn reserve(&mut self, capacity: PolygonCapacity) {
68 self.coords.reserve(capacity.coord_capacity);
69 self.ring_offsets.reserve(capacity.ring_capacity);
70 self.geom_offsets.reserve(capacity.geom_capacity);
71 }
72
73 pub fn reserve_exact(&mut self, capacity: PolygonCapacity) {
85 self.coords.reserve_exact(capacity.coord_capacity);
86 self.ring_offsets.reserve_exact(capacity.ring_capacity);
87 self.geom_offsets.reserve_exact(capacity.geom_capacity);
88 }
89
90 pub fn shrink_to_fit(&mut self) {
92 self.coords.shrink_to_fit();
93 self.ring_offsets.shrink_to_fit();
94 self.geom_offsets.shrink_to_fit();
95 }
97
98 #[inline]
105 #[allow(dead_code)]
106 pub(crate) fn try_push_geom_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
107 self.geom_offsets.try_push_usize(offsets_length)?;
108 self.validity.append(true);
109 Ok(())
110 }
111
112 #[inline]
119 #[allow(dead_code)]
120 pub(crate) fn try_push_ring_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
121 self.ring_offsets.try_push_usize(offsets_length)?;
122 Ok(())
123 }
124
125 pub fn finish(mut self) -> PolygonArray {
127 let validity = self.validity.finish();
128
129 PolygonArray::new(
130 self.coords.finish(),
131 self.geom_offsets.finish(),
132 self.ring_offsets.finish(),
133 validity,
134 self.data_type.metadata().clone(),
135 )
136 }
137
138 #[inline]
144 pub fn push_polygon(
145 &mut self,
146 value: Option<&impl PolygonTrait<T = f64>>,
147 ) -> GeoArrowResult<()> {
148 if let Some(polygon) = value {
149 let exterior_ring = polygon.exterior();
150 if exterior_ring.is_none() {
151 self.push_empty();
152 return Ok(());
153 }
154
155 let ext_ring = polygon.exterior().unwrap();
159 self.ring_offsets.try_push_usize(ext_ring.num_coords())?;
160 for coord in ext_ring.coords() {
161 self.coords.push_coord(&coord);
162 }
163
164 let num_interiors = polygon.num_interiors();
166 self.geom_offsets.try_push_usize(num_interiors + 1)?;
167
168 for int_ring in polygon.interiors() {
173 self.ring_offsets.try_push_usize(int_ring.num_coords())?;
174 for coord in int_ring.coords() {
175 self.coords.push_coord(&coord);
176 }
177 }
178
179 self.validity.append(true);
180 } else {
181 self.push_null();
182 }
183 Ok(())
184 }
185
186 #[inline]
188 pub fn push_rect(&mut self, value: Option<&impl RectTrait<T = f64>>) -> GeoArrowResult<()> {
189 if let Some(rect) = value {
190 let rect_wrapper = RectWrapper::try_new(rect)?;
191 self.push_polygon(Some(&rect_wrapper))?;
192 } else {
193 self.push_null();
194 }
195 Ok(())
196 }
197
198 #[inline]
202 pub fn push_geometry(
203 &mut self,
204 value: Option<&impl GeometryTrait<T = f64>>,
205 ) -> GeoArrowResult<()> {
206 if let Some(value) = value {
207 match value.as_type() {
208 GeometryType::Polygon(g) => self.push_polygon(Some(g))?,
209 GeometryType::MultiPolygon(mp) => {
210 let num_polygons = mp.num_polygons();
211 if num_polygons == 0 {
212 self.push_empty();
213 } else if num_polygons == 1 {
214 self.push_polygon(Some(&mp.polygon(0).unwrap()))?
215 } else {
216 return Err(GeoArrowError::IncorrectGeometryType(format!(
217 "Expected MultiPolygon with only one polygon in PolygonBuilder, got {num_polygons} polygons",
218 )));
219 }
220 }
221 GeometryType::Rect(g) => self.push_rect(Some(g))?,
222 GeometryType::Triangle(tri) => self.push_polygon(Some(&TriangleWrapper(tri)))?,
223 gt => {
224 return Err(GeoArrowError::IncorrectGeometryType(format!(
225 "Expected Polygon 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 PolygonTrait<T = f64> + 'a)>>,
240 ) {
241 geoms
242 .into_iter()
243 .try_for_each(|maybe_polygon| self.push_polygon(maybe_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 push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
264 self.coords.push_coord(coord);
265 Ok(())
266 }
267
268 #[inline]
269 pub(crate) fn push_empty(&mut self) {
270 self.geom_offsets.extend_constant(1);
271 self.validity.append(true);
272 }
273
274 #[inline]
275 pub(crate) fn push_null(&mut self) {
276 self.geom_offsets.extend_constant(1);
279 self.validity.append(false);
280 }
281
282 pub fn from_polygons(geoms: &[impl PolygonTrait<T = f64>], typ: PolygonType) -> Self {
284 let capacity = PolygonCapacity::from_polygons(geoms.iter().map(Some));
285 let mut array = Self::with_capacity(typ, capacity);
286 array.extend_from_iter(geoms.iter().map(Some));
287 array
288 }
289
290 pub fn from_nullable_polygons(
292 geoms: &[Option<impl PolygonTrait<T = f64>>],
293 typ: PolygonType,
294 ) -> Self {
295 let capacity = PolygonCapacity::from_polygons(geoms.iter().map(|x| x.as_ref()));
296 let mut array = Self::with_capacity(typ, capacity);
297 array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
298 array
299 }
300
301 pub fn from_nullable_geometries(
303 geoms: &[Option<impl GeometryTrait<T = f64>>],
304 typ: PolygonType,
305 ) -> GeoArrowResult<Self> {
306 let capacity = PolygonCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
307 let mut array = Self::with_capacity(typ, capacity);
308 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
309 Ok(array)
310 }
311}
312
313impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, PolygonType)> for PolygonBuilder {
314 type Error = GeoArrowError;
315
316 fn try_from((value, typ): (GenericWkbArray<O>, PolygonType)) -> GeoArrowResult<Self> {
317 let wkb_objects = value
318 .iter()
319 .map(|x| x.transpose())
320 .collect::<GeoArrowResult<Vec<_>>>()?;
321 Self::from_nullable_geometries(&wkb_objects, typ)
322 }
323}
324
325impl GeoArrowArrayBuilder for PolygonBuilder {
326 fn len(&self) -> usize {
327 self.geom_offsets.len_proxy()
328 }
329
330 fn push_null(&mut self) {
331 self.push_null();
332 }
333
334 fn push_geometry(
335 &mut self,
336 geometry: Option<&impl GeometryTrait<T = f64>>,
337 ) -> GeoArrowResult<()> {
338 self.push_geometry(geometry)
339 }
340
341 fn finish(self) -> Arc<dyn GeoArrowArray> {
342 Arc::new(self.finish())
343 }
344}
345
346impl GeometryTypeId for PolygonBuilder {
347 const GEOMETRY_TYPE_OFFSET: i8 = 3;
348
349 fn dimension(&self) -> Dimension {
350 self.data_type.dimension()
351 }
352}
353
354#[cfg(test)]
355mod test {
356 use geo::BoundingRect;
357 use geo_traits::to_geo::ToGeoPolygon;
358 use geo_types::{Rect, coord};
359 use geoarrow_schema::{Dimension, PolygonType};
360
361 use crate::GeoArrowArrayAccessor;
362 use crate::builder::PolygonBuilder;
363
364 #[test]
365 fn test_push_rect() {
366 let mut builder = PolygonBuilder::new(PolygonType::new(Dimension::XY, Default::default()));
367
368 let rect = Rect::new(coord! { x: 10., y: 20. }, coord! { x: 30., y: 10. });
369 builder.push_rect(Some(&rect)).unwrap();
370 let array = builder.finish();
371
372 let polygon = array.value(0).unwrap().to_polygon();
373 let bounding_rect = polygon.bounding_rect().unwrap();
374 assert_eq!(rect, bounding_rect);
375 }
376}