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/// This is similar to an upstream [RecordBatchReader][arrow_array::RecordBatchReader], but for
543/// GeoArrow arrays instead of RecordBatches.
544///
545/// This will always yield an `Arc<dyn GeoArrowArray>` with the same [`GeoArrowType`], which is
546/// known in advance (see [`Self::data_type`]).
547///
548/// To create from an iterator, see [GeoArrowArrayIterator].
549pub trait GeoArrowArrayReader: Iterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>> {
550 /// Returns the field of this `GeoArrowArrayReader`.
551 ///
552 /// Implementation of this trait should guarantee that all `Arc<dyn GeoArrowArray>`'s returned
553 /// by this reader should have the same [`GeoArrowType`] as returned from this method.
554 fn data_type(&self) -> GeoArrowType;
555}
556
557impl<R: GeoArrowArrayReader + ?Sized> GeoArrowArrayReader for Box<R> {
558 fn data_type(&self) -> GeoArrowType {
559 self.as_ref().data_type()
560 }
561}
562
563/// An iterator of [`Arc<dyn GeoArrowArray>`] with an attached [`GeoArrowType`]
564pub struct GeoArrowArrayIterator<I>
565where
566 I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
567{
568 inner: I::IntoIter,
569 inner_type: GeoArrowType,
570}
571
572impl<I> GeoArrowArrayIterator<I>
573where
574 I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
575{
576 /// Create a new [GeoArrowArrayIterator].
577 ///
578 /// If `iter` is an infallible iterator, use `.map(Ok)`.
579 pub fn new(iter: I, data_type: GeoArrowType) -> Self {
580 Self {
581 inner: iter.into_iter(),
582 inner_type: data_type,
583 }
584 }
585}
586
587impl<I> Iterator for GeoArrowArrayIterator<I>
588where
589 I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
590{
591 type Item = I::Item;
592
593 fn next(&mut self) -> Option<Self::Item> {
594 self.inner.next()
595 }
596
597 fn size_hint(&self) -> (usize, Option<usize>) {
598 self.inner.size_hint()
599 }
600}
601
602impl<I> GeoArrowArrayReader for GeoArrowArrayIterator<I>
603where
604 I: IntoIterator<Item = GeoArrowResult<Arc<dyn GeoArrowArray>>>,
605{
606 fn data_type(&self) -> GeoArrowType {
607 self.inner_type.clone()
608 }
609}
610
611#[cfg(test)]
612mod test {
613 use std::sync::Arc;
614
615 use arrow_array::Array;
616 use arrow_array::builder::{ArrayBuilder, FixedSizeListBuilder, Float64Builder, StructBuilder};
617 use arrow_schema::{DataType, Field};
618 use geoarrow_schema::{CoordType, Dimension, GeometryType, PointType};
619
620 use super::*;
621 use crate::builder::GeometryBuilder;
622 use crate::trait_::GeoArrowArray;
623
624 #[test]
625 fn infer_type_interleaved_point() {
626 let test_cases = [
627 (2, Dimension::XY),
628 (3, Dimension::XYZ),
629 (4, Dimension::XYZM),
630 ];
631 for (list_size, dim) in test_cases.into_iter() {
632 let array = FixedSizeListBuilder::new(Float64Builder::new(), list_size).finish();
633 let t =
634 GeoArrowType::from_arrow_field(&Field::new("", array.data_type().clone(), true))
635 .unwrap();
636 assert_eq!(
637 t,
638 GeoArrowType::Point(
639 PointType::new(dim, Default::default()).with_coord_type(CoordType::Interleaved)
640 )
641 );
642 }
643 }
644
645 #[test]
646 fn infer_type_separated_point() {
647 let test_cases = [
648 (
649 vec![
650 Arc::new(Field::new("x", DataType::Float64, true)),
651 Arc::new(Field::new("y", DataType::Float64, true)),
652 ],
653 vec![
654 Box::new(Float64Builder::new()) as Box<dyn ArrayBuilder>,
655 Box::new(Float64Builder::new()),
656 ],
657 Dimension::XY,
658 ),
659 (
660 vec![
661 Arc::new(Field::new("x", DataType::Float64, true)),
662 Arc::new(Field::new("y", DataType::Float64, true)),
663 Arc::new(Field::new("z", DataType::Float64, true)),
664 ],
665 vec![
666 Box::new(Float64Builder::new()) as Box<dyn ArrayBuilder>,
667 Box::new(Float64Builder::new()),
668 Box::new(Float64Builder::new()),
669 ],
670 Dimension::XYZ,
671 ),
672 (
673 vec![
674 Arc::new(Field::new("x", DataType::Float64, true)),
675 Arc::new(Field::new("y", DataType::Float64, true)),
676 Arc::new(Field::new("z", DataType::Float64, true)),
677 Arc::new(Field::new("m", DataType::Float64, true)),
678 ],
679 vec![
680 Box::new(Float64Builder::new()) as Box<dyn ArrayBuilder>,
681 Box::new(Float64Builder::new()),
682 Box::new(Float64Builder::new()),
683 Box::new(Float64Builder::new()),
684 ],
685 Dimension::XYZM,
686 ),
687 ];
688 for (fields, builders, dim) in test_cases.into_iter() {
689 let array = StructBuilder::new(fields, builders).finish();
690 let t =
691 GeoArrowType::from_arrow_field(&Field::new("", array.data_type().clone(), true))
692 .unwrap();
693 assert_eq!(
694 t,
695 GeoArrowType::Point(
696 PointType::new(dim, Default::default()).with_coord_type(CoordType::Separated)
697 )
698 );
699 }
700 }
701
702 #[test]
703 fn native_type_round_trip() {
704 let point_array = crate::test::point::point_array(CoordType::Interleaved);
705 let field = point_array.data_type.to_field("geometry", true);
706 let data_type: GeoArrowType = (&field).try_into().unwrap();
707 assert_eq!(point_array.data_type(), data_type);
708
709 let ml_array = crate::test::multilinestring::ml_array(CoordType::Interleaved);
710 let field = ml_array.data_type.to_field("geometry", true);
711 let data_type: GeoArrowType = (&field).try_into().unwrap();
712 assert_eq!(ml_array.data_type(), data_type);
713
714 let mut builder = GeometryBuilder::new(
715 GeometryType::new(Default::default()).with_coord_type(CoordType::Interleaved),
716 );
717 builder
718 .push_geometry(Some(&crate::test::point::p0()))
719 .unwrap();
720 builder
721 .push_geometry(Some(&crate::test::point::p1()))
722 .unwrap();
723 builder
724 .push_geometry(Some(&crate::test::point::p2()))
725 .unwrap();
726 builder
727 .push_geometry(Some(&crate::test::multilinestring::ml0()))
728 .unwrap();
729 builder
730 .push_geometry(Some(&crate::test::multilinestring::ml1()))
731 .unwrap();
732 let geom_array = builder.finish();
733 let field = geom_array.data_type.to_field("geometry", true);
734 let data_type: GeoArrowType = (&field).try_into().unwrap();
735 assert_eq!(geom_array.data_type(), data_type);
736 }
737}