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 {
340 self.buffer_lengths().num_bytes()
341 }
342
343 #[inline]
353 pub fn slice(&self, offset: usize, length: usize) -> Self {
354 assert!(
355 offset + length <= self.len(),
356 "offset + length may not exceed length of array"
357 );
358 Self {
359 data_type: self.data_type.clone(),
360 type_ids: self.type_ids.slice(offset, length),
361 offsets: self.offsets.slice(offset, length),
362
363 points: self.points.clone(),
364 line_strings: self.line_strings.clone(),
365 polygons: self.polygons.clone(),
366 mpoints: self.mpoints.clone(),
367 mline_strings: self.mline_strings.clone(),
368 mpolygons: self.mpolygons.clone(),
369 gcs: self.gcs.clone(),
370 }
371 }
372
373 pub fn into_coord_type(self, coord_type: CoordType) -> Self {
375 Self {
376 data_type: self.data_type.with_coord_type(coord_type),
377 points: self.points.map(|arr| arr.into_coord_type(coord_type)),
378 line_strings: self.line_strings.map(|arr| arr.into_coord_type(coord_type)),
379 polygons: self.polygons.map(|arr| arr.into_coord_type(coord_type)),
380 mpoints: self.mpoints.map(|arr| arr.into_coord_type(coord_type)),
381 mline_strings: self
382 .mline_strings
383 .map(|arr| arr.into_coord_type(coord_type)),
384 mpolygons: self.mpolygons.map(|arr| arr.into_coord_type(coord_type)),
385 gcs: self.gcs.map(|arr| arr.into_coord_type(coord_type)),
386 ..self
387 }
388 }
389
390 pub fn with_metadata(self, metadata: Arc<Metadata>) -> Self {
392 Self {
393 data_type: self.data_type.with_metadata(metadata),
394 ..self
395 }
396 }
397
398 #[allow(dead_code)]
400 pub(crate) fn contained_types(&self) -> HashSet<GeoArrowType> {
401 let mut types = HashSet::new();
402 self.points.iter().for_each(|arr| {
403 if !arr.is_empty() {
404 types.insert(arr.data_type());
405 }
406 });
407 self.line_strings.iter().for_each(|arr| {
408 if !arr.is_empty() {
409 types.insert(arr.data_type());
410 }
411 });
412 self.polygons.iter().for_each(|arr| {
413 if !arr.is_empty() {
414 types.insert(arr.data_type());
415 }
416 });
417 self.mpoints.iter().for_each(|arr| {
418 if !arr.is_empty() {
419 types.insert(arr.data_type());
420 }
421 });
422 self.mline_strings.iter().for_each(|arr| {
423 if !arr.is_empty() {
424 types.insert(arr.data_type());
425 }
426 });
427 self.mpolygons.iter().for_each(|arr| {
428 if !arr.is_empty() {
429 types.insert(arr.data_type());
430 }
431 });
432 self.gcs.iter().for_each(|arr| {
433 if !arr.is_empty() {
434 types.insert(arr.data_type());
435 }
436 });
437
438 types
439 }
440}
441
442impl GeoArrowArray for GeometryArray {
443 fn as_any(&self) -> &dyn std::any::Any {
444 self
445 }
446
447 fn into_array_ref(self) -> ArrayRef {
448 Arc::new(self.into_arrow())
449 }
450
451 fn to_array_ref(&self) -> ArrayRef {
452 self.clone().into_array_ref()
453 }
454
455 #[inline]
456 fn len(&self) -> usize {
457 self.type_ids.len()
459 }
460
461 #[inline]
462 fn logical_nulls(&self) -> Option<NullBuffer> {
463 self.to_array_ref().logical_nulls()
464 }
465
466 #[inline]
467 fn logical_null_count(&self) -> usize {
468 self.to_array_ref().logical_null_count()
469 }
470
471 #[inline]
472 fn is_null(&self, i: usize) -> bool {
473 let type_id = self.type_ids[i];
474 let offset = self.offsets[i] as usize;
475 let dim = (type_id / 10) as usize;
476 match type_id % 10 {
477 1 => self.points[dim].is_null(offset),
478 2 => self.line_strings[dim].is_null(offset),
479 3 => self.polygons[dim].is_null(offset),
480 4 => self.mpoints[dim].is_null(offset),
481 5 => self.mline_strings[dim].is_null(offset),
482 6 => self.mpolygons[dim].is_null(offset),
483 7 => self.gcs[dim].is_null(offset),
484 _ => unreachable!("unknown type_id {}", type_id),
485 }
486 }
487
488 fn data_type(&self) -> GeoArrowType {
489 GeoArrowType::Geometry(self.data_type.clone())
490 }
491
492 fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
493 Arc::new(self.slice(offset, length))
494 }
495
496 fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
497 Arc::new(self.with_metadata(metadata))
498 }
499}
500
501impl<'a> GeoArrowArrayAccessor<'a> for GeometryArray {
502 type Item = Geometry<'a>;
503
504 unsafe fn value_unchecked(&'a self, index: usize) -> GeoArrowResult<Self::Item> {
505 let type_id = self.type_ids[index];
506 let offset = self.offsets[index] as usize;
507
508 let dim = (type_id / 10) as usize;
509
510 let result = match type_id % 10 {
511 1 => Geometry::Point(self.points[dim].value(offset)?),
512 2 => Geometry::LineString(self.line_strings[dim].value(offset)?),
513 3 => Geometry::Polygon(self.polygons[dim].value(offset)?),
514 4 => Geometry::MultiPoint(self.mpoints[dim].value(offset)?),
515 5 => Geometry::MultiLineString(self.mline_strings[dim].value(offset)?),
516 6 => Geometry::MultiPolygon(self.mpolygons[dim].value(offset)?),
517 7 => Geometry::GeometryCollection(self.gcs[dim].value(offset)?),
518 _ => unreachable!("unknown type_id {}", type_id),
519 };
520 Ok(result)
521 }
522}
523
524impl IntoArrow for GeometryArray {
525 type ArrowArray = UnionArray;
526 type ExtensionType = GeometryType;
527
528 fn into_arrow(self) -> Self::ArrowArray {
529 let union_fields = match self.data_type.data_type() {
530 DataType::Union(union_fields, _) => union_fields,
531 _ => unreachable!(),
532 };
533
534 let mut child_arrays: Vec<Option<ArrayRef>> = vec![None; 28];
536 for (i, arr) in self.points.into_iter().enumerate() {
537 child_arrays[i * 7] = Some(arr.into_array_ref());
538 }
539 for (i, arr) in self.line_strings.into_iter().enumerate() {
540 child_arrays[i * 7 + 1] = Some(arr.into_array_ref());
541 }
542 for (i, arr) in self.polygons.into_iter().enumerate() {
543 child_arrays[i * 7 + 2] = Some(arr.into_array_ref());
544 }
545 for (i, arr) in self.mpoints.into_iter().enumerate() {
546 child_arrays[i * 7 + 3] = Some(arr.into_array_ref());
547 }
548 for (i, arr) in self.mline_strings.into_iter().enumerate() {
549 child_arrays[i * 7 + 4] = Some(arr.into_array_ref());
550 }
551 for (i, arr) in self.mpolygons.into_iter().enumerate() {
552 child_arrays[i * 7 + 5] = Some(arr.into_array_ref());
553 }
554 for (i, arr) in self.gcs.into_iter().enumerate() {
555 child_arrays[i * 7 + 6] = Some(arr.into_array_ref());
556 }
557
558 UnionArray::try_new(
559 union_fields,
560 self.type_ids,
561 Some(self.offsets),
562 child_arrays.into_iter().map(|x| x.unwrap()).collect(),
563 )
564 .unwrap()
565 }
566
567 fn extension_type(&self) -> &Self::ExtensionType {
568 &self.data_type
569 }
570}
571
572impl TryFrom<(&UnionArray, GeometryType)> for GeometryArray {
573 type Error = GeoArrowError;
574
575 fn try_from((value, typ): (&UnionArray, GeometryType)) -> GeoArrowResult<Self> {
576 let mut points: [Option<PointArray>; 4] = Default::default();
577 let mut line_strings: [Option<LineStringArray>; 4] = Default::default();
578 let mut polygons: [Option<PolygonArray>; 4] = Default::default();
579 let mut mpoints: [Option<MultiPointArray>; 4] = Default::default();
580 let mut mline_strings: [Option<MultiLineStringArray>; 4] = Default::default();
581 let mut mpolygons: [Option<MultiPolygonArray>; 4] = Default::default();
582 let mut gcs: [Option<GeometryCollectionArray>; 4] = Default::default();
583
584 let coord_type = typ.coord_type();
585
586 match value.data_type() {
587 DataType::Union(fields, mode) => {
588 if !matches!(mode, UnionMode::Dense) {
589 return Err(ArrowError::SchemaError("Expected dense union".to_string()).into());
590 }
591
592 for (type_id, _field) in fields.iter() {
593 let dim = Dimension::from_order((type_id / 10) as _)?;
594 let index = dim.order();
595
596 match type_id % 10 {
597 1 => {
598 points[index] = Some(
599 (
600 value.child(type_id).as_ref(),
601 PointType::new(dim, Default::default())
602 .with_coord_type(coord_type),
603 )
604 .try_into()?,
605 );
606 }
607 2 => {
608 line_strings[index] = Some(
609 (
610 value.child(type_id).as_ref(),
611 LineStringType::new(dim, Default::default())
612 .with_coord_type(coord_type),
613 )
614 .try_into()?,
615 );
616 }
617 3 => {
618 polygons[index] = Some(
619 (
620 value.child(type_id).as_ref(),
621 PolygonType::new(dim, Default::default())
622 .with_coord_type(coord_type),
623 )
624 .try_into()?,
625 );
626 }
627 4 => {
628 mpoints[index] = Some(
629 (
630 value.child(type_id).as_ref(),
631 MultiPointType::new(dim, Default::default())
632 .with_coord_type(coord_type),
633 )
634 .try_into()?,
635 );
636 }
637 5 => {
638 mline_strings[index] = Some(
639 (
640 value.child(type_id).as_ref(),
641 MultiLineStringType::new(dim, Default::default())
642 .with_coord_type(coord_type),
643 )
644 .try_into()?,
645 );
646 }
647 6 => {
648 mpolygons[index] = Some(
649 (
650 value.child(type_id).as_ref(),
651 MultiPolygonType::new(dim, Default::default())
652 .with_coord_type(coord_type),
653 )
654 .try_into()?,
655 );
656 }
657 7 => {
658 gcs[index] = Some(
659 (
660 value.child(type_id).as_ref(),
661 GeometryCollectionType::new(dim, Default::default())
662 .with_coord_type(coord_type),
663 )
664 .try_into()?,
665 );
666 }
667 _ => {
668 return Err(GeoArrowError::InvalidGeoArrow(format!(
669 "Unexpected type_id when converting to GeometryArray {}",
670 type_id
671 )));
672 }
673 }
674 }
675 }
676 _ => {
677 return Err(GeoArrowError::InvalidGeoArrow(
678 "expected union type when converting to GeometryArray".to_string(),
679 ));
680 }
681 };
682
683 let type_ids = value.type_ids().clone();
684 let offsets = value.offsets().unwrap().clone();
686
687 points.iter_mut().enumerate().for_each(|(i, arr)| {
695 let new_val = if let Some(arr) = arr.take() {
696 arr
697 } else {
698 PointBuilder::new(
699 PointType::new(Dimension::from_order(i).unwrap(), Default::default())
700 .with_coord_type(coord_type),
701 )
702 .finish()
703 };
704 arr.replace(new_val);
705 });
706 line_strings.iter_mut().enumerate().for_each(|(i, arr)| {
707 let new_val = if let Some(arr) = arr.take() {
708 arr
709 } else {
710 LineStringBuilder::new(
711 LineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
712 .with_coord_type(coord_type),
713 )
714 .finish()
715 };
716 arr.replace(new_val);
717 });
718 polygons.iter_mut().enumerate().for_each(|(i, arr)| {
719 let new_val = if let Some(arr) = arr.take() {
720 arr
721 } else {
722 PolygonBuilder::new(
723 PolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
724 .with_coord_type(coord_type),
725 )
726 .finish()
727 };
728 arr.replace(new_val);
729 });
730 mpoints.iter_mut().enumerate().for_each(|(i, arr)| {
731 let new_val = if let Some(arr) = arr.take() {
732 arr
733 } else {
734 MultiPointBuilder::new(
735 MultiPointType::new(Dimension::from_order(i).unwrap(), Default::default())
736 .with_coord_type(coord_type),
737 )
738 .finish()
739 };
740 arr.replace(new_val);
741 });
742 mline_strings.iter_mut().enumerate().for_each(|(i, arr)| {
743 let new_val = if let Some(arr) = arr.take() {
744 arr
745 } else {
746 MultiLineStringBuilder::new(
747 MultiLineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
748 .with_coord_type(coord_type),
749 )
750 .finish()
751 };
752 arr.replace(new_val);
753 });
754 mpolygons.iter_mut().enumerate().for_each(|(i, arr)| {
755 let new_val = if let Some(arr) = arr.take() {
756 arr
757 } else {
758 MultiPolygonBuilder::new(
759 MultiPolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
760 .with_coord_type(coord_type),
761 )
762 .finish()
763 };
764 arr.replace(new_val);
765 });
766 gcs.iter_mut().enumerate().for_each(|(i, arr)| {
767 let new_val = if let Some(arr) = arr.take() {
768 arr
769 } else {
770 GeometryCollectionBuilder::new(
771 GeometryCollectionType::new(
772 Dimension::from_order(i).unwrap(),
773 Default::default(),
774 )
775 .with_coord_type(coord_type),
776 )
777 .finish()
778 };
779 arr.replace(new_val);
780 });
781
782 Ok(Self::new(
783 type_ids,
784 offsets,
785 points.map(|x| x.unwrap()),
786 line_strings.map(|x| x.unwrap()),
787 polygons.map(|x| x.unwrap()),
788 mpoints.map(|x| x.unwrap()),
789 mline_strings.map(|x| x.unwrap()),
790 mpolygons.map(|x| x.unwrap()),
791 gcs.map(|x| x.unwrap()),
792 Default::default(),
793 ))
794 }
795}
796
797impl TryFrom<(&dyn Array, GeometryType)> for GeometryArray {
798 type Error = GeoArrowError;
799
800 fn try_from((value, typ): (&dyn Array, GeometryType)) -> GeoArrowResult<Self> {
801 match value.data_type() {
802 DataType::Union(_, _) => (value.as_union(), typ).try_into(),
803 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
804 "Unexpected GeometryArray DataType: {:?}",
805 dt
806 ))),
807 }
808 }
809}
810
811impl TryFrom<(&dyn Array, &Field)> for GeometryArray {
812 type Error = GeoArrowError;
813
814 fn try_from((arr, field): (&dyn Array, &Field)) -> GeoArrowResult<Self> {
815 let typ = field.try_extension_type::<GeometryType>()?;
816 (arr, typ).try_into()
817 }
818}
819
820impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, GeometryType)> for GeometryArray {
821 type Error = GeoArrowError;
822
823 fn try_from(value: (GenericWkbArray<O>, GeometryType)) -> GeoArrowResult<Self> {
824 let mut_arr: GeometryBuilder = value.try_into()?;
825 Ok(mut_arr.finish())
826 }
827}
828
829pub(crate) trait DimensionIndex: Sized {
830 fn order(&self) -> usize;
832
833 fn from_order(index: usize) -> GeoArrowResult<Self>;
834}
835
836impl DimensionIndex for Dimension {
837 fn order(&self) -> usize {
838 match self {
839 Self::XY => 0,
840 Self::XYZ => 1,
841 Self::XYM => 2,
842 Self::XYZM => 3,
843 }
844 }
845
846 fn from_order(index: usize) -> GeoArrowResult<Self> {
847 match index {
848 0 => Ok(Self::XY),
849 1 => Ok(Self::XYZ),
850 2 => Ok(Self::XYM),
851 3 => Ok(Self::XYZM),
852 i => Err(
853 ArrowError::SchemaError(format!("unsupported index in from_order: {}", i)).into(),
854 ),
855 }
856 }
857}
858
859impl PartialEq for GeometryArray {
860 fn eq(&self, other: &Self) -> bool {
861 self.type_ids == other.type_ids
862 && self.offsets == other.offsets
863 && self.points == other.points
864 && self.line_strings == other.line_strings
865 && self.polygons == other.polygons
866 && self.mpoints == other.mpoints
867 && self.mline_strings == other.mline_strings
868 && self.mpolygons == other.mpolygons
869 && self.gcs == other.gcs
870 }
871}
872
873impl TypeId for PointArray {
874 const ARRAY_TYPE_OFFSET: i8 = 1;
875}
876impl TypeId for LineStringArray {
877 const ARRAY_TYPE_OFFSET: i8 = 2;
878}
879impl TypeId for PolygonArray {
880 const ARRAY_TYPE_OFFSET: i8 = 3;
881}
882impl TypeId for MultiPointArray {
883 const ARRAY_TYPE_OFFSET: i8 = 4;
884}
885impl TypeId for MultiLineStringArray {
886 const ARRAY_TYPE_OFFSET: i8 = 5;
887}
888impl TypeId for MultiPolygonArray {
889 const ARRAY_TYPE_OFFSET: i8 = 6;
890}
891impl TypeId for GeometryCollectionArray {
892 const ARRAY_TYPE_OFFSET: i8 = 7;
893}
894
895type ChildrenArrays = (
896 [PointArray; 4],
897 [LineStringArray; 4],
898 [PolygonArray; 4],
899 [MultiPointArray; 4],
900 [MultiLineStringArray; 4],
901 [MultiPolygonArray; 4],
902 [GeometryCollectionArray; 4],
903);
904
905fn empty_children(coord_type: CoordType) -> ChildrenArrays {
910 (
911 core::array::from_fn(|i| {
912 PointBuilder::new(
913 PointType::new(Dimension::from_order(i).unwrap(), Default::default())
914 .with_coord_type(coord_type),
915 )
916 .finish()
917 }),
918 core::array::from_fn(|i| {
919 LineStringBuilder::new(
920 LineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
921 .with_coord_type(coord_type),
922 )
923 .finish()
924 }),
925 core::array::from_fn(|i| {
926 PolygonBuilder::new(
927 PolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
928 .with_coord_type(coord_type),
929 )
930 .finish()
931 }),
932 core::array::from_fn(|i| {
933 MultiPointBuilder::new(
934 MultiPointType::new(Dimension::from_order(i).unwrap(), Default::default())
935 .with_coord_type(coord_type),
936 )
937 .finish()
938 }),
939 core::array::from_fn(|i| {
940 MultiLineStringBuilder::new(
941 MultiLineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
942 .with_coord_type(coord_type),
943 )
944 .finish()
945 }),
946 core::array::from_fn(|i| {
947 MultiPolygonBuilder::new(
948 MultiPolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
949 .with_coord_type(coord_type),
950 )
951 .finish()
952 }),
953 core::array::from_fn(|i| {
954 GeometryCollectionBuilder::new(
955 GeometryCollectionType::new(Dimension::from_order(i).unwrap(), Default::default())
956 .with_coord_type(coord_type),
957 )
958 .finish()
959 }),
960 )
961}
962
963macro_rules! impl_primitive_cast {
964 ($source_array:ty, $value_edit:tt) => {
965 impl From<$source_array> for GeometryArray {
966 fn from(value: $source_array) -> Self {
967 let coord_type = value.data_type.coord_type();
968 let dim = value.data_type.dimension();
969 let metadata = value.data_type.metadata().clone();
970
971 let type_ids = vec![value.type_id(dim); value.len()].into();
972 let offsets = ScalarBuffer::from_iter(0..value.len() as i32);
973 let data_type = GeometryType::new(metadata).with_coord_type(coord_type);
974 let mut children = empty_children(coord_type);
975
976 children.$value_edit[dim.order()] = value;
977 Self {
978 data_type,
979 type_ids,
980 offsets,
981 points: children.0,
982 line_strings: children.1,
983 polygons: children.2,
984 mpoints: children.3,
985 mline_strings: children.4,
986 mpolygons: children.5,
987 gcs: children.6,
988 }
989 }
990 }
991 };
992}
993
994impl_primitive_cast!(PointArray, 0);
995impl_primitive_cast!(LineStringArray, 1);
996impl_primitive_cast!(PolygonArray, 2);
997impl_primitive_cast!(MultiPointArray, 3);
998impl_primitive_cast!(MultiLineStringArray, 4);
999impl_primitive_cast!(MultiPolygonArray, 5);
1000impl_primitive_cast!(GeometryCollectionArray, 6);
1001
1002#[cfg(test)]
1003mod test {
1004 use geo_traits::to_geo::ToGeoGeometry;
1005 use geoarrow_test::raw;
1006
1007 use super::*;
1008 use crate::test::{linestring, multilinestring, multipoint, multipolygon, point, polygon};
1009
1010 fn geoms() -> Vec<geo_types::Geometry> {
1011 vec![
1012 point::p0().into(),
1013 point::p1().into(),
1014 point::p2().into(),
1015 linestring::ls0().into(),
1016 linestring::ls1().into(),
1017 polygon::p0().into(),
1018 polygon::p1().into(),
1019 multipoint::mp0().into(),
1020 multipoint::mp1().into(),
1021 multilinestring::ml0().into(),
1022 multilinestring::ml1().into(),
1023 multipolygon::mp0().into(),
1024 multipolygon::mp1().into(),
1025 ]
1026 }
1027
1028 fn geom_array(coord_type: CoordType) -> GeometryArray {
1029 let geoms = geoms().into_iter().map(Some).collect::<Vec<_>>();
1030 let typ = GeometryType::new(Default::default()).with_coord_type(coord_type);
1031 GeometryBuilder::from_nullable_geometries(&geoms, typ)
1032 .unwrap()
1033 .finish()
1034 }
1035
1036 #[test]
1037 fn test_2d() {
1038 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1039 let geoms = geoms();
1040 let geometry_array = geom_array(coord_type);
1041 let geoms_again = geometry_array
1042 .iter_values()
1043 .map(|g| g.unwrap().to_geometry())
1044 .collect::<Vec<_>>();
1045 assert_eq!(geoms, geoms_again);
1046 }
1047 }
1048
1049 #[test]
1050 fn test_2d_roundtrip_arrow() {
1051 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1052 let geoms = geoms();
1053 let geometry_array = geom_array(coord_type);
1054 let field = geometry_array.data_type.to_field("geometry", true);
1055 let union_array = geometry_array.into_arrow();
1056
1057 let geometry_array_again =
1058 GeometryArray::try_from((&union_array as _, &field)).unwrap();
1059 let geoms_again = geometry_array_again
1060 .iter_values()
1061 .map(|g| g.unwrap().to_geometry())
1062 .collect::<Vec<_>>();
1063 assert_eq!(geoms, geoms_again);
1064 }
1065 }
1066
1067 #[test]
1068 fn try_from_arrow() {
1069 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1070 for prefer_multi in [true, false] {
1071 let geo_arr = crate::test::geometry::array(coord_type, prefer_multi);
1072
1073 let point_type = geo_arr.extension_type().clone();
1074 let field = point_type.to_field("geometry", true);
1075
1076 let arrow_arr = geo_arr.to_array_ref();
1077
1078 let geo_arr2: GeometryArray = (arrow_arr.as_ref(), point_type).try_into().unwrap();
1079 let geo_arr3: GeometryArray = (arrow_arr.as_ref(), &field).try_into().unwrap();
1080
1081 assert_eq!(geo_arr, geo_arr2);
1082 assert_eq!(geo_arr, geo_arr3);
1083 }
1084 }
1085 }
1086
1087 #[test]
1088 fn test_nullability() {
1089 let geoms = raw::geometry::geoms();
1090 let null_idxs = geoms
1091 .iter()
1092 .enumerate()
1093 .filter_map(|(i, geom)| if geom.is_none() { Some(i) } else { None })
1094 .collect::<Vec<_>>();
1095
1096 let typ = GeometryType::new(Default::default());
1097 let geo_arr = GeometryBuilder::from_nullable_geometries(&geoms, typ)
1098 .unwrap()
1099 .finish();
1100
1101 for null_idx in &null_idxs {
1102 assert!(geo_arr.is_null(*null_idx));
1103 }
1104 }
1105
1106 #[test]
1107 fn test_logical_nulls() {
1108 let geoms = raw::geometry::geoms();
1109 let expected_nulls = NullBuffer::from_iter(geoms.iter().map(|g| g.is_some()));
1110
1111 let typ = GeometryType::new(Default::default());
1112 let geo_arr = GeometryBuilder::from_nullable_geometries(&geoms, typ)
1113 .unwrap()
1114 .finish();
1115
1116 assert_eq!(geo_arr.logical_nulls().unwrap(), expected_nulls);
1117 }
1118
1119 #[test]
1120 fn into_coord_type() {
1121 for prefer_multi in [true, false] {
1122 let geo_arr = crate::test::geometry::array(CoordType::Interleaved, prefer_multi);
1123 let geo_arr2 = geo_arr
1124 .clone()
1125 .into_coord_type(CoordType::Separated)
1126 .into_coord_type(CoordType::Interleaved);
1127
1128 assert_eq!(geo_arr, geo_arr2);
1129 }
1130 }
1131
1132 #[test]
1133 fn partial_eq() {
1134 for prefer_multi in [true, false] {
1135 let arr1 = crate::test::geometry::array(CoordType::Interleaved, prefer_multi);
1136 let arr2 = crate::test::geometry::array(CoordType::Separated, prefer_multi);
1137
1138 assert_eq!(arr1, arr1);
1139 assert_eq!(arr2, arr2);
1140 assert_eq!(arr1, arr2);
1141
1142 assert_ne!(arr1, arr2.slice(0, 2));
1143 }
1144 }
1145}
1146
1147