pub(crate) mod coordinate;
mod point;
mod polygon;
mod wkb;
use std::fmt::Display;
use std::sync::Arc;
use geo_types::Geometry;
use geoarrow::datatypes::Crs;
use geoarrow::datatypes::Metadata;
pub use point::*;
pub use polygon::*;
use vortex_array::ArrayRef;
use vortex_array::ExecutionCtx;
use vortex_array::IntoArray;
use vortex_array::arrays::ConstantArray;
use vortex_array::arrays::ExtensionArray;
use vortex_array::arrays::extension::ExtensionArrayExt;
use vortex_array::scalar::Scalar;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_error::vortex_err;
pub use wkb::*;
pub(crate) fn geometries(
array: &ArrayRef,
ctx: &mut ExecutionCtx,
) -> VortexResult<Vec<Geometry<f64>>> {
let Some(ext) = array.dtype().as_extension_opt() else {
vortex_bail!(
"geo: operand is not a geometry extension type, was {}",
array.dtype()
);
};
let storage = array
.clone()
.execute::<ExtensionArray>(ctx)?
.storage_array()
.clone();
if ext.is::<Point>() {
point_geometries(&storage, ctx)
} else if ext.is::<Polygon>() {
polygon_geometries(&storage, ctx)
} else {
vortex_bail!("geo: unsupported geometry extension {}", array.dtype())
}
}
pub(crate) fn single_geometry(
scalar: &Scalar,
ctx: &mut ExecutionCtx,
) -> VortexResult<Geometry<f64>> {
let array = ConstantArray::new(scalar.clone(), 1).into_array();
geometries(&array, ctx)?
.pop()
.ok_or_else(|| vortex_err!("geo: constant operand decoded to no geometry"))
}
#[derive(Clone, PartialEq, Eq, Hash, prost::Message)]
pub struct GeoMetadata {
#[prost(optional, string, tag = "1")]
pub crs: Option<String>,
}
impl Display for GeoMetadata {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.crs.as_ref() {
Some(crs) => write!(f, "Geometry(crs={crs})"),
None => write!(f, "Geometry(unreferenced)"),
}
}
}
pub(crate) fn geoarrow_metadata(geo_metadata: &GeoMetadata) -> Arc<Metadata> {
Arc::new(Metadata::new(
geo_metadata
.crs
.as_ref()
.map(|crs| Crs::from_unknown_crs_type(crs.to_string()))
.unwrap_or_default(),
None,
))
}
pub(crate) fn geo_metadata_from_arrow(metadata: &Metadata) -> GeoMetadata {
let crs = metadata.crs().crs_value().map(|value| {
value
.as_str()
.map(str::to_string)
.unwrap_or_else(|| value.to_string())
});
GeoMetadata { crs }
}
#[cfg(test)]
mod tests {
use prost::Message;
use crate::extension::GeoMetadata;
#[test]
fn test_metadata() {
let meta = GeoMetadata {
crs: Some("EPSG:4326".to_string()),
};
assert_eq!(meta.to_string(), "Geometry(crs=EPSG:4326)");
let bytes = meta.encode_to_vec();
let decoded = GeoMetadata::decode(bytes.as_slice()).unwrap();
assert_eq!(decoded, meta);
}
}