use std::convert::TryFrom;
use std::ffi::c_void;
use std::ptr;
use gdal_sys::CPLErr;
use crate::cpl::CslStringList;
use crate::dataset::Dataset;
use crate::errors::*;
use crate::utils::_last_cpl_err;
use crate::vector::Geometry;
#[derive(Copy, Clone, Debug)]
pub enum BurnSource {
UserSupplied,
Z,
}
#[derive(Copy, Clone, Debug)]
pub enum MergeAlgorithm {
Replace,
Add,
}
#[derive(Copy, Clone, Debug)]
pub enum OptimizeMode {
Automatic,
Raster,
Vector,
}
#[derive(Copy, Clone, Debug)]
pub struct RasterizeOptions {
pub all_touched: bool,
pub source: BurnSource,
pub merge_algorithm: MergeAlgorithm,
pub chunk_y_size: usize,
pub optimize: OptimizeMode,
}
impl Default for RasterizeOptions {
fn default() -> Self {
RasterizeOptions {
all_touched: false,
source: BurnSource::UserSupplied,
merge_algorithm: MergeAlgorithm::Replace,
chunk_y_size: 0,
optimize: OptimizeMode::Automatic,
}
}
}
impl TryFrom<RasterizeOptions> for CslStringList {
type Error = GdalError;
fn try_from(value: RasterizeOptions) -> Result<CslStringList> {
let mut options = CslStringList::new();
options.set_name_value(
"ALL_TOUCHED",
if value.all_touched { "TRUE" } else { "FALSE" },
)?;
options.set_name_value(
"MERGE_ALG",
match value.merge_algorithm {
MergeAlgorithm::Replace => "REPLACE",
MergeAlgorithm::Add => "ADD",
},
)?;
options.set_name_value("CHUNKYSIZE", &value.chunk_y_size.to_string())?;
options.set_name_value(
"OPTIM",
match value.optimize {
OptimizeMode::Automatic => "AUTO",
OptimizeMode::Raster => "RASTER",
OptimizeMode::Vector => "VECTOR",
},
)?;
if let BurnSource::Z = value.source {
options.set_name_value("BURN_VALUE_FROM", "Z")?;
}
Ok(options)
}
}
pub fn rasterize(
dataset: &mut Dataset,
bands: &[usize],
geometries: &[Geometry],
burn_values: &[f64],
options: Option<RasterizeOptions>,
) -> Result<()> {
if bands.is_empty() {
return Err(GdalError::BadArgument(
"`bands` must not be empty".to_string(),
));
}
if burn_values.len() != geometries.len() {
return Err(GdalError::BadArgument(format!(
"Burn values length ({}) must match geometries length ({})",
burn_values.len(),
geometries.len()
)));
}
let raster_count = dataset.raster_count();
for band in bands {
let is_good = *band > 0 && *band <= raster_count;
if !is_good {
return Err(GdalError::BadArgument(format!(
"Band index {} is out of bounds",
*band
)));
}
}
let bands: Vec<i32> = bands.iter().map(|&band| band as i32).collect();
let options = options.unwrap_or_default();
let geometries: Vec<_> = geometries
.iter()
.map(|geo| unsafe { geo.c_geometry() })
.collect();
let burn_values: Vec<f64> = burn_values
.iter()
.flat_map(|burn| std::iter::repeat(burn).take(bands.len()))
.copied()
.collect();
let c_options = CslStringList::try_from(options).unwrap();
unsafe {
let error = gdal_sys::GDALRasterizeGeometries(
dataset.c_dataset(),
bands.len() as i32,
bands.as_ptr() as *mut i32,
geometries.len() as i32,
geometries.as_ptr() as *mut *mut c_void,
None,
ptr::null_mut(),
burn_values.as_ptr() as *mut f64,
c_options.as_ptr(),
None,
ptr::null_mut(),
);
if error != CPLErr::CE_None {
return Err(_last_cpl_err(error));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use crate::cpl::CslStringList;
use super::RasterizeOptions;
#[test]
fn test_rasterizeoptions_as_ptr() {
let c_options = CslStringList::try_from(RasterizeOptions::default()).unwrap();
assert_eq!(
c_options.fetch_name_value("ALL_TOUCHED"),
Some("FALSE".to_string())
);
assert_eq!(c_options.fetch_name_value("BURN_VALUE_FROM"), None);
assert_eq!(
c_options.fetch_name_value("MERGE_ALG"),
Some("REPLACE".to_string())
);
assert_eq!(
c_options.fetch_name_value("CHUNKYSIZE"),
Some("0".to_string())
);
assert_eq!(
c_options.fetch_name_value("OPTIM"),
Some("AUTO".to_string())
);
}
}