1use std::collections::HashSet;
2use std::sync::Arc;
3
4use arrow_array::cast::AsArray;
5use arrow_array::{Array, ArrayRef, OffsetSizeTrait, UnionArray};
6use arrow_buffer::{NullBuffer, ScalarBuffer};
7use arrow_schema::{ArrowError, DataType, Field, UnionMode};
8use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
9use geoarrow_schema::{
10 CoordType, Dimension, GeoArrowType, GeometryCollectionType, GeometryType, LineStringType,
11 Metadata, MultiLineStringType, MultiPointType, MultiPolygonType, PointType, PolygonType,
12};
13
14use crate::array::*;
15use crate::builder::*;
16use crate::capacity::GeometryCapacity;
17use crate::scalar::Geometry;
18use crate::trait_::{GeoArrowArray, GeoArrowArrayAccessor, IntoArrow};
19
20#[derive(Debug, Clone)]
56pub struct GeometryArray {
57 pub(crate) data_type: GeometryType,
58
59 pub(crate) type_ids: ScalarBuffer<i8>,
62
63 pub(crate) offsets: ScalarBuffer<i32>,
65
66 pub(crate) points: [PointArray; 4],
68 pub(crate) line_strings: [LineStringArray; 4],
69 pub(crate) polygons: [PolygonArray; 4],
70 pub(crate) mpoints: [MultiPointArray; 4],
71 pub(crate) mline_strings: [MultiLineStringArray; 4],
72 pub(crate) mpolygons: [MultiPolygonArray; 4],
73 pub(crate) gcs: [GeometryCollectionArray; 4],
74}
75
76impl GeometryArray {
77 #[allow(clippy::too_many_arguments)]
88 pub fn new(
89 type_ids: ScalarBuffer<i8>,
90 offsets: ScalarBuffer<i32>,
91 points: [PointArray; 4],
92 line_strings: [LineStringArray; 4],
93 polygons: [PolygonArray; 4],
94 mpoints: [MultiPointArray; 4],
95 mline_strings: [MultiLineStringArray; 4],
96 mpolygons: [MultiPolygonArray; 4],
97 gcs: [GeometryCollectionArray; 4],
98 metadata: Arc<Metadata>,
99 ) -> Self {
100 let mut coord_types = HashSet::new();
102 points.iter().for_each(|arr| {
103 coord_types.insert(arr.data_type.coord_type());
104 });
105 line_strings.iter().for_each(|arr| {
106 coord_types.insert(arr.data_type.coord_type());
107 });
108 polygons.iter().for_each(|arr| {
109 coord_types.insert(arr.data_type.coord_type());
110 });
111 mpoints.iter().for_each(|arr| {
112 coord_types.insert(arr.data_type.coord_type());
113 });
114 mline_strings.iter().for_each(|arr| {
115 coord_types.insert(arr.data_type.coord_type());
116 });
117 mpolygons.iter().for_each(|arr| {
118 coord_types.insert(arr.data_type.coord_type());
119 });
120
121 assert!(coord_types.len() == 1);
122 let coord_type = coord_types.into_iter().next().unwrap();
123
124 Self {
125 data_type: GeometryType::new(metadata).with_coord_type(coord_type),
126 type_ids,
127 offsets,
128 points,
129 line_strings,
130 polygons,
131 mpoints,
132 mline_strings,
133 mpolygons,
134 gcs,
135 }
136 }
137
138 pub fn buffer_lengths(&self) -> GeometryCapacity {
140 GeometryCapacity::new(
141 0,
142 core::array::from_fn(|i| self.points[i].buffer_lengths()),
143 core::array::from_fn(|i| self.line_strings[i].buffer_lengths()),
144 core::array::from_fn(|i| self.polygons[i].buffer_lengths()),
145 core::array::from_fn(|i| self.mpoints[i].buffer_lengths()),
146 core::array::from_fn(|i| self.mline_strings[i].buffer_lengths()),
147 core::array::from_fn(|i| self.mpolygons[i].buffer_lengths()),
148 core::array::from_fn(|i| self.gcs[i].buffer_lengths()),
149 )
150 }
151
152 pub fn type_ids(&self) -> &ScalarBuffer<i8> {
154 &self.type_ids
155 }
156
157 pub fn offsets(&self) -> &ScalarBuffer<i32> {
159 &self.offsets
160 }
161
162 pub(crate) fn has_points(&self, dim: Dimension) -> bool {
164 !self.points[dim.order()].is_empty()
165 }
166
167 pub(crate) fn has_line_strings(&self, dim: Dimension) -> bool {
168 !self.line_strings[dim.order()].is_empty()
169 }
170
171 pub(crate) fn has_polygons(&self, dim: Dimension) -> bool {
172 !self.polygons[dim.order()].is_empty()
173 }
174
175 pub(crate) fn has_multi_points(&self, dim: Dimension) -> bool {
176 !self.mpoints[dim.order()].is_empty()
177 }
178
179 pub(crate) fn has_multi_line_strings(&self, dim: Dimension) -> bool {
180 !self.mline_strings[dim.order()].is_empty()
181 }
182
183 pub(crate) fn has_multi_polygons(&self, dim: Dimension) -> bool {
184 !self.mpolygons[dim.order()].is_empty()
185 }
186
187 #[allow(dead_code)]
188 pub(crate) fn has_geometry_collections(&self, dim: Dimension) -> bool {
189 !self.gcs[dim.order()].is_empty()
190 }
191
192 pub fn has_dimension(&self, dim: Dimension) -> bool {
194 self.has_points(dim)
195 || self.has_line_strings(dim)
196 || self.has_polygons(dim)
197 || self.has_multi_points(dim)
198 || self.has_multi_line_strings(dim)
199 || self.has_multi_polygons(dim)
200 }
201
202 pub fn has_only_dimension(&self, dim: Dimension) -> bool {
205 use Dimension::*;
206 let existant_dims = [
207 self.has_dimension(XY),
208 self.has_dimension(XYZ),
209 self.has_dimension(XYM),
210 self.has_dimension(XYZM),
211 ];
212 existant_dims.iter().map(|b| *b as u8).sum::<u8>() == 1 && existant_dims[dim.order()]
213 }
214
215 pub fn num_bytes(&self) -> usize {
217 self.buffer_lengths().num_bytes()
218 }
219
220 #[inline]
230 pub fn slice(&self, offset: usize, length: usize) -> Self {
231 assert!(
232 offset + length <= self.len(),
233 "offset + length may not exceed length of array"
234 );
235 Self {
236 data_type: self.data_type.clone(),
237 type_ids: self.type_ids.slice(offset, length),
238 offsets: self.offsets.slice(offset, length),
239
240 points: self.points.clone(),
241 line_strings: self.line_strings.clone(),
242 polygons: self.polygons.clone(),
243 mpoints: self.mpoints.clone(),
244 mline_strings: self.mline_strings.clone(),
245 mpolygons: self.mpolygons.clone(),
246 gcs: self.gcs.clone(),
247 }
248 }
249
250 pub fn into_coord_type(self, coord_type: CoordType) -> Self {
252 Self {
253 data_type: self.data_type.with_coord_type(coord_type),
254 points: self.points.map(|arr| arr.into_coord_type(coord_type)),
255 line_strings: self.line_strings.map(|arr| arr.into_coord_type(coord_type)),
256 polygons: self.polygons.map(|arr| arr.into_coord_type(coord_type)),
257 mpoints: self.mpoints.map(|arr| arr.into_coord_type(coord_type)),
258 mline_strings: self
259 .mline_strings
260 .map(|arr| arr.into_coord_type(coord_type)),
261 mpolygons: self.mpolygons.map(|arr| arr.into_coord_type(coord_type)),
262 gcs: self.gcs.map(|arr| arr.into_coord_type(coord_type)),
263 ..self
264 }
265 }
266
267 pub fn with_metadata(self, metadata: Arc<Metadata>) -> Self {
269 Self {
270 data_type: self.data_type.with_metadata(metadata),
271 ..self
272 }
273 }
274
275 #[allow(dead_code)]
277 pub(crate) fn contained_types(&self) -> HashSet<GeoArrowType> {
278 let mut types = HashSet::new();
279 self.points.iter().for_each(|arr| {
280 if !arr.is_empty() {
281 types.insert(arr.data_type());
282 }
283 });
284 self.line_strings.iter().for_each(|arr| {
285 if !arr.is_empty() {
286 types.insert(arr.data_type());
287 }
288 });
289 self.polygons.iter().for_each(|arr| {
290 if !arr.is_empty() {
291 types.insert(arr.data_type());
292 }
293 });
294 self.mpoints.iter().for_each(|arr| {
295 if !arr.is_empty() {
296 types.insert(arr.data_type());
297 }
298 });
299 self.mline_strings.iter().for_each(|arr| {
300 if !arr.is_empty() {
301 types.insert(arr.data_type());
302 }
303 });
304 self.mpolygons.iter().for_each(|arr| {
305 if !arr.is_empty() {
306 types.insert(arr.data_type());
307 }
308 });
309 self.gcs.iter().for_each(|arr| {
310 if !arr.is_empty() {
311 types.insert(arr.data_type());
312 }
313 });
314
315 types
316 }
317}
318
319impl GeoArrowArray for GeometryArray {
320 fn as_any(&self) -> &dyn std::any::Any {
321 self
322 }
323
324 fn into_array_ref(self) -> ArrayRef {
325 Arc::new(self.into_arrow())
326 }
327
328 fn to_array_ref(&self) -> ArrayRef {
329 self.clone().into_array_ref()
330 }
331
332 #[inline]
333 fn len(&self) -> usize {
334 self.type_ids.len()
336 }
337
338 #[inline]
339 fn logical_nulls(&self) -> Option<NullBuffer> {
340 self.to_array_ref().logical_nulls()
341 }
342
343 #[inline]
344 fn logical_null_count(&self) -> usize {
345 self.to_array_ref().logical_null_count()
346 }
347
348 #[inline]
349 fn is_null(&self, i: usize) -> bool {
350 let type_id = self.type_ids[i];
351 let offset = self.offsets[i] as usize;
352 let dim = (type_id / 10) as usize;
353 match type_id % 10 {
354 1 => self.points[dim].is_null(offset),
355 2 => self.line_strings[dim].is_null(offset),
356 3 => self.polygons[dim].is_null(offset),
357 4 => self.mpoints[dim].is_null(offset),
358 5 => self.mline_strings[dim].is_null(offset),
359 6 => self.mpolygons[dim].is_null(offset),
360 7 => self.gcs[dim].is_null(offset),
361 _ => unreachable!("unknown type_id {}", type_id),
362 }
363 }
364
365 fn data_type(&self) -> GeoArrowType {
366 GeoArrowType::Geometry(self.data_type.clone())
367 }
368
369 fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
370 Arc::new(self.slice(offset, length))
371 }
372
373 fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
374 Arc::new(self.with_metadata(metadata))
375 }
376}
377
378impl<'a> GeoArrowArrayAccessor<'a> for GeometryArray {
379 type Item = Geometry<'a>;
380
381 unsafe fn value_unchecked(&'a self, index: usize) -> GeoArrowResult<Self::Item> {
382 let type_id = self.type_ids[index];
383 let offset = self.offsets[index] as usize;
384
385 let dim = (type_id / 10) as usize;
386
387 let result = match type_id % 10 {
388 1 => Geometry::Point(self.points[dim].value(offset)?),
389 2 => Geometry::LineString(self.line_strings[dim].value(offset)?),
390 3 => Geometry::Polygon(self.polygons[dim].value(offset)?),
391 4 => Geometry::MultiPoint(self.mpoints[dim].value(offset)?),
392 5 => Geometry::MultiLineString(self.mline_strings[dim].value(offset)?),
393 6 => Geometry::MultiPolygon(self.mpolygons[dim].value(offset)?),
394 7 => Geometry::GeometryCollection(self.gcs[dim].value(offset)?),
395 _ => unreachable!("unknown type_id {}", type_id),
396 };
397 Ok(result)
398 }
399}
400
401impl IntoArrow for GeometryArray {
402 type ArrowArray = UnionArray;
403 type ExtensionType = GeometryType;
404
405 fn into_arrow(self) -> Self::ArrowArray {
406 let union_fields = match self.data_type.data_type() {
407 DataType::Union(union_fields, _) => union_fields,
408 _ => unreachable!(),
409 };
410
411 let mut child_arrays: Vec<Option<ArrayRef>> = vec![None; 28];
413 for (i, arr) in self.points.into_iter().enumerate() {
414 child_arrays[i * 7] = Some(arr.into_array_ref());
415 }
416 for (i, arr) in self.line_strings.into_iter().enumerate() {
417 child_arrays[i * 7 + 1] = Some(arr.into_array_ref());
418 }
419 for (i, arr) in self.polygons.into_iter().enumerate() {
420 child_arrays[i * 7 + 2] = Some(arr.into_array_ref());
421 }
422 for (i, arr) in self.mpoints.into_iter().enumerate() {
423 child_arrays[i * 7 + 3] = Some(arr.into_array_ref());
424 }
425 for (i, arr) in self.mline_strings.into_iter().enumerate() {
426 child_arrays[i * 7 + 4] = Some(arr.into_array_ref());
427 }
428 for (i, arr) in self.mpolygons.into_iter().enumerate() {
429 child_arrays[i * 7 + 5] = Some(arr.into_array_ref());
430 }
431 for (i, arr) in self.gcs.into_iter().enumerate() {
432 child_arrays[i * 7 + 6] = Some(arr.into_array_ref());
433 }
434
435 UnionArray::try_new(
436 union_fields,
437 self.type_ids,
438 Some(self.offsets),
439 child_arrays.into_iter().map(|x| x.unwrap()).collect(),
440 )
441 .unwrap()
442 }
443
444 fn extension_type(&self) -> &Self::ExtensionType {
445 &self.data_type
446 }
447}
448
449impl TryFrom<(&UnionArray, GeometryType)> for GeometryArray {
450 type Error = GeoArrowError;
451
452 fn try_from((value, typ): (&UnionArray, GeometryType)) -> GeoArrowResult<Self> {
453 let mut points: [Option<PointArray>; 4] = Default::default();
454 let mut line_strings: [Option<LineStringArray>; 4] = Default::default();
455 let mut polygons: [Option<PolygonArray>; 4] = Default::default();
456 let mut mpoints: [Option<MultiPointArray>; 4] = Default::default();
457 let mut mline_strings: [Option<MultiLineStringArray>; 4] = Default::default();
458 let mut mpolygons: [Option<MultiPolygonArray>; 4] = Default::default();
459 let mut gcs: [Option<GeometryCollectionArray>; 4] = Default::default();
460
461 let coord_type = typ.coord_type();
462
463 match value.data_type() {
464 DataType::Union(fields, mode) => {
465 if !matches!(mode, UnionMode::Dense) {
466 return Err(ArrowError::SchemaError("Expected dense union".to_string()).into());
467 }
468
469 for (type_id, _field) in fields.iter() {
470 let dim = Dimension::from_order((type_id / 10) as _)?;
471 let index = dim.order();
472
473 match type_id % 10 {
474 1 => {
475 points[index] = Some(
476 (
477 value.child(type_id).as_ref(),
478 PointType::new(dim, Default::default())
479 .with_coord_type(coord_type),
480 )
481 .try_into()?,
482 );
483 }
484 2 => {
485 line_strings[index] = Some(
486 (
487 value.child(type_id).as_ref(),
488 LineStringType::new(dim, Default::default())
489 .with_coord_type(coord_type),
490 )
491 .try_into()?,
492 );
493 }
494 3 => {
495 polygons[index] = Some(
496 (
497 value.child(type_id).as_ref(),
498 PolygonType::new(dim, Default::default())
499 .with_coord_type(coord_type),
500 )
501 .try_into()?,
502 );
503 }
504 4 => {
505 mpoints[index] = Some(
506 (
507 value.child(type_id).as_ref(),
508 MultiPointType::new(dim, Default::default())
509 .with_coord_type(coord_type),
510 )
511 .try_into()?,
512 );
513 }
514 5 => {
515 mline_strings[index] = Some(
516 (
517 value.child(type_id).as_ref(),
518 MultiLineStringType::new(dim, Default::default())
519 .with_coord_type(coord_type),
520 )
521 .try_into()?,
522 );
523 }
524 6 => {
525 mpolygons[index] = Some(
526 (
527 value.child(type_id).as_ref(),
528 MultiPolygonType::new(dim, Default::default())
529 .with_coord_type(coord_type),
530 )
531 .try_into()?,
532 );
533 }
534 7 => {
535 gcs[index] = Some(
536 (
537 value.child(type_id).as_ref(),
538 GeometryCollectionType::new(dim, Default::default())
539 .with_coord_type(coord_type),
540 )
541 .try_into()?,
542 );
543 }
544 _ => {
545 return Err(GeoArrowError::InvalidGeoArrow(format!(
546 "Unexpected type_id when converting to GeometryArray {type_id}",
547 )));
548 }
549 }
550 }
551 }
552 _ => {
553 return Err(GeoArrowError::InvalidGeoArrow(
554 "expected union type when converting to GeometryArray".to_string(),
555 ));
556 }
557 };
558
559 let type_ids = value.type_ids().clone();
560 let offsets = value.offsets().unwrap().clone();
562
563 points.iter_mut().enumerate().for_each(|(i, arr)| {
571 let new_val = if let Some(arr) = arr.take() {
572 arr
573 } else {
574 PointBuilder::new(
575 PointType::new(Dimension::from_order(i).unwrap(), Default::default())
576 .with_coord_type(coord_type),
577 )
578 .finish()
579 };
580 arr.replace(new_val);
581 });
582 line_strings.iter_mut().enumerate().for_each(|(i, arr)| {
583 let new_val = if let Some(arr) = arr.take() {
584 arr
585 } else {
586 LineStringBuilder::new(
587 LineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
588 .with_coord_type(coord_type),
589 )
590 .finish()
591 };
592 arr.replace(new_val);
593 });
594 polygons.iter_mut().enumerate().for_each(|(i, arr)| {
595 let new_val = if let Some(arr) = arr.take() {
596 arr
597 } else {
598 PolygonBuilder::new(
599 PolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
600 .with_coord_type(coord_type),
601 )
602 .finish()
603 };
604 arr.replace(new_val);
605 });
606 mpoints.iter_mut().enumerate().for_each(|(i, arr)| {
607 let new_val = if let Some(arr) = arr.take() {
608 arr
609 } else {
610 MultiPointBuilder::new(
611 MultiPointType::new(Dimension::from_order(i).unwrap(), Default::default())
612 .with_coord_type(coord_type),
613 )
614 .finish()
615 };
616 arr.replace(new_val);
617 });
618 mline_strings.iter_mut().enumerate().for_each(|(i, arr)| {
619 let new_val = if let Some(arr) = arr.take() {
620 arr
621 } else {
622 MultiLineStringBuilder::new(
623 MultiLineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
624 .with_coord_type(coord_type),
625 )
626 .finish()
627 };
628 arr.replace(new_val);
629 });
630 mpolygons.iter_mut().enumerate().for_each(|(i, arr)| {
631 let new_val = if let Some(arr) = arr.take() {
632 arr
633 } else {
634 MultiPolygonBuilder::new(
635 MultiPolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
636 .with_coord_type(coord_type),
637 )
638 .finish()
639 };
640 arr.replace(new_val);
641 });
642 gcs.iter_mut().enumerate().for_each(|(i, arr)| {
643 let new_val = if let Some(arr) = arr.take() {
644 arr
645 } else {
646 GeometryCollectionBuilder::new(
647 GeometryCollectionType::new(
648 Dimension::from_order(i).unwrap(),
649 Default::default(),
650 )
651 .with_coord_type(coord_type),
652 )
653 .finish()
654 };
655 arr.replace(new_val);
656 });
657
658 Ok(Self::new(
659 type_ids,
660 offsets,
661 points.map(|x| x.unwrap()),
662 line_strings.map(|x| x.unwrap()),
663 polygons.map(|x| x.unwrap()),
664 mpoints.map(|x| x.unwrap()),
665 mline_strings.map(|x| x.unwrap()),
666 mpolygons.map(|x| x.unwrap()),
667 gcs.map(|x| x.unwrap()),
668 Default::default(),
669 ))
670 }
671}
672
673impl TryFrom<(&dyn Array, GeometryType)> for GeometryArray {
674 type Error = GeoArrowError;
675
676 fn try_from((value, typ): (&dyn Array, GeometryType)) -> GeoArrowResult<Self> {
677 match value.data_type() {
678 DataType::Union(_, _) => (value.as_union(), typ).try_into(),
679 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
680 "Unexpected GeometryArray DataType: {dt:?}",
681 ))),
682 }
683 }
684}
685
686impl TryFrom<(&dyn Array, &Field)> for GeometryArray {
687 type Error = GeoArrowError;
688
689 fn try_from((arr, field): (&dyn Array, &Field)) -> GeoArrowResult<Self> {
690 let typ = field.try_extension_type::<GeometryType>()?;
691 (arr, typ).try_into()
692 }
693}
694
695impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, GeometryType)> for GeometryArray {
696 type Error = GeoArrowError;
697
698 fn try_from(value: (GenericWkbArray<O>, GeometryType)) -> GeoArrowResult<Self> {
699 let mut_arr: GeometryBuilder = value.try_into()?;
700 Ok(mut_arr.finish())
701 }
702}
703
704pub(crate) trait DimensionIndex: Sized {
705 fn order(&self) -> usize;
707
708 fn from_order(index: usize) -> GeoArrowResult<Self>;
709}
710
711impl DimensionIndex for Dimension {
712 fn order(&self) -> usize {
713 match self {
714 Self::XY => 0,
715 Self::XYZ => 1,
716 Self::XYM => 2,
717 Self::XYZM => 3,
718 }
719 }
720
721 fn from_order(index: usize) -> GeoArrowResult<Self> {
722 match index {
723 0 => Ok(Self::XY),
724 1 => Ok(Self::XYZ),
725 2 => Ok(Self::XYM),
726 3 => Ok(Self::XYZM),
727 i => {
728 Err(ArrowError::SchemaError(format!("unsupported index in from_order: {i}")).into())
729 }
730 }
731 }
732}
733
734impl PartialEq for GeometryArray {
735 fn eq(&self, other: &Self) -> bool {
736 self.type_ids == other.type_ids
737 && self.offsets == other.offsets
738 && self.points == other.points
739 && self.line_strings == other.line_strings
740 && self.polygons == other.polygons
741 && self.mpoints == other.mpoints
742 && self.mline_strings == other.mline_strings
743 && self.mpolygons == other.mpolygons
744 && self.gcs == other.gcs
745 }
746}
747
748impl TypeId for PointArray {
749 const ARRAY_TYPE_OFFSET: i8 = 1;
750}
751impl TypeId for LineStringArray {
752 const ARRAY_TYPE_OFFSET: i8 = 2;
753}
754impl TypeId for PolygonArray {
755 const ARRAY_TYPE_OFFSET: i8 = 3;
756}
757impl TypeId for MultiPointArray {
758 const ARRAY_TYPE_OFFSET: i8 = 4;
759}
760impl TypeId for MultiLineStringArray {
761 const ARRAY_TYPE_OFFSET: i8 = 5;
762}
763impl TypeId for MultiPolygonArray {
764 const ARRAY_TYPE_OFFSET: i8 = 6;
765}
766impl TypeId for GeometryCollectionArray {
767 const ARRAY_TYPE_OFFSET: i8 = 7;
768}
769
770type ChildrenArrays = (
771 [PointArray; 4],
772 [LineStringArray; 4],
773 [PolygonArray; 4],
774 [MultiPointArray; 4],
775 [MultiLineStringArray; 4],
776 [MultiPolygonArray; 4],
777 [GeometryCollectionArray; 4],
778);
779
780fn empty_children(coord_type: CoordType) -> ChildrenArrays {
785 (
786 core::array::from_fn(|i| {
787 PointBuilder::new(
788 PointType::new(Dimension::from_order(i).unwrap(), Default::default())
789 .with_coord_type(coord_type),
790 )
791 .finish()
792 }),
793 core::array::from_fn(|i| {
794 LineStringBuilder::new(
795 LineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
796 .with_coord_type(coord_type),
797 )
798 .finish()
799 }),
800 core::array::from_fn(|i| {
801 PolygonBuilder::new(
802 PolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
803 .with_coord_type(coord_type),
804 )
805 .finish()
806 }),
807 core::array::from_fn(|i| {
808 MultiPointBuilder::new(
809 MultiPointType::new(Dimension::from_order(i).unwrap(), Default::default())
810 .with_coord_type(coord_type),
811 )
812 .finish()
813 }),
814 core::array::from_fn(|i| {
815 MultiLineStringBuilder::new(
816 MultiLineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
817 .with_coord_type(coord_type),
818 )
819 .finish()
820 }),
821 core::array::from_fn(|i| {
822 MultiPolygonBuilder::new(
823 MultiPolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
824 .with_coord_type(coord_type),
825 )
826 .finish()
827 }),
828 core::array::from_fn(|i| {
829 GeometryCollectionBuilder::new(
830 GeometryCollectionType::new(Dimension::from_order(i).unwrap(), Default::default())
831 .with_coord_type(coord_type),
832 )
833 .finish()
834 }),
835 )
836}
837
838macro_rules! impl_primitive_cast {
839 ($source_array:ty, $value_edit:tt) => {
840 impl From<$source_array> for GeometryArray {
841 fn from(value: $source_array) -> Self {
842 let coord_type = value.data_type.coord_type();
843 let dim = value.data_type.dimension();
844 let metadata = value.data_type.metadata().clone();
845
846 let type_ids = vec![value.type_id(dim); value.len()].into();
847 let offsets = ScalarBuffer::from_iter(0..value.len() as i32);
848 let data_type = GeometryType::new(metadata).with_coord_type(coord_type);
849 let mut children = empty_children(coord_type);
850
851 children.$value_edit[dim.order()] = value;
852 Self {
853 data_type,
854 type_ids,
855 offsets,
856 points: children.0,
857 line_strings: children.1,
858 polygons: children.2,
859 mpoints: children.3,
860 mline_strings: children.4,
861 mpolygons: children.5,
862 gcs: children.6,
863 }
864 }
865 }
866 };
867}
868
869impl_primitive_cast!(PointArray, 0);
870impl_primitive_cast!(LineStringArray, 1);
871impl_primitive_cast!(PolygonArray, 2);
872impl_primitive_cast!(MultiPointArray, 3);
873impl_primitive_cast!(MultiLineStringArray, 4);
874impl_primitive_cast!(MultiPolygonArray, 5);
875impl_primitive_cast!(GeometryCollectionArray, 6);
876
877#[cfg(test)]
878mod test {
879 use geo_traits::to_geo::ToGeoGeometry;
880 use geoarrow_test::raw;
881
882 use super::*;
883 use crate::test::{linestring, multilinestring, multipoint, multipolygon, point, polygon};
884
885 fn geoms() -> Vec<geo_types::Geometry> {
886 vec![
887 point::p0().into(),
888 point::p1().into(),
889 point::p2().into(),
890 linestring::ls0().into(),
891 linestring::ls1().into(),
892 polygon::p0().into(),
893 polygon::p1().into(),
894 multipoint::mp0().into(),
895 multipoint::mp1().into(),
896 multilinestring::ml0().into(),
897 multilinestring::ml1().into(),
898 multipolygon::mp0().into(),
899 multipolygon::mp1().into(),
900 ]
901 }
902
903 fn geom_array(coord_type: CoordType) -> GeometryArray {
904 let geoms = geoms().into_iter().map(Some).collect::<Vec<_>>();
905 let typ = GeometryType::new(Default::default()).with_coord_type(coord_type);
906 GeometryBuilder::from_nullable_geometries(&geoms, typ)
907 .unwrap()
908 .finish()
909 }
910
911 #[test]
912 fn test_2d() {
913 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
914 let geoms = geoms();
915 let geometry_array = geom_array(coord_type);
916 let geoms_again = geometry_array
917 .iter_values()
918 .map(|g| g.unwrap().to_geometry())
919 .collect::<Vec<_>>();
920 assert_eq!(geoms, geoms_again);
921 }
922 }
923
924 #[test]
925 fn test_2d_roundtrip_arrow() {
926 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
927 let geoms = geoms();
928 let geometry_array = geom_array(coord_type);
929 let field = geometry_array.data_type.to_field("geometry", true);
930 let union_array = geometry_array.into_arrow();
931
932 let geometry_array_again =
933 GeometryArray::try_from((&union_array as _, &field)).unwrap();
934 let geoms_again = geometry_array_again
935 .iter_values()
936 .map(|g| g.unwrap().to_geometry())
937 .collect::<Vec<_>>();
938 assert_eq!(geoms, geoms_again);
939 }
940 }
941
942 #[test]
943 fn try_from_arrow() {
944 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
945 for prefer_multi in [true, false] {
946 let geo_arr = crate::test::geometry::array(coord_type, prefer_multi);
947
948 let point_type = geo_arr.extension_type().clone();
949 let field = point_type.to_field("geometry", true);
950
951 let arrow_arr = geo_arr.to_array_ref();
952
953 let geo_arr2: GeometryArray = (arrow_arr.as_ref(), point_type).try_into().unwrap();
954 let geo_arr3: GeometryArray = (arrow_arr.as_ref(), &field).try_into().unwrap();
955
956 assert_eq!(geo_arr, geo_arr2);
957 assert_eq!(geo_arr, geo_arr3);
958 }
959 }
960 }
961
962 #[test]
963 fn test_nullability() {
964 let geoms = raw::geometry::geoms();
965 let null_idxs = geoms
966 .iter()
967 .enumerate()
968 .filter_map(|(i, geom)| if geom.is_none() { Some(i) } else { None })
969 .collect::<Vec<_>>();
970
971 let typ = GeometryType::new(Default::default());
972 let geo_arr = GeometryBuilder::from_nullable_geometries(&geoms, typ)
973 .unwrap()
974 .finish();
975
976 for null_idx in &null_idxs {
977 assert!(geo_arr.is_null(*null_idx));
978 }
979 }
980
981 #[test]
982 fn test_logical_nulls() {
983 let geoms = raw::geometry::geoms();
984 let expected_nulls = NullBuffer::from_iter(geoms.iter().map(|g| g.is_some()));
985
986 let typ = GeometryType::new(Default::default());
987 let geo_arr = GeometryBuilder::from_nullable_geometries(&geoms, typ)
988 .unwrap()
989 .finish();
990
991 assert_eq!(geo_arr.logical_nulls().unwrap(), expected_nulls);
992 }
993
994 #[test]
995 fn into_coord_type() {
996 for prefer_multi in [true, false] {
997 let geo_arr = crate::test::geometry::array(CoordType::Interleaved, prefer_multi);
998 let geo_arr2 = geo_arr
999 .clone()
1000 .into_coord_type(CoordType::Separated)
1001 .into_coord_type(CoordType::Interleaved);
1002
1003 assert_eq!(geo_arr, geo_arr2);
1004 }
1005 }
1006
1007 #[test]
1008 fn partial_eq() {
1009 for prefer_multi in [true, false] {
1010 let arr1 = crate::test::geometry::array(CoordType::Interleaved, prefer_multi);
1011 let arr2 = crate::test::geometry::array(CoordType::Separated, prefer_multi);
1012
1013 assert_eq!(arr1, arr1);
1014 assert_eq!(arr2, arr2);
1015 assert_eq!(arr1, arr2);
1016
1017 assert_ne!(arr1, arr2.slice(0, 2));
1018 }
1019 }
1020}
1021
1022