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