1use std::sync::Arc;
5
6use vortex_array::arrow::ArrowSessionExt;
7use vortex_array::dtype::session::DTypeSessionExt;
8use vortex_array::scalar_fn::session::ScalarFnSessionExt;
9use vortex_session::VortexSession;
10
11use crate::extension::Point;
12use crate::extension::WellKnownBinary;
13use crate::scalar_fn::distance::GeoDistance;
14
15pub mod extension;
16pub mod scalar_fn;
17pub fn initialize(session: &VortexSession) {
19 session.dtypes().register(WellKnownBinary);
21 session.arrow().register_exporter(Arc::new(WellKnownBinary));
22 session.arrow().register_importer(Arc::new(WellKnownBinary));
23 session.dtypes().register(Point);
24
25 session.scalar_fns().register(GeoDistance);
27}
28
29#[cfg(test)]
30mod tests {
31 use std::sync::Arc;
32 use std::sync::LazyLock;
33
34 use arrow_array::Array as _;
35 use arrow_array::ArrayRef as ArrowArrayRef;
36 use arrow_array::BinaryArray;
37 use arrow_array::BinaryViewArray;
38 use arrow_array::LargeBinaryArray;
39 use arrow_array::cast::AsArray;
40 use arrow_schema::DataType;
41 use arrow_schema::Field;
42 use arrow_schema::extension::ExtensionType as _;
43 use geo_traits::to_geo::ToGeoGeometry;
44 use geo_types::Coord;
45 use geo_types::Geometry;
46 use geo_types::LineString;
47 use geo_types::Polygon;
48 use geoarrow::datatypes::Crs;
49 use geoarrow::datatypes::Metadata;
50 use geoarrow::datatypes::WkbType;
51 use vortex_array::IntoArray;
52 use vortex_array::VortexSessionExecute;
53 use vortex_array::arrays::ExtensionArray;
54 use vortex_array::arrays::varbin::builder::VarBinBuilder;
55 use vortex_array::arrow::ArrowSessionExt;
56 use vortex_array::dtype::DType;
57 use vortex_array::dtype::Nullability;
58 use vortex_array::dtype::extension::ExtDType;
59 use vortex_array::dtype::extension::ExtVTable;
60 use vortex_array::session::ArraySession;
61 use vortex_error::VortexResult;
62 use vortex_error::vortex_err;
63 use vortex_session::VortexSession;
64 use wkb::writer::WriteOptions;
65
66 use crate::extension::GeoMetadata;
67 use crate::extension::WellKnownBinary;
68
69 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
70 let session = VortexSession::empty().with::<ArraySession>();
71 crate::initialize(&session);
72 session
73 });
74
75 #[test]
76 fn test_array() -> VortexResult<()> {
77 let mut execution_ctx = SESSION.create_execution_ctx();
78
79 let mut buf = Vec::new();
80
81 let mut builder = VarBinBuilder::<i32>::with_capacity(3);
82
83 let polygon = Geometry::Polygon(Polygon::new(
84 LineString::new(vec![
85 Coord::zero(),
86 Coord { x: 100.0, y: 0.0 },
87 Coord { x: 100.0, y: 100.0 },
88 Coord { x: 0.0, y: 100.0 },
89 Coord::zero(),
90 ]),
91 vec![],
92 ));
93
94 wkb::writer::write_geometry(&mut buf, &polygon, &WriteOptions::default())
96 .map_err(|e| vortex_err!("writing WKB failed: {e}"))?;
97
98 builder.append_value(&buf);
100 builder.append_value(&buf);
101 builder.append_value(&buf);
102
103 let dtype = ExtDType::<WellKnownBinary>::try_new(
104 GeoMetadata {
105 crs: Some("EPSG:4326".to_string()),
106 },
107 DType::Binary(Nullability::NonNullable),
108 )?;
109
110 let array = builder.finish(DType::Binary(Nullability::NonNullable));
111 let array = ExtensionArray::new(dtype.clone().erased(), array.into_array()).into_array();
112
113 for idx in 0..3 {
114 let geom = array.execute_scalar(idx, &mut execution_ctx)?;
115 let wkb = WellKnownBinary::unpack_native(&dtype, geom.value().unwrap())?;
116
117 assert_eq!(wkb.to_geometry(), polygon);
118 }
119
120 Ok(())
121 }
122
123 fn wkb_extension_array() -> VortexResult<(Vec<u8>, vortex_array::ArrayRef)> {
124 let polygon = Geometry::Polygon(Polygon::new(
125 LineString::new(vec![
126 Coord::zero(),
127 Coord { x: 100.0, y: 0.0 },
128 Coord { x: 100.0, y: 100.0 },
129 Coord { x: 0.0, y: 100.0 },
130 Coord::zero(),
131 ]),
132 vec![],
133 ));
134 let mut buf = Vec::new();
135 wkb::writer::write_geometry(&mut buf, &polygon, &WriteOptions::default())
136 .map_err(|e| vortex_err!("writing WKB failed: {e}"))?;
137
138 let mut builder = VarBinBuilder::<i32>::with_capacity(3);
139 builder.append_value(&buf);
140 builder.append_value(&buf);
141 builder.append_value(&buf);
142
143 let dtype = ExtDType::<WellKnownBinary>::try_new(
144 GeoMetadata {
145 crs: Some("EPSG:4326".to_string()),
146 },
147 DType::Binary(Nullability::NonNullable),
148 )?;
149 let storage = builder.finish(DType::Binary(Nullability::NonNullable));
150 let array = ExtensionArray::new(dtype.erased(), storage.into_array()).into_array();
151 Ok((buf, array))
152 }
153
154 #[test]
155 fn export_arrow_field_carries_wkb_extension() -> VortexResult<()> {
156 let (_, array) = wkb_extension_array()?;
157 let field = SESSION.arrow().to_arrow_field("geom", array.dtype())?;
158 assert_eq!(field.extension_type_name(), Some(WkbType::NAME));
159 assert_eq!(field.data_type(), &DataType::BinaryView);
161 Ok(())
162 }
163
164 #[test]
165 fn execute_arrow_exports_wkb_to_binary() -> VortexResult<()> {
166 let mut ctx = SESSION.create_execution_ctx();
167 let (wkb_bytes, array) = wkb_extension_array()?;
168
169 let field = Field::new("geom", DataType::Binary, false)
170 .with_extension_type(WkbType::new(Default::default()));
171 let exported = SESSION
172 .arrow()
173 .execute_arrow(array, Some(&field), &mut ctx)?;
174
175 assert_eq!(exported.data_type(), &DataType::Binary);
176 let binary = exported.as_binary::<i32>();
177 assert_eq!(binary.len(), 3);
178 for idx in 0..3 {
179 assert_eq!(binary.value(idx), wkb_bytes.as_slice());
180 }
181 Ok(())
182 }
183
184 #[test]
185 fn execute_arrow_exports_wkb_to_large_binary() -> VortexResult<()> {
186 let mut ctx = SESSION.create_execution_ctx();
187 let (wkb_bytes, array) = wkb_extension_array()?;
188
189 let field = Field::new("geom", DataType::LargeBinary, false)
190 .with_extension_type(WkbType::new(Default::default()));
191 let exported = SESSION
192 .arrow()
193 .execute_arrow(array, Some(&field), &mut ctx)?;
194
195 assert_eq!(exported.data_type(), &DataType::LargeBinary);
196 let binary = exported.as_binary::<i64>();
197 assert_eq!(binary.len(), 3);
198 for idx in 0..3 {
199 assert_eq!(binary.value(idx), wkb_bytes.as_slice());
200 }
201 Ok(())
202 }
203
204 #[test]
205 fn execute_arrow_exports_wkb_to_binary_view() -> VortexResult<()> {
206 let mut ctx = SESSION.create_execution_ctx();
207 let (wkb_bytes, array) = wkb_extension_array()?;
208
209 let field = Field::new("geom", DataType::BinaryView, false)
210 .with_extension_type(WkbType::new(Default::default()));
211 let exported = SESSION
212 .arrow()
213 .execute_arrow(array, Some(&field), &mut ctx)?;
214
215 assert_eq!(exported.data_type(), &DataType::BinaryView);
216 let binary = exported.as_binary_view();
217 assert_eq!(binary.len(), 3);
218 for idx in 0..3 {
219 assert_eq!(binary.value(idx), wkb_bytes.as_slice());
220 }
221 Ok(())
222 }
223
224 fn wkb_field(name: &str, data_type: DataType, nullable: bool, crs: Option<&str>) -> Field {
225 let crs = crs
226 .map(|crs| Crs::from_unknown_crs_type(crs.to_string()))
227 .unwrap_or_default();
228 let wkb_type = WkbType::new(Arc::new(Metadata::new(crs, None)));
229 Field::new(name, data_type, nullable).with_extension_type(wkb_type)
230 }
231
232 fn assert_imported_wkb_dtype(dtype: &DType, expected_crs: Option<&str>, nullable: bool) {
233 let DType::Extension(ext) = dtype else {
234 panic!("expected Extension dtype, got {dtype}");
235 };
236 assert!(ext.is::<WellKnownBinary>());
237 assert_eq!(ext.storage_dtype(), &DType::Binary(nullable.into()));
238 let geo = ext.metadata::<WellKnownBinary>();
239 assert_eq!(geo.crs.as_deref(), expected_crs);
240 }
241
242 #[test]
243 fn import_arrow_field_recovers_wkb_extension() -> VortexResult<()> {
244 let field = wkb_field("geom", DataType::Binary, false, Some("EPSG:4326"));
245 let dtype = SESSION.arrow().from_arrow_field(&field)?;
246 assert_imported_wkb_dtype(&dtype, Some("EPSG:4326"), false);
247 Ok(())
248 }
249
250 #[test]
251 fn import_arrow_field_without_crs() -> VortexResult<()> {
252 let field = wkb_field("geom", DataType::BinaryView, true, None);
253 let dtype = SESSION.arrow().from_arrow_field(&field)?;
254 assert_imported_wkb_dtype(&dtype, None, true);
255 Ok(())
256 }
257
258 #[test]
259 fn import_arrow_array_from_binary() -> VortexResult<()> {
260 let mut ctx = SESSION.create_execution_ctx();
261 let (wkb_bytes, _) = wkb_extension_array()?;
262 let arrow: ArrowArrayRef = Arc::new(BinaryArray::from_iter_values([
263 wkb_bytes.as_slice(),
264 wkb_bytes.as_slice(),
265 wkb_bytes.as_slice(),
266 ]));
267 let field = wkb_field("geom", DataType::Binary, false, Some("EPSG:4326"));
268
269 let imported = SESSION.arrow().from_arrow_array(arrow, &field)?;
270 assert_imported_wkb_dtype(imported.dtype(), Some("EPSG:4326"), false);
271
272 for idx in 0..3 {
273 let geom = imported.execute_scalar(idx, &mut ctx)?;
274 assert_eq!(geom.value().unwrap().as_binary().as_slice(), wkb_bytes);
275 }
276 Ok(())
277 }
278
279 #[test]
280 fn import_arrow_array_from_large_binary() -> VortexResult<()> {
281 let (wkb_bytes, _) = wkb_extension_array()?;
282 let arrow: ArrowArrayRef = Arc::new(LargeBinaryArray::from_iter_values([
283 wkb_bytes.as_slice(),
284 wkb_bytes.as_slice(),
285 ]));
286 let field = wkb_field("geom", DataType::LargeBinary, false, Some("EPSG:4326"));
287
288 let imported = SESSION.arrow().from_arrow_array(arrow, &field)?;
289 assert_imported_wkb_dtype(imported.dtype(), Some("EPSG:4326"), false);
290 assert_eq!(imported.len(), 2);
291 Ok(())
292 }
293
294 #[test]
295 fn import_arrow_array_from_binary_view() -> VortexResult<()> {
296 let (wkb_bytes, _) = wkb_extension_array()?;
297 let arrow: ArrowArrayRef = Arc::new(BinaryViewArray::from_iter_values([
298 wkb_bytes.as_slice(),
299 wkb_bytes.as_slice(),
300 wkb_bytes.as_slice(),
301 wkb_bytes.as_slice(),
302 ]));
303 let field = wkb_field("geom", DataType::BinaryView, false, None);
304
305 let imported = SESSION.arrow().from_arrow_array(arrow, &field)?;
306 assert_imported_wkb_dtype(imported.dtype(), None, false);
307 assert_eq!(imported.len(), 4);
308 Ok(())
309 }
310
311 #[test]
312 fn import_arrow_array_roundtrips_through_export() -> VortexResult<()> {
313 let mut ctx = SESSION.create_execution_ctx();
314 let (wkb_bytes, original) = wkb_extension_array()?;
315
316 let field = Field::new("geom", DataType::BinaryView, false)
317 .with_extension_type(WkbType::new(Default::default()));
318 let exported = SESSION
319 .arrow()
320 .execute_arrow(original, Some(&field), &mut ctx)?;
321 let arrow_field = wkb_field("geom", DataType::BinaryView, false, Some("EPSG:4326"));
322 let reimported = SESSION.arrow().from_arrow_array(exported, &arrow_field)?;
323
324 assert_imported_wkb_dtype(reimported.dtype(), Some("EPSG:4326"), false);
325 for idx in 0..3 {
326 let geom = reimported.execute_scalar(idx, &mut ctx)?;
327 assert_eq!(geom.value().unwrap().as_binary().as_slice(), wkb_bytes);
328 }
329 Ok(())
330 }
331}