diff --git a/Cargo.lock b/Cargo.lock index 5a975d9fd..2337df69b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,9 +794,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3946acbe1ead1301ba6862e712c7903ca9bb230bdf1fbd1b5ac54158ef2ab1f" +checksum = "4fa63ad37685ceb7762fa4d73d06f1d5493feb88e3f27259b9ed277f4c01b185" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1108,9 +1108,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.36" +version = "1.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" dependencies = [ "find-msvc-tools", "jobserver", @@ -1265,9 +1265,9 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "comfy-table" -version = "7.2.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8e18d0dca9578507f13f9803add0df13362b02c501c1c17734f0dbb52eaf0b" +checksum = "b03b7db8e0b4b2fdad6c551e634134e99ec000e5c8c3b6856c65e8bbaded7a3b" dependencies = [ "crossterm", "unicode-segmentation", @@ -1296,9 +1296,9 @@ dependencies = [ [[package]] name = "const_panic" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb8a602185c3c95b52f86dc78e55a6df9a287a7a93ddbcf012509930880cf879" +checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" dependencies = [ "typewit", ] @@ -2617,7 +2617,7 @@ dependencies = [ [[package]] name = "geo-generic-alg" version = "0.1.0" -source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#86f5f3aba769d998336a3c36ac0a3d9f5536596f" +source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#567d1da8e094e74d0fdc5ce90f9bdd8f90dae40b" dependencies = [ "earcutr", "float_next_after", @@ -2652,7 +2652,7 @@ dependencies = [ [[package]] name = "geo-traits" version = "0.2.0" -source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#86f5f3aba769d998336a3c36ac0a3d9f5536596f" +source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#567d1da8e094e74d0fdc5ce90f9bdd8f90dae40b" dependencies = [ "geo-types", ] @@ -2669,7 +2669,7 @@ dependencies = [ [[package]] name = "geo-traits-ext" version = "0.1.0" -source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#86f5f3aba769d998336a3c36ac0a3d9f5536596f" +source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#567d1da8e094e74d0fdc5ce90f9bdd8f90dae40b" dependencies = [ "approx", "geo-traits 0.2.0", @@ -2681,7 +2681,7 @@ dependencies = [ [[package]] name = "geo-types" version = "0.7.16" -source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#86f5f3aba769d998336a3c36ac0a3d9f5536596f" +source = "git+https://github.com/wherobots/geo.git?branch=generic-alg#567d1da8e094e74d0fdc5ce90f9bdd8f90dae40b" dependencies = [ "approx", "num-traits", @@ -2933,9 +2933,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" @@ -3046,9 +3046,9 @@ checksum = "155181bc97d770181cf9477da51218a19ee92a8e5be642e796661aee2b601139" [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -4641,9 +4641,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8" dependencies = [ "aws-lc-rs", "ring", @@ -5855,15 +5855,15 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "typewit" -version = "1.14.1" +version = "1.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c98488b93df24b7c794d6a58c4198d7a2abde676324beaca84f7fb5b39c0811" +checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-segmentation" @@ -6130,13 +6130,13 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.1.3", + "windows-link 0.2.0", "windows-result", "windows-strings", ] @@ -6177,20 +6177,20 @@ checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" dependencies = [ - "windows-link 0.1.3", + "windows-link 0.2.0", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" dependencies = [ - "windows-link 0.1.3", + "windows-link 0.2.0", ] [[package]] diff --git a/c/sedona-geos/src/st_dwithin.rs b/c/sedona-geos/src/st_dwithin.rs index 03bbaa1d8..bc7bbd102 100644 --- a/c/sedona-geos/src/st_dwithin.rs +++ b/c/sedona-geos/src/st_dwithin.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use arrow_array::builder::BooleanBuilder; use arrow_schema::DataType; -use datafusion_common::{error::Result, DataFusionError}; +use datafusion_common::{cast::as_float64_array, error::Result, DataFusionError}; use datafusion_expr::ColumnarValue; use geos::Geom; use sedona_expr::scalar_udf::{ScalarKernelRef, SedonaScalarKernel}; @@ -53,26 +53,14 @@ impl SedonaScalarKernel for STDWithin { arg_types: &[SedonaType], args: &[ColumnarValue], ) -> Result { - // Extract the constant scalar value before looping over the input geometries - let distance: Option; let arg2 = args[2].cast_to(&DataType::Float64, None)?; - if let ColumnarValue::Scalar(scalar_arg) = &arg2 { - if scalar_arg.is_null() { - distance = None; - } else { - distance = Some(f64::try_from(scalar_arg.clone())?); - } - } else { - return Err(DataFusionError::Execution(format!( - "Invalid distance: {:?}", - args[2] - ))); - } - let executor = GeosExecutor::new(arg_types, args); + let arg2_array = arg2.to_array(executor.num_iterations())?; + let arg2_f64_array = as_float64_array(&arg2_array)?; + let mut arg2_iter = arg2_f64_array.iter(); let mut builder = BooleanBuilder::with_capacity(executor.num_iterations()); executor.execute_wkb_wkb_void(|lhs, rhs| { - match (lhs, rhs, distance) { + match (lhs, rhs, arg2_iter.next().unwrap()) { (Some(lhs), Some(rhs), Some(distance)) => { builder.append_value(invoke_scalar(lhs, rhs, distance)?); } @@ -151,9 +139,16 @@ mod tests { let expected: ArrayRef = arrow_array!(Boolean, [Some(true), Some(false), None, Some(true)]); assert_array_equal( &tester - .invoke_array_array_scalar(arg1, arg2, distance) + .invoke_array_array_scalar(Arc::clone(&arg1), Arc::clone(&arg2), distance) .unwrap(), &expected, ); + + let distance = arrow_array!(Int32, [Some(1), Some(1), Some(1), Some(1)]); + let expected: ArrayRef = arrow_array!(Boolean, [Some(true), Some(false), None, Some(true)]); + assert_array_equal( + &tester.invoke_arrays(vec![arg1, arg2, distance]).unwrap(), + &expected, + ); } } diff --git a/rust/sedona-geo/benches/geo-functions.rs b/rust/sedona-geo/benches/geo-functions.rs index 3576cbee1..18d288c14 100644 --- a/rust/sedona-geo/benches/geo-functions.rs +++ b/rust/sedona-geo/benches/geo-functions.rs @@ -41,6 +41,30 @@ fn criterion_benchmark(c: &mut Criterion) { "st_intersects", ArrayScalar(Point, Polygon(500)), ); + + benchmark::scalar(c, &f, "geo", "st_distance", ArrayScalar(Point, Polygon(10))); + benchmark::scalar( + c, + &f, + "geo", + "st_distance", + ArrayScalar(Point, Polygon(500)), + ); + + benchmark::scalar( + c, + &f, + "geo", + "st_dwithin", + ArrayArrayScalar(Polygon(10), Polygon(10), Float64(1.0, 2.0)), + ); + benchmark::scalar( + c, + &f, + "geo", + "st_dwithin", + ArrayArrayScalar(Polygon(10), Polygon(500), Float64(1.0, 2.0)), + ); } fn criterion_benchmark_aggr(c: &mut Criterion) { diff --git a/rust/sedona-geo/src/lib.rs b/rust/sedona-geo/src/lib.rs index 24c640806..6af5dd20f 100644 --- a/rust/sedona-geo/src/lib.rs +++ b/rust/sedona-geo/src/lib.rs @@ -18,6 +18,8 @@ pub mod centroid; pub mod register; mod st_area; mod st_centroid; +mod st_distance; +mod st_dwithin; mod st_intersection_aggr; mod st_intersects; mod st_length; diff --git a/rust/sedona-geo/src/register.rs b/rust/sedona-geo/src/register.rs index 9db2c32e5..5dfb25d06 100644 --- a/rust/sedona-geo/src/register.rs +++ b/rust/sedona-geo/src/register.rs @@ -21,8 +21,8 @@ use crate::st_intersection_aggr::st_intersection_aggr_impl; use crate::st_line_interpolate_point::st_line_interpolate_point_impl; use crate::st_union_aggr::st_union_aggr_impl; use crate::{ - st_area::st_area_impl, st_centroid::st_centroid_impl, st_intersects::st_intersects_impl, - st_length::st_length_impl, + st_area::st_area_impl, st_centroid::st_centroid_impl, st_distance::st_distance_impl, + st_dwithin::st_dwithin_impl, st_intersects::st_intersects_impl, st_length::st_length_impl, }; pub fn scalar_kernels() -> Vec<(&'static str, ScalarKernelRef)> { @@ -30,6 +30,8 @@ pub fn scalar_kernels() -> Vec<(&'static str, ScalarKernelRef)> { ("st_intersects", st_intersects_impl()), ("st_area", st_area_impl()), ("st_centroid", st_centroid_impl()), + ("st_distance", st_distance_impl()), + ("st_dwithin", st_dwithin_impl()), ("st_length", st_length_impl()), ("st_lineinterpolatepoint", st_line_interpolate_point_impl()), ] diff --git a/rust/sedona-geo/src/st_distance.rs b/rust/sedona-geo/src/st_distance.rs new file mode 100644 index 000000000..4900690a5 --- /dev/null +++ b/rust/sedona-geo/src/st_distance.rs @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +use std::sync::Arc; + +use arrow_array::builder::Float64Builder; +use arrow_schema::DataType; +use datafusion_common::error::Result; +use datafusion_expr::ColumnarValue; +use geo_generic_alg::line_measures::DistanceExt; +use sedona_expr::scalar_udf::{ScalarKernelRef, SedonaScalarKernel}; +use sedona_functions::executor::WkbExecutor; +use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher}; +use wkb::reader::Wkb; + +/// ST_Distance() implementation using [DistanceExt] +pub fn st_distance_impl() -> ScalarKernelRef { + Arc::new(STDistance {}) +} + +#[derive(Debug)] +struct STDistance {} + +impl SedonaScalarKernel for STDistance { + fn return_type(&self, args: &[SedonaType]) -> Result> { + let matcher = ArgMatcher::new( + vec![ArgMatcher::is_geometry(), ArgMatcher::is_geometry()], + SedonaType::Arrow(DataType::Float64), + ); + + matcher.match_args(args) + } + + fn invoke_batch( + &self, + arg_types: &[SedonaType], + args: &[ColumnarValue], + ) -> Result { + let executor = WkbExecutor::new(arg_types, args); + let mut builder = Float64Builder::with_capacity(executor.num_iterations()); + executor.execute_wkb_wkb_void(|maybe_wkb0, maybe_wkb1| { + match (maybe_wkb0, maybe_wkb1) { + (Some(wkb0), Some(wkb1)) => { + builder.append_value(invoke_scalar(wkb0, wkb1)?); + } + _ => builder.append_null(), + } + + Ok(()) + })?; + + executor.finish(Arc::new(builder.finish())) + } +} + +fn invoke_scalar(wkb_a: &Wkb, wkb_b: &Wkb) -> Result { + Ok(wkb_a.distance_ext(wkb_b)) +} + +#[cfg(test)] +mod tests { + use datafusion_common::scalar::ScalarValue; + use rstest::rstest; + use sedona_expr::scalar_udf::SedonaScalarUDF; + use sedona_schema::datatypes::{WKB_GEOMETRY, WKB_VIEW_GEOMETRY}; + use sedona_testing::create::create_scalar; + use sedona_testing::testers::ScalarUdfTester; + + use super::*; + + #[rstest] + fn udf( + #[values(WKB_GEOMETRY, WKB_VIEW_GEOMETRY)] left_sedona_type: SedonaType, + #[values(WKB_GEOMETRY, WKB_VIEW_GEOMETRY)] right_sedona_type: SedonaType, + ) { + let udf = SedonaScalarUDF::from_kernel("st_distance", st_distance_impl()); + let tester = ScalarUdfTester::new( + udf.into(), + vec![left_sedona_type.clone(), right_sedona_type.clone()], + ); + + assert_eq!( + tester.return_type().unwrap(), + SedonaType::Arrow(DataType::Float64) + ); + + // Test distance between two points (3-4-5 triangle) + let point_0_0 = create_scalar(Some("POINT (0 0)"), &left_sedona_type); + let point_3_4 = create_scalar(Some("POINT (3 4)"), &right_sedona_type); + + let result = tester + .invoke_scalar_scalar(point_0_0.clone(), point_3_4.clone()) + .unwrap(); + if let ScalarValue::Float64(Some(distance)) = result { + assert!((distance - 5.0).abs() < 1e-10); + } else { + panic!("Expected Float64 result"); + } + + // Test with null values + let result = tester + .invoke_scalar_scalar(ScalarValue::Null, point_3_4.clone()) + .unwrap(); + assert!(result.is_null()); + let result = tester + .invoke_scalar_scalar(point_0_0.clone(), ScalarValue::Null) + .unwrap(); + assert!(result.is_null()); + } +} diff --git a/rust/sedona-geo/src/st_dwithin.rs b/rust/sedona-geo/src/st_dwithin.rs new file mode 100644 index 000000000..2ba3f5dea --- /dev/null +++ b/rust/sedona-geo/src/st_dwithin.rs @@ -0,0 +1,179 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +use std::sync::Arc; + +use arrow_array::builder::BooleanBuilder; +use arrow_schema::DataType; +use datafusion_common::{cast::as_float64_array, error::Result}; +use datafusion_expr::ColumnarValue; +use geo_generic_alg::line_measures::DistanceExt; +use sedona_expr::scalar_udf::{ScalarKernelRef, SedonaScalarKernel}; +use sedona_functions::executor::WkbExecutor; +use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher}; +use wkb::reader::Wkb; + +/// ST_DWithin() implementation using [DistanceExt] +pub fn st_dwithin_impl() -> ScalarKernelRef { + Arc::new(STDWithin {}) +} + +#[derive(Debug)] +struct STDWithin {} + +impl SedonaScalarKernel for STDWithin { + fn return_type(&self, args: &[SedonaType]) -> Result> { + let matcher = ArgMatcher::new( + vec![ + ArgMatcher::is_geometry(), + ArgMatcher::is_geometry(), + ArgMatcher::is_numeric(), + ], + SedonaType::Arrow(DataType::Boolean), + ); + + matcher.match_args(args) + } + + fn invoke_batch( + &self, + arg_types: &[SedonaType], + args: &[ColumnarValue], + ) -> Result { + let arg2 = args[2].cast_to(&DataType::Float64, None)?; + let executor = WkbExecutor::new(arg_types, args); + let arg2_array = arg2.to_array(executor.num_iterations())?; + let arg2_f64_array = as_float64_array(&arg2_array)?; + let mut arg2_iter = arg2_f64_array.iter(); + let mut builder = BooleanBuilder::with_capacity(executor.num_iterations()); + executor.execute_wkb_wkb_void(|maybe_wkb0, maybe_wkb1| { + match (maybe_wkb0, maybe_wkb1, arg2_iter.next().unwrap()) { + (Some(wkb0), Some(wkb1), Some(distance)) => { + builder.append_value(invoke_scalar(wkb0, wkb1, distance)?); + } + _ => builder.append_null(), + } + + Ok(()) + })?; + + executor.finish(Arc::new(builder.finish())) + } +} + +fn invoke_scalar(wkb_a: &Wkb, wkb_b: &Wkb, distance_bound: f64) -> Result { + let actual_distance = wkb_a.distance_ext(wkb_b); + Ok(actual_distance <= distance_bound) +} + +#[cfg(test)] +mod tests { + use arrow_array::{create_array as arrow_array, ArrayRef}; + use datafusion_common::scalar::ScalarValue; + use rstest::rstest; + use sedona_expr::scalar_udf::SedonaScalarUDF; + use sedona_schema::datatypes::{WKB_GEOMETRY, WKB_VIEW_GEOMETRY}; + use sedona_testing::create::create_scalar; + use sedona_testing::testers::ScalarUdfTester; + + use super::*; + + #[rstest] + fn udf( + #[values(WKB_GEOMETRY, WKB_VIEW_GEOMETRY)] left_sedona_type: SedonaType, + #[values(WKB_GEOMETRY, WKB_VIEW_GEOMETRY)] right_sedona_type: SedonaType, + ) { + use sedona_testing::{compare::assert_array_equal, create::create_array}; + + let udf = SedonaScalarUDF::from_kernel("st_dwithin", st_dwithin_impl()); + let tester = ScalarUdfTester::new( + udf.into(), + vec![ + left_sedona_type.clone(), + right_sedona_type.clone(), + SedonaType::Arrow(DataType::Float64), + ], + ); + + assert_eq!( + tester.return_type().unwrap(), + SedonaType::Arrow(DataType::Boolean) + ); + + // Test points within distance (3-4-5 triangle, distance = 5.0) + let point_0_0 = create_scalar(Some("POINT (0 0)"), &left_sedona_type); + let point_3_4 = create_scalar(Some("POINT (3 4)"), &right_sedona_type); + let distance_5 = ScalarValue::Float64(Some(5.0)); + let distance_4 = ScalarValue::Float64(Some(4.0)); + + let result = tester + .invoke_scalar_scalar_scalar(point_0_0.clone(), point_3_4.clone(), distance_5.clone()) + .unwrap(); + assert_eq!(result, ScalarValue::Boolean(Some(true))); + + // Test points outside distance + let result = tester + .invoke_scalar_scalar_scalar(point_0_0.clone(), point_3_4.clone(), distance_4.clone()) + .unwrap(); + assert_eq!(result, ScalarValue::Boolean(Some(false))); + + // Test with null values + let result = tester + .invoke_scalar_scalar_scalar(ScalarValue::Null, point_3_4.clone(), distance_5.clone()) + .unwrap(); + assert!(result.is_null()); + let result = tester + .invoke_scalar_scalar_scalar(point_0_0.clone(), ScalarValue::Null, distance_5.clone()) + .unwrap(); + assert!(result.is_null()); + + // Test with null distance + let result = tester + .invoke_scalar_scalar_scalar( + point_0_0.clone(), + point_3_4.clone(), + ScalarValue::Float64(None), + ) + .unwrap(); + assert!(result.is_null()); + + // Test with array args + let arg1 = create_array( + &[ + Some("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))"), + Some("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))"), + None, + Some("POINT EMPTY"), + ], + &WKB_GEOMETRY, + ); + let arg2 = create_array( + &[ + Some("POINT (0.5 0.5)"), + Some("POINT (5 5)"), + Some("POINT (0 0)"), + Some("POINT EMPTY"), + ], + &WKB_GEOMETRY, + ); + let distance = arrow_array!(Int32, [Some(1), Some(1), Some(1), Some(1)]); + let expected: ArrayRef = arrow_array!(Boolean, [Some(true), Some(false), None, Some(true)]); + assert_array_equal( + &tester.invoke_arrays(vec![arg1, arg2, distance]).unwrap(), + &expected, + ); + } +}