Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions rust/sedona-raster-functions/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl<'a, 'b> RasterExecutor<'a, 'b> {
/// 4. Calling the provided function with each raster
pub fn execute_raster_void<F>(&self, mut func: F) -> Result<()>
where
F: FnMut(usize, Option<RasterRefImpl<'_>>) -> Result<()>,
F: FnMut(usize, Option<&RasterRefImpl<'_>>) -> Result<()>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing reference instead of moving value into the closure to avoid repeatedly constructing RasterRefImpl from raster array or cloning it. We will not want to persist the RasterRefImpl value passed in for most of the time.

{
if self.arg_types[0] != RASTER {
return sedona_internal_err!("First argument must be a raster type");
Expand All @@ -85,22 +85,30 @@ impl<'a, 'b> RasterExecutor<'a, 'b> {
continue;
}
let raster = raster_array.get(i)?;
func(i, Some(raster))?;
func(i, Some(&raster))?;
}

Ok(())
}
ColumnarValue::Scalar(scalar_value) => match scalar_value {
ScalarValue::Struct(arc_struct) => {
let raster_array = RasterStructArray::new(arc_struct.as_ref());
if raster_array.is_null(0) {
func(0, None)
let raster_opt = if raster_array.is_null(0) {
None
} else {
let raster = raster_array.get(0)?;
func(0, Some(raster))
Some(raster_array.get(0)?)
};
for i in 0..self.num_iterations {
func(i, raster_opt.as_ref())?;
}
Ok(())
}
ScalarValue::Null => {
for i in 0..self.num_iterations {
func(i, None)?;
}
Ok(())
}
ScalarValue::Null => func(0, None),
_ => sedona_internal_err!("Expected Struct scalar for raster"),
},
}
Expand Down
2 changes: 1 addition & 1 deletion rust/sedona-raster-functions/src/rs_envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl SedonaScalarKernel for RsEnvelope {
executor.execute_raster_void(|_i, raster_opt| {
match raster_opt {
Some(raster) => {
create_envelope_wkb(&raster, &mut builder)?;
create_envelope_wkb(raster, &mut builder)?;
builder.append_value([]);
}
None => builder.append_null(),
Expand Down
2 changes: 1 addition & 1 deletion rust/sedona-raster-functions/src/rs_geotransform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ impl SedonaScalarKernel for RsGeoTransform {
let metadata = raster.metadata();
match self.param {
GeoTransformParam::Rotation => {
let rotation = rotation(&raster);
let rotation = rotation(raster);
builder.append_value(rotation);
}
GeoTransformParam::ScaleX => builder.append_value(metadata.scale_x()),
Expand Down
91 changes: 89 additions & 2 deletions rust/sedona-raster-functions/src/rs_rastercoordinate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl SedonaScalarKernel for RsCoordinateMapper {

match (raster_opt, x_opt, y_opt) {
(Some(raster), Some(x), Some(y)) => {
let (raster_x, raster_y) = to_raster_coordinate(&raster, x, y)?;
let (raster_x, raster_y) = to_raster_coordinate(raster, x, y)?;
match self.coord {
Coord::X => builder.append_value(raster_x),
Coord::Y => builder.append_value(raster_y),
Expand Down Expand Up @@ -216,7 +216,7 @@ impl SedonaScalarKernel for RsCoordinatePoint {

match (raster_opt, x_opt, y_opt) {
(Some(raster), Some(world_x), Some(world_y)) => {
let (raster_x, raster_y) = to_raster_coordinate(&raster, world_x, world_y)?;
let (raster_x, raster_y) = to_raster_coordinate(raster, world_x, world_y)?;
item[5..13].copy_from_slice(&(raster_x as f64).to_le_bytes());
item[13..21].copy_from_slice(&(raster_y as f64).to_le_bytes());
builder.append_value(item);
Expand All @@ -234,6 +234,7 @@ impl SedonaScalarKernel for RsCoordinatePoint {
mod tests {
use super::*;
use arrow_schema::DataType;
use datafusion_common::ScalarValue;
use datafusion_expr::ScalarUDF;
use rstest::rstest;
use sedona_schema::datatypes::{RASTER, WKB_GEOMETRY};
Expand Down Expand Up @@ -368,4 +369,90 @@ mod tests {
.unwrap();
assert_array_equal(&result, &expected);
}

#[rstest]
fn udf_invoke_xy_with_scalar_raster_array_coords(#[values(Coord::Y, Coord::X)] coord: Coord) {
let udf = match coord {
Coord::X => rs_worldtorastercoordx_udf(),
Coord::Y => rs_worldtorastercoordy_udf(),
};
let tester = ScalarUdfTester::new(
udf.into(),
vec![
RASTER,
SedonaType::Arrow(DataType::Float64),
SedonaType::Arrow(DataType::Float64),
],
);

let rasters = generate_test_rasters(2, Some(0)).unwrap();
let scalar_raster = ScalarValue::try_from_array(&rasters, 1).unwrap();

let world_x = Arc::new(arrow_array::Float64Array::from(vec![2.0, 2.5, 3.25]));
let world_y = Arc::new(arrow_array::Float64Array::from(vec![3.0, 2.5, 1.75]));

let expected_coords = match coord {
Coord::X => vec![Some(0_i64), Some(4_i64), Some(10_i64)],
Coord::Y => vec![Some(0_i64), Some(3_i64), Some(8_i64)],
};

let result = tester
.invoke(vec![
ColumnarValue::Scalar(scalar_raster),
ColumnarValue::Array(world_x.clone()),
ColumnarValue::Array(world_y.clone()),
])
.unwrap();

let array = match result {
ColumnarValue::Array(array) => array,
ColumnarValue::Scalar(_) => panic!("Expected array result"),
};

let expected: Arc<dyn arrow_array::Array> =
Arc::new(arrow_array::Int64Array::from(expected_coords));
assert_array_equal(&array, &expected);
}

#[test]
fn udf_invoke_pt_with_scalar_raster_array_coords() {
let udf = rs_worldtorastercoord_udf();
let tester = ScalarUdfTester::new(
udf.into(),
vec![
RASTER,
SedonaType::Arrow(DataType::Float64),
SedonaType::Arrow(DataType::Float64),
],
);

let rasters = generate_test_rasters(2, Some(0)).unwrap();
let scalar_raster = ScalarValue::try_from_array(&rasters, 1).unwrap();

let world_x = Arc::new(arrow_array::Float64Array::from(vec![2.0, 2.5, 3.25]));
let world_y = Arc::new(arrow_array::Float64Array::from(vec![3.0, 2.5, 1.75]));

let result = tester
.invoke(vec![
ColumnarValue::Scalar(scalar_raster),
ColumnarValue::Array(world_x.clone()),
ColumnarValue::Array(world_y.clone()),
])
.unwrap();

let array = match result {
ColumnarValue::Array(array) => array,
ColumnarValue::Scalar(_) => panic!("Expected array result"),
};

let expected = create_array(
&[
Some("POINT (0 0)"),
Some("POINT (4 3)"),
Some("POINT (10 8)"),
],
&WKB_GEOMETRY,
);
assert_array_equal(&array, &expected);
}
}
90 changes: 88 additions & 2 deletions rust/sedona-raster-functions/src/rs_worldcoordinate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ impl SedonaScalarKernel for RsCoordinateMapper {

match (raster_opt, x_opt, y_opt) {
(Some(raster), Some(x), Some(y)) => {
let (world_x, world_y) = to_world_coordinate(&raster, x, y);
let (world_x, world_y) = to_world_coordinate(raster, x, y);
match self.coord {
Coord::X => builder.append_value(world_x),
Coord::Y => builder.append_value(world_y),
Expand Down Expand Up @@ -214,7 +214,7 @@ impl SedonaScalarKernel for RsCoordinatePoint {

match (raster_opt, x_opt, y_opt) {
(Some(raster), Some(x), Some(y)) => {
let (world_x, world_y) = to_world_coordinate(&raster, x, y);
let (world_x, world_y) = to_world_coordinate(raster, x, y);
item[5..13].copy_from_slice(&world_x.to_le_bytes());
item[13..21].copy_from_slice(&world_y.to_le_bytes());
builder.append_value(item);
Expand All @@ -232,6 +232,7 @@ impl SedonaScalarKernel for RsCoordinatePoint {
mod tests {
use super::*;
use arrow_array::Array;
use datafusion_common::ScalarValue;
use datafusion_expr::ScalarUDF;
use rstest::rstest;
use sedona_schema::datatypes::{RASTER, WKB_GEOMETRY};
Expand Down Expand Up @@ -357,4 +358,89 @@ mod tests {
}
}
}

#[rstest]
fn udf_invoke_xy_with_scalar_raster_array_coords(#[values(Coord::Y, Coord::X)] coord: Coord) {
let udf = match coord {
Coord::X => rs_rastertoworldcoordx_udf(),
Coord::Y => rs_rastertoworldcoordy_udf(),
};
let tester = ScalarUdfTester::new(
udf.into(),
vec![
RASTER,
SedonaType::Arrow(DataType::Int32),
SedonaType::Arrow(DataType::Int32),
],
);

let rasters = generate_test_rasters(2, None).unwrap();
let scalar_raster = ScalarValue::try_from_array(&rasters, 1).unwrap();

let x_vals = vec![0_i32, 1_i32];
let y_vals = vec![0_i32, 1_i32];
let x_coords: Arc<dyn Array> = Arc::new(arrow_array::Int32Array::from(x_vals.clone()));
let y_coords: Arc<dyn Array> = Arc::new(arrow_array::Int32Array::from(y_vals.clone()));

let result = tester
.invoke(vec![
ColumnarValue::Scalar(scalar_raster),
ColumnarValue::Array(x_coords),
ColumnarValue::Array(y_coords),
])
.unwrap();

let array = match result {
ColumnarValue::Array(array) => array,
ColumnarValue::Scalar(_) => panic!("Expected array result"),
};

let expected_values = match coord {
Coord::X => vec![Some(2.0), Some(2.13)],
Coord::Y => vec![Some(3.0), Some(2.84)],
};
let expected: Arc<dyn arrow_array::Array> =
Arc::new(arrow_array::Float64Array::from(expected_values));
assert_array_equal(&array, &expected);
}

#[test]
fn udf_invoke_pt_with_scalar_raster_array_coords() {
let udf = rs_rastertoworldcoord_udf();
let tester = ScalarUdfTester::new(
udf.into(),
vec![
RASTER,
SedonaType::Arrow(DataType::Int32),
SedonaType::Arrow(DataType::Int32),
],
);

let rasters = generate_test_rasters(2, None).unwrap();
let scalar_raster = ScalarValue::try_from_array(&rasters, 1).unwrap();

let x_vals = vec![0_i32, 1_i32];
let y_vals = vec![0_i32, 1_i32];
let x_coords: Arc<dyn Array> = Arc::new(arrow_array::Int32Array::from(x_vals.clone()));
let y_coords: Arc<dyn Array> = Arc::new(arrow_array::Int32Array::from(y_vals.clone()));

let result = tester
.invoke(vec![
ColumnarValue::Scalar(scalar_raster),
ColumnarValue::Array(x_coords),
ColumnarValue::Array(y_coords),
])
.unwrap();

let array = match result {
ColumnarValue::Array(array) => array,
ColumnarValue::Scalar(_) => panic!("Expected array result"),
};

let expected = create_array(
&[Some("POINT (2 3)"), Some("POINT (2.13 2.84)")],
&WKB_GEOMETRY,
);
assert_array_equal(&array, &expected);
}
}