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::{CoordType, GeoArrowType, Metadata, PolygonType};
9
10use crate::array::{CoordBuffer, GenericWkbArray, RectArray};
11use crate::builder::PolygonBuilder;
12use crate::capacity::PolygonCapacity;
13use crate::eq::offset_buffer_eq;
14use crate::scalar::Polygon;
15use crate::trait_::{GeoArrowArray, GeoArrowArrayAccessor, IntoArrow};
16use crate::util::{OffsetBufferUtils, offsets_buffer_i64_to_i32};
17
18#[derive(Debug, Clone)]
24pub struct PolygonArray {
26 pub(crate) data_type: PolygonType,
27
28 pub(crate) coords: CoordBuffer,
29
30 pub(crate) geom_offsets: OffsetBuffer<i32>,
32
33 pub(crate) ring_offsets: OffsetBuffer<i32>,
35
36 pub(crate) nulls: Option<NullBuffer>,
38}
39
40pub(super) fn check(
41 coords: &CoordBuffer,
42 geom_offsets: &OffsetBuffer<i32>,
43 ring_offsets: &OffsetBuffer<i32>,
44 validity_len: Option<usize>,
45) -> GeoArrowResult<()> {
46 if validity_len.is_some_and(|len| len != geom_offsets.len_proxy()) {
47 return Err(GeoArrowError::InvalidGeoArrow(
48 "nulls mask length must match the number of values".to_string(),
49 ));
50 }
51
52 if *ring_offsets.last() as usize != coords.len() {
53 return Err(GeoArrowError::InvalidGeoArrow(
54 "largest ring offset must match coords length".to_string(),
55 ));
56 }
57
58 if *geom_offsets.last() as usize != ring_offsets.len_proxy() {
59 return Err(GeoArrowError::InvalidGeoArrow(
60 "largest geometry offset must match ring offsets length".to_string(),
61 ));
62 }
63
64 Ok(())
65}
66
67impl PolygonArray {
68 pub fn new(
80 coords: CoordBuffer,
81 geom_offsets: OffsetBuffer<i32>,
82 ring_offsets: OffsetBuffer<i32>,
83 nulls: Option<NullBuffer>,
84 metadata: Arc<Metadata>,
85 ) -> Self {
86 Self::try_new(coords, geom_offsets, ring_offsets, nulls, metadata).unwrap()
87 }
88
89 pub fn try_new(
101 coords: CoordBuffer,
102 geom_offsets: OffsetBuffer<i32>,
103 ring_offsets: OffsetBuffer<i32>,
104 nulls: Option<NullBuffer>,
105 metadata: Arc<Metadata>,
106 ) -> GeoArrowResult<Self> {
107 check(
108 &coords,
109 &geom_offsets,
110 &ring_offsets,
111 nulls.as_ref().map(|v| v.len()),
112 )?;
113 Ok(Self {
114 data_type: PolygonType::new(coords.dim(), metadata)
115 .with_coord_type(coords.coord_type()),
116 coords,
117 geom_offsets,
118 ring_offsets,
119 nulls,
120 })
121 }
122
123 fn vertices_field(&self) -> Arc<Field> {
124 Field::new("vertices", self.coords.storage_type(), false).into()
125 }
126
127 fn rings_field(&self) -> Arc<Field> {
128 let name = "rings";
129 Field::new_list(name, self.vertices_field(), false).into()
130 }
131
132 pub fn coords(&self) -> &CoordBuffer {
134 &self.coords
135 }
136
137 pub fn geom_offsets(&self) -> &OffsetBuffer<i32> {
139 &self.geom_offsets
140 }
141
142 pub fn ring_offsets(&self) -> &OffsetBuffer<i32> {
144 &self.ring_offsets
145 }
146
147 pub fn buffer_lengths(&self) -> PolygonCapacity {
149 PolygonCapacity::new(
150 *self.ring_offsets.last() as usize,
151 *self.geom_offsets.last() as usize,
152 self.len(),
153 )
154 }
155
156 pub fn num_bytes(&self) -> usize {
158 let validity_len = self.nulls.as_ref().map(|v| v.buffer().len()).unwrap_or(0);
159 validity_len + self.buffer_lengths().num_bytes()
160 }
161
162 #[inline]
167 pub fn slice(&self, offset: usize, length: usize) -> Self {
168 assert!(
169 offset + length <= self.len(),
170 "offset + length may not exceed length of array"
171 );
172 Self {
175 data_type: self.data_type.clone(),
176 coords: self.coords.clone(),
177 geom_offsets: self.geom_offsets.slice(offset, length),
178 ring_offsets: self.ring_offsets.clone(),
179 nulls: self.nulls.as_ref().map(|v| v.slice(offset, length)),
180 }
181 }
182
183 pub fn into_coord_type(self, coord_type: CoordType) -> Self {
185 Self {
186 data_type: self.data_type.with_coord_type(coord_type),
187 coords: self.coords.into_coord_type(coord_type),
188 ..self
189 }
190 }
191
192 pub fn with_metadata(self, metadata: Arc<Metadata>) -> Self {
194 Self {
195 data_type: self.data_type.with_metadata(metadata),
196 ..self
197 }
198 }
199}
200
201impl GeoArrowArray for PolygonArray {
202 fn as_any(&self) -> &dyn std::any::Any {
203 self
204 }
205
206 fn into_array_ref(self) -> ArrayRef {
207 Arc::new(self.into_arrow())
208 }
209
210 fn to_array_ref(&self) -> ArrayRef {
211 self.clone().into_array_ref()
212 }
213
214 #[inline]
215 fn len(&self) -> usize {
216 self.geom_offsets.len_proxy()
217 }
218
219 #[inline]
220 fn logical_nulls(&self) -> Option<NullBuffer> {
221 self.nulls.clone()
222 }
223
224 #[inline]
225 fn logical_null_count(&self) -> usize {
226 self.nulls.as_ref().map(|v| v.null_count()).unwrap_or(0)
227 }
228
229 #[inline]
230 fn is_null(&self, i: usize) -> bool {
231 self.nulls
232 .as_ref()
233 .map(|n| n.is_null(i))
234 .unwrap_or_default()
235 }
236
237 fn data_type(&self) -> GeoArrowType {
238 GeoArrowType::Polygon(self.data_type.clone())
239 }
240
241 fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
242 Arc::new(self.slice(offset, length))
243 }
244
245 fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
246 Arc::new(self.with_metadata(metadata))
247 }
248}
249
250impl<'a> GeoArrowArrayAccessor<'a> for PolygonArray {
251 type Item = Polygon<'a>;
252
253 unsafe fn value_unchecked(&'a self, index: usize) -> GeoArrowResult<Self::Item> {
254 Ok(Polygon::new(
255 &self.coords,
256 &self.geom_offsets,
257 &self.ring_offsets,
258 index,
259 ))
260 }
261}
262
263impl IntoArrow for PolygonArray {
264 type ArrowArray = GenericListArray<i32>;
265 type ExtensionType = PolygonType;
266
267 fn into_arrow(self) -> Self::ArrowArray {
268 let vertices_field = self.vertices_field();
269 let rings_field = self.rings_field();
270 let nulls = self.nulls;
271 let coord_array = self.coords.into();
272 let ring_array = Arc::new(GenericListArray::new(
273 vertices_field,
274 self.ring_offsets,
275 coord_array,
276 None,
277 ));
278 GenericListArray::new(rings_field, self.geom_offsets, ring_array, nulls)
279 }
280
281 fn extension_type(&self) -> &Self::ExtensionType {
282 &self.data_type
283 }
284}
285
286impl TryFrom<(&GenericListArray<i32>, PolygonType)> for PolygonArray {
287 type Error = GeoArrowError;
288
289 fn try_from((geom_array, typ): (&GenericListArray<i32>, PolygonType)) -> GeoArrowResult<Self> {
290 let geom_offsets = geom_array.offsets();
291 let nulls = geom_array.nulls();
292
293 let rings_dyn_array = geom_array.values();
294 let rings_array = rings_dyn_array.as_list::<i32>();
295
296 let ring_offsets = rings_array.offsets();
297 let coords = CoordBuffer::from_arrow(rings_array.values().as_ref(), typ.dimension())?;
298
299 Ok(Self::new(
300 coords,
301 geom_offsets.clone(),
302 ring_offsets.clone(),
303 nulls.cloned(),
304 typ.metadata().clone(),
305 ))
306 }
307}
308
309impl TryFrom<(&GenericListArray<i64>, PolygonType)> for PolygonArray {
310 type Error = GeoArrowError;
311
312 fn try_from((geom_array, typ): (&GenericListArray<i64>, PolygonType)) -> GeoArrowResult<Self> {
313 let geom_offsets = offsets_buffer_i64_to_i32(geom_array.offsets())?;
314 let nulls = geom_array.nulls();
315
316 let rings_dyn_array = geom_array.values();
317 let rings_array = rings_dyn_array.as_list::<i64>();
318
319 let ring_offsets = offsets_buffer_i64_to_i32(rings_array.offsets())?;
320 let coords = CoordBuffer::from_arrow(rings_array.values().as_ref(), typ.dimension())?;
321
322 Ok(Self::new(
323 coords,
324 geom_offsets,
325 ring_offsets,
326 nulls.cloned(),
327 typ.metadata().clone(),
328 ))
329 }
330}
331impl TryFrom<(&dyn Array, PolygonType)> for PolygonArray {
332 type Error = GeoArrowError;
333
334 fn try_from((value, typ): (&dyn Array, PolygonType)) -> GeoArrowResult<Self> {
335 match value.data_type() {
336 DataType::List(_) => (value.as_list::<i32>(), typ).try_into(),
337 DataType::LargeList(_) => (value.as_list::<i64>(), typ).try_into(),
338 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
339 "Unexpected Polygon DataType: {:?}",
340 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
404#[cfg(test)]
405mod test {
406 use geo_traits::to_geo::ToGeoPolygon;
407 use geoarrow_schema::{CoordType, Dimension};
408
409 use super::*;
410 use crate::test::polygon;
411
412 #[test]
413 fn geo_round_trip() {
414 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
415 let geoms = [Some(polygon::p0()), None, Some(polygon::p1()), None];
416 let typ =
417 PolygonType::new(Dimension::XY, Default::default()).with_coord_type(coord_type);
418 let geo_arr = PolygonBuilder::from_nullable_polygons(&geoms, typ).finish();
419
420 for (i, g) in geo_arr.iter().enumerate() {
421 assert_eq!(geoms[i], g.transpose().unwrap().map(|g| g.to_polygon()));
422 }
423
424 for (i, g) in geo_arr.slice(2, 2).iter().enumerate() {
426 assert_eq!(geoms[i + 2], g.transpose().unwrap().map(|g| g.to_polygon()));
427 }
428 }
429 }
430
431 #[test]
432 fn geo_round_trip2() {
433 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
434 let geo_arr = polygon::array(coord_type, Dimension::XY);
435 let geo_geoms = geo_arr
436 .iter()
437 .map(|x| x.transpose().unwrap().map(|g| g.to_polygon()))
438 .collect::<Vec<_>>();
439
440 let typ =
441 PolygonType::new(Dimension::XY, Default::default()).with_coord_type(coord_type);
442 let geo_arr2 = PolygonBuilder::from_nullable_polygons(&geo_geoms, typ).finish();
443 assert_eq!(geo_arr, geo_arr2);
444 }
445 }
446
447 #[test]
448 fn try_from_arrow() {
449 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
450 for dim in [
451 Dimension::XY,
452 Dimension::XYZ,
453 Dimension::XYM,
454 Dimension::XYZM,
455 ] {
456 let geo_arr = polygon::array(coord_type, dim);
457
458 let extension_type = geo_arr.extension_type().clone();
459 let field = extension_type.to_field("geometry", true);
460
461 let arrow_arr = geo_arr.to_array_ref();
462
463 let geo_arr2: PolygonArray =
464 (arrow_arr.as_ref(), extension_type).try_into().unwrap();
465 let geo_arr3: PolygonArray = (arrow_arr.as_ref(), &field).try_into().unwrap();
466
467 assert_eq!(geo_arr, geo_arr2);
468 assert_eq!(geo_arr, geo_arr3);
469 }
470 }
471 }
472
473 #[test]
474 fn partial_eq() {
475 let arr1 = polygon::p_array(CoordType::Interleaved);
476 let arr2 = polygon::p_array(CoordType::Separated);
477 assert_eq!(arr1, arr1);
478 assert_eq!(arr2, arr2);
479 assert_eq!(arr1, arr2);
480
481 assert_ne!(arr1, arr2.slice(0, 2));
482 }
483}