1pub(crate) mod coordinate;
5mod linestring;
6mod multilinestring;
7mod multipoint;
8mod multipolygon;
9mod point;
10mod polygon;
11mod rect;
12mod wkb;
13
14use std::fmt::Display;
15use std::sync::Arc;
16
17use ::wkb::reader::GeometryType;
18use arrow_array::BinaryArray;
19use geo_types::Geometry;
20use geoarrow::array::GenericWkbArray;
21use geoarrow::array::GeoArrowArray;
22use geoarrow::datatypes::CoordType;
23use geoarrow::datatypes::Crs;
24use geoarrow::datatypes::Dimension;
25use geoarrow::datatypes::GeoArrowType;
26use geoarrow::datatypes::LineStringType;
27use geoarrow::datatypes::Metadata;
28use geoarrow::datatypes::MultiLineStringType;
29use geoarrow::datatypes::MultiPointType;
30use geoarrow::datatypes::MultiPolygonType;
31use geoarrow::datatypes::PointType;
32use geoarrow::datatypes::PolygonType;
33use geoarrow::datatypes::WkbType;
34use geoarrow_cast::cast::cast;
35pub use linestring::*;
36pub use multilinestring::*;
37pub use multipoint::*;
38pub use multipolygon::*;
39pub use point::*;
40pub use polygon::*;
41pub use rect::*;
42use vortex_array::ArrayRef;
43use vortex_array::ExecutionCtx;
44use vortex_array::IntoArray;
45use vortex_array::arrays::ConstantArray;
46use vortex_array::arrays::ExtensionArray;
47use vortex_array::arrays::ListViewArray;
48use vortex_array::arrays::StructArray;
49use vortex_array::arrays::extension::ExtensionArrayExt;
50use vortex_array::arrays::list::ListArraySlotsExt;
51use vortex_array::arrays::listview::ListViewArraySlotsExt;
52use vortex_array::arrays::listview::list_from_list_view;
53use vortex_array::builtins::ArrayBuiltins;
54use vortex_array::dtype::DType;
55use vortex_array::dtype::Nullability;
56use vortex_array::dtype::PType;
57use vortex_array::dtype::extension::ExtDType;
58use vortex_array::dtype::extension::ExtVTable;
59use vortex_array::scalar::Scalar;
60use vortex_arrow::FromArrowArray;
61use vortex_buffer::Buffer;
62use vortex_error::VortexResult;
63use vortex_error::vortex_bail;
64use vortex_error::vortex_err;
65pub use wkb::*;
66
67pub(crate) fn is_native_geometry(dtype: &DType) -> bool {
69 dtype.as_extension_opt().is_some_and(|ext| {
70 ext.is::<Point>()
71 || ext.is::<LineString>()
72 || ext.is::<MultiPoint>()
73 || ext.is::<Polygon>()
74 || ext.is::<MultiLineString>()
75 || ext.is::<MultiPolygon>()
76 || ext.is::<Rect>()
77 })
78}
79
80pub(crate) fn flatten_coordinates(
83 array: &ArrayRef,
84 ctx: &mut ExecutionCtx,
85) -> VortexResult<StructArray> {
86 if !is_native_geometry(array.dtype()) {
87 vortex_bail!(
88 "geo: operand is not a native geometry extension type, was {}",
89 array.dtype()
90 );
91 }
92 let mut node = array
93 .clone()
94 .execute::<ExtensionArray>(ctx)?
95 .storage_array()
96 .clone();
97 while node.dtype().is_list() {
98 node = node.execute::<ListViewArray>(ctx)?.elements().clone();
99 }
100 node.execute::<StructArray>(ctx)
101}
102
103pub(crate) fn flatten_row_offsets(
122 storage: ArrayRef,
123 ctx: &mut ExecutionCtx,
124) -> VortexResult<(Vec<usize>, StructArray)> {
125 let len = storage.len();
126
127 let mut row_offsets: Vec<usize> = (0..=len).collect();
129 let mut level = storage;
130 while level.dtype().is_list() {
131 let list = list_from_list_view(level.execute::<ListViewArray>(ctx)?, ctx)?;
132 let offsets = list
133 .offsets()
134 .clone()
135 .cast(DType::Primitive(PType::U64, Nullability::NonNullable))?
136 .execute::<Buffer<u64>>(ctx)?;
137 for row_offset in &mut row_offsets {
138 *row_offset = usize::try_from(offsets[*row_offset])
139 .map_err(|_| vortex_err!("geo: list offset exceeds usize"))?;
140 }
141 level = list.elements().clone();
142 }
143 Ok((row_offsets, level.execute::<StructArray>(ctx)?))
144}
145
146pub(crate) fn geometries(
148 array: &ArrayRef,
149 ctx: &mut ExecutionCtx,
150) -> VortexResult<Vec<Geometry<f64>>> {
151 let Some(ext) = array.dtype().as_extension_opt() else {
152 vortex_bail!(
153 "geo: operand is not a geometry extension type, was {}",
154 array.dtype()
155 );
156 };
157 let storage = array
158 .clone()
159 .execute::<ExtensionArray>(ctx)?
160 .storage_array()
161 .clone();
162 if ext.is::<Point>() {
163 point_geometries(&storage, ctx)
164 } else if ext.is::<LineString>() {
165 linestring_geometries(&storage, ctx)
166 } else if ext.is::<MultiPoint>() {
167 multipoint_geometries(&storage, ctx)
168 } else if ext.is::<Polygon>() {
169 polygon_geometries(&storage, ctx)
170 } else if ext.is::<MultiLineString>() {
171 multilinestring_geometries(&storage, ctx)
172 } else if ext.is::<MultiPolygon>() {
173 multipolygon_geometries(&storage, ctx)
174 } else if ext.is::<Rect>() {
175 rect_geometries(&storage, ctx)
176 } else {
177 vortex_bail!("geo: unsupported geometry extension {}", array.dtype())
178 }
179}
180
181pub(crate) fn single_geometry(
184 scalar: &Scalar,
185 ctx: &mut ExecutionCtx,
186) -> VortexResult<Geometry<f64>> {
187 let array = ConstantArray::new(scalar.clone(), 1).into_array();
188 geometries(&array, ctx)?
189 .pop()
190 .ok_or_else(|| vortex_err!("geo: constant operand decoded to no geometry"))
191}
192
193pub fn native_geometry_scalar_from_wkb(bytes: &[u8]) -> VortexResult<Option<Scalar>> {
196 let metadata = geoarrow_metadata(&GeoMetadata::default());
197 let binary = BinaryArray::from(vec![Some(bytes)]);
198 let wkb = GenericWkbArray::<i32>::try_from((
199 &binary as &dyn arrow_array::Array,
200 WkbType::new(Arc::clone(&metadata)),
201 ))
202 .map_err(|e| vortex_err!("failed to read WKB literal: {e}"))?;
203
204 let to_storage = |target: &GeoArrowType| -> VortexResult<ArrayRef> {
206 let native =
207 cast(&wkb, target).map_err(|e| vortex_err!("failed to cast WKB literal: {e}"))?;
208 ArrayRef::from_arrow(native.to_array_ref().as_ref(), false)
209 };
210
211 let scalar = match Wkb::try_from_bytes(bytes)?.geometry_type() {
212 GeometryType::Point => {
213 let target = GeoArrowType::Point(
214 PointType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
215 );
216 geo_ext_scalar(Point, to_storage(&target)?)?
217 }
218 GeometryType::LineString => {
219 let target = GeoArrowType::LineString(
220 LineStringType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
221 );
222 geo_ext_scalar(LineString, to_storage(&target)?)?
223 }
224 GeometryType::Polygon => {
225 let target = GeoArrowType::Polygon(
226 PolygonType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
227 );
228 geo_ext_scalar(Polygon, to_storage(&target)?)?
229 }
230 GeometryType::MultiPoint => {
231 let target = GeoArrowType::MultiPoint(
232 MultiPointType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
233 );
234 geo_ext_scalar(MultiPoint, to_storage(&target)?)?
235 }
236 GeometryType::MultiLineString => {
237 let target = GeoArrowType::MultiLineString(
238 MultiLineStringType::new(Dimension::XY, metadata)
239 .with_coord_type(CoordType::Separated),
240 );
241 geo_ext_scalar(MultiLineString, to_storage(&target)?)?
242 }
243 GeometryType::MultiPolygon => {
244 let target = GeoArrowType::MultiPolygon(
245 MultiPolygonType::new(Dimension::XY, metadata)
246 .with_coord_type(CoordType::Separated),
247 );
248 geo_ext_scalar(MultiPolygon, to_storage(&target)?)?
249 }
250 _ => return Ok(None),
251 };
252 Ok(Some(scalar))
253}
254
255#[allow(deprecated)]
258fn geo_ext_scalar<V: ExtVTable<Metadata = GeoMetadata>>(
259 vtable: V,
260 storage: ArrayRef,
261) -> VortexResult<Scalar> {
262 let ext = ExtDType::try_with_vtable(vtable, GeoMetadata::default(), storage.dtype().clone())?
263 .erased();
264 ExtensionArray::try_new(ext, storage)?
265 .into_array()
266 .scalar_at(0)
267}
268
269#[derive(Clone, PartialEq, Eq, Hash, prost::Message)]
275pub struct GeoMetadata {
276 #[prost(optional, string, tag = "1")]
277 pub crs: Option<String>,
278}
279
280impl Display for GeoMetadata {
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 match self.crs.as_ref() {
283 Some(crs) => write!(f, "Geometry(crs={crs})"),
284 None => write!(f, "Geometry(unreferenced)"),
285 }
286 }
287}
288
289pub(crate) fn geoarrow_metadata(geo_metadata: &GeoMetadata) -> Arc<Metadata> {
291 Arc::new(Metadata::new(
292 geo_metadata
293 .crs
294 .as_ref()
295 .map(|crs| Crs::from_unknown_crs_type(crs.to_string()))
296 .unwrap_or_default(),
297 None,
298 ))
299}
300
301pub(crate) fn geoarrow_to_wkb(geo_array: &dyn GeoArrowArray) -> VortexResult<ArrayRef> {
304 let wkb_type = GeoArrowType::WkbView(WkbType::new(geoarrow_metadata(&GeoMetadata::default())));
305 let wkb = cast(geo_array, &wkb_type)
306 .map_err(|e| vortex_err!("failed to cast geometry to WKB: {e}"))?;
307 ArrayRef::from_arrow(wkb.to_array_ref().as_ref(), false)
308}
309
310pub(crate) fn geo_metadata_from_arrow(metadata: &Metadata) -> GeoMetadata {
312 let crs = metadata.crs().crs_value().map(|value| {
313 value
317 .as_str()
318 .map(str::to_string)
319 .unwrap_or_else(|| value.to_string())
320 });
321 GeoMetadata { crs }
322}
323
324#[cfg(test)]
325mod tests {
326 use prost::Message;
327 use vortex_array::dtype::DType;
328 use vortex_error::VortexResult;
329 use vortex_error::vortex_err;
330
331 use super::LineString;
332 use super::MultiLineString;
333 use super::MultiPoint;
334 use super::Point;
335 use super::Polygon;
336 use super::native_geometry_scalar_from_wkb;
337 use crate::extension::GeoMetadata;
338
339 #[test]
340 fn test_metadata() {
341 let meta = GeoMetadata {
342 crs: Some("EPSG:4326".to_string()),
343 };
344
345 assert_eq!(meta.to_string(), "Geometry(crs=EPSG:4326)");
346 let bytes = meta.encode_to_vec();
348 let decoded = GeoMetadata::decode(bytes.as_slice()).unwrap();
349 assert_eq!(decoded, meta);
350 }
351
352 #[test]
354 fn decodes_wkb_point_to_native() -> VortexResult<()> {
355 let mut wkb = vec![1u8]; wkb.extend_from_slice(&1u32.to_le_bytes()); wkb.extend_from_slice(&1.0f64.to_le_bytes()); wkb.extend_from_slice(&2.0f64.to_le_bytes()); let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a point scalar");
361 let DType::Extension(ext) = scalar.dtype() else {
362 panic!("expected an extension dtype, got {}", scalar.dtype());
363 };
364 assert!(ext.is::<Point>());
365 Ok(())
366 }
367
368 #[test]
370 fn decodes_wkb_polygon_to_native() -> VortexResult<()> {
371 let ring = [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (0.0, 0.0)];
372 let mut wkb = vec![1u8]; wkb.extend_from_slice(&3u32.to_le_bytes()); wkb.extend_from_slice(&1u32.to_le_bytes()); let ring_len = u32::try_from(ring.len()).map_err(|e| vortex_err!("{e}"))?;
376 wkb.extend_from_slice(&ring_len.to_le_bytes());
377 for (x, y) in ring {
378 wkb.extend_from_slice(&f64::to_le_bytes(x));
379 wkb.extend_from_slice(&f64::to_le_bytes(y));
380 }
381
382 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a polygon scalar");
383 let DType::Extension(ext) = scalar.dtype() else {
384 panic!("expected an extension dtype, got {}", scalar.dtype());
385 };
386 assert!(ext.is::<Polygon>());
387 Ok(())
388 }
389
390 #[test]
392 fn decodes_wkb_linestring_to_native() -> VortexResult<()> {
393 let points = [(0.0, 0.0), (1.0, 1.0)];
394 let mut wkb = vec![1u8]; wkb.extend_from_slice(&2u32.to_le_bytes()); let len = u32::try_from(points.len()).map_err(|e| vortex_err!("{e}"))?;
397 wkb.extend_from_slice(&len.to_le_bytes());
398 for (x, y) in points {
399 wkb.extend_from_slice(&f64::to_le_bytes(x));
400 wkb.extend_from_slice(&f64::to_le_bytes(y));
401 }
402
403 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a linestring scalar");
404 let DType::Extension(ext) = scalar.dtype() else {
405 panic!("expected an extension dtype, got {}", scalar.dtype());
406 };
407 assert!(ext.is::<LineString>());
408 Ok(())
409 }
410
411 #[test]
413 fn decodes_wkb_multipoint_to_native() -> VortexResult<()> {
414 let points = [(0.0, 0.0), (1.0, 1.0)];
415 let mut wkb = vec![1u8]; wkb.extend_from_slice(&4u32.to_le_bytes()); let len = u32::try_from(points.len()).map_err(|e| vortex_err!("{e}"))?;
418 wkb.extend_from_slice(&len.to_le_bytes());
419 for (x, y) in points {
420 wkb.push(1u8);
422 wkb.extend_from_slice(&1u32.to_le_bytes());
423 wkb.extend_from_slice(&f64::to_le_bytes(x));
424 wkb.extend_from_slice(&f64::to_le_bytes(y));
425 }
426
427 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a multipoint scalar");
428 let DType::Extension(ext) = scalar.dtype() else {
429 panic!("expected an extension dtype, got {}", scalar.dtype());
430 };
431 assert!(ext.is::<MultiPoint>());
432 Ok(())
433 }
434
435 #[test]
437 fn decodes_wkb_multilinestring_to_native() -> VortexResult<()> {
438 let lines = [[(0.0, 0.0), (1.0, 1.0)], [(2.0, 2.0), (3.0, 3.0)]];
439 let mut wkb = vec![1u8]; wkb.extend_from_slice(&5u32.to_le_bytes()); let num_lines = u32::try_from(lines.len()).map_err(|e| vortex_err!("{e}"))?;
442 wkb.extend_from_slice(&num_lines.to_le_bytes());
443 for line in lines {
444 wkb.push(1u8);
446 wkb.extend_from_slice(&2u32.to_le_bytes());
447 let len = u32::try_from(line.len()).map_err(|e| vortex_err!("{e}"))?;
448 wkb.extend_from_slice(&len.to_le_bytes());
449 for (x, y) in line {
450 wkb.extend_from_slice(&f64::to_le_bytes(x));
451 wkb.extend_from_slice(&f64::to_le_bytes(y));
452 }
453 }
454
455 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a multilinestring scalar");
456 let DType::Extension(ext) = scalar.dtype() else {
457 panic!("expected an extension dtype, got {}", scalar.dtype());
458 };
459 assert!(ext.is::<MultiLineString>());
460 Ok(())
461 }
462}