geoarrow_array/array/
geometry.rs

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/// An immutable array of geometries of unknown geometry type and dimension.
21///
22// # Invariants
23//
24// - All arrays must have the same dimension
25// - All arrays must have the same coordinate layout (interleaved or separated)
26//
27// - 1: Point
28// - 2: LineString
29// - 3: Polygon
30// - 4: MultiPoint
31// - 5: MultiLineString
32// - 6: MultiPolygon
33// - 7: GeometryCollection
34// - 11: Point Z
35// - 12: LineString Z
36// - 13: Polygon Z
37// - 14: MultiPoint Z
38// - 15: MultiLineString Z
39// - 16: MultiPolygon Z
40// - 17: GeometryCollection Z
41// - 21: Point M
42// - 22: LineString M
43// - 23: Polygon M
44// - 24: MultiPoint M
45// - 25: MultiLineString M
46// - 26: MultiPolygon M
47// - 27: GeometryCollection M
48// - 31: Point ZM
49// - 32: LineString ZM
50// - 33: Polygon ZM
51// - 34: MultiPoint ZM
52// - 35: MultiLineString ZM
53// - 36: MultiPolygon ZM
54// - 37: GeometryCollection ZM
55#[derive(Debug, Clone)]
56pub struct GeometryArray {
57    pub(crate) data_type: GeometryType,
58
59    /// Invariant: every item in `type_ids` is `> 0 && < fields.len()` if `type_ids` are not
60    /// provided. If `type_ids` exist in the NativeType, then every item in `type_ids` is `> 0 && `
61    pub(crate) type_ids: ScalarBuffer<i8>,
62
63    /// Invariant: `offsets.len() == type_ids.len()`
64    pub(crate) offsets: ScalarBuffer<i32>,
65
66    /// An array of PointArray, ordered XY, XYZ, XYM, XYZM
67    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    /// Create a new GeometryArray from parts
78    ///
79    /// # Implementation
80    ///
81    /// This function is `O(1)`.
82    ///
83    /// # Panics
84    ///
85    /// - if the validity is not `None` and its length is different from the number of geometries
86    /// - if the largest geometry offset does not match the number of coordinates
87    #[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        // Validate that all arrays have the same coord type.
101        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    /// The lengths of each buffer contained in this array.
139    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    /// Returns the `type_ids` buffer for this array
153    pub fn type_ids(&self) -> &ScalarBuffer<i8> {
154        &self.type_ids
155    }
156
157    /// Returns the `offsets` buffer for this array
158    pub fn offsets(&self) -> &ScalarBuffer<i32> {
159        &self.offsets
160    }
161
162    // TODO: handle slicing
163    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    /// Return `true` if this array holds at least one non-empty array of the given dimension
193    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    /// Return `true` if this array holds at least one geometry array of the given dimension and no
203    /// arrays of any other dimension.
204    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    // Handle sliced data before downcasting.
216    // pub fn compact_children()
217
218    // /// The number of non-empty child arrays
219    // fn num_non_empty_children(&self) -> usize {
220    //     let mut count = 0;
221
222    //     if !self.point_xy.is_empty() {
223    //         count += 1
224    //     };
225    //     if !self.line_string_xy.is_empty() {
226    //         count += 1
227    //     };
228    //     if !self.polygon_xy.is_empty() {
229    //         count += 1
230    //     };
231    //     if !self.mpoint_xy.is_empty() {
232    //         count += 1
233    //     };
234    //     if !self.mline_string_xy.is_empty() {
235    //         count += 1
236    //     };
237    //     if !self.mpolygon_xy.is_empty() {
238    //         count += 1
239    //     };
240
241    //     if !self.point_xyz.is_empty() {
242    //         count += 1
243    //     };
244    //     if !self.line_string_xyz.is_empty() {
245    //         count += 1
246    //     };
247    //     if !self.polygon_xyz.is_empty() {
248    //         count += 1
249    //     };
250    //     if !self.mpoint_xyz.is_empty() {
251    //         count += 1
252    //     };
253    //     if !self.mline_string_xyz.is_empty() {
254    //         count += 1
255    //     };
256    //     if !self.mpolygon_xyz.is_empty() {
257    //         count += 1
258    //     };
259
260    //     count
261    // }
262
263    // TODO: restore to enable downcasting
264
265    // pub fn has_only_type(&self, typ: NativeType) -> bool {
266    //     use Dimension::*;
267
268    //     if self.num_non_empty_children() == 0 {
269    //         // Empty array
270    //         false
271    //     }
272
273    //     if self.num_non_empty_children() > 1 {}
274
275    //     match typ {
276    //         NativeType::Point(_, dim)
277    //     }
278
279    //     self.has_points(XY)
280    //         && !self.has_line_strings(XY)
281    //         && !self.has_polygons(XY)
282    //         && !self.has_multi_points(XY)
283    //         && !self.has_multi_line_strings(XY)
284    //         && !self.has_multi_polygons(XY)
285    //         && !self.has_points(XYZ)
286    //         && !self.has_line_strings(XYZ)
287    //         && !self.has_polygons(XYZ)
288    //         && !self.has_multi_points(XYZ)
289    //         && !self.has_multi_line_strings(XYZ)
290    //         && !self.has_multi_polygons(XYZ)
291    // }
292
293    // pub fn has_only_line_strings(&self) -> bool {
294    //     !self.has_points()
295    //         && self.has_line_strings()
296    //         && !self.has_polygons()
297    //         && !self.has_multi_points()
298    //         && !self.has_multi_line_strings()
299    //         && !self.has_multi_polygons()
300    // }
301
302    // pub fn has_only_polygons(&self) -> bool {
303    //     !self.has_points()
304    //         && !self.has_line_strings()
305    //         && self.has_polygons()
306    //         && !self.has_multi_points()
307    //         && !self.has_multi_line_strings()
308    //         && !self.has_multi_polygons()
309    // }
310
311    // pub fn has_only_multi_points(&self) -> bool {
312    //     !self.has_points()
313    //         && !self.has_line_strings()
314    //         && !self.has_polygons()
315    //         && self.has_multi_points()
316    //         && !self.has_multi_line_strings()
317    //         && !self.has_multi_polygons()
318    // }
319
320    // pub fn has_only_multi_line_strings(&self) -> bool {
321    //     !self.has_points()
322    //         && !self.has_line_strings()
323    //         && !self.has_polygons()
324    //         && !self.has_multi_points()
325    //         && self.has_multi_line_strings()
326    //         && !self.has_multi_polygons()
327    // }
328
329    // pub fn has_only_multi_polygons(&self) -> bool {
330    //     !self.has_points()
331    //         && !self.has_line_strings()
332    //         && !self.has_polygons()
333    //         && !self.has_multi_points()
334    //         && !self.has_multi_line_strings()
335    //         && self.has_multi_polygons()
336    // }
337
338    /// The number of bytes occupied by this array.
339    pub fn num_bytes(&self) -> usize {
340        self.buffer_lengths().num_bytes()
341    }
342
343    /// Slice this [`GeometryArray`].
344    ///
345    /// # Implementation
346    ///
347    /// This operation is `O(F)` where `F` is the number of fields.
348    ///
349    /// # Panic
350    ///
351    /// This function panics iff `offset + length > self.len()`.
352    #[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    /// Change the [`CoordType`] of this array.
374    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    /// Change the [`Metadata`] of this array.
391    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    // TODO: recursively expand the types from the geometry collection array
399    #[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        // Note that `type_ids` is sliced as usual, and thus always has the correct length.
458        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        // https://stackoverflow.com/a/34406459/7319250
535        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        // This is after checking for dense union
685        let offsets = value.offsets().unwrap().clone();
686
687        // We need to convert the array [Option<PointArray>; 4] into `[PointArray; 4]`.
688        // But we also need to ensure the underlying PointArray has the correct `Dimension` for the
689        // given array index.
690        // In order to do this, we need the index of the array, which `map` doesn't give us. And
691        // using `core::array::from_fn` doesn't let us move out of the existing array.
692        // So we mutate the existing array of `[Option<PointArray>; 4]` to ensure all values are
693        // `Some`, and then later we call `unwrap` on all array values in a `map`.
694        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    /// Get the positional index of the internal array for the given dimension.
831    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
905/// Initialize empty children with the given coord type.
906///
907/// This is used in the impls like `From<PointArray> for GeometryArray`. This lets us initialize
908/// all empty children and then just swap in the one array that's valid.
909fn 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// #[cfg(test)]
1148// mod test {
1149//     use super::*;
1150//     use crate::test::{linestring, multilinestring, multipoint, multipolygon, point, polygon};
1151
1152//     #[test]
1153//     fn geo_roundtrip_accurate_points() {
1154//         let geoms: Vec<geo::Geometry> = vec![
1155//             geo::Geometry::Point(point::p0()),
1156//             geo::Geometry::Point(point::p1()),
1157//             geo::Geometry::Point(point::p2()),
1158//         ];
1159
1160//         let arr: GeometryArray = GeometryBuilder::from_geometries(
1161//             geoms.as_slice(),
1162//             CoordType::Interleaved,
1163//             Default::default(),
1164//             false,
1165//         )
1166//         .unwrap()
1167//         .finish();
1168
1169//         assert_eq!(arr.value_as_geo(0), geo::Geometry::Point(point::p0()));
1170//         assert_eq!(arr.value_as_geo(1), geo::Geometry::Point(point::p1()));
1171//         assert_eq!(arr.value_as_geo(2), geo::Geometry::Point(point::p2()));
1172//     }
1173
1174//     #[test]
1175//     fn geo_roundtrip_accurate_multi_points() {
1176//         let geoms: Vec<geo::Geometry> = vec![
1177//             geo::Geometry::Point(point::p0()),
1178//             geo::Geometry::Point(point::p1()),
1179//             geo::Geometry::Point(point::p2()),
1180//         ];
1181//         let arr: GeometryArray = GeometryBuilder::from_geometries(
1182//             geoms.as_slice(),
1183//             CoordType::Interleaved,
1184//             Default::default(),
1185//             true,
1186//         )
1187//         .unwrap()
1188//         .finish();
1189
1190//         assert_eq!(
1191//             arr.value_as_geo(0),
1192//             geo::Geometry::MultiPoint(geo::MultiPoint(vec![point::p0()]))
1193//         );
1194//         assert_eq!(
1195//             arr.value_as_geo(1),
1196//             geo::Geometry::MultiPoint(geo::MultiPoint(vec![point::p1()]))
1197//         );
1198//         assert_eq!(
1199//             arr.value_as_geo(2),
1200//             geo::Geometry::MultiPoint(geo::MultiPoint(vec![point::p2()]))
1201//         );
1202//     }
1203
1204//     #[test]
1205//     fn geo_roundtrip_accurate_all() {
1206//         let geoms: Vec<geo::Geometry> = vec![
1207//             geo::Geometry::Point(point::p0()),
1208//             geo::Geometry::LineString(linestring::ls0()),
1209//             geo::Geometry::Polygon(polygon::p0()),
1210//             geo::Geometry::MultiPoint(multipoint::mp0()),
1211//             geo::Geometry::MultiLineString(multilinestring::ml0()),
1212//             geo::Geometry::MultiPolygon(multipolygon::mp0()),
1213//         ];
1214
1215//         let arr: GeometryArray = GeometryBuilder::from_geometries(
1216//             geoms.as_slice(),
1217//             CoordType::Interleaved,
1218//             Default::default(),
1219//             false,
1220//         )
1221//         .unwrap()
1222//         .finish();
1223
1224//         assert_eq!(arr.value_as_geo(0), geoms[0]);
1225//         assert_eq!(arr.value_as_geo(1), geoms[1]);
1226//         assert_eq!(arr.value_as_geo(2), geoms[2]);
1227//         assert_eq!(arr.value_as_geo(3), geoms[3]);
1228//         assert_eq!(arr.value_as_geo(4), geoms[4]);
1229//         assert_eq!(arr.value_as_geo(5), geoms[5]);
1230//     }
1231
1232//     #[test]
1233//     fn arrow_roundtrip() {
1234//         let geoms: Vec<geo::Geometry> = vec![
1235//             geo::Geometry::Point(point::p0()),
1236//             geo::Geometry::LineString(linestring::ls0()),
1237//             geo::Geometry::Polygon(polygon::p0()),
1238//             geo::Geometry::MultiPoint(multipoint::mp0()),
1239//             geo::Geometry::MultiLineString(multilinestring::ml0()),
1240//             geo::Geometry::MultiPolygon(multipolygon::mp0()),
1241//         ];
1242
1243//         let arr: GeometryArray = GeometryBuilder::from_geometries(
1244//             geoms.as_slice(),
1245//             CoordType::Interleaved,
1246//             Default::default(),
1247//             false,
1248//         )
1249//         .unwrap()
1250//         .finish();
1251
1252//         // Round trip to/from arrow-rs
1253//         let arrow_array = arr.into_arrow();
1254//         let round_trip_arr: GeometryArray = (&arrow_array).try_into().unwrap();
1255
1256//         assert_eq!(round_trip_arr.value_as_geo(0), geoms[0]);
1257//         assert_eq!(round_trip_arr.value_as_geo(1), geoms[1]);
1258//         assert_eq!(round_trip_arr.value_as_geo(2), geoms[2]);
1259//         assert_eq!(round_trip_arr.value_as_geo(3), geoms[3]);
1260//         assert_eq!(round_trip_arr.value_as_geo(4), geoms[4]);
1261//         assert_eq!(round_trip_arr.value_as_geo(5), geoms[5]);
1262//     }
1263
1264//     #[test]
1265//     fn arrow_roundtrip_not_all_types() {
1266//         let geoms: Vec<geo::Geometry> = vec![
1267//             geo::Geometry::MultiPoint(multipoint::mp0()),
1268//             geo::Geometry::MultiLineString(multilinestring::ml0()),
1269//             geo::Geometry::MultiPolygon(multipolygon::mp0()),
1270//         ];
1271
1272//         let arr: GeometryArray = GeometryBuilder::from_geometries(
1273//             geoms.as_slice(),
1274//             CoordType::Interleaved,
1275//             Default::default(),
1276//             false,
1277//         )
1278//         .unwrap()
1279//         .finish();
1280
1281//         // Round trip to/from arrow-rs
1282//         let arrow_array = arr.into_arrow();
1283//         let round_trip_arr: GeometryArray = (&arrow_array).try_into().unwrap();
1284
1285//         assert_eq!(round_trip_arr.value_as_geo(0), geoms[0]);
1286//         assert_eq!(round_trip_arr.value_as_geo(1), geoms[1]);
1287//         assert_eq!(round_trip_arr.value_as_geo(2), geoms[2]);
1288//     }
1289
1290//     #[test]
1291//     fn arrow_roundtrip_not_all_types2() {
1292//         let geoms: Vec<geo::Geometry> = vec![
1293//             geo::Geometry::MultiPoint(multipoint::mp0()),
1294//             geo::Geometry::MultiPolygon(multipolygon::mp0()),
1295//         ];
1296
1297//         let arr: GeometryArray = GeometryBuilder::from_geometries(
1298//             geoms.as_slice(),
1299//             CoordType::Interleaved,
1300//             Default::default(),
1301//             false,
1302//         )
1303//         .unwrap()
1304//         .finish();
1305
1306//         // Round trip to/from arrow-rs
1307//         let arrow_array = arr.into_arrow();
1308//         let round_trip_arr: GeometryArray = (&arrow_array).try_into().unwrap();
1309
1310//         assert_eq!(round_trip_arr.value_as_geo(0), geoms[0]);
1311//         assert_eq!(round_trip_arr.value_as_geo(1), geoms[1]);
1312//     }
1313
1314//     #[test]
1315//     fn test_slicing() {
1316//         let geoms: Vec<geo::Geometry> = vec![
1317//             geo::Geometry::Point(point::p0()),
1318//             geo::Geometry::LineString(linestring::ls0()),
1319//             geo::Geometry::Polygon(polygon::p0()),
1320//             geo::Geometry::MultiPoint(multipoint::mp0()),
1321//             geo::Geometry::MultiLineString(multilinestring::ml0()),
1322//             geo::Geometry::MultiPolygon(multipolygon::mp0()),
1323//         ];
1324
1325//         let arr: GeometryArray = GeometryBuilder::from_geometries(
1326//             geoms.as_slice(),
1327//             CoordType::Interleaved,
1328//             Default::default(),
1329//             false,
1330//         )
1331//         .unwrap()
1332//         .finish();
1333
1334//         assert_eq!(arr.slice(1, 2).value_as_geo(0), geoms[1]);
1335//         assert_eq!(arr.slice(1, 2).value_as_geo(1), geoms[2]);
1336//         assert_eq!(arr.slice(3, 3).value_as_geo(2), geoms[5]);
1337//     }
1338// }