blob: 84e35ca7b25ef10e33f6ff978ff6796d9a4f3a89 [file] [log] [blame]
// 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, vec};
use arrow_schema::DataType;
use datafusion_common::error::Result;
use datafusion_expr::{
scalar_doc_sections::DOC_SECTION_OTHER, ColumnarValue, Documentation, Volatility,
};
use sedona_expr::scalar_udf::{SedonaScalarKernel, SedonaScalarUDF};
use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher};
/// ST_AsBinary() scalar UDF implementation
///
/// An implementation of WKB writing using GeoRust's wkt crate.
pub fn st_asbinary_udf() -> SedonaScalarUDF {
let udf = SedonaScalarUDF::new(
"st_asbinary",
vec![Arc::new(STAsBinary {})],
Volatility::Immutable,
Some(st_asbinary_doc()),
);
udf.with_aliases(vec!["st_aswkb".to_string()])
}
fn st_asbinary_doc() -> Documentation {
Documentation::builder(
DOC_SECTION_OTHER,
"Return the Well-Known Binary representation of a geometry or geography",
"ST_AsBinary (A: Geometry)",
)
.with_argument("geom", "geometry: Input geometry or geography")
.with_sql_example("SELECT ST_AsBinary(ST_Point(1.0, 2.0))")
.build()
}
#[derive(Debug)]
struct STAsBinary {}
impl SedonaScalarKernel for STAsBinary {
fn return_type(&self, args: &[SedonaType]) -> Result<Option<SedonaType>> {
// If we have WkbView input, return BinaryView to avoid a cast
if args.len() == 1 {
if let SedonaType::WkbView(_, _) = args[0] {
return Ok(Some(SedonaType::Arrow(DataType::BinaryView)));
}
}
let matcher = ArgMatcher::new(
vec![ArgMatcher::is_geometry_or_geography()],
SedonaType::Arrow(DataType::Binary),
);
matcher.match_args(args)
}
fn invoke_batch(&self, _: &[SedonaType], args: &[ColumnarValue]) -> Result<ColumnarValue> {
// This currently works because our return_type() ensure we didn't need a cast
Ok(args[0].clone())
}
}
#[cfg(test)]
mod tests {
use arrow_array::{ArrayRef, BinaryArray, BinaryViewArray};
use datafusion_common::scalar::ScalarValue;
use datafusion_expr::ScalarUDF;
use rstest::rstest;
use sedona_schema::datatypes::{
WKB_GEOGRAPHY, WKB_GEOMETRY, WKB_VIEW_GEOGRAPHY, WKB_VIEW_GEOMETRY,
};
use sedona_testing::testers::ScalarUdfTester;
use super::*;
const POINT12: [u8; 21] = [
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
];
#[test]
fn udf_metadata() {
let udf: ScalarUDF = st_asbinary_udf().into();
assert_eq!(udf.name(), "st_asbinary");
assert!(udf.documentation().is_some())
}
#[rstest]
fn udf_geometry_input(#[values(WKB_GEOMETRY, WKB_GEOGRAPHY)] sedona_type: SedonaType) {
let udf = st_asbinary_udf();
let tester = ScalarUdfTester::new(udf.into(), vec![sedona_type]);
assert_eq!(
tester.invoke_wkb_scalar(Some("POINT (1 2)")).unwrap(),
ScalarValue::Binary(Some(POINT12.to_vec()))
);
assert_eq!(
tester.invoke_wkb_scalar(None).unwrap(),
ScalarValue::Binary(None)
);
let expected_array: BinaryArray = [Some(POINT12), None, Some(POINT12)].iter().collect();
assert_eq!(
&tester
.invoke_wkb_array(vec![Some("POINT (1 2)"), None, Some("POINT (1 2)")])
.unwrap(),
&(Arc::new(expected_array) as ArrayRef)
);
}
#[rstest]
fn udf_geometry_view_input(
#[values(WKB_VIEW_GEOMETRY, WKB_VIEW_GEOGRAPHY)] sedona_type: SedonaType,
) {
let udf = st_asbinary_udf();
let tester = ScalarUdfTester::new(udf.into(), vec![sedona_type]);
assert_eq!(
tester.invoke_wkb_scalar(Some("POINT (1 2)")).unwrap(),
ScalarValue::BinaryView(Some(POINT12.to_vec()))
);
assert_eq!(
tester.invoke_wkb_scalar(None).unwrap(),
ScalarValue::BinaryView(None)
);
let expected_array: BinaryViewArray = [Some(POINT12), None, Some(POINT12)].iter().collect();
assert_eq!(
&tester
.invoke_wkb_array(vec![Some("POINT (1 2)"), None, Some("POINT (1 2)")])
.unwrap(),
&(Arc::new(expected_array) as ArrayRef)
);
}
#[test]
fn aliases() {
let udf: ScalarUDF = st_asbinary_udf().into();
assert!(udf.aliases().contains(&"st_aswkb".to_string()));
}
}