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 let metadata = typ.metadata().clone();
463
464 match value.data_type() {
469 DataType::Union(fields, mode) => {
470 if !matches!(mode, UnionMode::Dense) {
471 return Err(ArrowError::SchemaError("Expected dense union".to_string()).into());
472 }
473
474 for (type_id, _field) in fields.iter() {
475 let dim = Dimension::from_order((type_id / 10) as _)?;
476 let index = dim.order();
477
478 match type_id % 10 {
479 1 => {
480 points[index] = Some(
481 (
482 value.child(type_id).as_ref(),
483 PointType::new(dim, Default::default())
484 .with_coord_type(coord_type),
485 )
486 .try_into()?,
487 );
488 }
489 2 => {
490 line_strings[index] = Some(
491 (
492 value.child(type_id).as_ref(),
493 LineStringType::new(dim, Default::default())
494 .with_coord_type(coord_type),
495 )
496 .try_into()?,
497 );
498 }
499 3 => {
500 polygons[index] = Some(
501 (
502 value.child(type_id).as_ref(),
503 PolygonType::new(dim, Default::default())
504 .with_coord_type(coord_type),
505 )
506 .try_into()?,
507 );
508 }
509 4 => {
510 mpoints[index] = Some(
511 (
512 value.child(type_id).as_ref(),
513 MultiPointType::new(dim, Default::default())
514 .with_coord_type(coord_type),
515 )
516 .try_into()?,
517 );
518 }
519 5 => {
520 mline_strings[index] = Some(
521 (
522 value.child(type_id).as_ref(),
523 MultiLineStringType::new(dim, Default::default())
524 .with_coord_type(coord_type),
525 )
526 .try_into()?,
527 );
528 }
529 6 => {
530 mpolygons[index] = Some(
531 (
532 value.child(type_id).as_ref(),
533 MultiPolygonType::new(dim, Default::default())
534 .with_coord_type(coord_type),
535 )
536 .try_into()?,
537 );
538 }
539 7 => {
540 gcs[index] = Some(
541 (
542 value.child(type_id).as_ref(),
543 GeometryCollectionType::new(dim, Default::default())
544 .with_coord_type(coord_type),
545 )
546 .try_into()?,
547 );
548 }
549 _ => {
550 return Err(GeoArrowError::InvalidGeoArrow(format!(
551 "Unexpected type_id when converting to GeometryArray {type_id}",
552 )));
553 }
554 }
555 }
556 }
557 _ => {
558 return Err(GeoArrowError::InvalidGeoArrow(
559 "expected union type when converting to GeometryArray".to_string(),
560 ));
561 }
562 };
563
564 let type_ids = value.type_ids().clone();
565 let offsets = value.offsets().unwrap().clone();
567
568 points.iter_mut().enumerate().for_each(|(i, arr)| {
576 let new_val = if let Some(arr) = arr.take() {
577 arr
578 } else {
579 PointBuilder::new(
580 PointType::new(Dimension::from_order(i).unwrap(), Default::default())
581 .with_coord_type(coord_type),
582 )
583 .finish()
584 };
585 arr.replace(new_val);
586 });
587 line_strings.iter_mut().enumerate().for_each(|(i, arr)| {
588 let new_val = if let Some(arr) = arr.take() {
589 arr
590 } else {
591 LineStringBuilder::new(
592 LineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
593 .with_coord_type(coord_type),
594 )
595 .finish()
596 };
597 arr.replace(new_val);
598 });
599 polygons.iter_mut().enumerate().for_each(|(i, arr)| {
600 let new_val = if let Some(arr) = arr.take() {
601 arr
602 } else {
603 PolygonBuilder::new(
604 PolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
605 .with_coord_type(coord_type),
606 )
607 .finish()
608 };
609 arr.replace(new_val);
610 });
611 mpoints.iter_mut().enumerate().for_each(|(i, arr)| {
612 let new_val = if let Some(arr) = arr.take() {
613 arr
614 } else {
615 MultiPointBuilder::new(
616 MultiPointType::new(Dimension::from_order(i).unwrap(), Default::default())
617 .with_coord_type(coord_type),
618 )
619 .finish()
620 };
621 arr.replace(new_val);
622 });
623 mline_strings.iter_mut().enumerate().for_each(|(i, arr)| {
624 let new_val = if let Some(arr) = arr.take() {
625 arr
626 } else {
627 MultiLineStringBuilder::new(
628 MultiLineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
629 .with_coord_type(coord_type),
630 )
631 .finish()
632 };
633 arr.replace(new_val);
634 });
635 mpolygons.iter_mut().enumerate().for_each(|(i, arr)| {
636 let new_val = if let Some(arr) = arr.take() {
637 arr
638 } else {
639 MultiPolygonBuilder::new(
640 MultiPolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
641 .with_coord_type(coord_type),
642 )
643 .finish()
644 };
645 arr.replace(new_val);
646 });
647 gcs.iter_mut().enumerate().for_each(|(i, arr)| {
648 let new_val = if let Some(arr) = arr.take() {
649 arr
650 } else {
651 GeometryCollectionBuilder::new(
652 GeometryCollectionType::new(
653 Dimension::from_order(i).unwrap(),
654 Default::default(),
655 )
656 .with_coord_type(coord_type),
657 )
658 .finish()
659 };
660 arr.replace(new_val);
661 });
662
663 Ok(Self::new(
664 type_ids,
665 offsets,
666 points.map(|x| x.unwrap()),
667 line_strings.map(|x| x.unwrap()),
668 polygons.map(|x| x.unwrap()),
669 mpoints.map(|x| x.unwrap()),
670 mline_strings.map(|x| x.unwrap()),
671 mpolygons.map(|x| x.unwrap()),
672 gcs.map(|x| x.unwrap()),
673 metadata,
674 ))
675 }
676}
677
678impl TryFrom<(&dyn Array, GeometryType)> for GeometryArray {
679 type Error = GeoArrowError;
680
681 fn try_from((value, typ): (&dyn Array, GeometryType)) -> GeoArrowResult<Self> {
682 match value.data_type() {
683 DataType::Union(_, _) => (value.as_union(), typ).try_into(),
684 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
685 "Unexpected GeometryArray DataType: {dt:?}",
686 ))),
687 }
688 }
689}
690
691impl TryFrom<(&dyn Array, &Field)> for GeometryArray {
692 type Error = GeoArrowError;
693
694 fn try_from((arr, field): (&dyn Array, &Field)) -> GeoArrowResult<Self> {
695 let typ = field.try_extension_type::<GeometryType>()?;
696 (arr, typ).try_into()
697 }
698}
699
700impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, GeometryType)> for GeometryArray {
701 type Error = GeoArrowError;
702
703 fn try_from(value: (GenericWkbArray<O>, GeometryType)) -> GeoArrowResult<Self> {
704 let mut_arr: GeometryBuilder = value.try_into()?;
705 Ok(mut_arr.finish())
706 }
707}
708
709pub(crate) trait DimensionIndex: Sized {
710 fn order(&self) -> usize;
712
713 fn from_order(index: usize) -> GeoArrowResult<Self>;
714}
715
716impl DimensionIndex for Dimension {
717 fn order(&self) -> usize {
718 match self {
719 Self::XY => 0,
720 Self::XYZ => 1,
721 Self::XYM => 2,
722 Self::XYZM => 3,
723 }
724 }
725
726 fn from_order(index: usize) -> GeoArrowResult<Self> {
727 match index {
728 0 => Ok(Self::XY),
729 1 => Ok(Self::XYZ),
730 2 => Ok(Self::XYM),
731 3 => Ok(Self::XYZM),
732 i => {
733 Err(ArrowError::SchemaError(format!("unsupported index in from_order: {i}")).into())
734 }
735 }
736 }
737}
738
739impl PartialEq for GeometryArray {
740 fn eq(&self, other: &Self) -> bool {
741 self.type_ids == other.type_ids
742 && self.offsets == other.offsets
743 && self.points == other.points
744 && self.line_strings == other.line_strings
745 && self.polygons == other.polygons
746 && self.mpoints == other.mpoints
747 && self.mline_strings == other.mline_strings
748 && self.mpolygons == other.mpolygons
749 && self.gcs == other.gcs
750 }
751}
752
753impl TypeId for PointArray {
754 const ARRAY_TYPE_OFFSET: i8 = 1;
755}
756impl TypeId for LineStringArray {
757 const ARRAY_TYPE_OFFSET: i8 = 2;
758}
759impl TypeId for PolygonArray {
760 const ARRAY_TYPE_OFFSET: i8 = 3;
761}
762impl TypeId for MultiPointArray {
763 const ARRAY_TYPE_OFFSET: i8 = 4;
764}
765impl TypeId for MultiLineStringArray {
766 const ARRAY_TYPE_OFFSET: i8 = 5;
767}
768impl TypeId for MultiPolygonArray {
769 const ARRAY_TYPE_OFFSET: i8 = 6;
770}
771impl TypeId for GeometryCollectionArray {
772 const ARRAY_TYPE_OFFSET: i8 = 7;
773}
774
775type ChildrenArrays = (
776 [PointArray; 4],
777 [LineStringArray; 4],
778 [PolygonArray; 4],
779 [MultiPointArray; 4],
780 [MultiLineStringArray; 4],
781 [MultiPolygonArray; 4],
782 [GeometryCollectionArray; 4],
783);
784
785fn empty_children(coord_type: CoordType) -> ChildrenArrays {
790 (
791 core::array::from_fn(|i| {
792 PointBuilder::new(
793 PointType::new(Dimension::from_order(i).unwrap(), Default::default())
794 .with_coord_type(coord_type),
795 )
796 .finish()
797 }),
798 core::array::from_fn(|i| {
799 LineStringBuilder::new(
800 LineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
801 .with_coord_type(coord_type),
802 )
803 .finish()
804 }),
805 core::array::from_fn(|i| {
806 PolygonBuilder::new(
807 PolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
808 .with_coord_type(coord_type),
809 )
810 .finish()
811 }),
812 core::array::from_fn(|i| {
813 MultiPointBuilder::new(
814 MultiPointType::new(Dimension::from_order(i).unwrap(), Default::default())
815 .with_coord_type(coord_type),
816 )
817 .finish()
818 }),
819 core::array::from_fn(|i| {
820 MultiLineStringBuilder::new(
821 MultiLineStringType::new(Dimension::from_order(i).unwrap(), Default::default())
822 .with_coord_type(coord_type),
823 )
824 .finish()
825 }),
826 core::array::from_fn(|i| {
827 MultiPolygonBuilder::new(
828 MultiPolygonType::new(Dimension::from_order(i).unwrap(), Default::default())
829 .with_coord_type(coord_type),
830 )
831 .finish()
832 }),
833 core::array::from_fn(|i| {
834 GeometryCollectionBuilder::new(
835 GeometryCollectionType::new(Dimension::from_order(i).unwrap(), Default::default())
836 .with_coord_type(coord_type),
837 )
838 .finish()
839 }),
840 )
841}
842
843macro_rules! impl_primitive_cast {
844 ($source_array:ty, $value_edit:tt) => {
845 impl From<$source_array> for GeometryArray {
846 fn from(value: $source_array) -> Self {
847 let coord_type = value.data_type.coord_type();
848 let dim = value.data_type.dimension();
849 let metadata = value.data_type.metadata().clone();
850
851 let type_ids = vec![value.type_id(dim); value.len()].into();
852 let offsets = ScalarBuffer::from_iter(0..value.len() as i32);
853 let data_type = GeometryType::new(metadata).with_coord_type(coord_type);
854 let mut children = empty_children(coord_type);
855
856 children.$value_edit[dim.order()] = value;
857 Self {
858 data_type,
859 type_ids,
860 offsets,
861 points: children.0,
862 line_strings: children.1,
863 polygons: children.2,
864 mpoints: children.3,
865 mline_strings: children.4,
866 mpolygons: children.5,
867 gcs: children.6,
868 }
869 }
870 }
871 };
872}
873
874impl_primitive_cast!(PointArray, 0);
875impl_primitive_cast!(LineStringArray, 1);
876impl_primitive_cast!(PolygonArray, 2);
877impl_primitive_cast!(MultiPointArray, 3);
878impl_primitive_cast!(MultiLineStringArray, 4);
879impl_primitive_cast!(MultiPolygonArray, 5);
880impl_primitive_cast!(GeometryCollectionArray, 6);
881
882#[cfg(test)]
883mod test {
884 use geo_traits::to_geo::ToGeoGeometry;
885 use geoarrow_schema::Crs;
886 use geoarrow_test::raw;
887
888 use super::*;
889 use crate::test::{linestring, multilinestring, multipoint, multipolygon, point, polygon};
890
891 fn geoms() -> Vec<geo_types::Geometry> {
892 vec![
893 point::p0().into(),
894 point::p1().into(),
895 point::p2().into(),
896 linestring::ls0().into(),
897 linestring::ls1().into(),
898 polygon::p0().into(),
899 polygon::p1().into(),
900 multipoint::mp0().into(),
901 multipoint::mp1().into(),
902 multilinestring::ml0().into(),
903 multilinestring::ml1().into(),
904 multipolygon::mp0().into(),
905 multipolygon::mp1().into(),
906 ]
907 }
908
909 fn geom_array(coord_type: CoordType) -> GeometryArray {
910 let geoms = geoms().into_iter().map(Some).collect::<Vec<_>>();
911 let typ = GeometryType::new(Default::default()).with_coord_type(coord_type);
912 GeometryBuilder::from_nullable_geometries(&geoms, typ)
913 .unwrap()
914 .finish()
915 }
916
917 #[test]
918 fn test_2d() {
919 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
920 let geoms = geoms();
921 let geometry_array = geom_array(coord_type);
922 let geoms_again = geometry_array
923 .iter_values()
924 .map(|g| g.unwrap().to_geometry())
925 .collect::<Vec<_>>();
926 assert_eq!(geoms, geoms_again);
927 }
928 }
929
930 #[test]
931 fn test_2d_roundtrip_arrow() {
932 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
933 let geoms = geoms();
934 let geometry_array = geom_array(coord_type);
935 let field = geometry_array.data_type.to_field("geometry", true);
936 let union_array = geometry_array.into_arrow();
937
938 let geometry_array_again =
939 GeometryArray::try_from((&union_array as _, &field)).unwrap();
940 let geoms_again = geometry_array_again
941 .iter_values()
942 .map(|g| g.unwrap().to_geometry())
943 .collect::<Vec<_>>();
944 assert_eq!(geoms, geoms_again);
945 }
946 }
947
948 #[test]
949 fn try_from_arrow() {
950 for coord_type in [CoordType::Interleaved, CoordType::Separated] {
951 for prefer_multi in [true, false] {
952 let geo_arr = crate::test::geometry::array(coord_type, prefer_multi);
953
954 let point_type = geo_arr.extension_type().clone();
955 let field = point_type.to_field("geometry", true);
956
957 let arrow_arr = geo_arr.to_array_ref();
958
959 let geo_arr2: GeometryArray = (arrow_arr.as_ref(), point_type).try_into().unwrap();
960 let geo_arr3: GeometryArray = (arrow_arr.as_ref(), &field).try_into().unwrap();
961
962 assert_eq!(geo_arr, geo_arr2);
963 assert_eq!(geo_arr, geo_arr3);
964 }
965 }
966 }
967
968 #[test]
969 fn test_nullability() {
970 let geoms = raw::geometry::geoms();
971 let null_idxs = geoms
972 .iter()
973 .enumerate()
974 .filter_map(|(i, geom)| if geom.is_none() { Some(i) } else { None })
975 .collect::<Vec<_>>();
976
977 let typ = GeometryType::new(Default::default());
978 let geo_arr = GeometryBuilder::from_nullable_geometries(&geoms, typ)
979 .unwrap()
980 .finish();
981
982 for null_idx in &null_idxs {
983 assert!(geo_arr.is_null(*null_idx));
984 }
985 }
986
987 #[test]
988 fn test_logical_nulls() {
989 let geoms = raw::geometry::geoms();
990 let expected_nulls = NullBuffer::from_iter(geoms.iter().map(|g| g.is_some()));
991
992 let typ = GeometryType::new(Default::default());
993 let geo_arr = GeometryBuilder::from_nullable_geometries(&geoms, typ)
994 .unwrap()
995 .finish();
996
997 assert_eq!(geo_arr.logical_nulls().unwrap(), expected_nulls);
998 }
999
1000 #[test]
1001 fn into_coord_type() {
1002 for prefer_multi in [true, false] {
1003 let geo_arr = crate::test::geometry::array(CoordType::Interleaved, prefer_multi);
1004 let geo_arr2 = geo_arr
1005 .clone()
1006 .into_coord_type(CoordType::Separated)
1007 .into_coord_type(CoordType::Interleaved);
1008
1009 assert_eq!(geo_arr, geo_arr2);
1010 }
1011 }
1012
1013 #[test]
1014 fn partial_eq() {
1015 for prefer_multi in [true, false] {
1016 let arr1 = crate::test::geometry::array(CoordType::Interleaved, prefer_multi);
1017 let arr2 = crate::test::geometry::array(CoordType::Separated, prefer_multi);
1018
1019 assert_eq!(arr1, arr1);
1020 assert_eq!(arr2, arr2);
1021 assert_eq!(arr1, arr2);
1022
1023 assert_ne!(arr1, arr2.slice(0, 2));
1024 }
1025 }
1026
1027 #[test]
1028 fn should_persist_crs() {
1029 let geo_arr = crate::test::geometry::array(CoordType::Interleaved, false);
1030 let crs = Crs::from_authority_code("EPSG:4326".to_string());
1031 let geo_arr = geo_arr.with_metadata(Arc::new(Metadata::new(crs.clone(), None)));
1032
1033 let arrow_arr = geo_arr.to_array_ref();
1034 let field = geo_arr.data_type().to_field("geometry", true);
1035
1036 let geo_arr2: GeometryArray = (arrow_arr.as_ref(), &field).try_into().unwrap();
1037
1038 assert_eq!(geo_arr, geo_arr2);
1039 assert_eq!(geo_arr2.data_type.metadata().crs().clone(), crs);
1040 }
1041}
1042
1043