blob: d5bce5c85e35986619cae74f45c387b2cd185028 [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 crate::executor::RasterExecutor;
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_raster::builder::RasterBuilder;
use sedona_raster::traits::BandMetadata;
use sedona_raster::traits::RasterMetadata;
use sedona_schema::{
crs::lnglat,
datatypes::SedonaType,
matchers::ArgMatcher,
raster::{BandDataType, StorageType},
};
/// RS_Example() scalar UDF implementation
///
/// Creates a simple concrete example for testing purposes
/// May expand with additional optional parameters in the future
pub fn rs_example_udf() -> SedonaScalarUDF {
SedonaScalarUDF::new(
"rs_example",
vec![Arc::new(RsExample {})],
Volatility::Immutable,
Some(rs_example_doc()),
)
}
fn rs_example_doc() -> Documentation {
Documentation::builder(
DOC_SECTION_OTHER,
"Return an example raster".to_string(),
"RS_Example()".to_string(),
)
.with_sql_example("SELECT RS_Example()".to_string())
.build()
}
#[derive(Debug)]
struct RsExample {}
impl SedonaScalarKernel for RsExample {
fn return_type(&self, args: &[SedonaType]) -> Result<Option<SedonaType>> {
let matcher = ArgMatcher::new(vec![], SedonaType::Raster);
matcher.match_args(args)
}
fn invoke_batch(
&self,
arg_types: &[SedonaType],
args: &[ColumnarValue],
) -> Result<ColumnarValue> {
let executor = RasterExecutor::new(arg_types, args);
let mut builder = RasterBuilder::new(1);
let raster_metadata = RasterMetadata {
width: 64,
height: 32,
upperleft_x: 43.08,
upperleft_y: 79.07,
scale_x: 2.0,
scale_y: 2.0,
skew_x: 1.0,
skew_y: 1.0,
};
let crs = lnglat().unwrap().to_json();
builder.start_raster(&raster_metadata, Some(&crs))?;
let nodata_value = 127u8;
for band_id in 1..=3 {
builder.start_band(BandMetadata {
datatype: BandDataType::UInt8,
nodata_value: Some(vec![nodata_value]),
storage_type: StorageType::InDb,
outdb_url: None,
outdb_band_id: None,
})?;
let mut band_data =
vec![band_id as u8; (raster_metadata.width * raster_metadata.height) as usize];
band_data[0] = nodata_value; // set the top corner to nodata
builder.band_data_writer().append_value(&band_data);
builder.finish_band()?;
}
builder.finish_raster()?;
executor.finish(Arc::new(builder.finish()?))
}
}
#[cfg(test)]
mod tests {
use super::*;
use datafusion_common::ScalarValue;
use datafusion_expr::ScalarUDF;
use sedona_raster::array::RasterStructArray;
use sedona_raster::traits::RasterRef;
#[test]
fn udf_size() {
let udf: ScalarUDF = rs_example_udf().into();
assert_eq!(udf.name(), "rs_example");
assert!(udf.documentation().is_some());
}
#[test]
fn udf_invoke() {
let kernel = RsExample {};
let args = [];
let arg_types = vec![];
let result = kernel.invoke_batch(&arg_types, &args).unwrap();
if let ColumnarValue::Scalar(ScalarValue::Struct(arc_struct)) = result {
let raster_array = RasterStructArray::new(arc_struct.as_ref());
assert_eq!(raster_array.len(), 1);
let raster = raster_array.get(0).unwrap();
let metadata = raster.metadata();
assert_eq!(metadata.width(), 64);
assert_eq!(metadata.height(), 32);
let bands = raster.bands();
let band = bands.band(1).unwrap();
let band_metadata = band.metadata();
assert_eq!(band_metadata.data_type(), BandDataType::UInt8);
assert_eq!(band_metadata.nodata_value(), Some(&[127u8][..]));
assert_eq!(band_metadata.storage_type(), StorageType::InDb);
} else {
panic!("Expected scalar struct result");
}
}
}