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::ListArrayExt;
51use vortex_array::arrays::listview::ListViewArrayExt;
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_ensure;
65use vortex_error::vortex_err;
66pub use wkb::*;
67
68pub(crate) fn is_native_geometry(dtype: &DType) -> bool {
70 dtype.as_extension_opt().is_some_and(|ext| {
71 ext.is::<Point>()
72 || ext.is::<LineString>()
73 || ext.is::<MultiPoint>()
74 || ext.is::<Polygon>()
75 || ext.is::<MultiLineString>()
76 || ext.is::<MultiPolygon>()
77 || ext.is::<Rect>()
78 })
79}
80
81pub(crate) fn validate_geometry_operands(dtypes: &[DType]) -> VortexResult<()> {
87 for dtype in dtypes {
88 vortex_ensure!(
89 is_native_geometry(dtype),
90 "geo: operand {dtype} is not a native geometry type"
91 );
92 }
93 Ok(())
94}
95
96pub(crate) fn flatten_coordinates(
99 array: &ArrayRef,
100 ctx: &mut ExecutionCtx,
101) -> VortexResult<StructArray> {
102 if !is_native_geometry(array.dtype()) {
103 vortex_bail!(
104 "geo: operand is not a native geometry extension type, was {}",
105 array.dtype()
106 );
107 }
108 let mut node = array
109 .clone()
110 .execute::<ExtensionArray>(ctx)?
111 .storage_array()
112 .clone();
113 while node.dtype().is_list() {
114 node = node.execute::<ListViewArray>(ctx)?.elements().clone();
115 }
116 node.execute::<StructArray>(ctx)
117}
118
119pub(crate) fn flatten_row_offsets(
138 storage: ArrayRef,
139 ctx: &mut ExecutionCtx,
140) -> VortexResult<(Vec<usize>, StructArray)> {
141 let len = storage.len();
142
143 let mut row_offsets: Vec<usize> = (0..=len).collect();
145 let mut level = storage;
146 while level.dtype().is_list() {
147 let list = list_from_list_view(level.execute::<ListViewArray>(ctx)?, ctx)?;
148 let offsets = list
149 .offsets()
150 .clone()
151 .cast(DType::Primitive(PType::U64, Nullability::NonNullable))?
152 .execute::<Buffer<u64>>(ctx)?;
153 for row_offset in &mut row_offsets {
154 *row_offset = usize::try_from(offsets[*row_offset])
155 .map_err(|_| vortex_err!("geo: list offset exceeds usize"))?;
156 }
157 level = list.elements().clone();
158 }
159 Ok((row_offsets, level.execute::<StructArray>(ctx)?))
160}
161
162pub(crate) fn geometries(
164 array: &ArrayRef,
165 ctx: &mut ExecutionCtx,
166) -> VortexResult<Vec<Geometry<f64>>> {
167 let Some(ext) = array.dtype().as_extension_opt() else {
168 vortex_bail!(
169 "geo: operand is not a geometry extension type, was {}",
170 array.dtype()
171 );
172 };
173 let storage = array
174 .clone()
175 .execute::<ExtensionArray>(ctx)?
176 .storage_array()
177 .clone();
178 if ext.is::<Point>() {
179 point_geometries(&storage, ctx)
180 } else if ext.is::<LineString>() {
181 linestring_geometries(&storage, ctx)
182 } else if ext.is::<MultiPoint>() {
183 multipoint_geometries(&storage, ctx)
184 } else if ext.is::<Polygon>() {
185 polygon_geometries(&storage, ctx)
186 } else if ext.is::<MultiLineString>() {
187 multilinestring_geometries(&storage, ctx)
188 } else if ext.is::<MultiPolygon>() {
189 multipolygon_geometries(&storage, ctx)
190 } else if ext.is::<Rect>() {
191 rect_geometries(&storage, ctx)
192 } else {
193 vortex_bail!("geo: unsupported geometry extension {}", array.dtype())
194 }
195}
196
197pub(crate) fn single_geometry(
200 scalar: &Scalar,
201 ctx: &mut ExecutionCtx,
202) -> VortexResult<Geometry<f64>> {
203 let array = ConstantArray::new(scalar.clone(), 1).into_array();
204 geometries(&array, ctx)?
205 .pop()
206 .ok_or_else(|| vortex_err!("geo: constant operand decoded to no geometry"))
207}
208
209pub fn native_geometry_scalar_from_wkb(bytes: &[u8]) -> VortexResult<Option<Scalar>> {
212 let metadata = geoarrow_metadata(&GeoMetadata::default());
213 let binary = BinaryArray::from(vec![Some(bytes)]);
214 let wkb = GenericWkbArray::<i32>::try_from((
215 &binary as &dyn arrow_array::Array,
216 WkbType::new(Arc::clone(&metadata)),
217 ))
218 .map_err(|e| vortex_err!("failed to read WKB literal: {e}"))?;
219
220 let to_storage = |target: &GeoArrowType| -> VortexResult<ArrayRef> {
222 let native =
223 cast(&wkb, target).map_err(|e| vortex_err!("failed to cast WKB literal: {e}"))?;
224 ArrayRef::from_arrow(native.to_array_ref().as_ref(), false)
225 };
226
227 let scalar = match Wkb::try_from_bytes(bytes)?.geometry_type() {
228 GeometryType::Point => {
229 let target = GeoArrowType::Point(
230 PointType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
231 );
232 geo_ext_scalar(Point, to_storage(&target)?)?
233 }
234 GeometryType::LineString => {
235 let target = GeoArrowType::LineString(
236 LineStringType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
237 );
238 geo_ext_scalar(LineString, to_storage(&target)?)?
239 }
240 GeometryType::Polygon => {
241 let target = GeoArrowType::Polygon(
242 PolygonType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
243 );
244 geo_ext_scalar(Polygon, to_storage(&target)?)?
245 }
246 GeometryType::MultiPoint => {
247 let target = GeoArrowType::MultiPoint(
248 MultiPointType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
249 );
250 geo_ext_scalar(MultiPoint, to_storage(&target)?)?
251 }
252 GeometryType::MultiLineString => {
253 let target = GeoArrowType::MultiLineString(
254 MultiLineStringType::new(Dimension::XY, metadata)
255 .with_coord_type(CoordType::Separated),
256 );
257 geo_ext_scalar(MultiLineString, to_storage(&target)?)?
258 }
259 GeometryType::MultiPolygon => {
260 let target = GeoArrowType::MultiPolygon(
261 MultiPolygonType::new(Dimension::XY, metadata)
262 .with_coord_type(CoordType::Separated),
263 );
264 geo_ext_scalar(MultiPolygon, to_storage(&target)?)?
265 }
266 _ => return Ok(None),
267 };
268 Ok(Some(scalar))
269}
270
271#[allow(deprecated)]
274fn geo_ext_scalar<V: ExtVTable<Metadata = GeoMetadata>>(
275 vtable: V,
276 storage: ArrayRef,
277) -> VortexResult<Scalar> {
278 let ext = ExtDType::try_with_vtable(vtable, GeoMetadata::default(), storage.dtype().clone())?
279 .erased();
280 ExtensionArray::try_new(ext, storage)?
281 .into_array()
282 .scalar_at(0)
283}
284
285#[derive(Clone, PartialEq, Eq, Hash, prost::Message)]
291pub struct GeoMetadata {
292 #[prost(optional, string, tag = "1")]
293 pub crs: Option<String>,
294}
295
296impl Display for GeoMetadata {
297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298 match self.crs.as_ref() {
299 Some(crs) => write!(f, "Geometry(crs={crs})"),
300 None => write!(f, "Geometry(unreferenced)"),
301 }
302 }
303}
304
305pub(crate) fn geoarrow_metadata(geo_metadata: &GeoMetadata) -> Arc<Metadata> {
307 Arc::new(Metadata::new(
308 geo_metadata
309 .crs
310 .as_ref()
311 .map(|crs| Crs::from_unknown_crs_type(crs.to_string()))
312 .unwrap_or_default(),
313 None,
314 ))
315}
316
317pub(crate) fn geoarrow_to_wkb(geo_array: &dyn GeoArrowArray) -> VortexResult<ArrayRef> {
320 let wkb_type = GeoArrowType::WkbView(WkbType::new(geoarrow_metadata(&GeoMetadata::default())));
321 let wkb = cast(geo_array, &wkb_type)
322 .map_err(|e| vortex_err!("failed to cast geometry to WKB: {e}"))?;
323 ArrayRef::from_arrow(wkb.to_array_ref().as_ref(), false)
324}
325
326pub(crate) fn geo_metadata_from_arrow(metadata: &Metadata) -> GeoMetadata {
328 let crs = metadata.crs().crs_value().map(|value| {
329 value
333 .as_str()
334 .map(str::to_string)
335 .unwrap_or_else(|| value.to_string())
336 });
337 GeoMetadata { crs }
338}
339
340#[cfg(test)]
341mod tests {
342 use prost::Message;
343 use vortex_array::dtype::DType;
344 use vortex_error::VortexResult;
345 use vortex_error::vortex_err;
346
347 use super::LineString;
348 use super::MultiLineString;
349 use super::MultiPoint;
350 use super::Point;
351 use super::Polygon;
352 use super::native_geometry_scalar_from_wkb;
353 use crate::extension::GeoMetadata;
354
355 #[test]
356 fn test_metadata() {
357 let meta = GeoMetadata {
358 crs: Some("EPSG:4326".to_string()),
359 };
360
361 assert_eq!(meta.to_string(), "Geometry(crs=EPSG:4326)");
362 let bytes = meta.encode_to_vec();
364 let decoded = GeoMetadata::decode(bytes.as_slice()).unwrap();
365 assert_eq!(decoded, meta);
366 }
367
368 #[test]
370 fn decodes_wkb_point_to_native() -> VortexResult<()> {
371 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");
377 let DType::Extension(ext) = scalar.dtype() else {
378 panic!("expected an extension dtype, got {}", scalar.dtype());
379 };
380 assert!(ext.is::<Point>());
381 Ok(())
382 }
383
384 #[test]
386 fn decodes_wkb_polygon_to_native() -> VortexResult<()> {
387 let ring = [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (0.0, 0.0)];
388 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}"))?;
392 wkb.extend_from_slice(&ring_len.to_le_bytes());
393 for (x, y) in ring {
394 wkb.extend_from_slice(&f64::to_le_bytes(x));
395 wkb.extend_from_slice(&f64::to_le_bytes(y));
396 }
397
398 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a polygon scalar");
399 let DType::Extension(ext) = scalar.dtype() else {
400 panic!("expected an extension dtype, got {}", scalar.dtype());
401 };
402 assert!(ext.is::<Polygon>());
403 Ok(())
404 }
405
406 #[test]
408 fn decodes_wkb_linestring_to_native() -> VortexResult<()> {
409 let points = [(0.0, 0.0), (1.0, 1.0)];
410 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}"))?;
413 wkb.extend_from_slice(&len.to_le_bytes());
414 for (x, y) in points {
415 wkb.extend_from_slice(&f64::to_le_bytes(x));
416 wkb.extend_from_slice(&f64::to_le_bytes(y));
417 }
418
419 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a linestring scalar");
420 let DType::Extension(ext) = scalar.dtype() else {
421 panic!("expected an extension dtype, got {}", scalar.dtype());
422 };
423 assert!(ext.is::<LineString>());
424 Ok(())
425 }
426
427 #[test]
429 fn decodes_wkb_multipoint_to_native() -> VortexResult<()> {
430 let points = [(0.0, 0.0), (1.0, 1.0)];
431 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}"))?;
434 wkb.extend_from_slice(&len.to_le_bytes());
435 for (x, y) in points {
436 wkb.push(1u8);
438 wkb.extend_from_slice(&1u32.to_le_bytes());
439 wkb.extend_from_slice(&f64::to_le_bytes(x));
440 wkb.extend_from_slice(&f64::to_le_bytes(y));
441 }
442
443 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a multipoint scalar");
444 let DType::Extension(ext) = scalar.dtype() else {
445 panic!("expected an extension dtype, got {}", scalar.dtype());
446 };
447 assert!(ext.is::<MultiPoint>());
448 Ok(())
449 }
450
451 #[test]
453 fn decodes_wkb_multilinestring_to_native() -> VortexResult<()> {
454 let lines = [[(0.0, 0.0), (1.0, 1.0)], [(2.0, 2.0), (3.0, 3.0)]];
455 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}"))?;
458 wkb.extend_from_slice(&num_lines.to_le_bytes());
459 for line in lines {
460 wkb.push(1u8);
462 wkb.extend_from_slice(&2u32.to_le_bytes());
463 let len = u32::try_from(line.len()).map_err(|e| vortex_err!("{e}"))?;
464 wkb.extend_from_slice(&len.to_le_bytes());
465 for (x, y) in line {
466 wkb.extend_from_slice(&f64::to_le_bytes(x));
467 wkb.extend_from_slice(&f64::to_le_bytes(y));
468 }
469 }
470
471 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a multilinestring scalar");
472 let DType::Extension(ext) = scalar.dtype() else {
473 panic!("expected an extension dtype, got {}", scalar.dtype());
474 };
475 assert!(ext.is::<MultiLineString>());
476 Ok(())
477 }
478}