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