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