use vortex_array::ArrayRef;
use vortex_array::IntoArray;
use vortex_array::arrays::ExtensionArray;
use vortex_array::arrays::ListArray;
use vortex_array::arrays::PrimitiveArray;
use vortex_array::arrays::StructArray;
use vortex_array::dtype::DType;
use vortex_array::dtype::Nullability;
use vortex_array::dtype::extension::ExtDType;
use vortex_array::dtype::extension::ExtVTable;
use vortex_array::scalar::Scalar;
use vortex_array::validity::Validity;
use vortex_error::VortexResult;
use vortex_error::vortex_err;
use vortex_session::VortexSession;
use crate::extension::GeoMetadata;
use crate::extension::LineString;
use crate::extension::MultiLineString;
use crate::extension::MultiPoint;
use crate::extension::MultiPolygon;
use crate::extension::Point;
use crate::extension::Polygon;
use crate::extension::Rect;
use crate::extension::box_storage_dtype;
use crate::extension::coordinate::Coordinate;
use crate::extension::coordinate::Dimension;
use crate::extension::coordinate::coordinate_from_struct;
use crate::extension::linestring_storage_dtype;
use crate::extension::multilinestring_storage_dtype;
use crate::extension::multipoint_storage_dtype;
use crate::extension::multipolygon_storage_dtype;
use crate::extension::polygon_storage_dtype;
pub(crate) fn geo_session() -> VortexSession {
let session = vortex_array::array_session();
crate::initialize(&session);
session
}
fn wgs84() -> GeoMetadata {
GeoMetadata {
crs: Some("EPSG:4326".to_string()),
}
}
fn xy_struct(xs: Vec<f64>, ys: Vec<f64>) -> VortexResult<ArrayRef> {
Ok(StructArray::from_fields(&[
("x", PrimitiveArray::from_iter(xs).into_array()),
("y", PrimitiveArray::from_iter(ys).into_array()),
])?
.into_array())
}
fn offset(n: usize) -> VortexResult<i32> {
i32::try_from(n).map_err(|_| vortex_err!("geometry offset overflow"))
}
fn nest<T>(rows: &[Vec<T>], elements: ArrayRef) -> VortexResult<ArrayRef> {
let mut offsets = vec![0i32];
let mut len = 0usize;
for row in rows {
len += row.len();
offsets.push(offset(len)?);
}
Ok(ListArray::try_new(
elements,
PrimitiveArray::from_iter(offsets).into_array(),
Validity::NonNullable,
)?
.into_array())
}
fn vertex_lists(rows: &[Vec<(f64, f64)>]) -> VortexResult<ArrayRef> {
let (xs, ys) = rows.iter().flatten().copied().unzip();
nest(rows, xy_struct(xs, ys)?)
}
fn vertex_list_lists(rows: &[Vec<Vec<(f64, f64)>>]) -> VortexResult<ArrayRef> {
let inner: Vec<Vec<(f64, f64)>> = rows.iter().flatten().cloned().collect();
nest(rows, vertex_lists(&inner)?)
}
fn geo_column<V: ExtVTable<Metadata = GeoMetadata> + Default>(
storage: ArrayRef,
storage_dtype: DType,
) -> VortexResult<ArrayRef> {
let dtype = ExtDType::<V>::try_new(wgs84(), storage_dtype)?;
Ok(ExtensionArray::try_new(dtype.erased(), storage)?.into_array())
}
pub(crate) fn point_column(xs: Vec<f64>, ys: Vec<f64>) -> VortexResult<ArrayRef> {
let storage = xy_struct(xs, ys)?;
let storage_dtype = storage.dtype().clone();
geo_column::<Point>(storage, storage_dtype)
}
pub(crate) fn linestring_column(lines: Vec<Vec<(f64, f64)>>) -> VortexResult<ArrayRef> {
geo_column::<LineString>(
vertex_lists(&lines)?,
linestring_storage_dtype(Dimension::Xy, Nullability::NonNullable),
)
}
pub(crate) fn multipoint_column(points: Vec<Vec<(f64, f64)>>) -> VortexResult<ArrayRef> {
geo_column::<MultiPoint>(
vertex_lists(&points)?,
multipoint_storage_dtype(Dimension::Xy, Nullability::NonNullable),
)
}
pub(crate) fn polygon_column(polygons: Vec<Vec<Vec<(f64, f64)>>>) -> VortexResult<ArrayRef> {
geo_column::<Polygon>(
vertex_list_lists(&polygons)?,
polygon_storage_dtype(Dimension::Xy, Nullability::NonNullable),
)
}
pub(crate) fn multilinestring_column(
multilines: Vec<Vec<Vec<(f64, f64)>>>,
) -> VortexResult<ArrayRef> {
geo_column::<MultiLineString>(
vertex_list_lists(&multilines)?,
multilinestring_storage_dtype(Dimension::Xy, Nullability::NonNullable),
)
}
pub(crate) type MultiPolygonRings = Vec<Vec<Vec<(f64, f64)>>>;
pub(crate) fn multipolygon_column(multipolygons: Vec<MultiPolygonRings>) -> VortexResult<ArrayRef> {
let polygons: Vec<Vec<Vec<(f64, f64)>>> = multipolygons.iter().flatten().cloned().collect();
geo_column::<MultiPolygon>(
nest(&multipolygons, vertex_list_lists(&polygons)?)?,
multipolygon_storage_dtype(Dimension::Xy, Nullability::NonNullable),
)
}
pub(crate) fn rect_column(boxes: Vec<(f64, f64, f64, f64)>) -> VortexResult<ArrayRef> {
let field = |select: fn(&(f64, f64, f64, f64)) -> f64| {
PrimitiveArray::from_iter(boxes.iter().map(select)).into_array()
};
let storage = StructArray::from_fields(&[
("xmin", field(|b| b.0)),
("ymin", field(|b| b.1)),
("xmax", field(|b| b.2)),
("ymax", field(|b| b.3)),
])?
.into_array();
geo_column::<Rect>(
storage,
box_storage_dtype(Dimension::Xy, Nullability::NonNullable),
)
}
pub(crate) fn coordinate_from_scalar(scalar: &Scalar) -> VortexResult<Coordinate> {
match scalar.as_extension_opt() {
Some(ext_scalar) => coordinate_from_struct(&ext_scalar.to_storage_scalar()),
None => coordinate_from_struct(scalar),
}
}