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::PolygonType;
10use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
11
12use crate::GeoArrowArray;
13use crate::array::{GenericWkbArray, PolygonArray};
14use crate::builder::geo_trait_wrappers::{RectWrapper, TriangleWrapper};
15use crate::builder::{CoordBufferBuilder, OffsetsBuilder};
16use crate::capacity::PolygonCapacity;
17use crate::trait_::{GeoArrowArrayAccessor, GeoArrowArrayBuilder};
18use crate::util::GeometryTypeName;
19
20#[derive(Debug)]
24pub struct PolygonBuilder {
25 data_type: PolygonType,
26
27 pub(crate) coords: CoordBufferBuilder,
28
29 pub(crate) geom_offsets: OffsetsBuilder<i32>,
31
32 pub(crate) ring_offsets: OffsetsBuilder<i32>,
34
35 pub(crate) validity: NullBufferBuilder,
37}
38
39impl PolygonBuilder {
40 pub fn new(typ: PolygonType) -> Self {
42 Self::with_capacity(typ, Default::default())
43 }
44
45 pub fn with_capacity(typ: PolygonType, capacity: PolygonCapacity) -> Self {
47 let coords = CoordBufferBuilder::with_capacity(
48 capacity.coord_capacity,
49 typ.coord_type(),
50 typ.dimension(),
51 );
52 Self {
53 coords,
54 geom_offsets: OffsetsBuilder::with_capacity(capacity.geom_capacity),
55 ring_offsets: OffsetsBuilder::with_capacity(capacity.ring_capacity),
56 validity: NullBufferBuilder::new(capacity.geom_capacity),
57 data_type: typ,
58 }
59 }
60
61 pub fn reserve(&mut self, capacity: PolygonCapacity) {
67 self.coords.reserve(capacity.coord_capacity);
68 self.ring_offsets.reserve(capacity.ring_capacity);
69 self.geom_offsets.reserve(capacity.geom_capacity);
70 }
71
72 pub fn reserve_exact(&mut self, capacity: PolygonCapacity) {
84 self.coords.reserve_exact(capacity.coord_capacity);
85 self.ring_offsets.reserve_exact(capacity.ring_capacity);
86 self.geom_offsets.reserve_exact(capacity.geom_capacity);
87 }
88
89 pub fn shrink_to_fit(&mut self) {
91 self.coords.shrink_to_fit();
92 self.ring_offsets.shrink_to_fit();
93 self.geom_offsets.shrink_to_fit();
94 }
96
97 #[inline]
104 pub(crate) fn try_push_geom_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
105 self.geom_offsets.try_push_usize(offsets_length)?;
106 self.validity.append(true);
107 Ok(())
108 }
109
110 #[inline]
117 pub(crate) fn try_push_ring_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
118 self.ring_offsets.try_push_usize(offsets_length)?;
119 Ok(())
120 }
121
122 pub fn finish(mut self) -> PolygonArray {
124 let validity = self.validity.finish();
125
126 PolygonArray::new(
127 self.coords.finish(),
128 self.geom_offsets.finish(),
129 self.ring_offsets.finish(),
130 validity,
131 self.data_type.metadata().clone(),
132 )
133 }
134
135 #[inline]
141 pub fn push_polygon(
142 &mut self,
143 value: Option<&impl PolygonTrait<T = f64>>,
144 ) -> GeoArrowResult<()> {
145 if let Some(polygon) = value {
146 let exterior_ring = polygon.exterior();
147 if exterior_ring.is_none() {
148 self.push_empty();
149 return Ok(());
150 }
151
152 let ext_ring = polygon.exterior().unwrap();
156 self.ring_offsets.try_push_usize(ext_ring.num_coords())?;
157 for coord in ext_ring.coords() {
158 self.coords.push_coord(&coord);
159 }
160
161 let num_interiors = polygon.num_interiors();
163 self.geom_offsets.try_push_usize(num_interiors + 1)?;
164
165 for int_ring in polygon.interiors() {
170 self.ring_offsets.try_push_usize(int_ring.num_coords())?;
171 for coord in int_ring.coords() {
172 self.coords.push_coord(&coord);
173 }
174 }
175
176 self.validity.append(true);
177 } else {
178 self.push_null();
179 }
180 Ok(())
181 }
182
183 #[inline]
185 pub fn push_rect(&mut self, value: Option<&impl RectTrait<T = f64>>) -> GeoArrowResult<()> {
186 if let Some(rect) = value {
187 let rect_wrapper = RectWrapper::try_new(rect)?;
188 self.push_polygon(Some(&rect_wrapper))?;
189 } else {
190 self.push_null();
191 }
192 Ok(())
193 }
194
195 #[inline]
199 pub fn push_geometry(
200 &mut self,
201 value: Option<&impl GeometryTrait<T = f64>>,
202 ) -> GeoArrowResult<()> {
203 if let Some(value) = value {
204 match value.as_type() {
205 GeometryType::Polygon(g) => self.push_polygon(Some(g))?,
206 GeometryType::MultiPolygon(mp) => {
207 let num_polygons = mp.num_polygons();
208 if num_polygons == 0 {
209 self.push_empty();
210 } else if num_polygons == 1 {
211 self.push_polygon(Some(&mp.polygon(0).unwrap()))?
212 } else {
213 return Err(GeoArrowError::IncorrectGeometryType(format!(
214 "Expected MultiPolygon with only one polygon in PolygonBuilder, got {num_polygons} polygons",
215 )));
216 }
217 }
218 GeometryType::Rect(g) => self.push_rect(Some(g))?,
219 GeometryType::Triangle(tri) => self.push_polygon(Some(&TriangleWrapper(tri)))?,
220 gt => {
221 return Err(GeoArrowError::IncorrectGeometryType(format!(
222 "Expected Polygon compatible geometry, got {}",
223 gt.name()
224 )));
225 }
226 }
227 } else {
228 self.push_null();
229 };
230 Ok(())
231 }
232
233 pub fn extend_from_iter<'a>(
235 &mut self,
236 geoms: impl Iterator<Item = Option<&'a (impl PolygonTrait<T = f64> + 'a)>>,
237 ) {
238 geoms
239 .into_iter()
240 .try_for_each(|maybe_polygon| self.push_polygon(maybe_polygon))
241 .unwrap();
242 }
243
244 pub fn extend_from_geometry_iter<'a>(
246 &mut self,
247 geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait<T = f64> + 'a)>>,
248 ) -> GeoArrowResult<()> {
249 geoms.into_iter().try_for_each(|g| self.push_geometry(g))?;
250 Ok(())
251 }
252
253 #[inline]
260 pub(crate) fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
261 self.coords.push_coord(coord);
262 Ok(())
263 }
264
265 #[inline]
266 pub(crate) fn push_empty(&mut self) {
267 self.geom_offsets.extend_constant(1);
268 self.validity.append(true);
269 }
270
271 #[inline]
272 pub(crate) fn push_null(&mut self) {
273 self.geom_offsets.extend_constant(1);
276 self.validity.append(false);
277 }
278
279 pub fn from_polygons(geoms: &[impl PolygonTrait<T = f64>], typ: PolygonType) -> Self {
281 let capacity = PolygonCapacity::from_polygons(geoms.iter().map(Some));
282 let mut array = Self::with_capacity(typ, capacity);
283 array.extend_from_iter(geoms.iter().map(Some));
284 array
285 }
286
287 pub fn from_nullable_polygons(
289 geoms: &[Option<impl PolygonTrait<T = f64>>],
290 typ: PolygonType,
291 ) -> Self {
292 let capacity = PolygonCapacity::from_polygons(geoms.iter().map(|x| x.as_ref()));
293 let mut array = Self::with_capacity(typ, capacity);
294 array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
295 array
296 }
297
298 pub fn from_nullable_geometries(
300 geoms: &[Option<impl GeometryTrait<T = f64>>],
301 typ: PolygonType,
302 ) -> GeoArrowResult<Self> {
303 let capacity = PolygonCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
304 let mut array = Self::with_capacity(typ, capacity);
305 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
306 Ok(array)
307 }
308}
309
310impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, PolygonType)> for PolygonBuilder {
311 type Error = GeoArrowError;
312
313 fn try_from((value, typ): (GenericWkbArray<O>, PolygonType)) -> GeoArrowResult<Self> {
314 let wkb_objects = value
315 .iter()
316 .map(|x| x.transpose())
317 .collect::<GeoArrowResult<Vec<_>>>()?;
318 Self::from_nullable_geometries(&wkb_objects, typ)
319 }
320}
321
322impl GeoArrowArrayBuilder for PolygonBuilder {
323 fn len(&self) -> usize {
324 self.geom_offsets.len_proxy()
325 }
326
327 fn push_null(&mut self) {
328 self.push_null();
329 }
330
331 fn push_geometry(
332 &mut self,
333 geometry: Option<&impl GeometryTrait<T = f64>>,
334 ) -> GeoArrowResult<()> {
335 self.push_geometry(geometry)
336 }
337
338 fn finish(self) -> Arc<dyn GeoArrowArray> {
339 Arc::new(self.finish())
340 }
341}
342
343#[cfg(test)]
344mod test {
345 use geo::BoundingRect;
346 use geo_traits::to_geo::ToGeoPolygon;
347 use geo_types::{Rect, coord};
348 use geoarrow_schema::{Dimension, PolygonType};
349
350 use crate::GeoArrowArrayAccessor;
351 use crate::builder::PolygonBuilder;
352
353 #[test]
354 fn test_push_rect() {
355 let mut builder = PolygonBuilder::new(PolygonType::new(Dimension::XY, Default::default()));
356
357 let rect = Rect::new(coord! { x: 10., y: 20. }, coord! { x: 30., y: 10. });
358 builder.push_rect(Some(&rect)).unwrap();
359 let array = builder.finish();
360
361 let polygon = array.value(0).unwrap().to_polygon();
362 let bounding_rect = polygon.bounding_rect().unwrap();
363 assert_eq!(rect, bounding_rect);
364 }
365}