use prost::Message;
use vortex_array::dtype::extension::ExtDType;
use vortex_array::dtype::extension::ExtId;
use vortex_array::dtype::extension::ExtVTable;
use vortex_array::scalar::Scalar;
use vortex_array::scalar::ScalarValue;
use vortex_error::VortexResult;
use super::GeoMetadata;
use super::coordinate::Coordinate;
use super::coordinate::coordinate_dimension;
use super::coordinate::coordinate_from_struct;
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct Point;
impl ExtVTable for Point {
type Metadata = GeoMetadata;
type NativeValue<'a> = Coordinate;
fn id(&self) -> ExtId {
ExtId::new_static("vortex.geo.point")
}
fn serialize_metadata(&self, metadata: &Self::Metadata) -> VortexResult<Vec<u8>> {
Ok(metadata.encode_to_vec())
}
fn deserialize_metadata(&self, metadata: &[u8]) -> VortexResult<Self::Metadata> {
Ok(GeoMetadata::decode(metadata)?)
}
fn validate_dtype(ext_dtype: &ExtDType<Self>) -> VortexResult<()> {
coordinate_dimension(ext_dtype.storage_dtype()).map(|_| ())
}
fn unpack_native<'a>(
ext_dtype: &'a ExtDType<Self>,
storage_value: &'a ScalarValue,
) -> VortexResult<Coordinate> {
let storage = Scalar::try_new(
ext_dtype.storage_dtype().clone(),
Some(storage_value.clone()),
)?;
coordinate_from_struct(&storage)
}
}
#[cfg(test)]
mod tests {
use vortex_array::IntoArray;
use vortex_array::VortexSessionExecute;
use vortex_array::arrays::ExtensionArray;
use vortex_array::arrays::PrimitiveArray;
use vortex_array::arrays::StructArray;
use vortex_array::dtype::DType;
use vortex_array::dtype::FieldNames;
use vortex_array::dtype::Nullability;
use vortex_array::dtype::PType;
use vortex_array::dtype::StructFields;
use vortex_array::dtype::extension::ExtDType;
use vortex_array::session::ArraySession;
use vortex_error::VortexResult;
use vortex_session::VortexSession;
use super::Point;
use crate::extension::GeoMetadata;
use crate::extension::coordinate::Coordinate;
use crate::extension::coordinate::Dimension;
use crate::extension::coordinate::coordinate_dimension;
use crate::extension::coordinate::coordinate_from_scalar;
fn geo_meta() -> GeoMetadata {
GeoMetadata {
crs: Some("EPSG:4326".to_string()),
}
}
fn coordinate_dtype(names: &[&'static str]) -> DType {
let fields = std::iter::repeat_n(
DType::Primitive(PType::F64, Nullability::NonNullable),
names.len(),
)
.collect::<Vec<_>>();
DType::Struct(
StructFields::new(FieldNames::from(names), fields),
Nullability::NonNullable,
)
}
#[test]
fn point_validates_every_dimension() -> VortexResult<()> {
let cases = [
(Dimension::Xy, ["x", "y"].as_slice()),
(Dimension::Xyz, ["x", "y", "z"].as_slice()),
(Dimension::Xym, ["x", "y", "m"].as_slice()),
(Dimension::Xyzm, ["x", "y", "z", "m"].as_slice()),
];
for (dim, names) in cases {
let storage = coordinate_dtype(names);
assert_eq!(coordinate_dimension(&storage)?, dim);
ExtDType::<Point>::try_new(geo_meta(), storage)?;
}
Ok(())
}
#[test]
fn point_rejects_invalid_storage() -> VortexResult<()> {
let primitive = DType::Primitive(PType::F64, Nullability::NonNullable);
assert!(ExtDType::<Point>::try_new(geo_meta(), primitive).is_err());
let wrong_fields = StructArray::from_fields(&[
("a", PrimitiveArray::from_iter(vec![0.0f64]).into_array()),
("b", PrimitiveArray::from_iter(vec![0.0f64]).into_array()),
])?
.into_array();
assert!(ExtDType::<Point>::try_new(geo_meta(), wrong_fields.dtype().clone()).is_err());
Ok(())
}
#[test]
fn point_unpacks_coordinates() -> VortexResult<()> {
let session = VortexSession::empty().with::<ArraySession>();
let mut ctx = session.create_execution_ctx();
let storage = StructArray::from_fields(&[
(
"x",
PrimitiveArray::from_iter(vec![1.0f64, -111.7610]).into_array(),
),
(
"y",
PrimitiveArray::from_iter(vec![2.0f64, 34.8697]).into_array(),
),
])?
.into_array();
let dtype = ExtDType::<Point>::try_new(geo_meta(), storage.dtype().clone())?;
let points = ExtensionArray::new(dtype.erased(), storage).into_array();
assert_eq!(
coordinate_from_scalar(&points.execute_scalar(0, &mut ctx)?)?,
Coordinate::xy(1.0, 2.0)
);
assert_eq!(
coordinate_from_scalar(&points.execute_scalar(1, &mut ctx)?)?,
Coordinate::xy(-111.7610, 34.8697)
);
Ok(())
}
}