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 #[allow(dead_code)]
105 pub(crate) fn try_push_geom_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
106 self.geom_offsets.try_push_usize(offsets_length)?;
107 self.validity.append(true);
108 Ok(())
109 }
110
111 #[inline]
118 #[allow(dead_code)]
119 pub(crate) fn try_push_ring_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
120 self.ring_offsets.try_push_usize(offsets_length)?;
121 Ok(())
122 }
123
124 pub fn finish(mut self) -> PolygonArray {
126 let validity = self.validity.finish();
127
128 PolygonArray::new(
129 self.coords.finish(),
130 self.geom_offsets.finish(),
131 self.ring_offsets.finish(),
132 validity,
133 self.data_type.metadata().clone(),
134 )
135 }
136
137 #[inline]
143 pub fn push_polygon(
144 &mut self,
145 value: Option<&impl PolygonTrait<T = f64>>,
146 ) -> GeoArrowResult<()> {
147 if let Some(polygon) = value {
148 let exterior_ring = polygon.exterior();
149 if exterior_ring.is_none() {
150 self.push_empty();
151 return Ok(());
152 }
153
154 let ext_ring = polygon.exterior().unwrap();
158 self.ring_offsets.try_push_usize(ext_ring.num_coords())?;
159 for coord in ext_ring.coords() {
160 self.coords.push_coord(&coord);
161 }
162
163 let num_interiors = polygon.num_interiors();
165 self.geom_offsets.try_push_usize(num_interiors + 1)?;
166
167 for int_ring in polygon.interiors() {
172 self.ring_offsets.try_push_usize(int_ring.num_coords())?;
173 for coord in int_ring.coords() {
174 self.coords.push_coord(&coord);
175 }
176 }
177
178 self.validity.append(true);
179 } else {
180 self.push_null();
181 }
182 Ok(())
183 }
184
185 #[inline]
187 pub fn push_rect(&mut self, value: Option<&impl RectTrait<T = f64>>) -> GeoArrowResult<()> {
188 if let Some(rect) = value {
189 let rect_wrapper = RectWrapper::try_new(rect)?;
190 self.push_polygon(Some(&rect_wrapper))?;
191 } else {
192 self.push_null();
193 }
194 Ok(())
195 }
196
197 #[inline]
201 pub fn push_geometry(
202 &mut self,
203 value: Option<&impl GeometryTrait<T = f64>>,
204 ) -> GeoArrowResult<()> {
205 if let Some(value) = value {
206 match value.as_type() {
207 GeometryType::Polygon(g) => self.push_polygon(Some(g))?,
208 GeometryType::MultiPolygon(mp) => {
209 let num_polygons = mp.num_polygons();
210 if num_polygons == 0 {
211 self.push_empty();
212 } else if num_polygons == 1 {
213 self.push_polygon(Some(&mp.polygon(0).unwrap()))?
214 } else {
215 return Err(GeoArrowError::IncorrectGeometryType(format!(
216 "Expected MultiPolygon with only one polygon in PolygonBuilder, got {num_polygons} polygons",
217 )));
218 }
219 }
220 GeometryType::Rect(g) => self.push_rect(Some(g))?,
221 GeometryType::Triangle(tri) => self.push_polygon(Some(&TriangleWrapper(tri)))?,
222 gt => {
223 return Err(GeoArrowError::IncorrectGeometryType(format!(
224 "Expected Polygon compatible geometry, got {}",
225 gt.name()
226 )));
227 }
228 }
229 } else {
230 self.push_null();
231 };
232 Ok(())
233 }
234
235 pub fn extend_from_iter<'a>(
237 &mut self,
238 geoms: impl Iterator<Item = Option<&'a (impl PolygonTrait<T = f64> + 'a)>>,
239 ) {
240 geoms
241 .into_iter()
242 .try_for_each(|maybe_polygon| self.push_polygon(maybe_polygon))
243 .unwrap();
244 }
245
246 pub fn extend_from_geometry_iter<'a>(
248 &mut self,
249 geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait<T = f64> + 'a)>>,
250 ) -> GeoArrowResult<()> {
251 geoms.into_iter().try_for_each(|g| self.push_geometry(g))?;
252 Ok(())
253 }
254
255 #[inline]
262 pub(crate) fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
263 self.coords.push_coord(coord);
264 Ok(())
265 }
266
267 #[inline]
268 pub(crate) fn push_empty(&mut self) {
269 self.geom_offsets.extend_constant(1);
270 self.validity.append(true);
271 }
272
273 #[inline]
274 pub(crate) fn push_null(&mut self) {
275 self.geom_offsets.extend_constant(1);
278 self.validity.append(false);
279 }
280
281 pub fn from_polygons(geoms: &[impl PolygonTrait<T = f64>], typ: PolygonType) -> Self {
283 let capacity = PolygonCapacity::from_polygons(geoms.iter().map(Some));
284 let mut array = Self::with_capacity(typ, capacity);
285 array.extend_from_iter(geoms.iter().map(Some));
286 array
287 }
288
289 pub fn from_nullable_polygons(
291 geoms: &[Option<impl PolygonTrait<T = f64>>],
292 typ: PolygonType,
293 ) -> Self {
294 let capacity = PolygonCapacity::from_polygons(geoms.iter().map(|x| x.as_ref()));
295 let mut array = Self::with_capacity(typ, capacity);
296 array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
297 array
298 }
299
300 pub fn from_nullable_geometries(
302 geoms: &[Option<impl GeometryTrait<T = f64>>],
303 typ: PolygonType,
304 ) -> GeoArrowResult<Self> {
305 let capacity = PolygonCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
306 let mut array = Self::with_capacity(typ, capacity);
307 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
308 Ok(array)
309 }
310}
311
312impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, PolygonType)> for PolygonBuilder {
313 type Error = GeoArrowError;
314
315 fn try_from((value, typ): (GenericWkbArray<O>, PolygonType)) -> GeoArrowResult<Self> {
316 let wkb_objects = value
317 .iter()
318 .map(|x| x.transpose())
319 .collect::<GeoArrowResult<Vec<_>>>()?;
320 Self::from_nullable_geometries(&wkb_objects, typ)
321 }
322}
323
324impl GeoArrowArrayBuilder for PolygonBuilder {
325 fn len(&self) -> usize {
326 self.geom_offsets.len_proxy()
327 }
328
329 fn push_null(&mut self) {
330 self.push_null();
331 }
332
333 fn push_geometry(
334 &mut self,
335 geometry: Option<&impl GeometryTrait<T = f64>>,
336 ) -> GeoArrowResult<()> {
337 self.push_geometry(geometry)
338 }
339
340 fn finish(self) -> Arc<dyn GeoArrowArray> {
341 Arc::new(self.finish())
342 }
343}
344
345#[cfg(test)]
346mod test {
347 use geo::BoundingRect;
348 use geo_traits::to_geo::ToGeoPolygon;
349 use geo_types::{Rect, coord};
350 use geoarrow_schema::{Dimension, PolygonType};
351
352 use crate::GeoArrowArrayAccessor;
353 use crate::builder::PolygonBuilder;
354
355 #[test]
356 fn test_push_rect() {
357 let mut builder = PolygonBuilder::new(PolygonType::new(Dimension::XY, Default::default()));
358
359 let rect = Rect::new(coord! { x: 10., y: 20. }, coord! { x: 30., y: 10. });
360 builder.push_rect(Some(&rect)).unwrap();
361 let array = builder.finish();
362
363 let polygon = array.value(0).unwrap().to_polygon();
364 let bounding_rect = polygon.bounding_rect().unwrap();
365 assert_eq!(rect, bounding_rect);
366 }
367}