vortex_geo/extension/
wkb.rs1use std::fmt::Display;
5use std::ops::Deref;
6use std::sync::Arc;
7
8use arrow_array::ArrayRef as ArrowArrayRef;
9use arrow_schema::DataType;
10use arrow_schema::Field;
11use arrow_schema::extension::ExtensionType;
12use geoarrow::array::GenericWkbArray;
13use geoarrow::array::IntoArrow;
14use geoarrow::array::WkbViewArray;
15use geoarrow::datatypes::WkbType;
16use prost::Message;
17use vortex_array::ArrayRef;
18use vortex_array::ExecutionCtx;
19use vortex_array::IntoArray;
20use vortex_array::arrays::ExtensionArray;
21use vortex_array::arrays::extension::ExtensionArrayExt;
22use vortex_array::arrow::ArrowExport;
23use vortex_array::arrow::ArrowExportVTable;
24use vortex_array::arrow::ArrowImport;
25use vortex_array::arrow::ArrowImportVTable;
26use vortex_array::arrow::ArrowSession;
27use vortex_array::arrow::ArrowSessionExt;
28use vortex_array::arrow::FromArrowArray;
29use vortex_array::dtype::DType;
30use vortex_array::dtype::extension::ExtDType;
31use vortex_array::dtype::extension::ExtId;
32use vortex_array::dtype::extension::ExtVTable;
33use vortex_array::scalar::ScalarValue;
34use vortex_error::VortexError;
35use vortex_error::VortexResult;
36use vortex_error::vortex_ensure;
37use vortex_error::vortex_err;
38use vortex_session::registry::CachedId;
39use vortex_session::registry::Id;
40use wkb::reader::GeometryType;
41
42use crate::extension::GeoMetadata;
43use crate::extension::geo_metadata_from_arrow;
44use crate::extension::geoarrow_metadata;
45
46#[derive(Debug, Clone)]
50pub struct WellKnownBinaryData {
51 ext: ExtensionArray,
52}
53
54impl WellKnownBinaryData {
55 pub fn wkb_values(&self) -> &ArrayRef {
57 self.ext.storage_array()
58 }
59
60 pub fn geo_metadata(&self) -> &GeoMetadata {
62 self.ext
63 .dtype()
64 .as_extension()
65 .metadata::<WellKnownBinary>()
66 }
67}
68
69impl TryFrom<ExtensionArray> for WellKnownBinaryData {
70 type Error = VortexError;
71
72 fn try_from(ext: ExtensionArray) -> Result<Self, Self::Error> {
73 vortex_ensure!(
74 ext.ext_dtype().is::<WellKnownBinary>(),
75 "array extension dtype {} is not a WKB",
76 ext.ext_dtype()
77 );
78
79 Ok(Self { ext })
80 }
81}
82
83#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
88pub struct WellKnownBinary;
89
90pub struct Wkb<'a>(wkb::reader::Wkb<'a>);
95
96impl<'a> Wkb<'a> {
97 pub fn try_from_bytes(bytes: &'a [u8]) -> VortexResult<Self> {
102 wkb::reader::Wkb::try_new(bytes)
103 .map_err(|e| vortex_err!("failed parsing WKB: {e}"))
104 .map(Wkb)
105 }
106}
107
108impl<'a> Display for Wkb<'a> {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 let geometry_kind = match self.0.geometry_type() {
111 GeometryType::Point => "point",
112 GeometryType::LineString => "linestring",
113 GeometryType::Polygon => "polygon",
114 GeometryType::MultiPoint => "multipoint",
115 GeometryType::MultiLineString => "multilinestring",
116 GeometryType::MultiPolygon => "multipolygon",
117 GeometryType::GeometryCollection => "geometrycollection",
118 _ => "unknown",
119 };
120 write!(f, "WKB({geometry_kind})")
122 }
123}
124
125impl<'a> Deref for Wkb<'a> {
126 type Target = wkb::reader::Wkb<'a>;
127
128 fn deref(&self) -> &wkb::reader::Wkb<'a> {
129 &self.0
130 }
131}
132
133impl ExtVTable for WellKnownBinary {
134 type Metadata = GeoMetadata;
135
136 type NativeValue<'a> = Wkb<'a>;
137
138 fn id(&self) -> ExtId {
139 static ID: CachedId = CachedId::new("vortex.geo.wkb");
140 *ID
141 }
142
143 fn serialize_metadata(&self, metadata: &Self::Metadata) -> VortexResult<Vec<u8>> {
144 Ok(metadata.encode_to_vec())
145 }
146
147 fn deserialize_metadata(&self, metadata: &[u8]) -> VortexResult<Self::Metadata> {
148 Ok(GeoMetadata::decode(metadata)?)
149 }
150
151 fn validate_dtype(ext_dtype: &ExtDType<Self>) -> VortexResult<()> {
152 vortex_ensure!(
153 ext_dtype.storage_dtype().is_binary(),
154 "geo.wkb must have binary storage type, was {}",
155 ext_dtype.storage_dtype()
156 );
157
158 Ok(())
159 }
160
161 fn unpack_native<'a>(
162 _ext_dtype: &'a ExtDType<Self>,
163 storage_value: &'a ScalarValue,
164 ) -> VortexResult<Self::NativeValue<'a>> {
165 Wkb::try_from_bytes(storage_value.as_binary().as_slice())
166 }
167}
168
169static ARROW_WKB: CachedId = CachedId::new(WkbType::NAME);
170
171impl ArrowExportVTable for WellKnownBinary {
172 fn arrow_ext_id(&self) -> Id {
173 *ARROW_WKB
174 }
175
176 fn vortex_id(&self) -> Id {
177 self.id()
178 }
179
180 fn to_arrow_field(
181 &self,
182 name: &str,
183 dtype: &DType,
184 session: &ArrowSession,
185 ) -> VortexResult<Option<Field>> {
186 let ext_type = dtype.as_extension();
187 let geo_metadata = ext_type.metadata::<WellKnownBinary>();
188
189 let mut field = session.to_arrow_field(name, ext_type.storage_dtype())?;
190 field.try_with_extension_type(wkb_type(geo_metadata))?;
191
192 Ok(Some(field))
193 }
194
195 fn execute_arrow(
196 &self,
197 array: ArrayRef,
198 target: &Field,
199 ctx: &mut ExecutionCtx,
200 ) -> VortexResult<ArrowExport> {
201 let is_wkb = array
202 .dtype()
203 .as_extension_opt()
204 .map(|ext| ext.is::<WellKnownBinary>())
205 .unwrap_or(false);
206 if !is_wkb {
207 return Ok(ArrowExport::Unsupported(array));
208 }
209
210 let Ok(wkb_meta) = target.try_extension_type::<WkbType>() else {
211 return Ok(ArrowExport::Unsupported(array));
212 };
213
214 let executed = array.execute::<ExtensionArray>(ctx)?;
215 let storage = executed.storage_array().clone();
216
217 let storage_field = Field::new(
218 String::new(),
219 target.data_type().clone(),
220 target.is_nullable(),
221 );
222 let session = ctx.session().clone();
223 let arrow_storage = session
224 .arrow()
225 .execute_arrow(storage, Some(&storage_field), ctx)?;
226
227 let arrow_ref: ArrowArrayRef = match target.data_type() {
231 DataType::Binary => Arc::new(
232 GenericWkbArray::<i32>::try_from((arrow_storage.as_ref(), wkb_meta))
233 .map_err(|e| vortex_err!("failed to construct WkbArray: {e}"))?
234 .into_arrow(),
235 ),
236 DataType::LargeBinary => Arc::new(
237 GenericWkbArray::<i64>::try_from((arrow_storage.as_ref(), wkb_meta))
238 .map_err(|e| vortex_err!("failed to construct LargeWkbArray: {e}"))?
239 .into_arrow(),
240 ),
241 DataType::BinaryView => Arc::new(
242 WkbViewArray::try_from((arrow_storage.as_ref(), wkb_meta))
243 .map_err(|e| vortex_err!("failed to construct WkbViewArray: {e}"))?
244 .into_arrow(),
245 ),
246 _ => unreachable!("target data type was validated above"),
247 };
248
249 Ok(ArrowExport::Exported(arrow_ref))
250 }
251}
252
253impl ArrowImportVTable for WellKnownBinary {
254 fn arrow_ext_id(&self) -> Id {
255 *ARROW_WKB
256 }
257
258 fn from_arrow_field(&self, field: &Field) -> VortexResult<Option<DType>> {
259 let Ok(wkb_meta) = field.try_extension_type::<WkbType>() else {
260 return Ok(None);
261 };
262
263 let storage_dtype = DType::Binary(field.is_nullable().into());
264 Ok(Some(DType::Extension(
265 ExtDType::try_with_vtable(WellKnownBinary, geo_metadata(&wkb_meta), storage_dtype)?
266 .erased(),
267 )))
268 }
269
270 fn from_arrow_array(
271 &self,
272 array: ArrowArrayRef,
273 field: &Field,
274 dtype: &DType,
275 ) -> VortexResult<ArrowImport> {
276 let Some(ext_dtype) = dtype.as_extension_opt() else {
277 return Ok(ArrowImport::Unsupported(array));
278 };
279 if !ext_dtype.is::<WellKnownBinary>()
280 || field.try_extension_type::<WkbType>().is_err()
281 || !matches!(
282 array.data_type(),
283 DataType::Binary | DataType::LargeBinary | DataType::BinaryView
284 )
285 {
286 return Ok(ArrowImport::Unsupported(array));
287 }
288
289 let storage = ArrayRef::from_arrow(array.as_ref(), field.is_nullable())?;
290 Ok(ArrowImport::Imported(
291 ExtensionArray::new(ext_dtype.clone(), storage).into_array(),
292 ))
293 }
294}
295
296fn wkb_type(geo_metadata: &GeoMetadata) -> WkbType {
297 WkbType::new(geoarrow_metadata(geo_metadata))
298}
299
300fn geo_metadata(wkb_type: &WkbType) -> GeoMetadata {
301 geo_metadata_from_arrow(wkb_type.metadata())
302}