Skip to content

Commit 72aa947

Browse files
feat(vortex-geo): per-row bounding-box scalar function (vortex.geo.envelope)
`GeoEnvelope` computes the per-row 2-D axis-aligned bounding box of a native geometry column, returned as a native geoarrow.box (`Rect`) column — the per-row counterpart of the `GeometryAabb` aggregate (whole-column). Intended for row-oriented consumers such as bulk-loading an in-memory R-tree in a spatial-join operator, which read the resulting box column back row by row. Computed directly over the native coordinate storage — no `geo_types` decode and no Arrow round-trip: walk the nested `ListView` storage down to the leaf x/y buffers, tracking which top-level row owns each coordinate, then min/max per row. A `Rect` input is the identity; a null row or an empty geometry yields a null box, so the output is always nullable. Hoist the shared `f64_field` coordinate-column accessor into `extension::coordinate`, used by both this function and the aggregate. Signed-off-by: Nemo Yu <zyu379@wisc.edu>
1 parent 2ea51cb commit 72aa947

6 files changed

Lines changed: 500 additions & 10 deletions

File tree

vortex-geo/src/aggregate_fn/aabb.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ use vortex_array::aggregate_fn::AggregateFnRef;
1313
use vortex_array::aggregate_fn::AggregateFnVTable;
1414
use vortex_array::aggregate_fn::AggregateFnVTableExt;
1515
use vortex_array::aggregate_fn::EmptyOptions;
16-
use vortex_array::arrays::PrimitiveArray;
17-
use vortex_array::arrays::struct_::StructArrayExt;
1816
use vortex_array::dtype::DType;
1917
use vortex_array::dtype::Nullability;
2018
use vortex_array::dtype::extension::ExtDType;
@@ -29,6 +27,7 @@ use crate::extension::GeoMetadata;
2927
use crate::extension::Rect;
3028
use crate::extension::box_storage_dtype;
3129
use crate::extension::coordinate::Dimension;
30+
use crate::extension::coordinate::f64_field;
3231
use crate::extension::flatten_coordinates;
3332
use crate::extension::is_native_geometry;
3433

@@ -217,14 +216,8 @@ impl AggregateFnVTable for GeometryAabb {
217216
// `unmasked_field_by_name` reads are therefore safe. Min/max the raw x/y buffers directly:
218217
// cheap, and avoids `to_geometry`'s panic on empty points (which decoding would hit).
219218
let coords = flatten_coordinates(&array, ctx)?;
220-
let xs = coords
221-
.unmasked_field_by_name("x")?
222-
.clone()
223-
.execute::<PrimitiveArray>(ctx)?;
224-
let ys = coords
225-
.unmasked_field_by_name("y")?
226-
.clone()
227-
.execute::<PrimitiveArray>(ctx)?;
219+
let xs = f64_field(&coords, "x", ctx)?;
220+
let ys = f64_field(&coords, "y", ctx)?;
228221
if let Some(rect) = aabb_of(xs.as_slice::<f64>(), ys.as_slice::<f64>()) {
229222
partial.merge(rect);
230223
}

vortex-geo/src/extension/coordinate.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ use std::fmt::Display;
1515
use std::fmt::Formatter;
1616

1717
use geoarrow::datatypes::Dimension as GeoArrowDimension;
18+
use vortex_array::ExecutionCtx;
19+
use vortex_array::arrays::PrimitiveArray;
20+
use vortex_array::arrays::StructArray;
21+
use vortex_array::arrays::struct_::StructArrayExt;
1822
use vortex_array::dtype::DType;
1923
use vortex_array::dtype::FieldNames;
2024
use vortex_array::dtype::Nullability;
@@ -189,6 +193,19 @@ pub(crate) fn coordinate_from_struct(scalar: &Scalar) -> VortexResult<Coordinate
189193
})
190194
}
191195

196+
/// Materialize a named non-nullable `f64` field of a coordinate `Struct` column as a
197+
/// [`PrimitiveArray`], for bulk per-ordinate reads.
198+
pub(crate) fn f64_field(
199+
coords: &StructArray,
200+
name: &str,
201+
ctx: &mut ExecutionCtx,
202+
) -> VortexResult<PrimitiveArray> {
203+
coords
204+
.unmasked_field_by_name(name)?
205+
.clone()
206+
.execute::<PrimitiveArray>(ctx)
207+
}
208+
192209
#[cfg(test)]
193210
mod tests {
194211
use rstest::rstest;

vortex-geo/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::prune::GeoDistancePrune;
2323
use crate::prune::GeoIntersectsPrune;
2424
use crate::scalar_fn::contains::GeoContains;
2525
use crate::scalar_fn::distance::GeoDistance;
26+
use crate::scalar_fn::envelope::GeoEnvelope;
2627
use crate::scalar_fn::intersects::GeoIntersects;
2728

2829
pub mod aggregate_fn;
@@ -63,6 +64,7 @@ pub fn initialize(session: &VortexSession) {
6364
session.arrow().register_importer(Arc::new(Rect));
6465

6566
// Register the geometry scalar functions.
67+
session.scalar_fns().register(GeoEnvelope);
6668
session.scalar_fns().register(GeoContains);
6769
session.scalar_fns().register(GeoDistance);
6870
session.scalar_fns().register(GeoIntersects);

0 commit comments

Comments
 (0)