geoarrow_array/
trait_.rs

1use std::any::Any;
2use std::fmt::Debug;
3use std::sync::Arc;
4
5use arrow_array::{Array, ArrayRef};
6use arrow_buffer::NullBuffer;
7use arrow_schema::extension::ExtensionType;
8use geo_traits::GeometryTrait;
9use geoarrow_schema::error::GeoArrowResult;
10use geoarrow_schema::{GeoArrowType, Metadata};
11
12use crate::array::from_arrow_array;
13
14/// Convert GeoArrow arrays into their respective [arrow][arrow_array] arrays.
15pub trait IntoArrow {
16    /// The type of arrow array that this geoarrow array can be converted into.
17    type ArrowArray: Array;
18
19    /// The extension type representing this array. It will always be a type defined by
20    /// [geoarrow_schema].
21    type ExtensionType: ExtensionType;
22
23    /// Converts this geoarrow array into an arrow array.
24    ///
25    /// Note that [arrow][arrow_array] arrays do not maintain Arrow extension metadata, so the
26    /// result of this method will omit any spatial extension information. Ensure you call
27    /// [Self::extension_type] to get extension information that you can add to a
28    /// [`Field`][arrow_schema::Field].
29    fn into_arrow(self) -> Self::ArrowArray;
30
31    /// Return the Arrow extension type representing this array.
32    fn extension_type(&self) -> &Self::ExtensionType;
33}
34
35/// A base trait for all GeoArrow arrays.
36///
37/// This is a geospatial corollary to the upstream [`Array`] trait.
38pub trait GeoArrowArray: Debug + Send + Sync {
39    /// Returns the array as [`Any`] so that it can be downcasted to a specific implementation.
40    ///
41    /// Prefer using [`AsGeoArrowArray`][crate::cast::AsGeoArrowArray] instead of calling this
42    /// method and manually downcasting.
43    fn as_any(&self) -> &dyn Any;
44
45    /// Returns the [`GeoArrowType`] of this array.
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// # use geoarrow_array::builder::PointBuilder;
51    /// # use geoarrow_array::GeoArrowArray;
52    /// # use geoarrow_schema::{Dimension, PointType, GeoArrowType};
53    /// #
54    /// let point = geo_types::point!(x: 1., y: 2.);
55    /// let point_type = PointType::new(Dimension::XY, Default::default());
56    /// let point_array = PointBuilder::from_points([point].iter(), point_type.clone()).finish();
57    /// assert_eq!(point_array.data_type(), GeoArrowType::Point(point_type));
58    /// ```
59    fn data_type(&self) -> GeoArrowType;
60
61    /// Converts this array into an `Arc`ed [`arrow`][arrow_array] array, consuming the original
62    /// array.
63    ///
64    /// This is `O(1)`.
65    ///
66    /// Note that **this will omit any spatial extension information**. You must separately store
67    /// the spatial information in a [`Field`][arrow_schema::Field] derived from
68    /// [`Self::data_type`].
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// # use arrow_array::ArrayRef;
74    /// # use geoarrow_array::builder::PointBuilder;
75    /// # use geoarrow_array::GeoArrowArray;
76    /// # use geoarrow_schema::{Dimension, PointType};
77    /// #
78    /// let point = geo_types::point!(x: 1., y: 2.);
79    /// let point_type = PointType::new(Dimension::XY, Default::default());
80    /// let point_array = PointBuilder::from_points([point].iter(), point_type.clone()).finish();
81    /// let array_ref: ArrayRef = point_array.into_array_ref();
82    /// ```
83    #[must_use]
84    fn into_array_ref(self) -> ArrayRef;
85
86    /// Converts this array into an `Arc`ed [`arrow`][arrow_array] array.
87    ///
88    /// This is `O(1)`.
89    ///
90    /// Note that **this will omit any spatial extension information**. You must separately store
91    /// the spatial information in a [`Field`][arrow_schema::Field] derived from
92    /// [`Self::data_type`].
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// # use arrow_array::ArrayRef;
98    /// # use geoarrow_array::builder::PointBuilder;
99    /// # use geoarrow_array::GeoArrowArray;
100    /// # use geoarrow_schema::{Dimension, PointType};
101    /// #
102    /// let point = geo_types::point!(x: 1., y: 2.);
103    /// let point_type = PointType::new(Dimension::XY, Default::default());
104    /// let point_array = PointBuilder::from_points([point].iter(), point_type.clone()).finish();
105    /// let array_ref: ArrayRef = point_array.to_array_ref();
106    /// ```
107    #[must_use]
108    fn to_array_ref(&self) -> ArrayRef;
109
110    /// The number of geometries contained in this array.
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// # use arrow_array::ArrayRef;
116    /// # use geoarrow_array::builder::PointBuilder;
117    /// # use geoarrow_array::GeoArrowArray;
118    /// # use geoarrow_schema::{Dimension, PointType};
119    /// #
120    /// let point = geo_types::point!(x: 1., y: 2.);
121    /// let point_type = PointType::new(Dimension::XY, Default::default());
122    /// let point_array = PointBuilder::from_points([point].iter(), point_type.clone()).finish();
123    /// assert_eq!(point_array.len(), 1);
124    /// ```
125    fn len(&self) -> usize;
126
127    /// Returns `true` if the array is empty.
128    ///
129    /// # Examples
130    ///
131    /// ```
132    /// # use arrow_array::ArrayRef;
133    /// # use geoarrow_array::builder::PointBuilder;
134    /// # use geoarrow_array::GeoArrowArray;
135    /// # use geoarrow_schema::{Dimension, PointType};
136    /// #
137    /// let point = geo_types::point!(x: 1., y: 2.);
138    /// let point_type = PointType::new(Dimension::XY, Default::default());
139    /// let point_array = PointBuilder::from_points([point].iter(), point_type.clone()).finish();
140    /// assert!(!point_array.is_empty());
141    /// ```
142    fn is_empty(&self) -> bool {
143        self.len() == 0
144    }
145
146    /// Returns a potentially computed [`NullBuffer``] that represents the logical null values of
147    /// this array, if any.
148    ///
149    /// Logical nulls represent the values that are null in the array, regardless of the underlying
150    /// physical arrow representation.
151    ///
152    /// For most array types, this is equivalent to the "physical" nulls returned by
153    /// [`Array::nulls`]. However it is different for union arrays, including our
154    /// [`GeometryArray`][crate::array::GeometryArray] and
155    /// [`GeometryCollectionArray`][crate::array::GeometryCollectionArray] types, because the
156    /// unions aren't encoded in a single null buffer.
157    fn logical_nulls(&self) -> Option<NullBuffer>;
158
159    /// Returns the number of null slots in this array.
160    ///
161    /// This is `O(1)` since the number of null elements is pre-computed.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// # use geoarrow_array::GeoArrowArray;
167    /// # use geoarrow_array::builder::PointBuilder;
168    /// # use geoarrow_schema::{Dimension, PointType};
169    /// #
170    /// let point = geo_types::point!(x: 1., y: 2.);
171    /// let point_type = PointType::new(Dimension::XY, Default::default());
172    /// let point_array =
173    ///     PointBuilder::from_nullable_points([Some(&point), None].into_iter(), point_type.clone()).finish();
174    /// assert_eq!(point_array.logical_null_count(), 1);
175    /// ```
176    fn logical_null_count(&self) -> usize;
177
178    /// Returns whether slot `i` is null.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// # use geoarrow_array::GeoArrowArray;
184    /// # use geoarrow_array::builder::PointBuilder;
185    /// # use geoarrow_schema::{Dimension, PointType};
186    /// #
187    /// let point = geo_types::point!(x: 1., y: 2.);
188    ///
189    /// let point_type = PointType::new(Dimension::XY, Default::default());
190    /// let point_array =
191    ///     PointBuilder::from_nullable_points([Some(&point), None].into_iter(), point_type.clone()).finish();
192    /// assert!(point_array.is_null(1));
193    /// ```
194    ///
195    /// # Panics
196    ///
197    /// Panics iff `i >= self.len()`.
198    fn is_null(&self, i: usize) -> bool;
199
200    /// Returns whether slot `i` is valid.
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// # use geoarrow_array::GeoArrowArray;
206    /// # use geoarrow_array::builder::PointBuilder;
207    /// # use geoarrow_schema::{Dimension, PointType};
208    /// #
209    /// let point = geo_types::point!(x: 1., y: 2.);
210    ///
211    /// let point_type = PointType::new(Dimension::XY, Default::default());
212    /// let point_array =
213    ///     PointBuilder::from_nullable_points([Some(&point), None].into_iter(), point_type.clone()).finish();
214    /// assert!(point_array.is_valid(0));
215    /// ```
216    ///
217    /// # Panics
218    ///
219    /// Panics iff `i >= self.len()`.
220    #[inline]
221    fn is_valid(&self, i: usize) -> bool {
222        !self.is_null(i)
223    }
224
225    /// Returns a zero-copy slice of this array with the indicated offset and length.
226    ///
227    /// # Examples
228    ///
229    /// ```
230    /// # use std::sync::Arc;
231    /// #
232    /// # use geoarrow_array::GeoArrowArray;
233    /// # use geoarrow_array::builder::PointBuilder;
234    /// # use geoarrow_schema::{Dimension, PointType};
235    /// #
236    /// let point1 = geo_types::point!(x: 1., y: 2.);
237    /// let point2 = geo_types::point!(x: 3., y: 4.);
238    ///
239    /// let point_type = PointType::new(Dimension::XY, Default::default());
240    /// let point_array =
241    ///     Arc::new(PointBuilder::from_points([point1, point2].iter(), point_type.clone()).finish())
242    ///         as Arc<dyn GeoArrowArray>;
243    /// let sliced_array = point_array.slice(1, 1);
244    /// assert_eq!(sliced_array.len(), 1);
245    /// ```
246    ///
247    /// # Panics
248    ///
249    /// This function panics iff `offset + length > self.len()`.
250    #[must_use]
251    fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray>;
252
253    /// Change the [`Metadata`] of this array.
254    fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray>;
255}
256
257/// Ergonomics: Allow use of an `Arc<dyn GeoArrowArray>` as an `&dyn GeoArrowArray`
258impl GeoArrowArray for Arc<dyn GeoArrowArray> {
259    fn as_any(&self) -> &dyn Any {
260        self.as_ref().as_any()
261    }
262
263    fn data_type(&self) -> GeoArrowType {
264        self.as_ref().data_type()
265    }
266
267    fn into_array_ref(self) -> ArrayRef {
268        self.as_ref().to_array_ref()
269    }
270
271    fn to_array_ref(&self) -> ArrayRef {
272        self.as_ref().to_array_ref()
273    }
274
275    fn len(&self) -> usize {
276        self.as_ref().len()
277    }
278
279    fn logical_nulls(&self) -> Option<NullBuffer> {
280        self.as_ref().logical_nulls()
281    }
282
283    fn logical_null_count(&self) -> usize {
284        self.as_ref().logical_null_count()
285    }
286
287    fn is_null(&self, i: usize) -> bool {
288        self.as_ref().is_null(i)
289    }
290
291    fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
292        self.as_ref().slice(offset, length)
293    }
294
295    fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
296        // This is a hack to allow consuming self
297        let field = self.data_type().with_metadata(metadata).to_field("", true);
298        let array = self.as_ref().to_array_ref();
299        // This unwrap should be fine because we know we start with a GeoArrow array
300        from_arrow_array(array.as_ref(), &field).unwrap()
301    }
302}
303
304impl<T: GeoArrowArray> GeoArrowArray for &T {
305    fn as_any(&self) -> &dyn Any {
306        T::as_any(self)
307    }
308
309    fn data_type(&self) -> GeoArrowType {
310        T::data_type(self)
311    }
312
313    fn into_array_ref(self) -> ArrayRef {
314        T::to_array_ref(self)
315    }
316
317    fn to_array_ref(&self) -> ArrayRef {
318        T::to_array_ref(self)
319    }
320
321    fn len(&self) -> usize {
322        T::len(self)
323    }
324
325    fn logical_nulls(&self) -> Option<NullBuffer> {
326        T::logical_nulls(self)
327    }
328
329    fn logical_null_count(&self) -> usize {
330        T::logical_null_count(self)
331    }
332
333    fn is_null(&self, i: usize) -> bool {
334        T::is_null(self, i)
335    }
336
337    fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
338        T::slice(self, offset, length)
339    }
340
341    fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
342        // This is a hack to allow consuming self
343        let field = self.data_type().with_metadata(metadata).to_field("", true);
344        let array = T::to_array_ref(self);
345        // This unwrap should be fine because we know we start with a GeoArrow array
346        from_arrow_array(array.as_ref(), &field).unwrap()
347    }
348}
349
350/// A trait for accessing the values of a [`GeoArrowArray`].
351///
352/// # Performance
353///
354/// Accessing a geometry from a "native" array, such as `PointArray`, `MultiPolygonArray` or
355/// `GeometryArray` will always be constant-time and zero-copy.
356///
357/// Accessing a geometry from a "serialized" array such as `GenericWkbArray` or `GenericWktArray`
358/// will trigger some amount of parsing. In the case of `GenericWkbArray`, accessing an item will
359/// read the WKB header and scan the buffer if needed to find internal geometry offsets, but will
360/// not copy any internal coordinates. This allows for later access to be constant-time (though not
361/// necessarily zero-copy, since WKB is not byte-aligned). In the case of `GenericWktArray`,
362/// accessing a geometry will fully parse the WKT string and copy coordinates to a separate
363/// representation. This means that calling `.iter()` on a `GenericWktArray` will transparently
364/// fully parse every row.
365///
366/// # Validity
367///
368/// A [`GeoArrowArrayAccessor`] must always return a well-defined value for an index that is
369/// within the bounds `0..Array::len`, including for null indexes where [`Array::is_null`] is true.
370///
371/// The value at null indexes is unspecified, and implementations must not rely on a specific
372/// value such as [`Default::default`] being returned, however, it must not be undefined.
373pub trait GeoArrowArrayAccessor<'a>: GeoArrowArray {
374    /// The [geoarrow scalar object][crate::scalar] for this geometry array type.
375    type Item: Send + Sync + GeometryTrait<T = f64>;
376
377    /// Returns the element at index `i`, not considering validity.
378    ///
379    /// # Examples
380    ///
381    /// ```
382    /// use geo_traits::{CoordTrait, PointTrait};
383    /// # use geoarrow_array::GeoArrowArrayAccessor;
384    /// # use geoarrow_array::builder::PointBuilder;
385    /// # use geoarrow_schema::{Dimension, PointType};
386    ///
387    /// let point1 = geo_types::point!(x: 1., y: 2.);
388    ///
389    /// let point_type = PointType::new(Dimension::XY, Default::default());
390    /// let point_array =
391    ///     PointBuilder::from_nullable_points([Some(&point1), None].into_iter(), point_type.clone())
392    ///         .finish();
393    ///
394    /// let coord = point_array.value(0).unwrap().coord().unwrap();
395    /// assert_eq!(coord.x(), 1.);
396    /// assert_eq!(coord.y(), 2.);
397    /// ```
398    ///
399    /// # Errors
400    ///
401    /// Errors for invalid WKT and WKB geometries. Will never error for native arrays.
402    ///
403    /// # Panics
404    ///
405    /// Panics if the value is outside the bounds of the array.
406    fn value(&'a self, index: usize) -> GeoArrowResult<Self::Item> {
407        assert!(index < self.len());
408        unsafe { self.value_unchecked(index) }
409    }
410
411    /// Returns the element at index `i`, not considering validity.
412    ///
413    /// # Examples
414    ///
415    /// ```
416    /// use geo_traits::{CoordTrait, PointTrait};
417    /// # use geoarrow_array::GeoArrowArrayAccessor;
418    /// # use geoarrow_array::builder::PointBuilder;
419    /// # use geoarrow_schema::{Dimension, PointType};
420    ///
421    /// let point1 = geo_types::point!(x: 1., y: 2.);
422    ///
423    /// let point_type = PointType::new(Dimension::XY, Default::default());
424    /// let point_array =
425    ///     PointBuilder::from_nullable_points([Some(&point1), None].into_iter(), point_type.clone())
426    ///         .finish();
427    ///
428    /// let coord = unsafe { point_array.value_unchecked(0) }
429    ///     .unwrap()
430    ///     .coord()
431    ///     .unwrap();
432    /// assert_eq!(coord.x(), 1.);
433    /// assert_eq!(coord.y(), 2.);
434    /// ```
435    ///
436    /// # Errors
437    ///
438    /// Errors for invalid WKT and WKB geometries. Will never error for native arrays.
439    ///
440    /// # Safety
441    ///
442    /// Caller is responsible for ensuring that the index is within the bounds of the array
443    unsafe fn value_unchecked(&'a self, index: usize) -> GeoArrowResult<Self::Item>;
444
445    /// Returns the value at slot `i` as an Arrow scalar, considering validity.
446    ///
447    /// # Examples
448    ///
449    /// ```
450    /// # use geoarrow_array::GeoArrowArrayAccessor;
451    /// # use geoarrow_array::builder::PointBuilder;
452    /// # use geoarrow_schema::{Dimension, PointType};
453    /// #
454    /// let point1 = geo_types::point!(x: 1., y: 2.);
455    ///
456    /// let point_type = PointType::new(Dimension::XY, Default::default());
457    /// let point_array =
458    ///     PointBuilder::from_nullable_points([Some(&point1), None].into_iter(), point_type.clone())
459    ///         .finish();
460    ///
461    /// assert!(point_array.get(0).unwrap().is_some());
462    /// assert!(point_array.get(1).unwrap().is_none());
463    /// ```
464    ///
465    /// # Errors
466    ///
467    /// Errors for invalid WKT and WKB geometries. Will never error for native arrays.
468    fn get(&'a self, index: usize) -> GeoArrowResult<Option<Self::Item>> {
469        if self.is_null(index) {
470            return Ok(None);
471        }
472
473        Ok(Some(self.value(index)?))
474    }
475
476    /// Returns the value at slot `i` as an Arrow scalar, considering validity.
477    ///
478    /// # Errors
479    ///
480    /// Errors for invalid WKT and WKB geometries. Will never error for native arrays.
481    ///
482    /// # Safety
483    ///
484    /// Caller is responsible for ensuring that the index is within the bounds of the array
485    unsafe fn get_unchecked(&'a self, index: usize) -> Option<GeoArrowResult<Self::Item>> {
486        if self.is_null(index) {
487            return None;
488        }
489
490        Some(unsafe { self.value_unchecked(index) })
491    }
492
493    /// Iterates over this array's geoarrow scalar values, considering validity.
494    ///
495    /// # Errors
496    ///
497    /// Errors for invalid WKT and WKB geometries. Will never error for native arrays.
498    fn iter(&'a self) -> impl ExactSizeIterator<Item = Option<GeoArrowResult<Self::Item>>> + 'a {
499        (0..self.len()).map(|i| unsafe { self.get_unchecked(i) })
500    }
501
502    /// Iterator over geoarrow scalar values, not considering validity.
503    ///
504    /// # Errors
505    ///
506    /// Errors for invalid WKT and WKB geometries. Will never error for native arrays.
507    fn iter_values(&'a self) -> impl ExactSizeIterator<Item = GeoArrowResult<Self::Item>> + 'a {
508        (0..self.len()).map(|i| unsafe { self.value_unchecked(i) })
509    }
510}
511
512/// A trait describing a mutable geometry array; i.e. an array whose values can be changed.
513///
514// Note: This trait is not yet publicly exported from this crate, as we're not sure how the API
515// should be, and in particular whether we need this trait to be dyn-compatible or not.
516pub(crate) trait GeoArrowArrayBuilder: Debug + Send + Sync {
517    /// Returns the length of the array.
518    fn len(&self) -> usize;
519
520    /// Returns whether the array is empty.
521    fn is_empty(&self) -> bool {
522        self.len() == 0
523    }
524
525    /// Push a null value to this builder.
526    fn push_null(&mut self);
527
528    /// Push a geometry to this builder.
529    #[allow(dead_code)]
530    fn push_geometry(
531        &mut self,
532        geometry: Option<&impl GeometryTrait<T = f64>>,
533    ) -> GeoArrowResult<()>;
534
535    /// Finish the builder and return an [`Arc`] to the resulting array.
536    #[allow(dead_code)]
537    fn finish(self) -> Arc<dyn GeoArrowArray>;
538}
539
540/// Trait for types that can read `Arc<dyn GeoArrowArray>`'s.
541///
542/// There is no direct parallel to this in the upstream [arrow-array] crate. The closest is
543/// [RecordBatchReader][arrow_array::RecordBatchReader], which has the same implementation over an
544/// iterator of `RecordBatch`es. However, it is useful to have an iterator of GeoArrow arrays with
545/// a known [`GeoArrowType`].
546///
547/// To create from an iterator, see [GeoArrowArrayIterator].
548pub trait GeoArrowArrayReader: Iterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>> {
549    /// Returns the field of this `GeoArrowArrayReader`.
550    ///
551    /// Implementation of this trait should guarantee that all `Arc<dyn GeoArrowArray>`'s returned
552    /// by this reader should have the same [`GeoArrowType`] as returned from this method.
553    fn data_type(&self) -> GeoArrowType;
554}
555
556impl<R: GeoArrowArrayReader + ?Sized> GeoArrowArrayReader for Box<R> {
557    fn data_type(&self) -> GeoArrowType {
558        self.as_ref().data_type()
559    }
560}
561
562/// An iterator of [`Arc<dyn GeoArrowArray>`] with an attached [`GeoArrowType`]
563pub struct GeoArrowArrayIterator<I>
564where
565    I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
566{
567    inner: I::IntoIter,
568    inner_type: GeoArrowType,
569}
570
571impl<I> GeoArrowArrayIterator<I>
572where
573    I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
574{
575    /// Create a new [GeoArrowArrayIterator].
576    ///
577    /// If `iter` is an infallible iterator, use `.map(Ok)`.
578    pub fn new(iter: I, data_type: GeoArrowType) -> Self {
579        Self {
580            inner: iter.into_iter(),
581            inner_type: data_type,
582        }
583    }
584}
585
586impl<I> Iterator for GeoArrowArrayIterator<I>
587where
588    I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
589{
590    type Item = I::Item;
591
592    fn next(&mut self) -> Option<Self::Item> {
593        self.inner.next()
594    }
595
596    fn size_hint(&self) -> (usize, Option<usize>) {
597        self.inner.size_hint()
598    }
599}
600
601impl<I> GeoArrowArrayReader for GeoArrowArrayIterator<I>
602where
603    I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
604{
605    fn data_type(&self) -> GeoArrowType {
606        self.inner_type.clone()
607    }
608}
609
610#[cfg(test)]
611mod test {
612    use std::sync::Arc;
613
614    use arrow_array::Array;
615    use arrow_array::builder::{ArrayBuilder, FixedSizeListBuilder, Float64Builder, StructBuilder};
616    use arrow_schema::{DataType, Field};
617    use geoarrow_schema::{CoordType, Dimension, GeometryType, PointType};
618
619    use super::*;
620    use crate::builder::GeometryBuilder;
621    use crate::trait_::GeoArrowArray;
622
623    #[test]
624    fn infer_type_interleaved_point() {
625        let test_cases = [
626            (2, Dimension::XY),
627            (3, Dimension::XYZ),
628            (4, Dimension::XYZM),
629        ];
630        for (list_size, dim) in test_cases.into_iter() {
631            let array = FixedSizeListBuilder::new(Float64Builder::new(), list_size).finish();
632            let t =
633                GeoArrowType::from_arrow_field(&Field::new("", array.data_type().clone(), true))
634                    .unwrap();
635            assert_eq!(
636                t,
637                GeoArrowType::Point(
638                    PointType::new(dim, Default::default()).with_coord_type(CoordType::Interleaved)
639                )
640            );
641        }
642    }
643
644    #[test]
645    fn infer_type_separated_point() {
646        let test_cases = [
647            (
648                vec![
649                    Arc::new(Field::new("x", DataType::Float64, true)),
650                    Arc::new(Field::new("y", DataType::Float64, true)),
651                ],
652                vec![
653                    Box::new(Float64Builder::new()) as Box<dyn ArrayBuilder>,
654                    Box::new(Float64Builder::new()),
655                ],
656                Dimension::XY,
657            ),
658            (
659                vec![
660                    Arc::new(Field::new("x", DataType::Float64, true)),
661                    Arc::new(Field::new("y", DataType::Float64, true)),
662                    Arc::new(Field::new("z", DataType::Float64, true)),
663                ],
664                vec![
665                    Box::new(Float64Builder::new()) as Box<dyn ArrayBuilder>,
666                    Box::new(Float64Builder::new()),
667                    Box::new(Float64Builder::new()),
668                ],
669                Dimension::XYZ,
670            ),
671            (
672                vec![
673                    Arc::new(Field::new("x", DataType::Float64, true)),
674                    Arc::new(Field::new("y", DataType::Float64, true)),
675                    Arc::new(Field::new("z", DataType::Float64, true)),
676                    Arc::new(Field::new("m", DataType::Float64, true)),
677                ],
678                vec![
679                    Box::new(Float64Builder::new()) as Box<dyn ArrayBuilder>,
680                    Box::new(Float64Builder::new()),
681                    Box::new(Float64Builder::new()),
682                    Box::new(Float64Builder::new()),
683                ],
684                Dimension::XYZM,
685            ),
686        ];
687        for (fields, builders, dim) in test_cases.into_iter() {
688            let array = StructBuilder::new(fields, builders).finish();
689            let t =
690                GeoArrowType::from_arrow_field(&Field::new("", array.data_type().clone(), true))
691                    .unwrap();
692            assert_eq!(
693                t,
694                GeoArrowType::Point(
695                    PointType::new(dim, Default::default()).with_coord_type(CoordType::Separated)
696                )
697            );
698        }
699    }
700
701    #[test]
702    fn native_type_round_trip() {
703        let point_array = crate::test::point::point_array(CoordType::Interleaved);
704        let field = point_array.data_type.to_field("geometry", true);
705        let data_type: GeoArrowType = (&field).try_into().unwrap();
706        assert_eq!(point_array.data_type(), data_type);
707
708        let ml_array = crate::test::multilinestring::ml_array(CoordType::Interleaved);
709        let field = ml_array.data_type.to_field("geometry", true);
710        let data_type: GeoArrowType = (&field).try_into().unwrap();
711        assert_eq!(ml_array.data_type(), data_type);
712
713        let mut builder = GeometryBuilder::new(
714            GeometryType::new(Default::default()).with_coord_type(CoordType::Interleaved),
715        );
716        builder
717            .push_geometry(Some(&crate::test::point::p0()))
718            .unwrap();
719        builder
720            .push_geometry(Some(&crate::test::point::p1()))
721            .unwrap();
722        builder
723            .push_geometry(Some(&crate::test::point::p2()))
724            .unwrap();
725        builder
726            .push_geometry(Some(&crate::test::multilinestring::ml0()))
727            .unwrap();
728        builder
729            .push_geometry(Some(&crate::test::multilinestring::ml1()))
730            .unwrap();
731        let geom_array = builder.finish();
732        let field = geom_array.data_type.to_field("geometry", true);
733        let data_type: GeoArrowType = (&field).try_into().unwrap();
734        assert_eq!(geom_array.data_type(), data_type);
735    }
736}