1pub(crate) mod coordinate;
5mod linestring;
6mod multilinestring;
7mod multipoint;
8mod multipolygon;
9mod point;
10mod polygon;
11mod wkb;
12
13use std::fmt::Display;
14use std::sync::Arc;
15
16use ::wkb::reader::GeometryType;
17use arrow_array::BinaryArray;
18use geo_types::Geometry;
19use geoarrow::array::GenericWkbArray;
20use geoarrow::array::GeoArrowArray;
21use geoarrow::datatypes::CoordType;
22use geoarrow::datatypes::Crs;
23use geoarrow::datatypes::Dimension;
24use geoarrow::datatypes::GeoArrowType;
25use geoarrow::datatypes::LineStringType;
26use geoarrow::datatypes::Metadata;
27use geoarrow::datatypes::MultiLineStringType;
28use geoarrow::datatypes::MultiPointType;
29use geoarrow::datatypes::MultiPolygonType;
30use geoarrow::datatypes::PointType;
31use geoarrow::datatypes::PolygonType;
32use geoarrow::datatypes::WkbType;
33use geoarrow_cast::cast::cast;
34pub use linestring::*;
35pub use multilinestring::*;
36pub use multipoint::*;
37pub use multipolygon::*;
38pub use point::*;
39pub use polygon::*;
40use vortex_array::ArrayRef;
41use vortex_array::ExecutionCtx;
42use vortex_array::IntoArray;
43use vortex_array::arrays::ConstantArray;
44use vortex_array::arrays::ExtensionArray;
45use vortex_array::arrays::extension::ExtensionArrayExt;
46use vortex_array::arrow::FromArrowArray;
47use vortex_array::dtype::extension::ExtDType;
48use vortex_array::dtype::extension::ExtVTable;
49use vortex_array::scalar::Scalar;
50use vortex_error::VortexResult;
51use vortex_error::vortex_bail;
52use vortex_error::vortex_err;
53pub use wkb::*;
54
55pub(crate) fn geometries(
57 array: &ArrayRef,
58 ctx: &mut ExecutionCtx,
59) -> VortexResult<Vec<Geometry<f64>>> {
60 let Some(ext) = array.dtype().as_extension_opt() else {
61 vortex_bail!(
62 "geo: operand is not a geometry extension type, was {}",
63 array.dtype()
64 );
65 };
66 let storage = array
67 .clone()
68 .execute::<ExtensionArray>(ctx)?
69 .storage_array()
70 .clone();
71 if ext.is::<Point>() {
72 point_geometries(&storage, ctx)
73 } else if ext.is::<LineString>() {
74 linestring_geometries(&storage, ctx)
75 } else if ext.is::<MultiPoint>() {
76 multipoint_geometries(&storage, ctx)
77 } else if ext.is::<Polygon>() {
78 polygon_geometries(&storage, ctx)
79 } else if ext.is::<MultiLineString>() {
80 multilinestring_geometries(&storage, ctx)
81 } else if ext.is::<MultiPolygon>() {
82 multipolygon_geometries(&storage, ctx)
83 } else {
84 vortex_bail!("geo: unsupported geometry extension {}", array.dtype())
85 }
86}
87
88pub(crate) fn single_geometry(
91 scalar: &Scalar,
92 ctx: &mut ExecutionCtx,
93) -> VortexResult<Geometry<f64>> {
94 let array = ConstantArray::new(scalar.clone(), 1).into_array();
95 geometries(&array, ctx)?
96 .pop()
97 .ok_or_else(|| vortex_err!("geo: constant operand decoded to no geometry"))
98}
99
100pub fn native_geometry_scalar_from_wkb(bytes: &[u8]) -> VortexResult<Option<Scalar>> {
103 let metadata = geoarrow_metadata(&GeoMetadata::default());
104 let binary = BinaryArray::from(vec![Some(bytes)]);
105 let wkb = GenericWkbArray::<i32>::try_from((
106 &binary as &dyn arrow_array::Array,
107 WkbType::new(Arc::clone(&metadata)),
108 ))
109 .map_err(|e| vortex_err!("failed to read WKB literal: {e}"))?;
110
111 let to_storage = |target: &GeoArrowType| -> VortexResult<ArrayRef> {
113 let native =
114 cast(&wkb, target).map_err(|e| vortex_err!("failed to cast WKB literal: {e}"))?;
115 ArrayRef::from_arrow(native.to_array_ref().as_ref(), false)
116 };
117
118 let scalar = match Wkb::try_from_bytes(bytes)?.geometry_type() {
119 GeometryType::Point => {
120 let target = GeoArrowType::Point(
121 PointType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
122 );
123 geo_ext_scalar(Point, to_storage(&target)?)?
124 }
125 GeometryType::LineString => {
126 let target = GeoArrowType::LineString(
127 LineStringType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
128 );
129 geo_ext_scalar(LineString, to_storage(&target)?)?
130 }
131 GeometryType::Polygon => {
132 let target = GeoArrowType::Polygon(
133 PolygonType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
134 );
135 geo_ext_scalar(Polygon, to_storage(&target)?)?
136 }
137 GeometryType::MultiPoint => {
138 let target = GeoArrowType::MultiPoint(
139 MultiPointType::new(Dimension::XY, metadata).with_coord_type(CoordType::Separated),
140 );
141 geo_ext_scalar(MultiPoint, to_storage(&target)?)?
142 }
143 GeometryType::MultiLineString => {
144 let target = GeoArrowType::MultiLineString(
145 MultiLineStringType::new(Dimension::XY, metadata)
146 .with_coord_type(CoordType::Separated),
147 );
148 geo_ext_scalar(MultiLineString, to_storage(&target)?)?
149 }
150 GeometryType::MultiPolygon => {
151 let target = GeoArrowType::MultiPolygon(
152 MultiPolygonType::new(Dimension::XY, metadata)
153 .with_coord_type(CoordType::Separated),
154 );
155 geo_ext_scalar(MultiPolygon, to_storage(&target)?)?
156 }
157 _ => return Ok(None),
158 };
159 Ok(Some(scalar))
160}
161
162#[allow(deprecated)]
165fn geo_ext_scalar<V: ExtVTable<Metadata = GeoMetadata>>(
166 vtable: V,
167 storage: ArrayRef,
168) -> VortexResult<Scalar> {
169 let ext = ExtDType::try_with_vtable(vtable, GeoMetadata::default(), storage.dtype().clone())?
170 .erased();
171 ExtensionArray::try_new(ext, storage)?
172 .into_array()
173 .scalar_at(0)
174}
175
176#[derive(Clone, PartialEq, Eq, Hash, prost::Message)]
182pub struct GeoMetadata {
183 #[prost(optional, string, tag = "1")]
184 pub crs: Option<String>,
185}
186
187impl Display for GeoMetadata {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 match self.crs.as_ref() {
190 Some(crs) => write!(f, "Geometry(crs={crs})"),
191 None => write!(f, "Geometry(unreferenced)"),
192 }
193 }
194}
195
196pub(crate) fn geoarrow_metadata(geo_metadata: &GeoMetadata) -> Arc<Metadata> {
198 Arc::new(Metadata::new(
199 geo_metadata
200 .crs
201 .as_ref()
202 .map(|crs| Crs::from_unknown_crs_type(crs.to_string()))
203 .unwrap_or_default(),
204 None,
205 ))
206}
207
208pub(crate) fn geoarrow_to_wkb(geo_array: &dyn GeoArrowArray) -> VortexResult<ArrayRef> {
211 let wkb_type = GeoArrowType::WkbView(WkbType::new(geoarrow_metadata(&GeoMetadata::default())));
212 let wkb = cast(geo_array, &wkb_type)
213 .map_err(|e| vortex_err!("failed to cast geometry to WKB: {e}"))?;
214 ArrayRef::from_arrow(wkb.to_array_ref().as_ref(), false)
215}
216
217pub(crate) fn geo_metadata_from_arrow(metadata: &Metadata) -> GeoMetadata {
219 let crs = metadata.crs().crs_value().map(|value| {
220 value
224 .as_str()
225 .map(str::to_string)
226 .unwrap_or_else(|| value.to_string())
227 });
228 GeoMetadata { crs }
229}
230
231#[cfg(test)]
232mod tests {
233 use prost::Message;
234 use vortex_array::dtype::DType;
235 use vortex_error::VortexResult;
236 use vortex_error::vortex_err;
237
238 use super::LineString;
239 use super::MultiLineString;
240 use super::MultiPoint;
241 use super::Point;
242 use super::Polygon;
243 use super::native_geometry_scalar_from_wkb;
244 use crate::extension::GeoMetadata;
245
246 #[test]
247 fn test_metadata() {
248 let meta = GeoMetadata {
249 crs: Some("EPSG:4326".to_string()),
250 };
251
252 assert_eq!(meta.to_string(), "Geometry(crs=EPSG:4326)");
253 let bytes = meta.encode_to_vec();
255 let decoded = GeoMetadata::decode(bytes.as_slice()).unwrap();
256 assert_eq!(decoded, meta);
257 }
258
259 #[test]
261 fn decodes_wkb_point_to_native() -> VortexResult<()> {
262 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");
268 let DType::Extension(ext) = scalar.dtype() else {
269 panic!("expected an extension dtype, got {}", scalar.dtype());
270 };
271 assert!(ext.is::<Point>());
272 Ok(())
273 }
274
275 #[test]
277 fn decodes_wkb_polygon_to_native() -> VortexResult<()> {
278 let ring = [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (0.0, 0.0)];
279 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}"))?;
283 wkb.extend_from_slice(&ring_len.to_le_bytes());
284 for (x, y) in ring {
285 wkb.extend_from_slice(&f64::to_le_bytes(x));
286 wkb.extend_from_slice(&f64::to_le_bytes(y));
287 }
288
289 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a polygon scalar");
290 let DType::Extension(ext) = scalar.dtype() else {
291 panic!("expected an extension dtype, got {}", scalar.dtype());
292 };
293 assert!(ext.is::<Polygon>());
294 Ok(())
295 }
296
297 #[test]
299 fn decodes_wkb_linestring_to_native() -> VortexResult<()> {
300 let points = [(0.0, 0.0), (1.0, 1.0)];
301 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}"))?;
304 wkb.extend_from_slice(&len.to_le_bytes());
305 for (x, y) in points {
306 wkb.extend_from_slice(&f64::to_le_bytes(x));
307 wkb.extend_from_slice(&f64::to_le_bytes(y));
308 }
309
310 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a linestring scalar");
311 let DType::Extension(ext) = scalar.dtype() else {
312 panic!("expected an extension dtype, got {}", scalar.dtype());
313 };
314 assert!(ext.is::<LineString>());
315 Ok(())
316 }
317
318 #[test]
320 fn decodes_wkb_multipoint_to_native() -> VortexResult<()> {
321 let points = [(0.0, 0.0), (1.0, 1.0)];
322 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}"))?;
325 wkb.extend_from_slice(&len.to_le_bytes());
326 for (x, y) in points {
327 wkb.push(1u8);
329 wkb.extend_from_slice(&1u32.to_le_bytes());
330 wkb.extend_from_slice(&f64::to_le_bytes(x));
331 wkb.extend_from_slice(&f64::to_le_bytes(y));
332 }
333
334 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a multipoint scalar");
335 let DType::Extension(ext) = scalar.dtype() else {
336 panic!("expected an extension dtype, got {}", scalar.dtype());
337 };
338 assert!(ext.is::<MultiPoint>());
339 Ok(())
340 }
341
342 #[test]
344 fn decodes_wkb_multilinestring_to_native() -> VortexResult<()> {
345 let lines = [[(0.0, 0.0), (1.0, 1.0)], [(2.0, 2.0), (3.0, 3.0)]];
346 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}"))?;
349 wkb.extend_from_slice(&num_lines.to_le_bytes());
350 for line in lines {
351 wkb.push(1u8);
353 wkb.extend_from_slice(&2u32.to_le_bytes());
354 let len = u32::try_from(line.len()).map_err(|e| vortex_err!("{e}"))?;
355 wkb.extend_from_slice(&len.to_le_bytes());
356 for (x, y) in line {
357 wkb.extend_from_slice(&f64::to_le_bytes(x));
358 wkb.extend_from_slice(&f64::to_le_bytes(y));
359 }
360 }
361
362 let scalar = native_geometry_scalar_from_wkb(&wkb)?.expect("a multilinestring scalar");
363 let DType::Extension(ext) = scalar.dtype() else {
364 panic!("expected an extension dtype, got {}", scalar.dtype());
365 };
366 assert!(ext.is::<MultiLineString>());
367 Ok(())
368 }
369}