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    // Make this a callback so that we don't actually generate this vec when converting from WKB to
462    // WKT or WKB
463    let geoms_fn = || {
464        arr.iter()
465            .map(|g| g.transpose())
466            .collect::<GeoArrowResult<Vec<_>>>()
467    };
468
469    use GeoArrowType::*;
470    let result: Arc<dyn GeoArrowArray> = match to_type {
471        Point(typ) => Arc::new(PointBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish()),
472        LineString(typ) => {
473            Arc::new(LineStringBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
474        }
475        Polygon(typ) => {
476            Arc::new(PolygonBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
477        }
478        MultiPoint(typ) => {
479            Arc::new(MultiPointBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
480        }
481        MultiLineString(typ) => {
482            Arc::new(MultiLineStringBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
483        }
484        MultiPolygon(typ) => {
485            Arc::new(MultiPolygonBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
486        }
487        GeometryCollection(typ) => Arc::new(
488            GeometryCollectionBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish(),
489        ),
490        Rect(_) => {
491            return Err(GeoArrowError::IncorrectGeometryType(format!(
492                "Cannot decode WKB geometries to Rect geometry type in from_wkb {to_type:?}",
493            )));
494        }
495        Geometry(typ) => {
496            Arc::new(GeometryBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
497        }
498        Wkb(typ) => {
499            let mut wkb_arr = to_wkb::<i32>(arr)?;
500            wkb_arr.data_type = typ;
501            Arc::new(wkb_arr)
502        }
503        LargeWkb(typ) => {
504            let mut wkb_arr = to_wkb::<i64>(arr)?;
505            wkb_arr.data_type = typ;
506            Arc::new(wkb_arr)
507        }
508        WkbView(typ) => {
509            let mut wkb_view_arr = to_wkb_view(arr)?;
510            wkb_view_arr.data_type = typ;
511            Arc::new(wkb_view_arr)
512        }
513        Wkt(typ) => {
514            let mut wkt_arr = to_wkt::<i32>(arr)?;
515            wkt_arr.data_type = typ;
516            Arc::new(wkt_arr)
517        }
518        LargeWkt(typ) => {
519            let mut wkt_arr = to_wkt::<i64>(arr)?;
520            wkt_arr.data_type = typ;
521            Arc::new(wkt_arr)
522        }
523        WktView(typ) => {
524            let mut wkt_view_arr = to_wkt_view(arr)?;
525            wkt_view_arr.data_type = typ;
526            Arc::new(wkt_view_arr)
527        }
528    };
529    Ok(result)
530}
531
532/// Convert a [GeoArrowArray] to a [`GenericWktArray`].
533pub fn to_wkt<O: OffsetSizeTrait>(arr: &dyn GeoArrowArray) -> GeoArrowResult<GenericWktArray<O>> {
534    use GeoArrowType::*;
535    match arr.data_type() {
536        Point(_) => impl_to_wkt(arr.as_point()),
537        LineString(_) => impl_to_wkt(arr.as_line_string()),
538        Polygon(_) => impl_to_wkt(arr.as_polygon()),
539        MultiPoint(_) => impl_to_wkt(arr.as_multi_point()),
540        MultiLineString(_) => impl_to_wkt(arr.as_multi_line_string()),
541        MultiPolygon(_) => impl_to_wkt(arr.as_multi_polygon()),
542        Geometry(_) => impl_to_wkt(arr.as_geometry()),
543        GeometryCollection(_) => impl_to_wkt(arr.as_geometry_collection()),
544        Rect(_) => impl_to_wkt(arr.as_rect()),
545        Wkb(_) => impl_to_wkt(arr.as_wkb::<i32>()),
546        LargeWkb(_) => impl_to_wkt(arr.as_wkb::<i64>()),
547        WkbView(_) => impl_to_wkt(arr.as_wkb_view()),
548        Wkt(typ) => {
549            if O::IS_LARGE {
550                let large_arr: GenericWktArray<i64> = arr.as_wkt::<i32>().clone().into();
551                let array = large_arr.to_array_ref().as_string::<O>().clone();
552                Ok(GenericWktArray::new(array, typ.metadata().clone()))
553            } else {
554                // Since O is already i32, we can just go via ArrayRef, and use .as_string to cast
555                // to O
556                let array = arr.as_wkt::<i32>().to_array_ref();
557                let array = array.as_string::<O>().clone();
558                Ok(GenericWktArray::new(array, typ.metadata().clone()))
559            }
560        }
561        LargeWkt(typ) => {
562            if O::IS_LARGE {
563                // Since O is already i64, we can just go via ArrayRef, and use .as_string to cast
564                // to O
565                let array = arr.as_wkt::<i64>().to_array_ref();
566                let array = array.as_string::<O>().clone();
567                Ok(GenericWktArray::new(array, typ.metadata().clone()))
568            } else {
569                let small_arr: GenericWktArray<i32> = arr.as_wkt::<i64>().clone().try_into()?;
570                let array = small_arr.to_array_ref().as_string::<O>().clone();
571                Ok(GenericWktArray::new(array, typ.metadata().clone()))
572            }
573        }
574        WktView(_) => {
575            let wkt_view_arr = arr.as_wkt_view();
576            let metadata = wkt_view_arr.data_type().metadata().clone();
577            let array = wkt_view_arr.clone().into_arrow();
578
579            let mut builder = GenericStringBuilder::with_capacity(arr.len(), 0);
580            array.iter().for_each(|value| builder.append_option(value));
581            Ok(GenericWktArray::new(builder.finish(), metadata))
582        }
583    }
584}
585
586fn impl_to_wkt<'a, O: OffsetSizeTrait>(
587    geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
588) -> GeoArrowResult<GenericWktArray<O>> {
589    let metadata = geo_arr.data_type().metadata().clone();
590    let mut builder = GenericStringBuilder::with_capacity(geo_arr.len(), 0);
591
592    for maybe_geom in geo_arr.iter() {
593        if let Some(geom) = maybe_geom {
594            wkt::to_wkt::write_geometry(&mut builder, &geom?)
595                .map_err(|err| GeoArrowError::External(Box::new(err)))?;
596            builder.append_value("");
597        } else {
598            builder.append_null();
599        }
600    }
601
602    Ok(GenericWktArray::new(builder.finish(), metadata))
603}
604
605/// Convert a [GeoArrowArray] to a [`WktViewArray`].
606pub fn to_wkt_view(arr: &dyn GeoArrowArray) -> GeoArrowResult<WktViewArray> {
607    use GeoArrowType::*;
608    match arr.data_type() {
609        Point(_) => impl_to_wkt_view(arr.as_point()),
610        LineString(_) => impl_to_wkt_view(arr.as_line_string()),
611        Polygon(_) => impl_to_wkt_view(arr.as_polygon()),
612        MultiPoint(_) => impl_to_wkt_view(arr.as_multi_point()),
613        MultiLineString(_) => impl_to_wkt_view(arr.as_multi_line_string()),
614        MultiPolygon(_) => impl_to_wkt_view(arr.as_multi_polygon()),
615        Geometry(_) => impl_to_wkt_view(arr.as_geometry()),
616        GeometryCollection(_) => impl_to_wkt_view(arr.as_geometry_collection()),
617        Rect(_) => impl_to_wkt_view(arr.as_rect()),
618        Wkb(_) => impl_to_wkt_view(arr.as_wkb::<i32>()),
619        LargeWkb(_) => impl_to_wkt_view(arr.as_wkb::<i64>()),
620        WkbView(_) => impl_to_wkt_view(arr.as_wkb_view()),
621        Wkt(_) => wkt_array_to_wkt_view(arr.as_wkt::<i32>()),
622        LargeWkt(_) => wkt_array_to_wkt_view(arr.as_wkt::<i64>()),
623        WktView(_) => Ok(arr.as_wkt_view().clone()),
624    }
625}
626
627/// Convert an arbitrary GeoArrowArray to a WktViewArray.
628///
629/// This function will parse each geometry and re-encode it as WKT.
630fn impl_to_wkt_view<'a>(
631    geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
632) -> GeoArrowResult<WktViewArray> {
633    let metadata = geo_arr.data_type().metadata().clone();
634    let mut builder = StringViewBuilder::with_capacity(geo_arr.len());
635
636    for maybe_geom in geo_arr.iter() {
637        if let Some(geom) = maybe_geom {
638            let mut s = String::new();
639            wkt::to_wkt::write_geometry(&mut s, &geom?)
640                .map_err(|err| GeoArrowError::External(Box::new(err)))?;
641            builder.append_value(s);
642        } else {
643            builder.append_null();
644        }
645    }
646
647    Ok(WktViewArray::new(builder.finish(), metadata))
648}
649
650/// A fast path of converting to WktViewArray that does not parse and re-encode WKT buffers
651fn wkt_array_to_wkt_view<O: OffsetSizeTrait>(
652    arr: &GenericWktArray<O>,
653) -> GeoArrowResult<WktViewArray> {
654    let metadata = arr.data_type().metadata().clone();
655    let mut builder = StringViewBuilder::with_capacity(arr.len());
656
657    for value in arr.inner().iter() {
658        if let Some(s) = value {
659            builder.append_value(s);
660        } else {
661            builder.append_null();
662        }
663    }
664
665    Ok(WktViewArray::new(builder.finish(), metadata))
666}
667
668/// Parse a [`GenericWktArray`] or [`WktViewArray`] to a [`GeoArrowArray`] with the designated
669/// [`GeoArrowType`].
670///
671/// Note that the GeoArrow metadata on the new array is taken from `to_type` **not** the original
672/// array. Ensure you construct the [GeoArrowType] with the correct metadata.
673pub fn from_wkt<A: GenericWktArrayType>(
674    arr: &A,
675    to_type: GeoArrowType,
676) -> GeoArrowResult<Arc<dyn GeoArrowArray>> {
677    // Make this a callback so that we don't actually generate this vec when converting from WKT to
678    // WKT or WKB
679    let geoms_fn = || {
680        arr.iter()
681            .map(|g| g.transpose())
682            .collect::<GeoArrowResult<Vec<_>>>()
683    };
684
685    use GeoArrowType::*;
686    let result: Arc<dyn GeoArrowArray> = match to_type {
687        Point(typ) => Arc::new(PointBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish()),
688        LineString(typ) => {
689            Arc::new(LineStringBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
690        }
691        Polygon(typ) => {
692            Arc::new(PolygonBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
693        }
694        MultiPoint(typ) => {
695            Arc::new(MultiPointBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
696        }
697        MultiLineString(typ) => {
698            Arc::new(MultiLineStringBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
699        }
700        MultiPolygon(typ) => {
701            Arc::new(MultiPolygonBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
702        }
703        GeometryCollection(typ) => Arc::new(
704            GeometryCollectionBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish(),
705        ),
706        Rect(_) => {
707            return Err(GeoArrowError::IncorrectGeometryType(format!(
708                "Cannot decode WKT geometries to Rect geometry type in from_wkt {to_type:?}",
709            )));
710        }
711        Geometry(typ) => {
712            Arc::new(GeometryBuilder::from_nullable_geometries(&geoms_fn()?, typ)?.finish())
713        }
714        Wkb(typ) => {
715            let mut wkb_arr = to_wkb::<i32>(arr)?;
716            wkb_arr.data_type = typ;
717            Arc::new(wkb_arr)
718        }
719        LargeWkb(typ) => {
720            let mut wkb_arr = to_wkb::<i64>(arr)?;
721            wkb_arr.data_type = typ;
722            Arc::new(wkb_arr)
723        }
724        WkbView(typ) => {
725            let mut wkb_view_arr = to_wkb_view(arr)?;
726            wkb_view_arr.data_type = typ;
727            Arc::new(wkb_view_arr)
728        }
729        Wkt(typ) => {
730            let mut wkt_arr = to_wkt::<i32>(arr)?;
731            wkt_arr.data_type = typ;
732            Arc::new(wkt_arr)
733        }
734        LargeWkt(typ) => {
735            let mut wkt_arr = to_wkt::<i64>(arr)?;
736            wkt_arr.data_type = typ;
737            Arc::new(wkt_arr)
738        }
739        WktView(typ) => {
740            let mut wkt_view_arr = to_wkt_view(arr)?;
741            wkt_view_arr.data_type = typ;
742            Arc::new(wkt_view_arr)
743        }
744    };
745    Ok(result)
746}
747
748/// Re-export symbols needed for downcast macros
749///
750/// Name follows `serde` convention
751#[doc(hidden)]
752pub mod __private {
753    pub use geoarrow_schema::GeoArrowType;
754}
755
756/// Downcast a [GeoArrowArray] to a concrete-typed array based on its [`GeoArrowType`].
757///
758/// For example: computing unsigned area:
759///
760/// ```
761/// use arrow_array::Float64Array;
762/// use arrow_array::builder::Float64Builder;
763/// use geo::Area;
764/// use geo_traits::to_geo::ToGeoGeometry;
765/// use geoarrow_schema::error::GeoArrowResult;
766/// use geoarrow_array::{GeoArrowArrayAccessor, GeoArrowArray, downcast_geoarrow_array};
767///
768/// pub fn unsigned_area(array: &dyn GeoArrowArray) -> GeoArrowResult<Float64Array> {
769///     downcast_geoarrow_array!(array, impl_unsigned_area)
770/// }
771///
772/// fn impl_unsigned_area<'a>(array: &'a impl GeoArrowArrayAccessor<'a>) -> GeoArrowResult<Float64Array> {
773///     let mut builder = Float64Builder::with_capacity(array.len());
774///
775///     for item in array.iter() {
776///         if let Some(geom) = item {
777///             builder.append_value(geom?.to_geometry().unsigned_area());
778///         } else {
779///             builder.append_null();
780///         }
781///     }
782///
783///     Ok(builder.finish())
784/// }
785/// ```
786///
787/// You can also override the behavior of specific data types to specialize or provide a fast path.
788/// For example, we know that points and lines will always have an area of 0, and don't need to
789/// iterate over the input values to compute that.
790///
791/// ```
792/// # use arrow_array::Float64Array;
793/// # use arrow_array::builder::Float64Builder;
794/// # use geo::Area;
795/// # use geo_traits::to_geo::ToGeoGeometry;
796/// # use geoarrow_schema::error::GeoArrowResult;
797/// # use geoarrow_schema::GeoArrowType;
798/// # use geoarrow_array::GeoArrowArrayAccessor;
799/// #
800/// # fn impl_unsigned_area<'a>(array: &'a impl GeoArrowArrayAccessor<'a>) -> GeoArrowResult<Float64Array> {
801/// #     let mut builder = Float64Builder::with_capacity(array.len());
802/// #
803/// #     for item in array.iter() {
804/// #         if let Some(geom) = item {
805/// #             builder.append_value(geom?.to_geometry().unsigned_area());
806/// #         } else {
807/// #             builder.append_null();
808/// #         }
809/// #     }
810/// #
811/// #     Ok(builder.finish())
812/// # }
813/// #
814/// fn impl_unsigned_area_specialized<'a>(array: &'a impl GeoArrowArrayAccessor<'a>) -> GeoArrowResult<Float64Array> {
815///     use GeoArrowType::*;
816///     match array.data_type() {
817///         Point(_) | LineString(_) | MultiPoint(_) | MultiLineString(_) => {
818///             let values = vec![0.0f64; array.len()];
819///             Ok(Float64Array::new(values.into(), array.logical_nulls()))
820///         }
821///         _ => impl_unsigned_area(array),
822///     }
823/// }
824/// ```
825///
826/// This is a simplified version of the upstream
827/// [downcast_primitive_array][arrow_array::downcast_primitive_array].
828///
829/// If you would like to help in updating this `downcast_geoarrow_array` to support the full range
830/// of functionality of the upstream `downcast_primitive_array`, please create an issue or submit a
831/// PR.
832#[macro_export]
833macro_rules! downcast_geoarrow_array {
834    ($array:ident, $fn:expr $(, $args:expr )* $(,)?) => {
835        match $array.data_type() {
836            $crate::cast::__private::GeoArrowType::Point(_) => {
837                $fn($crate::cast::AsGeoArrowArray::as_point($array) $(, $args )*)
838            }
839            $crate::cast::__private::GeoArrowType::LineString(_) => {
840                $fn($crate::cast::AsGeoArrowArray::as_line_string($array) $(, $args )*)
841            }
842            $crate::cast::__private::GeoArrowType::Polygon(_) => {
843                $fn($crate::cast::AsGeoArrowArray::as_polygon($array) $(, $args )*)
844            }
845            $crate::cast::__private::GeoArrowType::MultiPoint(_) => {
846                $fn($crate::cast::AsGeoArrowArray::as_multi_point($array) $(, $args )*)
847            }
848            $crate::cast::__private::GeoArrowType::MultiLineString(_) => {
849                $fn($crate::cast::AsGeoArrowArray::as_multi_line_string($array) $(, $args )*)
850            }
851            $crate::cast::__private::GeoArrowType::MultiPolygon(_) => {
852                $fn($crate::cast::AsGeoArrowArray::as_multi_polygon($array) $(, $args )*)
853            }
854            $crate::cast::__private::GeoArrowType::Geometry(_) => {
855                $fn($crate::cast::AsGeoArrowArray::as_geometry($array) $(, $args )*)
856            }
857            $crate::cast::__private::GeoArrowType::GeometryCollection(_) => $fn(
858                $crate::cast::AsGeoArrowArray::as_geometry_collection($array) $(, $args )*
859            ),
860            $crate::cast::__private::GeoArrowType::Rect(_) => {
861                $fn($crate::cast::AsGeoArrowArray::as_rect($array) $(, $args )*)
862            }
863            $crate::cast::__private::GeoArrowType::Wkb(_) => {
864                $fn($crate::cast::AsGeoArrowArray::as_wkb::<i32>($array) $(, $args )*)
865            }
866            $crate::cast::__private::GeoArrowType::LargeWkb(_) => {
867                $fn($crate::cast::AsGeoArrowArray::as_wkb::<i64>($array) $(, $args )*)
868            }
869            $crate::cast::__private::GeoArrowType::WkbView(_) => {
870                $fn($crate::cast::AsGeoArrowArray::as_wkb_view($array) $(, $args )*)
871            }
872            $crate::cast::__private::GeoArrowType::Wkt(_) => {
873                $fn($crate::cast::AsGeoArrowArray::as_wkt::<i32>($array) $(, $args )*)
874            }
875            $crate::cast::__private::GeoArrowType::LargeWkt(_) => {
876                $fn($crate::cast::AsGeoArrowArray::as_wkt::<i64>($array) $(, $args )*)
877            }
878            $crate::cast::__private::GeoArrowType::WktView(_) => {
879                $fn($crate::cast::AsGeoArrowArray::as_wkt_view($array) $(, $args )*)
880            }
881        }
882    };
883}
884
885#[cfg(test)]
886mod test {
887    use std::sync::Arc;
888
889    use geoarrow_schema::{CoordType, Dimension, WkbType};
890
891    use super::*;
892    use crate::test;
893
894    #[test]
895    fn test_cast_wkb_in_to_wkb() {
896        let wkb_arr: GenericWkbArray<i32> =
897            to_wkb(&test::point::array(CoordType::Separated, Dimension::XY)).unwrap();
898        let wkb_arr2: GenericWkbArray<i32> = to_wkb(&wkb_arr).unwrap();
899        let wkb_arr3: GenericWkbArray<i64> = to_wkb(&wkb_arr2).unwrap();
900        let wkb_arr4: GenericWkbArray<i64> = to_wkb(&wkb_arr3).unwrap();
901        let wkb_arr5: GenericWkbArray<i32> = to_wkb(&wkb_arr4).unwrap();
902        assert_eq!(wkb_arr, wkb_arr5);
903    }
904
905    #[test]
906    fn test_cast_wkt_in_to_wkt() {
907        let wkt_arr: GenericWktArray<i32> =
908            to_wkt(&test::point::array(CoordType::Separated, Dimension::XY)).unwrap();
909        let wkt_arr2: GenericWktArray<i32> = to_wkt(&wkt_arr).unwrap();
910        let wkt_arr3: GenericWktArray<i64> = to_wkt(&wkt_arr2).unwrap();
911        let wkt_arr4: GenericWktArray<i64> = to_wkt(&wkt_arr3).unwrap();
912        let wkt_arr5: GenericWktArray<i32> = to_wkt(&wkt_arr4).unwrap();
913        assert_eq!(wkt_arr, wkt_arr5);
914    }
915
916    // Start WKB round trip tests
917    #[test]
918    fn test_round_trip_wkb_point() {
919        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
920            for dim in [
921                Dimension::XY,
922                Dimension::XYZ,
923                Dimension::XYM,
924                Dimension::XYZM,
925            ] {
926                let arr = test::point::array(coord_type, dim);
927
928                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
929                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
930                assert_eq!(&arr, arr2.as_point());
931
932                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
933                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
934                assert_eq!(&arr, arr3.as_point());
935            }
936        }
937    }
938
939    #[test]
940    fn test_round_trip_wkb_linestring() {
941        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
942            for dim in [
943                Dimension::XY,
944                Dimension::XYZ,
945                Dimension::XYM,
946                Dimension::XYZM,
947            ] {
948                let arr = test::linestring::array(coord_type, dim);
949
950                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
951                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
952                assert_eq!(&arr, arr2.as_line_string());
953
954                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
955                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
956                assert_eq!(&arr, arr3.as_line_string());
957            }
958        }
959    }
960
961    #[test]
962    fn test_round_trip_wkb_polygon() {
963        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
964            for dim in [
965                Dimension::XY,
966                Dimension::XYZ,
967                Dimension::XYM,
968                Dimension::XYZM,
969            ] {
970                let arr = test::polygon::array(coord_type, dim);
971
972                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
973                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
974                assert_eq!(&arr, arr2.as_polygon());
975
976                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
977                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
978                assert_eq!(&arr, arr3.as_polygon());
979            }
980        }
981    }
982
983    #[test]
984    fn test_round_trip_wkb_multipoint() {
985        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
986            for dim in [
987                Dimension::XY,
988                Dimension::XYZ,
989                Dimension::XYM,
990                Dimension::XYZM,
991            ] {
992                let arr = test::multipoint::array(coord_type, dim);
993
994                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
995                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
996                assert_eq!(&arr, arr2.as_multi_point());
997
998                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
999                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1000                assert_eq!(&arr, arr3.as_multi_point());
1001            }
1002        }
1003    }
1004
1005    #[test]
1006    fn test_round_trip_wkb_multilinestring() {
1007        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1008            for dim in [
1009                Dimension::XY,
1010                Dimension::XYZ,
1011                Dimension::XYM,
1012                Dimension::XYZM,
1013            ] {
1014                let arr = test::multilinestring::array(coord_type, dim);
1015
1016                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1017                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1018                assert_eq!(&arr, arr2.as_multi_line_string());
1019
1020                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1021                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1022                assert_eq!(&arr, arr3.as_multi_line_string());
1023            }
1024        }
1025    }
1026
1027    #[test]
1028    fn test_round_trip_wkb_multipolygon() {
1029        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1030            for dim in [
1031                Dimension::XY,
1032                Dimension::XYZ,
1033                Dimension::XYM,
1034                Dimension::XYZM,
1035            ] {
1036                let arr = test::multipolygon::array(coord_type, dim);
1037
1038                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1039                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1040                assert_eq!(&arr, arr2.as_multi_polygon());
1041
1042                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1043                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1044                assert_eq!(&arr, arr3.as_multi_polygon());
1045            }
1046        }
1047    }
1048
1049    #[test]
1050    fn test_round_trip_wkb_geometrycollection() {
1051        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1052            for dim in [
1053                Dimension::XY,
1054                Dimension::XYZ,
1055                Dimension::XYM,
1056                Dimension::XYZM,
1057            ] {
1058                let arr = test::geometrycollection::array(coord_type, dim, false);
1059
1060                let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1061                let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1062                assert_eq!(&arr, arr2.as_geometry_collection());
1063
1064                let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1065                let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1066                assert_eq!(&arr, arr3.as_geometry_collection());
1067            }
1068        }
1069    }
1070
1071    #[test]
1072    fn test_round_trip_wkb_geometry() {
1073        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1074            let arr = test::geometry::array(coord_type, false);
1075
1076            let wkb_arr = to_wkb::<i32>(&arr).unwrap();
1077            let arr2 = from_wkb(&wkb_arr, arr.data_type().clone()).unwrap();
1078            assert_eq!(&arr, arr2.as_geometry());
1079
1080            let wkb_arr2 = to_wkb::<i64>(&arr).unwrap();
1081            let arr3 = from_wkb(&wkb_arr2, arr.data_type().clone()).unwrap();
1082            assert_eq!(&arr, arr3.as_geometry());
1083        }
1084    }
1085
1086    // Start WKT round trip tests
1087    #[test]
1088    fn test_round_trip_wkt_point() {
1089        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1090            for dim in [
1091                Dimension::XY,
1092                Dimension::XYZ,
1093                Dimension::XYM,
1094                Dimension::XYZM,
1095            ] {
1096                let arr = test::point::array(coord_type, dim);
1097
1098                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1099                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1100                assert_eq!(&arr, arr2.as_point());
1101
1102                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1103                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1104                assert_eq!(&arr, arr3.as_point());
1105            }
1106        }
1107    }
1108
1109    #[test]
1110    fn test_round_trip_wkt_linestring() {
1111        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1112            for dim in [
1113                Dimension::XY,
1114                Dimension::XYZ,
1115                Dimension::XYM,
1116                Dimension::XYZM,
1117            ] {
1118                let arr = test::linestring::array(coord_type, dim);
1119
1120                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1121                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1122                assert_eq!(&arr, arr2.as_line_string());
1123
1124                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1125                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1126                assert_eq!(&arr, arr3.as_line_string());
1127            }
1128        }
1129    }
1130
1131    #[test]
1132    fn test_round_trip_wkt_polygon() {
1133        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1134            for dim in [
1135                Dimension::XY,
1136                Dimension::XYZ,
1137                Dimension::XYM,
1138                Dimension::XYZM,
1139            ] {
1140                let arr = test::polygon::array(coord_type, dim);
1141
1142                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1143                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1144                assert_eq!(&arr, arr2.as_polygon());
1145
1146                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1147                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1148                assert_eq!(&arr, arr3.as_polygon());
1149            }
1150        }
1151    }
1152
1153    #[test]
1154    fn test_round_trip_wkt_multipoint() {
1155        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1156            for dim in [
1157                Dimension::XY,
1158                Dimension::XYZ,
1159                Dimension::XYM,
1160                Dimension::XYZM,
1161            ] {
1162                let arr = test::multipoint::array(coord_type, dim);
1163
1164                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1165                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1166                assert_eq!(&arr, arr2.as_multi_point());
1167
1168                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1169                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1170                assert_eq!(&arr, arr3.as_multi_point());
1171            }
1172        }
1173    }
1174
1175    #[test]
1176    fn test_round_trip_wkt_multilinestring() {
1177        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1178            for dim in [
1179                Dimension::XY,
1180                Dimension::XYZ,
1181                Dimension::XYM,
1182                Dimension::XYZM,
1183            ] {
1184                let arr = test::multilinestring::array(coord_type, dim);
1185
1186                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1187                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1188                assert_eq!(&arr, arr2.as_multi_line_string());
1189
1190                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1191                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1192                assert_eq!(&arr, arr3.as_multi_line_string());
1193            }
1194        }
1195    }
1196
1197    #[test]
1198    fn test_round_trip_wkt_multipolygon() {
1199        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1200            for dim in [
1201                Dimension::XY,
1202                Dimension::XYZ,
1203                Dimension::XYM,
1204                Dimension::XYZM,
1205            ] {
1206                let arr = test::multipolygon::array(coord_type, dim);
1207
1208                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1209                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1210                assert_eq!(&arr, arr2.as_multi_polygon());
1211
1212                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1213                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1214                assert_eq!(&arr, arr3.as_multi_polygon());
1215            }
1216        }
1217    }
1218
1219    #[test]
1220    fn test_round_trip_wkt_geometrycollection() {
1221        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1222            for dim in [
1223                Dimension::XY,
1224                Dimension::XYZ,
1225                Dimension::XYM,
1226                Dimension::XYZM,
1227            ] {
1228                let arr = test::geometrycollection::array(coord_type, dim, false);
1229
1230                let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1231                let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1232                assert_eq!(&arr, arr2.as_geometry_collection());
1233
1234                let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1235                let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1236                assert_eq!(&arr, arr3.as_geometry_collection());
1237            }
1238        }
1239    }
1240
1241    #[test]
1242    fn test_round_trip_wkt_geometry() {
1243        for coord_type in [CoordType::Interleaved, CoordType::Separated] {
1244            let arr = test::geometry::array(coord_type, false);
1245
1246            let wkt_arr = to_wkt::<i32>(&arr).unwrap();
1247            let arr2 = from_wkt(&wkt_arr, arr.data_type().clone()).unwrap();
1248            assert_eq!(&arr, arr2.as_geometry());
1249
1250            let wkt_arr2 = to_wkt::<i64>(&arr).unwrap();
1251            let arr3 = from_wkt(&wkt_arr2, arr.data_type().clone()).unwrap();
1252            assert_eq!(&arr, arr3.as_geometry());
1253        }
1254    }
1255
1256    // Verify that this compiles with the macro
1257    #[allow(dead_code)]
1258    fn _to_wkb_test_downcast_macro(
1259        arr: &dyn GeoArrowArray,
1260    ) -> GeoArrowResult<GenericWkbArray<i32>> {
1261        downcast_geoarrow_array!(arr, impl_to_wkb)
1262    }
1263
1264    fn impl_to_wkb<'a>(
1265        geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
1266    ) -> GeoArrowResult<GenericWkbArray<i32>> {
1267        let geoms = geo_arr
1268            .iter()
1269            .map(|x| x.transpose())
1270            .collect::<std::result::Result<Vec<_>, _>>()
1271            .unwrap();
1272        let wkb_type = WkbType::new(geo_arr.data_type().metadata().clone());
1273        Ok(WkbBuilder::from_nullable_geometries(geoms.as_slice(), wkb_type)?.finish())
1274    }
1275
1276    // Verify that this compiles with the macro
1277    #[test]
1278    fn test_downcast_macro_with_param() {
1279        let arr =
1280            Arc::new(test::geometry::array(Default::default(), false)) as Arc<dyn GeoArrowArray>;
1281        let arr_ref = arr.as_ref();
1282        let x = downcast_geoarrow_array!(arr_ref, impl_inner_function_with_param, 1.0).unwrap();
1283        assert_eq!(x, 1.0);
1284    }
1285
1286    fn impl_inner_function_with_param<'a>(
1287        _geo_arr: &'a impl GeoArrowArrayAccessor<'a>,
1288        param: f64,
1289    ) -> GeoArrowResult<f64> {
1290        Ok(param)
1291    }
1292}