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, MultiPolygonType};
9
10use crate::array::{CoordBuffer, GenericWkbArray, PolygonArray};
11use crate::builder::MultiPolygonBuilder;
12use crate::capacity::MultiPolygonCapacity;
13use crate::eq::offset_buffer_eq;
14use crate::scalar::MultiPolygon;
15use crate::trait_::{GeoArrowArray, GeoArrowArrayAccessor, IntoArrow};
16use crate::util::{OffsetBufferUtils, offsets_buffer_i64_to_i32};
17
18#[derive(Debug, Clone)]
23pub struct MultiPolygonArray {
24 pub(crate) data_type: MultiPolygonType,
25
26 pub(crate) coords: CoordBuffer,
27
28 pub(crate) geom_offsets: OffsetBuffer<i32>,
30
31 pub(crate) polygon_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 polygon_offsets: &OffsetBuffer<i32>,
45 ring_offsets: &OffsetBuffer<i32>,
46 validity_len: Option<usize>,
47) -> GeoArrowResult<()> {
48 if validity_len.is_some_and(|len| len != geom_offsets.len_proxy()) {
49 return Err(GeoArrowError::InvalidGeoArrow(
50 "nulls mask length must match the number of values".to_string(),
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 *polygon_offsets.last() as usize != ring_offsets.len_proxy() {
60 return Err(GeoArrowError::InvalidGeoArrow(
61 "largest polygon offset must match ring offsets length".to_string(),
62 ));
63 }
64
65 if *geom_offsets.last() as usize != polygon_offsets.len_proxy() {
66 return Err(GeoArrowError::InvalidGeoArrow(
67 "largest geometry offset must match polygon offsets length".to_string(),
68 ));
69 }
70
71 Ok(())
72}
73
74impl MultiPolygonArray {
75 pub fn new(
88 coords: CoordBuffer,
89 geom_offsets: OffsetBuffer<i32>,
90 polygon_offsets: OffsetBuffer<i32>,
91 ring_offsets: OffsetBuffer<i32>,
92 nulls: Option<NullBuffer>,
93 metadata: Arc<Metadata>,
94 ) -> Self {
95 Self::try_new(
96 coords,
97 geom_offsets,
98 polygon_offsets,
99 ring_offsets,
100 nulls,
101 metadata,
102 )
103 .unwrap()
104 }
105
106 pub fn try_new(
119 coords: CoordBuffer,
120 geom_offsets: OffsetBuffer<i32>,
121 polygon_offsets: OffsetBuffer<i32>,
122 ring_offsets: OffsetBuffer<i32>,
123 nulls: Option<NullBuffer>,
124 metadata: Arc<Metadata>,
125 ) -> GeoArrowResult<Self> {
126 check(
127 &coords,
128 &geom_offsets,
129 &polygon_offsets,
130 &ring_offsets,
131 nulls.as_ref().map(|v| v.len()),
132 )?;
133 Ok(Self {
134 data_type: MultiPolygonType::new(coords.dim(), metadata)
135 .with_coord_type(coords.coord_type()),
136 coords,
137 geom_offsets,
138 polygon_offsets,
139 ring_offsets,
140 nulls,
141 })
142 }
143
144 fn vertices_field(&self) -> Arc<Field> {
145 Field::new("vertices", self.coords.storage_type(), false).into()
146 }
147
148 fn rings_field(&self) -> Arc<Field> {
149 let name = "rings";
150 Field::new_list(name, self.vertices_field(), false).into()
151 }
152
153 fn polygons_field(&self) -> Arc<Field> {
154 let name = "polygons";
155 Field::new_list(name, self.rings_field(), false).into()
156 }
157
158 pub fn coords(&self) -> &CoordBuffer {
160 &self.coords
161 }
162
163 pub fn geom_offsets(&self) -> &OffsetBuffer<i32> {
165 &self.geom_offsets
166 }
167
168 pub fn polygon_offsets(&self) -> &OffsetBuffer<i32> {
170 &self.polygon_offsets
171 }
172
173 pub fn ring_offsets(&self) -> &OffsetBuffer<i32> {
175 &self.ring_offsets
176 }
177
178 pub fn buffer_lengths(&self) -> MultiPolygonCapacity {
180 MultiPolygonCapacity::new(
181 *self.ring_offsets.last() as usize,
182 *self.polygon_offsets.last() as usize,
183 *self.geom_offsets.last() as usize,
184 self.len(),
185 )
186 }
187
188 pub fn num_bytes(&self) -> usize {
190 let validity_len = self.nulls.as_ref().map(|v| v.buffer().len()).unwrap_or(0);
191 validity_len + self.buffer_lengths().num_bytes()
192 }
193
194 #[inline]
199 pub fn slice(&self, offset: usize, length: usize) -> Self {
200 assert!(
201 offset + length <= self.len(),
202 "offset + length may not exceed length of array"
203 );
204 Self {
207 data_type: self.data_type.clone(),
208 coords: self.coords.clone(),
209 geom_offsets: self.geom_offsets.slice(offset, length),
210 polygon_offsets: self.polygon_offsets.clone(),
211 ring_offsets: self.ring_offsets.clone(),
212 nulls: self.nulls.as_ref().map(|v| v.slice(offset, length)),
213 }
214 }
215
216 pub fn into_coord_type(self, coord_type: CoordType) -> Self {
218 Self {
219 data_type: self.data_type.with_coord_type(coord_type),
220 coords: self.coords.into_coord_type(coord_type),
221 ..self
222 }
223 }
224
225 pub fn with_metadata(self, metadata: Arc<Metadata>) -> Self {
227 Self {
228 data_type: self.data_type.with_metadata(metadata),
229 ..self
230 }
231 }
232}
233
234impl GeoArrowArray for MultiPolygonArray {
235 fn as_any(&self) -> &dyn std::any::Any {
236 self
237 }
238
239 fn into_array_ref(self) -> ArrayRef {
240 Arc::new(self.into_arrow())
241 }
242
243 fn to_array_ref(&self) -> ArrayRef {
244 self.clone().into_array_ref()
245 }
246
247 #[inline]
248 fn len(&self) -> usize {
249 self.geom_offsets.len_proxy()
250 }
251
252 #[inline]
253 fn logical_nulls(&self) -> Option<NullBuffer> {
254 self.nulls.clone()
255 }
256
257 #[inline]
258 fn logical_null_count(&self) -> usize {
259 self.nulls.as_ref().map(|v| v.null_count()).unwrap_or(0)
260 }
261
262 #[inline]
263 fn is_null(&self, i: usize) -> bool {
264 self.nulls
265 .as_ref()
266 .map(|n| n.is_null(i))
267 .unwrap_or_default()
268 }
269
270 fn data_type(&self) -> GeoArrowType {
271 GeoArrowType::MultiPolygon(self.data_type.clone())
272 }
273
274 fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
275 Arc::new(self.slice(offset, length))
276 }
277
278 fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
279 Arc::new(self.with_metadata(metadata))
280 }
281}
282
283impl<'a> GeoArrowArrayAccessor<'a> for MultiPolygonArray {
284 type Item = MultiPolygon<'a>;
285
286 unsafe fn value_unchecked(&'a self, index: usize) -> GeoArrowResult<Self::Item> {
287 Ok(MultiPolygon::new(
288 &self.coords,
289 &self.geom_offsets,
290 &self.polygon_offsets,
291 &self.ring_offsets,
292 index,
293 ))
294 }
295}
296
297impl IntoArrow for MultiPolygonArray {
298 type ArrowArray = GenericListArray<i32>;
299 type ExtensionType = MultiPolygonType;
300
301 fn into_arrow(self) -> Self::ArrowArray {
302 let vertices_field = self.vertices_field();
303 let rings_field = self.rings_field();
304 let polygons_field = self.polygons_field();
305
306 let nulls = self.nulls;
307 let coord_array = ArrayRef::from(self.coords);
308 let ring_array = Arc::new(GenericListArray::new(
309 vertices_field,
310 self.ring_offsets,
311 coord_array,
312 None,
313 ));
314 let polygons_array = Arc::new(GenericListArray::new(
315 rings_field,
316 self.polygon_offsets,
317 ring_array,
318 None,
319 ));
320 GenericListArray::new(polygons_field, self.geom_offsets, polygons_array, nulls)
321 }
322
323 fn extension_type(&self) -> &Self::ExtensionType {
324 &self.data_type
325 }
326}
327
328impl TryFrom<(&GenericListArray<i32>, MultiPolygonType)> for MultiPolygonArray {
329 type Error = GeoArrowError;
330
331 fn try_from(
332 (geom_array, typ): (&GenericListArray<i32>, MultiPolygonType),
333 ) -> GeoArrowResult<Self> {
334 let geom_offsets = geom_array.offsets();
335 let nulls = geom_array.nulls();
336
337 let polygons_dyn_array = geom_array.values();
338 let polygons_array = polygons_dyn_array.as_list::<i32>();
339
340 let polygon_offsets = polygons_array.offsets();
341 let rings_dyn_array = polygons_array.values();
342 let rings_array = rings_dyn_array.as_list::<i32>();
343
344 let ring_offsets = rings_array.offsets();
345 let coords = CoordBuffer::from_arrow(rings_array.values().as_ref(), typ.dimension())?;
346
347 Ok(Self::new(
348 coords,
349 geom_offsets.clone(),
350 polygon_offsets.clone(),
351 ring_offsets.clone(),
352 nulls.cloned(),
353 typ.metadata().clone(),
354 ))
355 }
356}
357
358impl TryFrom<(&GenericListArray<i64>, MultiPolygonType)> for MultiPolygonArray {
359 type Error = GeoArrowError;
360
361 fn try_from(
362 (geom_array, typ): (&GenericListArray<i64>, MultiPolygonType),
363 ) -> GeoArrowResult<Self> {
364 let geom_offsets = offsets_buffer_i64_to_i32(geom_array.offsets())?;
365 let nulls = geom_array.nulls();
366
367 let polygons_dyn_array = geom_array.values();
368 let polygons_array = polygons_dyn_array.as_list::<i64>();
369
370 let polygon_offsets = offsets_buffer_i64_to_i32(polygons_array.offsets())?;
371 let rings_dyn_array = polygons_array.values();
372 let rings_array = rings_dyn_array.as_list::<i64>();
373
374 let ring_offsets = offsets_buffer_i64_to_i32(rings_array.offsets())?;
375 let coords = CoordBuffer::from_arrow(rings_array.values().as_ref(), typ.dimension())?;
376
377 Ok(Self::new(
378 coords,
379 geom_offsets,
380 polygon_offsets,
381 ring_offsets,
382 nulls.cloned(),
383 typ.metadata().clone(),
384 ))
385 }
386}
387
388impl TryFrom<(&dyn Array, MultiPolygonType)> for MultiPolygonArray {
389 type Error = GeoArrowError;
390
391 fn try_from((value, typ): (&dyn Array, MultiPolygonType)) -> GeoArrowResult<Self> {
392 match value.data_type() {
393 DataType::List(_) => (value.as_list::<i32>(), typ).try_into(),
394 DataType::LargeList(_) => (value.as_list::<i64>(), typ).try_into(),
395 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
396 "Unexpected MultiPolygon DataType: {:?}",
397 dt
398 ))),
399 }
400 }
401}
402
403impl TryFrom<(&dyn Array, &Field)> for MultiPolygonArray {
404 type Error = GeoArrowError;
405
406 fn try_from((arr, field): (&dyn Array, &Field)) -> GeoArrowResult<Self> {
407 let typ = field.try_extension_type::<MultiPolygonType>()?;
408 (arr, typ).try_into()
409 }
410}
411
412impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, MultiPolygonType)> for MultiPolygonArray {
413 type Error = GeoArrowError;
414
415 fn try_from(value: (GenericWkbArray<O>, MultiPolygonType)) -> GeoArrowResult<Self> {
416 let mut_arr: MultiPolygonBuilder = value.try_into()?;
417 Ok(mut_arr.finish())
418 }
419}
420
421impl From<PolygonArray> for MultiPolygonArray {
422 fn from(value: PolygonArray) -> Self {
423 let (coord_type, dimension, metadata) = value.data_type.into_inner();
424 let new_type = MultiPolygonType::new(dimension, metadata).with_coord_type(coord_type);
425
426 let coords = value.coords;
427 let geom_offsets = OffsetBuffer::from_lengths(vec![1; coords.len()]);
428 let ring_offsets = value.ring_offsets;
429 let polygon_offsets = value.geom_offsets;
430 let nulls = value.nulls;
431 Self {
432 data_type: new_type,
433 coords,
434 geom_offsets,
435 polygon_offsets,
436 ring_offsets,
437 nulls,
438 }
439 }
440}
441
442impl PartialEq for MultiPolygonArray {
443 fn eq(&self, other: &Self) -> bool {
444 self.nulls == other.nulls
445 && offset_buffer_eq(&self.geom_offsets, &other.geom_offsets)
446 && offset_buffer_eq(&self.polygon_offsets, &other.polygon_offsets)
447 && offset_buffer_eq(&self.ring_offsets, &other.ring_offsets)
448 && self.coords == other.coords
449 }
450}
451
452#[cfg(test)]
453mod test {
454 use geo_traits::to_geo::ToGeoMultiPolygon;
455 use geoarrow_schema::{CoordType, Dimension};
456
457 use super::*;
458 use crate::test::multipolygon;
459
460 #[test]
461 fn geo_round_trip() {
462 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
463 let geoms = [
464 Some(multipolygon::mp0()),
465 None,
466 Some(multipolygon::mp1()),
467 None,
468 ];
469 let typ = MultiPolygonType::new(Dimension::XY, Default::default())
470 .with_coord_type(coord_type);
471 let geo_arr = MultiPolygonBuilder::from_nullable_multi_polygons(&geoms, typ).finish();
472
473 for (i, g) in geo_arr.iter().enumerate() {
474 assert_eq!(
475 geoms[i],
476 g.transpose().unwrap().map(|g| g.to_multi_polygon())
477 );
478 }
479
480 for (i, g) in geo_arr.slice(2, 2).iter().enumerate() {
482 assert_eq!(
483 geoms[i + 2],
484 g.transpose().unwrap().map(|g| g.to_multi_polygon())
485 );
486 }
487 }
488 }
489
490 #[test]
491 fn geo_round_trip2() {
492 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
493 let geo_arr = multipolygon::array(coord_type, Dimension::XY);
494 let geo_geoms = geo_arr
495 .iter()
496 .map(|x| x.transpose().unwrap().map(|g| g.to_multi_polygon()))
497 .collect::<Vec<_>>();
498
499 let typ = MultiPolygonType::new(Dimension::XY, Default::default())
500 .with_coord_type(coord_type);
501 let geo_arr2 =
502 MultiPolygonBuilder::from_nullable_multi_polygons(&geo_geoms, typ).finish();
503 assert_eq!(geo_arr, geo_arr2);
504 }
505 }
506
507 #[test]
508 fn try_from_arrow() {
509 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
510 for dim in [
511 Dimension::XY,
512 Dimension::XYZ,
513 Dimension::XYM,
514 Dimension::XYZM,
515 ] {
516 let geo_arr = multipolygon::array(coord_type, dim);
517
518 let extension_type = geo_arr.extension_type().clone();
519 let field = extension_type.to_field("geometry", true);
520
521 let arrow_arr = geo_arr.to_array_ref();
522
523 let geo_arr2: MultiPolygonArray =
524 (arrow_arr.as_ref(), extension_type).try_into().unwrap();
525 let geo_arr3: MultiPolygonArray = (arrow_arr.as_ref(), &field).try_into().unwrap();
526
527 assert_eq!(geo_arr, geo_arr2);
528 assert_eq!(geo_arr, geo_arr3);
529 }
530 }
531 }
532
533 #[test]
534 fn partial_eq() {
535 for dim in [
536 Dimension::XY,
537 Dimension::XYZ,
538 Dimension::XYM,
539 Dimension::XYZM,
540 ] {
541 let arr1 = multipolygon::array(CoordType::Interleaved, dim);
542 let arr2 = multipolygon::array(CoordType::Separated, dim);
543 assert_eq!(arr1, arr1);
544 assert_eq!(arr2, arr2);
545 assert_eq!(arr1, arr2);
546
547 assert_ne!(arr1, arr2.slice(0, 2));
548 }
549 }
550}