geoarrow_array/
cast.rs

1//! Helper functions for downcasting [`dyn GeoArrowArray`][GeoArrowArray] to concrete types and for
2//! converting between GeoArrow array representations.
3
4use std::sync::Arc;
5
6use arrow_array::OffsetSizeTrait;
7use arrow_array::builder::{
8    BinaryViewBuilder, GenericByteBuilder, GenericStringBuilder, StringViewBuilder,
9};
10use arrow_array::cast::AsArray;
11use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
12use geoarrow_schema::{GeoArrowType, WkbType};
13use wkb::Endianness;
14use wkb::writer::WriteOptions;
15
16use crate::array::*;
17use crate::builder::{
18    GeometryBuilder, GeometryCollectionBuilder, LineStringBuilder, MultiLineStringBuilder,
19    MultiPointBuilder, MultiPolygonBuilder, PointBuilder, PolygonBuilder, WkbBuilder,
20};
21use crate::trait_::GeoArrowArray;
22use crate::{GeoArrowArrayAccessor, IntoArrow};
23
24/// Helpers for downcasting a [`GeoArrowArray`] to a concrete implementation.
25///
26/// ```
27/// use std::sync::Arc;
28/// use arrow_array::{Int32Array, RecordBatch};
29/// use arrow_schema::{Schema, Field, DataType, ArrowError};
30/// use geo_types::point;
31///
32/// use geoarrow_array::array::PointArray;
33/// use geoarrow_array::builder::PointBuilder;
34/// use geoarrow_array::cast::AsGeoArrowArray;
35/// use geoarrow_array::GeoArrowArray;
36/// use geo_traits::CoordTrait;
37/// use geoarrow_schema::{Dimension, PointType};
38///
39/// let point1 = point!(x: 1., y: 2.);
40/// let point2 = point!(x: 3., y: 4.);
41/// let point3 = point!(x: 5., y: 6.);
42/// let geoms = [point1, point2, point3];
43///
44/// let geom_type = PointType::new(Dimension::XY, Default::default());
45/// let point_array = PointBuilder::from_points(geoms.iter(), geom_type).finish();
46///
47/// let generic_array: Arc<dyn GeoArrowArray> = Arc::new(point_array.clone());
48///
49/// let point_array2 = generic_array.as_point();
50/// assert_eq!(&point_array, point_array2);
51/// ```
52pub trait AsGeoArrowArray {
53    /// Downcast this to a [`PointArray`] returning `None` if not possible
54    fn as_point_opt(&self) -> Option<&PointArray>;
55
56    /// Downcast this to a [`PointArray`] panicking if not possible
57    #[inline]
58    fn as_point(&self) -> &PointArray {
59        self.as_point_opt().unwrap()
60    }
61
62    /// Downcast this to a [`LineStringArray`] with `i32` offsets returning `None` if not possible
63    fn as_line_string_opt(&self) -> Option<&LineStringArray>;
64
65    /// Downcast this to a [`LineStringArray`] with `i32` offsets panicking if not possible
66    #[inline]
67    fn as_line_string(&self) -> &LineStringArray {
68        self.as_line_string_opt().unwrap()
69    }
70
71    /// Downcast this to a [`PolygonArray`] with `i32` offsets returning `None` if not possible
72    fn as_polygon_opt(&self) -> Option<&PolygonArray>;
73
74    /// Downcast this to a [`PolygonArray`] with `i32` offsets panicking if not possible
75    #[inline]
76    fn as_polygon(&self) -> &PolygonArray {
77        self.as_polygon_opt().unwrap()
78    }
79
80    /// Downcast this to a [`MultiPointArray`] with `i32` offsets returning `None` if not possible
81    fn as_multi_point_opt(&self) -> Option<&MultiPointArray>;
82
83    /// Downcast this to a [`MultiPointArray`] with `i32` offsets panicking if not possible
84    #[inline]
85    fn as_multi_point(&self) -> &MultiPointArray {
86        self.as_multi_point_opt().unwrap()
87    }
88
89    /// Downcast this to a [`MultiLineStringArray`] with `i32` offsets returning `None` if not
90    /// possible
91    fn as_multi_line_string_opt(&self) -> Option<&MultiLineStringArray>;
92
93    /// Downcast this to a [`MultiLineStringArray`] with `i32` offsets panicking if not possible
94    #[inline]
95    fn as_multi_line_string(&self) -> &MultiLineStringArray {
96        self.as_multi_line_string_opt().unwrap()
97    }
98
99    /// Downcast this to a [`MultiPolygonArray`] with `i32` offsets returning `None` if not
100    /// possible
101    fn as_multi_polygon_opt(&self) -> Option<&MultiPolygonArray>;
102
103    /// Downcast this to a [`MultiPolygonArray`] with `i32` offsets panicking if not possible
104    #[inline]
105    fn as_multi_polygon(&self) -> &MultiPolygonArray {
106        self.as_multi_polygon_opt().unwrap()
107    }
108
109    /// Downcast this to a [`GeometryCollectionArray`] with `i32` offsets returning `None` if not
110    /// possible
111    fn as_geometry_collection_opt(&self) -> Option<&GeometryCollectionArray>;
112
113    /// Downcast this to a [`GeometryCollectionArray`] with `i32` offsets panicking if not possible
114    #[inline]
115    fn as_geometry_collection(&self) -> &GeometryCollectionArray {
116        self.as_geometry_collection_opt().unwrap()
117    }
118
119    /// Downcast this to a [`RectArray`] returning `None` if not possible
120    fn as_rect_opt(&self) -> Option<&RectArray>;
121
122    /// Downcast this to a [`RectArray`] panicking if not possible
123    #[inline]
124    fn as_rect(&self) -> &RectArray {
125        self.as_rect_opt().unwrap()
126    }
127
128    /// Downcast this to a [`GeometryArray`] returning `None` if not possible
129    fn as_geometry_opt(&self) -> Option<&GeometryArray>;
130
131    /// Downcast this to a [`GeometryArray`] panicking if not possible
132    #[inline]
133    fn as_geometry(&self) -> &GeometryArray {
134        self.as_geometry_opt().unwrap()
135    }
136
137    /// Downcast this to a [`GenericWkbArray`] with `O` offsets returning `None` if not possible
138    fn as_wkb_opt<O: OffsetSizeTrait>(&self) -> Option<&GenericWkbArray<O>>;
139
140    /// Downcast this to a [`GenericWkbArray`] with `O` offsets panicking if not possible
141    #[inline]
142    fn as_wkb<O: OffsetSizeTrait>(&self) -> &GenericWkbArray<O> {
143        self.as_wkb_opt::<O>().unwrap()
144    }
145
146    /// Downcast this to a [`WkbViewArray`] returning `None` if not possible
147    fn as_wkb_view_opt(&self) -> Option<&WkbViewArray>;
148
149    /// Downcast this to a [`WkbViewArray`] panicking if not possible
150    #[inline]
151    fn as_wkb_view(&self) -> &WkbViewArray {
152        self.as_wkb_view_opt().unwrap()
153    }
154
155    /// Downcast this to a [`GenericWktArray`] with `O` offsets returning `None` if not possible
156    fn as_wkt_opt<O: OffsetSizeTrait>(&self) -> Option<&GenericWktArray<O>>;
157
158    /// Downcast this to a [`GenericWktArray`] with `O` offsets panicking if not possible
159    #[inline]
160    fn as_wkt<O: OffsetSizeTrait>(&self) -> &GenericWktArray<O> {
161        self.as_wkt_opt::<O>().unwrap()
162    }
163
164    /// Downcast this to a [`WktViewArray`] returning `None` if not possible
165    fn as_wkt_view_opt(&self) -> Option<&WktViewArray>;
166
167    /// Downcast this to a [`WktViewArray`] panicking if not possible
168    #[inline]
169    fn as_wkt_view(&self) -> &WktViewArray {
170        self.as_wkt_view_opt().unwrap()
171    }
172}
173
174// `dyn GeoArrowArray + '_` is the same as upstream Arrow
175impl AsGeoArrowArray for dyn GeoArrowArray + '_ {
176    #[inline]
177    fn as_point_opt(&self) -> Option<&PointArray> {
178        self.as_any().downcast_ref::<PointArray>()
179    }
180
181    #[inline]
182    fn as_line_string_opt(&self) -> Option<&LineStringArray> {
183        self.as_any().downcast_ref::<LineStringArray>()
184    }
185
186    #[inline]
187    fn as_polygon_opt(&self) -> Option<&PolygonArray> {
188        self.as_any().downcast_ref::<PolygonArray>()
189    }
190
191    #[inline]
192    fn as_multi_point_opt(&self) -> Option<&MultiPointArray> {
193        self.as_any().downcast_ref::<MultiPointArray>()
194    }
195
196    #[inline]
197    fn as_multi_line_string_opt(&self) -> Option<&MultiLineStringArray> {
198        self.as_any().downcast_ref::<MultiLineStringArray>()
199    }
200
201    #[inline]
202    fn as_multi_polygon_opt(&self) -> Option<&MultiPolygonArray> {
203        self.as_any().downcast_ref::<MultiPolygonArray>()
204    }
205
206    #[inline]
207    fn as_geometry_collection_opt(&self) -> Option<&GeometryCollectionArray> {
208        self.as_any().downcast_ref::<GeometryCollectionArray>()
209    }
210
211    #[inline]
212    fn as_rect_opt(&self) -> Option<&RectArray> {
213        self.as_any().downcast_ref::<RectArray>()
214    }
215
216    #[inline]
217    fn as_geometry_opt(&self) -> Option<&GeometryArray> {
218        self.as_any().downcast_ref::<GeometryArray>()
219    }
220
221    #[inline]
222    fn as_wkb_opt<O: OffsetSizeTrait>(&self) -> Option<&GenericWkbArray<O>> {
223        self.as_any().downcast_ref::<GenericWkbArray<O>>()
224    }
225
226    #[inline]
227    fn as_wkb_view_opt(&self) -> Option<&WkbViewArray> {
228        self.as_any().downcast_ref::<WkbViewArray>()
229    }
230
231    #[inline]
232    fn as_wkt_opt<O: OffsetSizeTrait>(&self) -> Option<&GenericWktArray<O>> {
233        self.as_any().downcast_ref::<GenericWktArray<O>>()
234    }
235
236    #[inline]
237    fn as_wkt_view_opt(&self) -> Option<&WktViewArray> {
238        self.as_any().downcast_ref::<WktViewArray>()
239    }
240}
241
242impl AsGeoArrowArray for Arc<dyn GeoArrowArray> {
243    #[inline]
244    fn as_point_opt(&self) -> Option<&PointArray> {
245        self.as_any().downcast_ref::<PointArray>()
246    }
247
248    #[inline]
249    fn as_line_string_opt(&self) -> Option<&LineStringArray> {
250        self.as_any().downcast_ref::<LineStringArray>()
251    }
252
253    #[inline]
254    fn as_polygon_opt(&self) -> Option<&PolygonArray> {
255        self.as_any().downcast_ref::<PolygonArray>()
256    }
257
258    #[inline]
259    fn as_multi_point_opt(&self) -> Option<&MultiPointArray> {
260        self.as_any().downcast_ref::<MultiPointArray>()
261    }
262
263    #[inline]
264    fn as_multi_line_string_opt(&self) -> Option<&MultiLineStringArray> {
265        self.as_any().downcast_ref::<MultiLineStringArray>()
266    }
267
268    #[inline]
269    fn as_multi_polygon_opt(&self) -> Option<&MultiPolygonArray> {
270        self.as_any().downcast_ref::<MultiPolygonArray>()
271    }
272
273    #[inline]
274    fn as_geometry_collection_opt(&self) -> Option<&GeometryCollectionArray> {
275        self.as_any().downcast_ref::<GeometryCollectionArray>()
276    }
277
278    #[inline]
279    fn as_rect_opt(&self) -> Option<&RectArray> {
280        self.as_any().downcast_ref::<RectArray>()
281    }
282
283    #[inline]
284    fn as_geometry_opt(&self) -> Option<&GeometryArray> {
285        self.as_any().downcast_ref::<GeometryArray>()
286    }
287
288    #[inline]
289    fn as_wkb_opt<O: OffsetSizeTrait>(&self) -> Option<&GenericWkbArray<O>> {
290        self.as_any().downcast_ref::<GenericWkbArray<O>>()
291    }
292
293    #[inline]
294    fn as_wkb_view_opt(&self) -> Option<&WkbViewArray> {
295        self.as_any().downcast_ref::<WkbViewArray>()
296    }
297
298    #[inline]
299    fn as_wkt_opt<O: OffsetSizeTrait>(&self) -> Option<&GenericWktArray<O>> {
300        self.as_any().downcast_ref::<GenericWktArray<O>>()
301    }
302
303    #[inline]
304    fn as_wkt_view_opt(&self) -> Option<&WktViewArray> {
305        self.as_any().downcast_ref::<WktViewArray>()
306    }
307}
308
309/// Convert a [GeoArrowArray] to a [`GenericWkbArray`].
310pub fn to_wkb<O: OffsetSizeTrait>(arr: &dyn GeoArrowArray) -> GeoArrowResult<GenericWkbArray<O>> {
311    use GeoArrowType::*;
312    match arr.data_type() {
313        Point(_) => impl_to_wkb(arr.as_point()),
314        LineString(_) => impl_to_wkb(arr.as_line_string()),
315        Polygon(_) => impl_to_wkb(arr.as_polygon()),
316        MultiPoint(_) => impl_to_wkb(arr.as_multi_point()),
317        MultiLineString(_) => impl_to_wkb(arr.as_multi_line_string()),
318        MultiPolygon(_) => impl_to_wkb(arr.as_multi_polygon()),
319        Geometry(_) => impl_to_wkb(arr.as_geometry()),
320        GeometryCollection(_) => impl_to_wkb(arr.as_geometry_collection()),
321        Rect(_) => impl_to_wkb(arr.as_rect()),
322        Wkb(typ) => {
323            // Note that here O is the _target_ offset type
324            if O::IS_LARGE {
325                // We need to convert from i32 to i64
326                let large_arr: GenericWkbArray<i64> = arr.as_wkb::<i32>().clone().into();
327                let array = large_arr.to_array_ref().as_binary::<O>().clone();
328                Ok(GenericWkbArray::new(array, typ.metadata().clone()))
329            } else {
330                // Since O is already i32, we can just go via ArrayRef, and use .as_binary to cast
331                // to O
332                let array = arr.as_wkb::<i32>().to_array_ref();
333                let array = array.as_binary::<O>().clone();
334                Ok(GenericWkbArray::new(array, typ.metadata().clone()))
335            }
336        }
337        LargeWkb(typ) => {
338            if O::IS_LARGE {
339                // Since O is already i64, we can just go via ArrayRef, and use .as_binary to cast
340                // to O
341                let array = arr.as_wkb::<i64>().to_array_ref();
342                let array = array.as_binary::<O>().clone();
343                Ok(GenericWkbArray::new(array, typ.metadata().clone()))
344            } else {
345                // We need to convert from i64 to i32
346                let small_arr: GenericWkbArray<i32> = arr.as_wkb::<i64>().clone().try_into()?;
347                let array = small_arr.to_array_ref().as_binary::<O>().clone();
348                Ok(GenericWkbArray::new(array, typ.metadata().clone()))
349            }
350        }
351        WkbView(_) => {
352            let wkb_view_arr = arr.as_wkb_view();
353            let metadata = wkb_view_arr.data_type().metadata().clone();
354            let array = wkb_view_arr.clone().into_arrow();
355
356            let mut builder = GenericByteBuilder::with_capacity(arr.len(), 0);
357            array.iter().for_each(|value| builder.append_option(value));
358            Ok(GenericWkbArray::new(builder.finish(), metadata))
359        }
360        Wkt(_) => impl_to_wkb(arr.as_wkt::<i32>()),
361        LargeWkt(_) => impl_to_wkb(arr.as_wkt::<i64>()),
362        WktView(_) => impl_to_wkb(arr.as_wkt_view()),
363    }
364}
365
366fn impl_to_wkb<'a, O: OffsetSizeTrait>(
367    geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
368) -> GeoArrowResult<GenericWkbArray<O>> {
369    let geoms = geo_arr
370        .iter()
371        .map(|x| x.transpose())
372        .collect::<GeoArrowResult<Vec<_>>>()?;
373    let wkb_type = WkbType::new(geo_arr.data_type().metadata().clone());
374    Ok(WkbBuilder::from_nullable_geometries(geoms.as_slice(), wkb_type)?.finish())
375}
376
377/// Convert a [GeoArrowArray] to a [`WkbViewArray`].
378pub fn to_wkb_view(arr: &dyn GeoArrowArray) -> GeoArrowResult<WkbViewArray> {
379    use GeoArrowType::*;
380    match arr.data_type() {
381        Point(_) => impl_to_wkb_view(arr.as_point()),
382        LineString(_) => impl_to_wkb_view(arr.as_line_string()),
383        Polygon(_) => impl_to_wkb_view(arr.as_polygon()),
384        MultiPoint(_) => impl_to_wkb_view(arr.as_multi_point()),
385        MultiLineString(_) => impl_to_wkb_view(arr.as_multi_line_string()),
386        MultiPolygon(_) => impl_to_wkb_view(arr.as_multi_polygon()),
387        Geometry(_) => impl_to_wkb_view(arr.as_geometry()),
388        GeometryCollection(_) => impl_to_wkb_view(arr.as_geometry_collection()),
389        Rect(_) => impl_to_wkb_view(arr.as_rect()),
390        Wkb(_) => wkb_array_to_wkb_view(arr.as_wkb::<i32>()),
391        LargeWkb(_) => wkb_array_to_wkb_view(arr.as_wkb::<i64>()),
392        WkbView(_) => Ok(arr.as_wkb_view().clone()),
393        Wkt(_) => impl_to_wkb_view(arr.as_wkt::<i32>()),
394        LargeWkt(_) => impl_to_wkb_view(arr.as_wkt::<i64>()),
395        WktView(_) => impl_to_wkb_view(arr.as_wkt_view()),
396    }
397}
398
399/// Convert an arbitrary GeoArrowArray to a WkbViewArray.
400///
401/// This function will parse each geometry and re-encode it as WKB.
402fn impl_to_wkb_view<'a>(
403    geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
404) -> GeoArrowResult<WkbViewArray> {
405    let geoms = geo_arr
406        .iter()
407        .map(|x| x.transpose())
408        .collect::<GeoArrowResult<Vec<_>>>()?;
409
410    let mut builder = BinaryViewBuilder::with_capacity(geo_arr.len());
411    let wkb_options = WriteOptions {
412        endianness: Endianness::LittleEndian,
413    };
414    for maybe_geom in geoms {
415        if let Some(geom) = maybe_geom {
416            let mut buf = Vec::new();
417            wkb::writer::write_geometry(&mut buf, &geom, &wkb_options).unwrap();
418            builder.append_value(buf);
419        } else {
420            builder.append_null();
421        }
422    }
423
424    let binary_view_arr = builder.finish();
425    Ok(WkbViewArray::new(
426        binary_view_arr,
427        geo_arr.data_type().metadata().clone(),
428    ))
429}
430
431/// A fast path of converting to WkbViewArray that does not parse and re-encode WKB buffers
432fn wkb_array_to_wkb_view<O: OffsetSizeTrait>(
433    arr: &GenericWkbArray<O>,
434) -> GeoArrowResult<WkbViewArray> {
435    let metadata = arr.data_type().metadata().clone();
436    let mut builder = BinaryViewBuilder::with_capacity(arr.len());
437
438    for value in arr.inner().iter() {
439        if let Some(bytes) = value {
440            builder.append_value(bytes);
441        } else {
442            builder.append_null();
443        }
444    }
445
446    Ok(WkbViewArray::new(builder.finish(), metadata))
447}
448
449/// Parse a [`GenericWkbArray`] or [`WkbViewArray`] to a [`GeoArrowArray`] with the designated
450/// [`GeoArrowType`].
451///
452/// Note that the GeoArrow metadata on the new array is taken from `to_type` **not** the original
453/// array. Ensure you construct the [GeoArrowType] with the correct metadata.
454///
455/// Note that this will be slow if converting from a WKB array to another WKB-typed array. If
456/// possible, use the `From` impls on WKB-typed arrays.
457pub fn from_wkb<'a, A: GenericWkbArrayType<'a>>(
458    arr: &'a A,
459    to_type: GeoArrowType,
460) -> GeoArrowResult<Arc<dyn GeoArrowArray>> {
461    let geoms = arr
462        .iter()
463        .map(|g| g.transpose())
464        .collect::<GeoArrowResult<Vec<_>>>()?;
465
466    use GeoArrowType::*;
467    let result: Arc<dyn GeoArrowArray> = match to_type {
468        Point(typ) => Arc::new(PointBuilder::from_nullable_geometries(&geoms, typ)?.finish()),
469        LineString(typ) => {
470            Arc::new(LineStringBuilder::from_nullable_geometries(&geoms, typ)?.finish())
471        }
472        Polygon(typ) => Arc::new(PolygonBuilder::from_nullable_geometries(&geoms, typ)?.finish()),
473        MultiPoint(typ) => {
474            Arc::new(MultiPointBuilder::from_nullable_geometries(&geoms, typ)?.finish())
475        }
476        MultiLineString(typ) => {
477            Arc::new(MultiLineStringBuilder::from_nullable_geometries(&geoms, typ)?.finish())
478        }
479        MultiPolygon(typ) => {
480            Arc::new(MultiPolygonBuilder::from_nullable_geometries(&geoms, typ)?.finish())
481        }
482        GeometryCollection(typ) => {
483            Arc::new(GeometryCollectionBuilder::from_nullable_geometries(&geoms, typ)?.finish())
484        }
485        Rect(_) => {
486            return Err(GeoArrowError::IncorrectGeometryType(format!(
487                "Cannot decode WKB geometries to Rect geometry type in from_wkb {to_type:?}",
488            )));
489        }
490        Geometry(typ) => Arc::new(GeometryBuilder::from_nullable_geometries(&geoms, typ)?.finish()),
491        Wkb(typ) => {
492            let mut wkb_arr = to_wkb::<i32>(arr)?;
493            wkb_arr.data_type = typ;
494            Arc::new(wkb_arr)
495        }
496        LargeWkb(typ) => {
497            let mut wkb_arr = to_wkb::<i64>(arr)?;
498            wkb_arr.data_type = typ;
499            Arc::new(wkb_arr)
500        }
501        WkbView(typ) => {
502            let mut wkb_view_arr = to_wkb_view(arr)?;
503            wkb_view_arr.data_type = typ;
504            Arc::new(wkb_view_arr)
505        }
506        Wkt(typ) => {
507            let mut wkt_arr = to_wkt::<i32>(arr)?;
508            wkt_arr.data_type = typ;
509            Arc::new(wkt_arr)
510        }
511        LargeWkt(typ) => {
512            let mut wkt_arr = to_wkt::<i64>(arr)?;
513            wkt_arr.data_type = typ;
514            Arc::new(wkt_arr)
515        }
516        WktView(typ) => {
517            let mut wkt_view_arr = to_wkt_view(arr)?;
518            wkt_view_arr.data_type = typ;
519            Arc::new(wkt_view_arr)
520        }
521    };
522    Ok(result)
523}
524
525/// Convert a [GeoArrowArray] to a [`GenericWktArray`].
526pub fn to_wkt<O: OffsetSizeTrait>(arr: &dyn GeoArrowArray) -> GeoArrowResult<GenericWktArray<O>> {
527    use GeoArrowType::*;
528    match arr.data_type() {
529        Point(_) => impl_to_wkt(arr.as_point()),
530        LineString(_) => impl_to_wkt(arr.as_line_string()),
531        Polygon(_) => impl_to_wkt(arr.as_polygon()),
532        MultiPoint(_) => impl_to_wkt(arr.as_multi_point()),
533        MultiLineString(_) => impl_to_wkt(arr.as_multi_line_string()),
534        MultiPolygon(_) => impl_to_wkt(arr.as_multi_polygon()),
535        Geometry(_) => impl_to_wkt(arr.as_geometry()),
536        GeometryCollection(_) => impl_to_wkt(arr.as_geometry_collection()),
537        Rect(_) => impl_to_wkt(arr.as_rect()),
538        Wkb(_) => impl_to_wkt(arr.as_wkb::<i32>()),
539        LargeWkb(_) => impl_to_wkt(arr.as_wkb::<i64>()),
540        WkbView(_) => impl_to_wkt(arr.as_wkb_view()),
541        Wkt(typ) => {
542            if O::IS_LARGE {
543                let large_arr: GenericWktArray<i64> = arr.as_wkt::<i32>().clone().into();
544                let array = large_arr.to_array_ref().as_string::<O>().clone();
545                Ok(GenericWktArray::new(array, typ.metadata().clone()))
546            } else {
547                // Since O is already i32, we can just go via ArrayRef, and use .as_string to cast
548                // to O
549                let array = arr.as_wkt::<i32>().to_array_ref();
550                let array = array.as_string::<O>().clone();
551                Ok(GenericWktArray::new(array, typ.metadata().clone()))
552            }
553        }
554        LargeWkt(typ) => {
555            if O::IS_LARGE {
556                // Since O is already i64, we can just go via ArrayRef, and use .as_string to cast
557                // to O
558                let array = arr.as_wkt::<i64>().to_array_ref();
559                let array = array.as_string::<O>().clone();
560                Ok(GenericWktArray::new(array, typ.metadata().clone()))
561            } else {
562                let small_arr: GenericWktArray<i32> = arr.as_wkt::<i64>().clone().try_into()?;
563                let array = small_arr.to_array_ref().as_string::<O>().clone();
564                Ok(GenericWktArray::new(array, typ.metadata().clone()))
565            }
566        }
567        WktView(_) => {
568            let wkt_view_arr = arr.as_wkt_view();
569            let metadata = wkt_view_arr.data_type().metadata().clone();
570            let array = wkt_view_arr.clone().into_arrow();
571
572            let mut builder = GenericStringBuilder::with_capacity(arr.len(), 0);
573            array.iter().for_each(|value| builder.append_option(value));
574            Ok(GenericWktArray::new(builder.finish(), metadata))
575        }
576    }
577}
578
579fn impl_to_wkt<'a, O: OffsetSizeTrait>(
580    geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
581) -> GeoArrowResult<GenericWktArray<O>> {
582    let metadata = geo_arr.data_type().metadata().clone();
583    let mut builder = GenericStringBuilder::with_capacity(geo_arr.len(), 0);
584
585    for maybe_geom in geo_arr.iter() {
586        if let Some(geom) = maybe_geom {
587            wkt::to_wkt::write_geometry(&mut builder, &geom?)
588                .map_err(|err| GeoArrowError::External(Box::new(err)))?;
589            builder.append_value("");
590        } else {
591            builder.append_null();
592        }
593    }
594
595    Ok(GenericWktArray::new(builder.finish(), metadata))
596}
597
598/// Convert a [GeoArrowArray] to a [`WktViewArray`].
599pub fn to_wkt_view(arr: &dyn GeoArrowArray) -> GeoArrowResult<WktViewArray> {
600    use GeoArrowType::*;
601    match arr.data_type() {
602        Point(_) => impl_to_wkt_view(arr.as_point()),
603        LineString(_) => impl_to_wkt_view(arr.as_line_string()),
604        Polygon(_) => impl_to_wkt_view(arr.as_polygon()),
605        MultiPoint(_) => impl_to_wkt_view(arr.as_multi_point()),
606        MultiLineString(_) => impl_to_wkt_view(arr.as_multi_line_string()),
607        MultiPolygon(_) => impl_to_wkt_view(arr.as_multi_polygon()),
608        Geometry(_) => impl_to_wkt_view(arr.as_geometry()),
609        GeometryCollection(_) => impl_to_wkt_view(arr.as_geometry_collection()),
610        Rect(_) => impl_to_wkt_view(arr.as_rect()),
611        Wkb(_) => impl_to_wkt_view(arr.as_wkb::<i32>()),
612        LargeWkb(_) => impl_to_wkt_view(arr.as_wkb::<i64>()),
613        WkbView(_) => impl_to_wkt_view(arr.as_wkb_view()),
614        Wkt(_) => wkt_array_to_wkt_view(arr.as_wkt::<i32>()),
615        LargeWkt(_) => wkt_array_to_wkt_view(arr.as_wkt::<i64>()),
616        WktView(_) => Ok(arr.as_wkt_view().clone()),
617    }
618}
619
620/// Convert an arbitrary GeoArrowArray to a WktViewArray.
621///
622/// This function will parse each geometry and re-encode it as WKT.
623fn impl_to_wkt_view<'a>(
624    geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
625) -> GeoArrowResult<WktViewArray> {
626    let metadata = geo_arr.data_type().metadata().clone();
627    let mut builder = StringViewBuilder::with_capacity(geo_arr.len());
628
629    for maybe_geom in geo_arr.iter() {
630        if let Some(geom) = maybe_geom {
631            let mut s = String::new();
632            wkt::to_wkt::write_geometry(&mut s, &geom?)
633                .map_err(|err| GeoArrowError::External(Box::new(err)))?;
634            builder.append_value(s);
635        } else {
636            builder.append_null();
637        }
638    }
639
640    Ok(WktViewArray::new(builder.finish(), metadata))
641}
642
643/// A fast path of converting to WktViewArray that does not parse and re-encode WKT buffers
644fn wkt_array_to_wkt_view<O: OffsetSizeTrait>(
645    arr: &GenericWktArray<O>,
646) -> GeoArrowResult<WktViewArray> {
647    let metadata = arr.data_type().metadata().clone();
648    let mut builder = StringViewBuilder::with_capacity(arr.len());
649
650    for value in arr.inner().iter() {
651        if let Some(s) = value {
652            builder.append_value(s);
653        } else {
654            builder.append_null();
655        }
656    }
657
658    Ok(WktViewArray::new(builder.finish(), metadata))
659}
660
661/// Parse a [`GenericWktArray`] or [`WktViewArray`] to a [`GeoArrowArray`] with the designated
662/// [`GeoArrowType`].
663///
664/// Note that the GeoArrow metadata on the new array is taken from `to_type` **not** the original
665/// array. Ensure you construct the [GeoArrowType] with the correct metadata.
666pub fn from_wkt<A: GenericWktArrayType>(
667    arr: &A,
668    to_type: GeoArrowType,
669) -> GeoArrowResult<Arc<dyn GeoArrowArray>> {
670    let geoms = arr
671        .iter()
672        .map(|g| g.transpose())
673        .collect::<GeoArrowResult<Vec<_>>>()?;
674
675    use GeoArrowType::*;
676    let result: Arc<dyn GeoArrowArray> = match to_type {
677        Point(typ) => Arc::new(PointBuilder::from_nullable_geometries(&geoms, typ)?.finish()),
678        LineString(typ) => {
679            Arc::new(LineStringBuilder::from_nullable_geometries(&geoms, typ)?.finish())
680        }
681        Polygon(typ) => Arc::new(PolygonBuilder::from_nullable_geometries(&geoms, typ)?.finish()),
682        MultiPoint(typ) => {
683            Arc::new(MultiPointBuilder::from_nullable_geometries(&geoms, typ)?.finish())
684        }
685        MultiLineString(typ) => {
686            Arc::new(MultiLineStringBuilder::from_nullable_geometries(&geoms, typ)?.finish())
687        }
688        MultiPolygon(typ) => {
689            Arc::new(MultiPolygonBuilder::from_nullable_geometries(&geoms, typ)?.finish())
690        }
691        GeometryCollection(typ) => {
692            Arc::new(GeometryCollectionBuilder::from_nullable_geometries(&geoms, typ)?.finish())
693        }
694        Rect(_) => {
695            return Err(GeoArrowError::IncorrectGeometryType(format!(
696                "Cannot decode WKT geometries to Rect geometry type in from_wkt {to_type:?}",
697            )));
698        }
699        Geometry(typ) => Arc::new(GeometryBuilder::from_nullable_geometries(&geoms, typ)?.finish()),
700        Wkb(typ) => {
701            let mut wkb_arr = to_wkb::<i32>(arr)?;
702            wkb_arr.data_type = typ;
703            Arc::new(wkb_arr)
704        }
705        LargeWkb(typ) => {
706            let mut wkb_arr = to_wkb::<i64>(arr)?;
707            wkb_arr.data_type = typ;
708            Arc::new(wkb_arr)
709        }
710        WkbView(typ) => {
711            let mut wkb_view_arr = to_wkb_view(arr)?;
712            wkb_view_arr.data_type = typ;
713            Arc::new(wkb_view_arr)
714        }
715        Wkt(typ) => {
716            let mut wkt_arr = to_wkt::<i32>(arr)?;
717            wkt_arr.data_type = typ;
718            Arc::new(wkt_arr)
719        }
720        LargeWkt(typ) => {
721            let mut wkt_arr = to_wkt::<i64>(arr)?;
722            wkt_arr.data_type = typ;
723            Arc::new(wkt_arr)
724        }
725        WktView(typ) => {
726            let mut wkt_view_arr = to_wkt_view(arr)?;
727            wkt_view_arr.data_type = typ;
728            Arc::new(wkt_view_arr)
729        }
730    };
731    Ok(result)
732}
733
734/// Re-export symbols needed for downcast macros
735///
736/// Name follows `serde` convention
737#[doc(hidden)]
738pub mod __private {
739    pub use geoarrow_schema::GeoArrowType;
740}
741
742/// Downcast a [GeoArrowArray] to a concrete-typed array based on its [`GeoArrowType`].
743///
744/// For example: computing unsigned area:
745///
746/// ```
747/// use arrow_array::Float64Array;
748/// use arrow_array::builder::Float64Builder;
749/// use geo::Area;
750/// use geo_traits::to_geo::ToGeoGeometry;
751/// use geoarrow_schema::error::GeoArrowResult;
752/// use geoarrow_array::{GeoArrowArrayAccessor, GeoArrowArray, downcast_geoarrow_array};
753///
754/// pub fn unsigned_area(array: &dyn GeoArrowArray) -> GeoArrowResult<Float64Array> {
755///     downcast_geoarrow_array!(array, impl_unsigned_area)
756/// }
757///
758/// fn impl_unsigned_area<'a>(array: &'a impl GeoArrowArrayAccessor<'a>) -> GeoArrowResult<Float64Array> {
759///     let mut builder = Float64Builder::with_capacity(array.len());
760///
761///     for item in array.iter() {
762///         if let Some(geom) = item {
763///             builder.append_value(geom?.to_geometry().unsigned_area());
764///         } else {
765///             builder.append_null();
766///         }
767///     }
768///
769///     Ok(builder.finish())
770/// }
771/// ```
772///
773/// You can also override the behavior of specific data types to specialize or provide a fast path.
774/// For example, we know that points and lines will always have an area of 0, and don't need to
775/// iterate over the input values to compute that.
776///
777/// ```
778/// # use arrow_array::Float64Array;
779/// # use arrow_array::builder::Float64Builder;
780/// # use geo::Area;
781/// # use geo_traits::to_geo::ToGeoGeometry;
782/// # use geoarrow_schema::error::GeoArrowResult;
783/// # use geoarrow_schema::GeoArrowType;
784/// # use geoarrow_array::GeoArrowArrayAccessor;
785/// #
786/// # fn impl_unsigned_area<'a>(array: &'a impl GeoArrowArrayAccessor<'a>) -> GeoArrowResult<Float64Array> {
787/// #     let mut builder = Float64Builder::with_capacity(array.len());
788/// #
789/// #     for item in array.iter() {
790/// #         if let Some(geom) = item {
791/// #             builder.append_value(geom?.to_geometry().unsigned_area());
792/// #         } else {
793/// #             builder.append_null();
794/// #         }
795/// #     }
796/// #
797/// #     Ok(builder.finish())
798/// # }
799/// #
800/// fn impl_unsigned_area_specialized<'a>(array: &'a impl GeoArrowArrayAccessor<'a>) -> GeoArrowResult<Float64Array> {
801///     use GeoArrowType::*;
802///     match array.data_type() {
803///         Point(_) | LineString(_) | MultiPoint(_) | MultiLineString(_) => {
804///             let values = vec![0.0f64; array.len()];
805///             Ok(Float64Array::new(values.into(), array.logical_nulls()))
806///         }
807///         _ => impl_unsigned_area(array),
808///     }
809/// }
810/// ```
811///
812/// This is a simplified version of the upstream
813/// [downcast_primitive_array][arrow_array::downcast_primitive_array].
814///
815/// If you would like to help in updating this `downcast_geoarrow_array` to support the full range
816/// of functionality of the upstream `downcast_primitive_array`, please create an issue or submit a
817/// PR.
818#[macro_export]
819macro_rules! downcast_geoarrow_array {
820    ($array:ident, $fn:expr $(, $args:expr )* $(,)?) => {
821        match $array.data_type() {
822            $crate::cast::__private::GeoArrowType::Point(_) => {
823                $fn($crate::cast::AsGeoArrowArray::as_point($array) $(, $args )*)
824            }
825            $crate::cast::__private::GeoArrowType::LineString(_) => {
826                $fn($crate::cast::AsGeoArrowArray::as_line_string($array) $(, $args )*)
827            }
828            $crate::cast::__private::GeoArrowType::Polygon(_) => {
829                $fn($crate::cast::AsGeoArrowArray::as_polygon($array) $(, $args )*)
830            }
831            $crate::cast::__private::GeoArrowType::MultiPoint(_) => {
832                $fn($crate::cast::AsGeoArrowArray::as_multi_point($array) $(, $args )*)
833            }
834            $crate::cast::__private::GeoArrowType::MultiLineString(_) => {
835                $fn($crate::cast::AsGeoArrowArray::as_multi_line_string($array) $(, $args )*)
836            }
837            $crate::cast::__private::GeoArrowType::MultiPolygon(_) => {
838                $fn($crate::cast::AsGeoArrowArray::as_multi_polygon($array) $(, $args )*)
839            }
840            $crate::cast::__private::GeoArrowType::Geometry(_) => {
841                $fn($crate::cast::AsGeoArrowArray::as_geometry($array) $(, $args )*)
842            }
843            $crate::cast::__private::GeoArrowType::GeometryCollection(_) => $fn(
844                $crate::cast::AsGeoArrowArray::as_geometry_collection($array) $(, $args )*
845            ),
846            $crate::cast::__private::GeoArrowType::Rect(_) => {
847                $fn($crate::cast::AsGeoArrowArray::as_rect($array) $(, $args )*)
848            }
849            $crate::cast::__private::GeoArrowType::Wkb(_) => {
850                $fn($crate::cast::AsGeoArrowArray::as_wkb::<i32>($array) $(, $args )*)
851            }
852            $crate::cast::__private::GeoArrowType::LargeWkb(_) => {
853                $fn($crate::cast::AsGeoArrowArray::as_wkb::<i64>($array) $(, $args )*)
854            }
855            $crate::cast::__private::GeoArrowType::WkbView(_) => {
856                $fn($crate::cast::AsGeoArrowArray::as_wkb_view($array) $(, $args )*)
857            }
858            $crate::cast::__private::GeoArrowType::Wkt(_) => {
859                $fn($crate::cast::AsGeoArrowArray::as_wkt::<i32>($array) $(, $args )*)
860            }
861            $crate::cast::__private::GeoArrowType::LargeWkt(_) => {
862                $fn($crate::cast::AsGeoArrowArray::as_wkt::<i64>($array) $(, $args )*)
863            }
864            $crate::cast::__private::GeoArrowType::WktView(_) => {
865                $fn($crate::cast::AsGeoArrowArray::as_wkt_view($array) $(, $args )*)
866            }
867        }
868    };
869}
870
871#[cfg(test)]
872mod test {
873    use std::sync::Arc;
874
875    use geoarrow_schema::{CoordType, Dimension, WkbType};
876
877    use super::*;
878    use crate::test;
879
880    #[test]
881    fn test_cast_wkb_in_to_wkb() {
882        let wkb_arr: GenericWkbArray<i32> =
883            to_wkb(&test::point::array(CoordType::Separated, Dimension::XY)).unwrap();
884        let wkb_arr2: GenericWkbArray<i32> = to_wkb(&wkb_arr).unwrap();
885        let wkb_arr3: GenericWkbArray<i64> = to_wkb(&wkb_arr2).unwrap();
886        let wkb_arr4: GenericWkbArray<i64> = to_wkb(&wkb_arr3).unwrap();
887        let wkb_arr5: GenericWkbArray<i32> = to_wkb(&wkb_arr4).unwrap();
888        assert_eq!(wkb_arr, wkb_arr5);
889    }
890
891    #[test]
892    fn test_cast_wkt_in_to_wkt() {
893        let wkt_arr: GenericWktArray<i32> =
894            to_wkt(&test::point::array(CoordType::Separated, Dimension::XY)).unwrap();
895        let wkt_arr2: GenericWktArray<i32> = to_wkt(&wkt_arr).unwrap();
896        let wkt_arr3: GenericWktArray<i64> = to_wkt(&wkt_arr2).unwrap();
897        let wkt_arr4: GenericWktArray<i64> = to_wkt(&wkt_arr3).unwrap();
898        let wkt_arr5: GenericWktArray<i32> = to_wkt(&wkt_arr4).unwrap();
899        assert_eq!(wkt_arr, wkt_arr5);
900    }
901
902    // Start WKB round trip tests
903    #[test]
904    fn test_round_trip_wkb_point() {
905        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
906            for dim in [
907                Dimension::XY,
908                Dimension::XYZ,
909                Dimension::XYM,
910                Dimension::XYZM,
911            ] {
912                let arr = test::point::array(coord_type, dim);
913
914                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
915                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
916                assert_eq!(&arr, arr2.as_point());
917
918                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
919                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
920                assert_eq!(&arr, arr3.as_point());
921            }
922        }
923    }
924
925    #[test]
926    fn test_round_trip_wkb_linestring() {
927        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
928            for dim in [
929                Dimension::XY,
930                Dimension::XYZ,
931                Dimension::XYM,
932                Dimension::XYZM,
933            ] {
934                let arr = test::linestring::array(coord_type, dim);
935
936                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
937                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
938                assert_eq!(&arr, arr2.as_line_string());
939
940                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
941                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
942                assert_eq!(&arr, arr3.as_line_string());
943            }
944        }
945    }
946
947    #[test]
948    fn test_round_trip_wkb_polygon() {
949        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
950            for dim in [
951                Dimension::XY,
952                Dimension::XYZ,
953                Dimension::XYM,
954                Dimension::XYZM,
955            ] {
956                let arr = test::polygon::array(coord_type, dim);
957
958                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
959                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
960                assert_eq!(&arr, arr2.as_polygon());
961
962                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
963                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
964                assert_eq!(&arr, arr3.as_polygon());
965            }
966        }
967    }
968
969    #[test]
970    fn test_round_trip_wkb_multipoint() {
971        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
972            for dim in [
973                Dimension::XY,
974                Dimension::XYZ,
975                Dimension::XYM,
976                Dimension::XYZM,
977            ] {
978                let arr = test::multipoint::array(coord_type, dim);
979
980                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
981                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
982                assert_eq!(&arr, arr2.as_multi_point());
983
984                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
985                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
986                assert_eq!(&arr, arr3.as_multi_point());
987            }
988        }
989    }
990
991    #[test]
992    fn test_round_trip_wkb_multilinestring() {
993        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
994            for dim in [
995                Dimension::XY,
996                Dimension::XYZ,
997                Dimension::XYM,
998                Dimension::XYZM,
999            ] {
1000                let arr = test::multilinestring::array(coord_type, dim);
1001
1002                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1003                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1004                assert_eq!(&arr, arr2.as_multi_line_string());
1005
1006                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1007                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1008                assert_eq!(&arr, arr3.as_multi_line_string());
1009            }
1010        }
1011    }
1012
1013    #[test]
1014    fn test_round_trip_wkb_multipolygon() {
1015        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1016            for dim in [
1017                Dimension::XY,
1018                Dimension::XYZ,
1019                Dimension::XYM,
1020                Dimension::XYZM,
1021            ] {
1022                let arr = test::multipolygon::array(coord_type, dim);
1023
1024                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1025                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1026                assert_eq!(&arr, arr2.as_multi_polygon());
1027
1028                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1029                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1030                assert_eq!(&arr, arr3.as_multi_polygon());
1031            }
1032        }
1033    }
1034
1035    #[test]
1036    fn test_round_trip_wkb_geometrycollection() {
1037        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1038            for dim in [
1039                Dimension::XY,
1040                Dimension::XYZ,
1041                Dimension::XYM,
1042                Dimension::XYZM,
1043            ] {
1044                let arr = test::geometrycollection::array(coord_type, dim, false);
1045
1046                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1047                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1048                assert_eq!(&arr, arr2.as_geometry_collection());
1049
1050                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1051                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1052                assert_eq!(&arr, arr3.as_geometry_collection());
1053            }
1054        }
1055    }
1056
1057    #[test]
1058    fn test_round_trip_wkb_geometry() {
1059        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1060            let arr = test::geometry::array(coord_type, false);
1061
1062            let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1063            let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1064            assert_eq!(&arr, arr2.as_geometry());
1065
1066            let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1067            let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1068            assert_eq!(&arr, arr3.as_geometry());
1069        }
1070    }
1071
1072    // Start WKT round trip tests
1073    #[test]
1074    fn test_round_trip_wkt_point() {
1075        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1076            for dim in [
1077                Dimension::XY,
1078                Dimension::XYZ,
1079                Dimension::XYM,
1080                Dimension::XYZM,
1081            ] {
1082                let arr = test::point::array(coord_type, dim);
1083
1084                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1085                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1086                assert_eq!(&arr, arr2.as_point());
1087
1088                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1089                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1090                assert_eq!(&arr, arr3.as_point());
1091            }
1092        }
1093    }
1094
1095    #[test]
1096    fn test_round_trip_wkt_linestring() {
1097        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1098            for dim in [
1099                Dimension::XY,
1100                Dimension::XYZ,
1101                Dimension::XYM,
1102                Dimension::XYZM,
1103            ] {
1104                let arr = test::linestring::array(coord_type, dim);
1105
1106                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1107                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1108                assert_eq!(&arr, arr2.as_line_string());
1109
1110                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1111                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1112                assert_eq!(&arr, arr3.as_line_string());
1113            }
1114        }
1115    }
1116
1117    #[test]
1118    fn test_round_trip_wkt_polygon() {
1119        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1120            for dim in [
1121                Dimension::XY,
1122                Dimension::XYZ,
1123                Dimension::XYM,
1124                Dimension::XYZM,
1125            ] {
1126                let arr = test::polygon::array(coord_type, dim);
1127
1128                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1129                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1130                assert_eq!(&arr, arr2.as_polygon());
1131
1132                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1133                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1134                assert_eq!(&arr, arr3.as_polygon());
1135            }
1136        }
1137    }
1138
1139    #[test]
1140    fn test_round_trip_wkt_multipoint() {
1141        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1142            for dim in [
1143                Dimension::XY,
1144                Dimension::XYZ,
1145                Dimension::XYM,
1146                Dimension::XYZM,
1147            ] {
1148                let arr = test::multipoint::array(coord_type, dim);
1149
1150                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1151                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1152                assert_eq!(&arr, arr2.as_multi_point());
1153
1154                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1155                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1156                assert_eq!(&arr, arr3.as_multi_point());
1157            }
1158        }
1159    }
1160
1161    #[test]
1162    fn test_round_trip_wkt_multilinestring() {
1163        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1164            for dim in [
1165                Dimension::XY,
1166                Dimension::XYZ,
1167                Dimension::XYM,
1168                Dimension::XYZM,
1169            ] {
1170                let arr = test::multilinestring::array(coord_type, dim);
1171
1172                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1173                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1174                assert_eq!(&arr, arr2.as_multi_line_string());
1175
1176                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1177                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1178                assert_eq!(&arr, arr3.as_multi_line_string());
1179            }
1180        }
1181    }
1182
1183    #[test]
1184    fn test_round_trip_wkt_multipolygon() {
1185        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1186            for dim in [
1187                Dimension::XY,
1188                Dimension::XYZ,
1189                Dimension::XYM,
1190                Dimension::XYZM,
1191            ] {
1192                let arr = test::multipolygon::array(coord_type, dim);
1193
1194                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1195                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1196                assert_eq!(&arr, arr2.as_multi_polygon());
1197
1198                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1199                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1200                assert_eq!(&arr, arr3.as_multi_polygon());
1201            }
1202        }
1203    }
1204
1205    #[test]
1206    fn test_round_trip_wkt_geometrycollection() {
1207        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1208            for dim in [
1209                Dimension::XY,
1210                Dimension::XYZ,
1211                Dimension::XYM,
1212                Dimension::XYZM,
1213            ] {
1214                let arr = test::geometrycollection::array(coord_type, dim, false);
1215
1216                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1217                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1218                assert_eq!(&arr, arr2.as_geometry_collection());
1219
1220                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1221                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1222                assert_eq!(&arr, arr3.as_geometry_collection());
1223            }
1224        }
1225    }
1226
1227    #[test]
1228    fn test_round_trip_wkt_geometry() {
1229        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1230            let arr = test::geometry::array(coord_type, false);
1231
1232            let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1233            let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1234            assert_eq!(&arr, arr2.as_geometry());
1235
1236            let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1237            let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1238            assert_eq!(&arr, arr3.as_geometry());
1239        }
1240    }
1241
1242    // Verify that this compiles with the macro
1243    #[allow(dead_code)]
1244    fn _to_wkb_test_downcast_macro(
1245        arr: &dyn GeoArrowArray,
1246    ) -> GeoArrowResult<GenericWkbArray<i32>> {
1247        downcast_geoarrow_array!(arr, impl_to_wkb)
1248    }
1249
1250    fn impl_to_wkb<'a>(
1251        geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
1252    ) -> GeoArrowResult<GenericWkbArray<i32>> {
1253        let geoms = geo_arr
1254            .iter()
1255            .map(|x| x.transpose())
1256            .collect::<std::result::Result<Vec<_>, _>>()
1257            .unwrap();
1258        let wkb_type = WkbType::new(geo_arr.data_type().metadata().clone());
1259        Ok(WkbBuilder::from_nullable_geometries(geoms.as_slice(), wkb_type)?.finish())
1260    }
1261
1262    // Verify that this compiles with the macro
1263    #[test]
1264    fn test_downcast_macro_with_param() {
1265        let arr =
1266            Arc::new(test::geometry::array(Default::default(), false)) as Arc<dyn GeoArrowArray>;
1267        let arr_ref = arr.as_ref();
1268        let x = downcast_geoarrow_array!(arr_ref, impl_inner_function_with_param, 1.0).unwrap();
1269        assert_eq!(x, 1.0);
1270    }
1271
1272    fn impl_inner_function_with_param<'a>(
1273        _geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
1274        param: f64,
1275    ) -> GeoArrowResult<f64> {
1276        Ok(param)
1277    }
1278}