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}