Skip to content
Merged
22 changes: 21 additions & 1 deletion c/sedona-gdal/src/gdal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::errors::Result;
use crate::gdal_api::GdalApi;
use crate::gdal_dyn_bindgen::{GDALOpenFlags, OGRFieldType};
use crate::mem::create_mem_dataset;
use crate::raster::polygonize::{polygonize, PolygonizeOptions};
use crate::raster::polygonize::{fpolygonize, polygonize, PolygonizeOptions};
use crate::raster::rasterband::RasterBand;
use crate::raster::rasterize::{rasterize, RasterizeOptions};
use crate::raster::rasterize_affine::rasterize_affine;
Expand Down Expand Up @@ -250,4 +250,24 @@ impl Gdal {
options,
)
}

/// Polygonize a raster band into a vector layer using float pixels.
/// See also [`fpolygonize`].
pub fn fpolygonize(
&self,
src_band: &RasterBand<'_>,
mask_band: Option<&RasterBand<'_>>,
out_layer: &Layer<'_>,
pixel_value_field: i32,
options: &PolygonizeOptions,
) -> Result<()> {
fpolygonize(
self.api,
src_band,
mask_band,
out_layer,
pixel_value_field,
options,
)
}
}
32 changes: 32 additions & 0 deletions c/sedona-gdal/src/raster/polygonize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,38 @@ pub fn polygonize(
Ok(())
}

/// Polygonize a raster band into a vector layer.
/// This uses `GDALFPolygonize`, which reads source pixels as floats.
pub fn fpolygonize(
api: &'static GdalApi,
src_band: &RasterBand<'_>,
mask_band: Option<&RasterBand<'_>>,
out_layer: &Layer<'_>,
pixel_value_field: i32,
options: &PolygonizeOptions,
) -> Result<()> {
let mask = mask_band.map_or(ptr::null_mut(), |b| b.c_rasterband());
let csl = options.to_options_list()?;

let rv = unsafe {
call_gdal_api!(
api,
GDALFPolygonize,
src_band.c_rasterband(),
mask,
out_layer.c_layer(),
pixel_value_field,
csl.as_ptr(),
ptr::null_mut(), // pfnProgress
ptr::null_mut() // pProgressData
)
};
if rv != CE_None {
return Err(api.last_cpl_err(rv as u32));
}
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
43 changes: 43 additions & 0 deletions docs/reference/sql/rs_polygonize.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
# 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.

title: RS_Polygonize
description: Converts a raster band into polygons grouped by connected pixel value regions.
kernels:
- returns: list
args:
- raster
- name: band
type: integer
---

## Description

Converts the specified raster `band` into vector polygons, returning one item per
connected region of pixels that shares the same value.

Each returned list item contains:

- `geom`: the polygon geometry encoded as WKB
- `value`: the pixel value represented by that polygon

## Examples

```sql
SELECT RS_Polygonize(RS_Example(), 1);
```
5 changes: 5 additions & 0 deletions rust/sedona-raster-gdal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,8 @@ path = "benches/rs_frompath.rs"
harness = false
name = "rs_metadata"
path = "benches/rs_metadata.rs"

[[bench]]
harness = false
name = "rs_polygonize"
path = "benches/rs_polygonize.rs"
91 changes: 91 additions & 0 deletions rust/sedona-raster-gdal/benches/rs_polygonize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// 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.

//! Benchmarks for RS_Polygonize UDF.

use std::{hint::black_box, sync::Arc};

use arrow_array::{ArrayRef, Int32Array};
use arrow_schema::DataType;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use datafusion_expr::ColumnarValue;
use datafusion_expr::ScalarUDF;
use sedona_schema::datatypes::{SedonaType, RASTER};
use sedona_testing::testers::ScalarUdfTester;

fn raster_array(rows: usize) -> ArrayRef {
assert!(rows > 0, "benchmark rows must be positive");

let example: ScalarUDF = sedona_raster_functions::rs_example::rs_example_udf().into();
let tester = ScalarUdfTester::new(example, vec![]);
let scalar = tester.invoke(vec![]).unwrap();
match scalar {
ColumnarValue::Scalar(value) => value.to_array_of_size(rows).unwrap(),
ColumnarValue::Array(array) => array,
}
}

fn band_array(rows: usize) -> ArrayRef {
Arc::new(Int32Array::from(vec![1; rows]))
}

fn bench_rs_polygonize(c: &mut Criterion) {
let udf: ScalarUDF = sedona_raster_gdal::rs_polygonize_udf().into();
let tester = ScalarUdfTester::new(udf, vec![RASTER, SedonaType::Arrow(DataType::Int32)]);

let single = raster_array(1);
let batched = raster_array(32);
let single_band = band_array(single.len());
let batched_band = band_array(batched.len());

let mut group = c.benchmark_group("rs_polygonize");

group.throughput(Throughput::Elements(single.len() as u64));
group.bench_with_input(
BenchmarkId::new("fixtures", "single_example"),
&single,
|b, input| {
b.iter(|| {
black_box(
tester
.invoke_arrays(vec![input.clone(), single_band.clone()])
.unwrap(),
)
})
},
);

group.throughput(Throughput::Elements(batched.len() as u64));
group.bench_with_input(
BenchmarkId::new("fixtures", "batched_example"),
&batched,
|b, input| {
b.iter(|| {
black_box(
tester
.invoke_arrays(vec![input.clone(), batched_band.clone()])
.unwrap(),
)
})
},
);

group.finish();
}

criterion_group!(benches, bench_rs_polygonize);
criterion_main!(benches);
2 changes: 2 additions & 0 deletions rust/sedona-raster-gdal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mod gdal_dataset_provider;
mod raster_loader;
mod rs_frompath;
mod rs_metadata;
mod rs_polygonize;
mod source_uri;
mod utils;

Expand All @@ -46,6 +47,7 @@ pub use gdal_common::{
pub use raster_loader::{GdalLoader, GDAL_FORMAT};
pub use rs_frompath::rs_frompath_udf;
pub use rs_metadata::rs_metadata_udf;
pub use rs_polygonize::rs_polygonize_udf;
pub use utils::{
append_as_indb_raster, append_as_outdb_raster, append_nd_from_dataset, dataset_to_indb_raster,
gdal_dataset_to_nd_raster,
Expand Down
1 change: 1 addition & 0 deletions rust/sedona-raster-gdal/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ pub fn default_function_set() -> FunctionSet {
let mut function_set = FunctionSet::new();
function_set.insert_scalar_udf(crate::rs_frompath::rs_frompath_udf());
function_set.insert_scalar_udf(crate::rs_metadata::rs_metadata_udf());
function_set.insert_scalar_udf(crate::rs_polygonize::rs_polygonize_udf());
function_set
}
Loading
Loading