1use std::sync::Arc;
2
3use arrow_array::cast::AsArray;
4use arrow_array::{Array, ArrayRef, GenericListArray, OffsetSizeTrait};
5use arrow_buffer::{NullBuffer, OffsetBuffer};
6use arrow_schema::{DataType, Field};
7use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
8use geoarrow_schema::type_id::GeometryTypeId;
9use geoarrow_schema::{CoordType, Dimension, GeoArrowType, Metadata, PolygonType};
10
11use crate::array::{CoordBuffer, GenericWkbArray, RectArray};
12use crate::builder::PolygonBuilder;
13use crate::capacity::PolygonCapacity;
14use crate::eq::offset_buffer_eq;
15use crate::scalar::Polygon;
16use crate::trait_::{GeoArrowArray, GeoArrowArrayAccessor, IntoArrow};
17use crate::util::{OffsetBufferUtils, offsets_buffer_i64_to_i32};
18
19#[derive(Debug, Clone)]
25pub struct PolygonArray {
27 pub(crate) data_type: PolygonType,
28
29 pub(crate) coords: CoordBuffer,
30
31 pub(crate) geom_offsets: OffsetBuffer<i32>,
33
34 pub(crate) ring_offsets: OffsetBuffer<i32>,
36
37 pub(crate) nulls: Option<NullBuffer>,
39}
40
41pub(super) fn check(
42 coords: &CoordBuffer,
43 geom_offsets: &OffsetBuffer<i32>,
44 ring_offsets: &OffsetBuffer<i32>,
45 validity_len: Option<usize>,
46) -> GeoArrowResult<()> {
47 if validity_len.is_some_and(|len| len != geom_offsets.len_proxy()) {
48 return Err(GeoArrowError::InvalidGeoArrow(
49 "nulls mask length must match the number of values".to_string(),
50 ));
51 }
52
53 if *ring_offsets.last() as usize != coords.len() {
54 return Err(GeoArrowError::InvalidGeoArrow(
55 "largest ring offset must match coords length".to_string(),
56 ));
57 }
58
59 if *geom_offsets.last() as usize > ring_offsets.len_proxy() {
61 return Err(GeoArrowError::InvalidGeoArrow(
62 "largest geometry offset must not be longer than ring offsets length".to_string(),
63 ));
64 }
65
66 Ok(())
67}
68
69impl PolygonArray {
70 pub fn new(
82 coords: CoordBuffer,
83 geom_offsets: OffsetBuffer<i32>,
84 ring_offsets: OffsetBuffer<i32>,
85 nulls: Option<NullBuffer>,
86 metadata: Arc<Metadata>,
87 ) -> Self {
88 Self::try_new(coords, geom_offsets, ring_offsets, nulls, metadata).unwrap()
89 }
90
91 pub fn try_new(
103 coords: CoordBuffer,
104 geom_offsets: OffsetBuffer<i32>,
105 ring_offsets: OffsetBuffer<i32>,
106 nulls: Option<NullBuffer>,
107 metadata: Arc<Metadata>,
108 ) -> GeoArrowResult<Self> {
109 check(
110 &coords,
111 &geom_offsets,
112 &ring_offsets,
113 nulls.as_ref().map(|v| v.len()),
114 )?;
115 Ok(Self {
116 data_type: PolygonType::new(coords.dim(), metadata)
117 .with_coord_type(coords.coord_type()),
118 coords,
119 geom_offsets,
120 ring_offsets,
121 nulls,
122 })
123 }
124
125 fn vertices_field(&self) -> Arc<Field> {
126 Field::new("vertices", self.coords.storage_type(), false).into()
127 }
128
129 fn rings_field(&self) -> Arc<Field> {
130 let name = "rings";
131 Field::new_list(name, self.vertices_field(), false).into()
132 }
133
134 pub fn coords(&self) -> &CoordBuffer {
136 &self.coords
137 }
138
139 pub fn geom_offsets(&self) -> &OffsetBuffer<i32> {
141 &self.geom_offsets
142 }
143
144 pub fn ring_offsets(&self) -> &OffsetBuffer<i32> {
146 &self.ring_offsets
147 }
148
149 pub fn buffer_lengths(&self) -> PolygonCapacity {
151 PolygonCapacity::new(
152 *self.ring_offsets.last() as usize,
153 *self.geom_offsets.last() as usize,
154 self.len(),
155 )
156 }
157
158 pub fn num_bytes(&self) -> usize {
160 let validity_len = self.nulls.as_ref().map(|v| v.buffer().len()).unwrap_or(0);
161 validity_len + self.buffer_lengths().num_bytes(self.data_type.dimension())
162 }
163
164 #[inline]
169 pub fn slice(&self, offset: usize, length: usize) -> Self {
170 assert!(
171 offset + length <= self.len(),
172 "offset + length may not exceed length of array"
173 );
174 Self {
177 data_type: self.data_type.clone(),
178 coords: self.coords.clone(),
179 geom_offsets: self.geom_offsets.slice(offset, length),
180 ring_offsets: self.ring_offsets.clone(),
181 nulls: self.nulls.as_ref().map(|v| v.slice(offset, length)),
182 }
183 }
184
185 pub fn into_coord_type(self, coord_type: CoordType) -> Self {
187 Self {
188 data_type: self.data_type.with_coord_type(coord_type),
189 coords: self.coords.into_coord_type(coord_type),
190 ..self
191 }
192 }
193
194 pub fn with_metadata(self, metadata: Arc<Metadata>) -> Self {
196 Self {
197 data_type: self.data_type.with_metadata(metadata),
198 ..self
199 }
200 }
201}
202
203impl GeoArrowArray for PolygonArray {
204 fn as_any(&self) -> &dyn std::any::Any {
205 self
206 }
207
208 fn into_array_ref(self) -> ArrayRef {
209 Arc::new(self.into_arrow())
210 }
211
212 fn to_array_ref(&self) -> ArrayRef {
213 self.clone().into_array_ref()
214 }
215
216 #[inline]
217 fn len(&self) -> usize {
218 self.geom_offsets.len_proxy()
219 }
220
221 #[inline]
222 fn logical_nulls(&self) -> Option<NullBuffer> {
223 self.nulls.clone()
224 }
225
226 #[inline]
227 fn logical_null_count(&self) -> usize {
228 self.nulls.as_ref().map(|v| v.null_count()).unwrap_or(0)
229 }
230
231 #[inline]
232 fn is_null(&self, i: usize) -> bool {
233 self.nulls
234 .as_ref()
235 .map(|n| n.is_null(i))
236 .unwrap_or_default()
237 }
238
239 fn data_type(&self) -> GeoArrowType {
240 GeoArrowType::Polygon(self.data_type.clone())
241 }
242
243 fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
244 Arc::new(self.slice(offset, length))
245 }
246
247 fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
248 Arc::new(self.with_metadata(metadata))
249 }
250}
251
252impl<'a> GeoArrowArrayAccessor<'a> for PolygonArray {
253 type Item = Polygon<'a>;
254
255 unsafe fn value_unchecked(&'a self, index: usize) -> GeoArrowResult<Self::Item> {
256 Ok(Polygon::new(
257 &self.coords,
258 &self.geom_offsets,
259 &self.ring_offsets,
260 index,
261 ))
262 }
263}
264
265impl IntoArrow for PolygonArray {
266 type ArrowArray = GenericListArray<i32>;
267 type ExtensionType = PolygonType;
268
269 fn into_arrow(self) -> Self::ArrowArray {
270 let vertices_field = self.vertices_field();
271 let rings_field = self.rings_field();
272 let nulls = self.nulls;
273 let coord_array = self.coords.into();
274 let ring_array = Arc::new(GenericListArray::new(
275 vertices_field,
276 self.ring_offsets,
277 coord_array,
278 None,
279 ));
280 GenericListArray::new(rings_field, self.geom_offsets, ring_array, nulls)
281 }
282
283 fn extension_type(&self) -> &Self::ExtensionType {
284 &self.data_type
285 }
286}
287
288impl TryFrom<(&GenericListArray<i32>, PolygonType)> for PolygonArray {
289 type Error = GeoArrowError;
290
291 fn try_from((geom_array, typ): (&GenericListArray<i32>, PolygonType)) -> GeoArrowResult<Self> {
292 let geom_offsets = geom_array.offsets();
293 let nulls = geom_array.nulls();
294
295 let rings_dyn_array = geom_array.values();
296 let rings_array = rings_dyn_array.as_list::<i32>();
297
298 let ring_offsets = rings_array.offsets();
299 let coords = CoordBuffer::from_arrow(rings_array.values().as_ref(), typ.dimension())?;
300
301 Ok(Self::new(
302 coords,
303 geom_offsets.clone(),
304 ring_offsets.clone(),
305 nulls.cloned(),
306 typ.metadata().clone(),
307 ))
308 }
309}
310
311impl TryFrom<(&GenericListArray<i64>, PolygonType)> for PolygonArray {
312 type Error = GeoArrowError;
313
314 fn try_from((geom_array, typ): (&GenericListArray<i64>, PolygonType)) -> GeoArrowResult<Self> {
315 let geom_offsets = offsets_buffer_i64_to_i32(geom_array.offsets())?;
316 let nulls = geom_array.nulls();
317
318 let rings_dyn_array = geom_array.values();
319 let rings_array = rings_dyn_array.as_list::<i64>();
320
321 let ring_offsets = offsets_buffer_i64_to_i32(rings_array.offsets())?;
322 let coords = CoordBuffer::from_arrow(rings_array.values().as_ref(), typ.dimension())?;
323
324 Ok(Self::new(
325 coords,
326 geom_offsets,
327 ring_offsets,
328 nulls.cloned(),
329 typ.metadata().clone(),
330 ))
331 }
332}
333impl TryFrom<(&dyn Array, PolygonType)> for PolygonArray {
334 type Error = GeoArrowError;
335
336 fn try_from((value, typ): (&dyn Array, PolygonType)) -> GeoArrowResult<Self> {
337 match value.data_type() {
338 DataType::List(_) => (value.as_list::<i32>(), typ).try_into(),
339 DataType::LargeList(_) => (value.as_list::<i64>(), typ).try_into(),
340 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
341 "Unexpected Polygon DataType: {dt:?}",
342 ))),
343 }
344 }
345}
346
347impl TryFrom<(&dyn Array, &Field)> for PolygonArray {
348 type Error = GeoArrowError;
349
350 fn try_from((arr, field): (&dyn Array, &Field)) -> GeoArrowResult<Self> {
351 let typ = field.try_extension_type::<PolygonType>()?;
352 (arr, typ).try_into()
353 }
354}
355
356impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, PolygonType)> for PolygonArray {
357 type Error = GeoArrowError;
358
359 fn try_from(value: (GenericWkbArray<O>, PolygonType)) -> GeoArrowResult<Self> {
360 let mut_arr: PolygonBuilder = value.try_into()?;
361 Ok(mut_arr.finish())
362 }
363}
364
365impl From<RectArray> for PolygonArray {
366 fn from(value: RectArray) -> Self {
367 let polygon_type = PolygonType::new(
368 value.data_type.dimension(),
369 value.data_type.metadata().clone(),
370 )
371 .with_coord_type(CoordType::Separated);
372
373 let geom_capacity = value.len();
375
376 let ring_capacity = geom_capacity;
378
379 let coord_capacity = (value.len() - value.logical_null_count()) * 5;
382
383 let capacity = PolygonCapacity::new(coord_capacity, ring_capacity, geom_capacity);
384 let mut output_array = PolygonBuilder::with_capacity(polygon_type, capacity);
385
386 value.iter().for_each(|maybe_g| {
387 output_array
388 .push_rect(maybe_g.transpose().unwrap().as_ref())
389 .unwrap()
390 });
391
392 output_array.finish()
393 }
394}
395
396impl PartialEq for PolygonArray {
397 fn eq(&self, other: &Self) -> bool {
398 self.nulls == other.nulls
399 && offset_buffer_eq(&self.geom_offsets, &other.geom_offsets)
400 && offset_buffer_eq(&self.ring_offsets, &other.ring_offsets)
401 && self.coords == other.coords
402 }
403}
404
405impl GeometryTypeId for PolygonArray {
406 const GEOMETRY_TYPE_OFFSET: i8 = 3;
407
408 fn dimension(&self) -> Dimension {
409 self.data_type.dimension()
410 }
411}
412
413#[cfg(test)]
414mod test {
415 use geo_traits::to_geo::ToGeoPolygon;
416 use geoarrow_schema::{CoordType, Dimension};
417
418 use super::*;
419 use crate::test::polygon;
420
421 #[test]
422 fn geo_round_trip() {
423 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
424 let geoms = [Some(polygon::p0()), None, Some(polygon::p1()), None];
425 let typ =
426 PolygonType::new(Dimension::XY, Default::default()).with_coord_type(coord_type);
427 let geo_arr = PolygonBuilder::from_nullable_polygons(&geoms, typ).finish();
428
429 for (i, g) in geo_arr.iter().enumerate() {
430 assert_eq!(geoms[i], g.transpose().unwrap().map(|g| g.to_polygon()));
431 }
432
433 for (i, g) in geo_arr.slice(2, 2).iter().enumerate() {
435 assert_eq!(geoms[i + 2], g.transpose().unwrap().map(|g| g.to_polygon()));
436 }
437 }
438 }
439
440 #[test]
441 fn geo_round_trip2() {
442 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
443 let geo_arr = polygon::array(coord_type, Dimension::XY);
444 let geo_geoms = geo_arr
445 .iter()
446 .map(|x| x.transpose().unwrap().map(|g| g.to_polygon()))
447 .collect::<Vec<_>>();
448
449 let typ =
450 PolygonType::new(Dimension::XY, Default::default()).with_coord_type(coord_type);
451 let geo_arr2 = PolygonBuilder::from_nullable_polygons(&geo_geoms, typ).finish();
452 assert_eq!(geo_arr, geo_arr2);
453 }
454 }
455
456 #[test]
457 fn try_from_arrow() {
458 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
459 for dim in [
460 Dimension::XY,
461 Dimension::XYZ,
462 Dimension::XYM,
463 Dimension::XYZM,
464 ] {
465 let geo_arr = polygon::array(coord_type, dim);
466
467 let extension_type = geo_arr.extension_type().clone();
468 let field = extension_type.to_field("geometry", true);
469
470 let arrow_arr = geo_arr.to_array_ref();
471
472 let geo_arr2: PolygonArray =
473 (arrow_arr.as_ref(), extension_type).try_into().unwrap();
474 let geo_arr3: PolygonArray = (arrow_arr.as_ref(), &field).try_into().unwrap();
475
476 assert_eq!(geo_arr, geo_arr2);
477 assert_eq!(geo_arr, geo_arr3);
478 }
479 }
480 }
481
482 #[test]
483 fn partial_eq() {
484 let arr1 = polygon::p_array(CoordType::Interleaved);
485 let arr2 = polygon::p_array(CoordType::Separated);
486 assert_eq!(arr1, arr1);
487 assert_eq!(arr2, arr2);
488 assert_eq!(arr1, arr2);
489
490 assert_ne!(arr1, arr2.slice(0, 2));
491 }
492
493 #[test]
494 fn test_validation_with_sliced_array() {
495 let arr = polygon::array(CoordType::Interleaved, Dimension::XY);
496 let sliced = arr.slice(0, 1);
497
498 let back =
499 PolygonArray::try_from((sliced.to_array_ref().as_ref(), arr.extension_type().clone()))
500 .unwrap();
501 assert_eq!(back.len(), 1);
502 }
503
504 #[test]
505 fn test_validation_with_array_sliced_by_arrow_rs() {
506 let arr = polygon::array(CoordType::Interleaved, Dimension::XY);
507 let sliced = arr.to_array_ref().slice(0, 1);
508
509 let back = PolygonArray::try_from((sliced.as_ref(), arr.extension_type().clone())).unwrap();
510 assert_eq!(back.len(), 1);
511 }
512}