1use std::str::FromStr;
2use std::sync::Arc;
3
4use arrow_array::builder::GenericStringBuilder;
5use arrow_array::cast::AsArray;
6use arrow_array::{
7 Array, ArrayRef, GenericStringArray, LargeStringArray, OffsetSizeTrait, StringArray,
8};
9use arrow_buffer::NullBuffer;
10use arrow_schema::{DataType, Field};
11use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
12use geoarrow_schema::{GeoArrowType, Metadata, WktType};
13use wkt::Wkt;
14
15use crate::GeoArrowArrayAccessor;
16use crate::array::WktViewArray;
17use crate::trait_::{GeoArrowArray, IntoArrow};
18use crate::util::{offsets_buffer_i32_to_i64, offsets_buffer_i64_to_i32};
19
20#[derive(Debug, Clone, PartialEq)]
27pub struct GenericWktArray<O: OffsetSizeTrait> {
28 pub(crate) data_type: WktType,
29 pub(crate) array: GenericStringArray<O>,
30}
31
32impl<O: OffsetSizeTrait> GenericWktArray<O> {
34 pub fn new(array: GenericStringArray<O>, metadata: Arc<Metadata>) -> Self {
36 Self {
37 data_type: WktType::new(metadata),
38 array,
39 }
40 }
41
42 pub fn is_empty(&self) -> bool {
44 self.len() == 0
45 }
46
47 pub fn inner(&self) -> &GenericStringArray<O> {
49 &self.array
50 }
51
52 #[inline]
57 pub fn slice(&self, offset: usize, length: usize) -> Self {
58 assert!(
59 offset + length <= self.len(),
60 "offset + length may not exceed length of array"
61 );
62 Self {
63 array: self.array.slice(offset, length),
64 data_type: self.data_type.clone(),
65 }
66 }
67
68 pub fn with_metadata(&self, metadata: Arc<Metadata>) -> Self {
70 let mut arr = self.clone();
71 arr.data_type = self.data_type.clone().with_metadata(metadata);
72 arr
73 }
74}
75
76impl<O: OffsetSizeTrait> GeoArrowArray for GenericWktArray<O> {
77 fn as_any(&self) -> &dyn std::any::Any {
78 self
79 }
80
81 fn into_array_ref(self) -> ArrayRef {
82 Arc::new(self.into_arrow())
83 }
84
85 fn to_array_ref(&self) -> ArrayRef {
86 self.clone().into_array_ref()
87 }
88
89 #[inline]
90 fn len(&self) -> usize {
91 self.array.len()
92 }
93
94 #[inline]
95 fn logical_nulls(&self) -> Option<NullBuffer> {
96 self.array.logical_nulls()
97 }
98
99 #[inline]
100 fn logical_null_count(&self) -> usize {
101 self.array.logical_null_count()
102 }
103
104 #[inline]
105 fn is_null(&self, i: usize) -> bool {
106 self.array.is_null(i)
107 }
108
109 fn data_type(&self) -> GeoArrowType {
110 if O::IS_LARGE {
111 GeoArrowType::LargeWkt(self.data_type.clone())
112 } else {
113 GeoArrowType::Wkt(self.data_type.clone())
114 }
115 }
116
117 fn slice(&self, offset: usize, length: usize) -> Arc<dyn GeoArrowArray> {
118 Arc::new(self.slice(offset, length))
119 }
120
121 fn with_metadata(self, metadata: Arc<Metadata>) -> Arc<dyn GeoArrowArray> {
122 Arc::new(Self::with_metadata(&self, metadata))
123 }
124}
125
126impl<'a, O: OffsetSizeTrait> GeoArrowArrayAccessor<'a> for GenericWktArray<O> {
127 type Item = Wkt<f64>;
128
129 unsafe fn value_unchecked(&'a self, index: usize) -> GeoArrowResult<Self::Item> {
130 let s = unsafe { self.array.value_unchecked(index) };
131 Wkt::from_str(s).map_err(|err| GeoArrowError::Wkt(err.to_string()))
132 }
133}
134
135impl<O: OffsetSizeTrait> IntoArrow for GenericWktArray<O> {
136 type ArrowArray = GenericStringArray<O>;
137 type ExtensionType = WktType;
138
139 fn into_arrow(self) -> Self::ArrowArray {
140 GenericStringArray::new(
141 self.array.offsets().clone(),
142 self.array.values().clone(),
143 self.array.nulls().cloned(),
144 )
145 }
146
147 fn extension_type(&self) -> &Self::ExtensionType {
148 &self.data_type
149 }
150}
151
152impl<O: OffsetSizeTrait> From<(GenericStringArray<O>, WktType)> for GenericWktArray<O> {
153 fn from((value, typ): (GenericStringArray<O>, WktType)) -> Self {
154 Self::new(value, typ.metadata().clone())
155 }
156}
157
158impl TryFrom<(&dyn Array, WktType)> for GenericWktArray<i32> {
159 type Error = GeoArrowError;
160
161 fn try_from((value, typ): (&dyn Array, WktType)) -> GeoArrowResult<Self> {
162 match value.data_type() {
163 DataType::Utf8 => Ok((value.as_string::<i32>().clone(), typ).into()),
164 DataType::LargeUtf8 => {
165 let geom_array: GenericWktArray<i64> =
166 (value.as_string::<i64>().clone(), typ).into();
167 geom_array.try_into()
168 }
169 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
170 "Unexpected WktArray DataType: {:?}",
171 dt
172 ))),
173 }
174 }
175}
176
177impl TryFrom<(&dyn Array, WktType)> for GenericWktArray<i64> {
178 type Error = GeoArrowError;
179
180 fn try_from((value, typ): (&dyn Array, WktType)) -> GeoArrowResult<Self> {
181 match value.data_type() {
182 DataType::Utf8 => {
183 let geom_array: GenericWktArray<i32> =
184 (value.as_string::<i32>().clone(), typ).into();
185 Ok(geom_array.into())
186 }
187 DataType::LargeUtf8 => Ok((value.as_string::<i64>().clone(), typ).into()),
188 dt => Err(GeoArrowError::InvalidGeoArrow(format!(
189 "Unexpected WktArray DataType: {:?}",
190 dt
191 ))),
192 }
193 }
194}
195
196impl TryFrom<(&dyn Array, &Field)> for GenericWktArray<i32> {
197 type Error = GeoArrowError;
198
199 fn try_from((arr, field): (&dyn Array, &Field)) -> GeoArrowResult<Self> {
200 let typ = field
201 .try_extension_type::<WktType>()
202 .ok()
203 .unwrap_or_default();
204 (arr, typ).try_into()
205 }
206}
207
208impl TryFrom<(&dyn Array, &Field)> for GenericWktArray<i64> {
209 type Error = GeoArrowError;
210
211 fn try_from((arr, field): (&dyn Array, &Field)) -> GeoArrowResult<Self> {
212 let typ = field
213 .try_extension_type::<WktType>()
214 .ok()
215 .unwrap_or_default();
216 (arr, typ).try_into()
217 }
218}
219
220impl From<GenericWktArray<i32>> for GenericWktArray<i64> {
221 fn from(value: GenericWktArray<i32>) -> Self {
222 let binary_array = value.array;
223 let (offsets, values, nulls) = binary_array.into_parts();
224 Self {
225 data_type: value.data_type,
226 array: LargeStringArray::new(offsets_buffer_i32_to_i64(&offsets), values, nulls),
227 }
228 }
229}
230
231impl TryFrom<GenericWktArray<i64>> for GenericWktArray<i32> {
232 type Error = GeoArrowError;
233
234 fn try_from(value: GenericWktArray<i64>) -> GeoArrowResult<Self> {
235 let binary_array = value.array;
236 let (offsets, values, nulls) = binary_array.into_parts();
237 Ok(Self {
238 data_type: value.data_type,
239 array: StringArray::new(offsets_buffer_i64_to_i32(&offsets)?, values, nulls),
240 })
241 }
242}
243
244impl<O: OffsetSizeTrait> From<WktViewArray> for GenericWktArray<O> {
245 fn from(value: WktViewArray) -> Self {
246 let wkb_type = value.data_type;
247 let binary_view_array = value.array;
248
249 let mut builder = GenericStringBuilder::new();
251 binary_view_array
252 .iter()
253 .for_each(|value| builder.append_option(value));
254
255 Self {
256 data_type: wkb_type,
257 array: builder.finish(),
258 }
259 }
260}
261
262pub type WktArray = GenericWktArray<i32>;
268
269pub type LargeWktArray = GenericWktArray<i64>;
275
276#[cfg(test)]
277mod test {
278 use arrow_array::builder::{LargeStringBuilder, StringBuilder};
279 use geoarrow_schema::{CoordType, Dimension};
280
281 use super::*;
282 use crate::GeoArrowArray;
283 use crate::cast::to_wkt;
284 use crate::test::point;
285
286 fn wkt_data<O: OffsetSizeTrait>() -> GenericWktArray<O> {
287 to_wkt(&point::array(CoordType::Interleaved, Dimension::XY)).unwrap()
288 }
289
290 #[test]
291 fn parse_dyn_array_i32() {
292 let wkb_array = wkt_data::<i32>();
293 let array = wkb_array.to_array_ref();
294 let field = Field::new("geometry", array.data_type().clone(), true)
295 .with_extension_type(wkb_array.data_type.clone());
296 let wkb_array_retour: GenericWktArray<i32> = (array.as_ref(), &field).try_into().unwrap();
297
298 assert_eq!(wkb_array, wkb_array_retour);
299 }
300
301 #[test]
302 fn parse_dyn_array_i64() {
303 let wkb_array = wkt_data::<i64>();
304 let array = wkb_array.to_array_ref();
305 let field = Field::new("geometry", array.data_type().clone(), true)
306 .with_extension_type(wkb_array.data_type.clone());
307 let wkb_array_retour: GenericWktArray<i64> = (array.as_ref(), &field).try_into().unwrap();
308
309 assert_eq!(wkb_array, wkb_array_retour);
310 }
311
312 #[test]
313 fn convert_i32_to_i64() {
314 let wkb_array = wkt_data::<i32>();
315 let wkb_array_i64: GenericWktArray<i64> = wkb_array.clone().into();
316 let wkb_array_i32: GenericWktArray<i32> = wkb_array_i64.clone().try_into().unwrap();
317
318 assert_eq!(wkb_array, wkb_array_i32);
319 }
320
321 #[test]
322 fn convert_i64_to_i32_to_i64() {
323 let wkb_array = wkt_data::<i64>();
324 let wkb_array_i32: GenericWktArray<i32> = wkb_array.clone().try_into().unwrap();
325 let wkb_array_i64: GenericWktArray<i64> = wkb_array_i32.clone().into();
326
327 assert_eq!(wkb_array, wkb_array_i64);
328 }
329
330 #[test]
332 fn allow_field_without_extension_name() {
333 let mut builder = StringBuilder::new();
335 builder.append_value("POINT(1 2)");
336 let array = Arc::new(builder.finish()) as ArrayRef;
337 let field = Field::new("geometry", array.data_type().clone(), true);
338 let _wkt_arr = GenericWktArray::<i32>::try_from((array.as_ref(), &field)).unwrap();
339
340 let mut builder = LargeStringBuilder::new();
342 builder.append_value("POINT(1 2)");
343 let array = Arc::new(builder.finish()) as ArrayRef;
344 let field = Field::new("geometry", array.data_type().clone(), true);
345 let _wkt_arr = GenericWktArray::<i64>::try_from((array.as_ref(), &field)).unwrap();
346 }
347}