diff --git a/src/raster/warp/mod.rs b/src/raster/warp/mod.rs index cce886ce..a560b77b 100644 --- a/src/raster/warp/mod.rs +++ b/src/raster/warp/mod.rs @@ -9,11 +9,11 @@ mod reproject_options; mod resample; mod warp_options; -use gdal_sys::CPLErr; +use gdal_sys::{CPLErr, GDALDatasetH, GDALWarp}; pub use reproject_options::*; pub use resample::*; use std::ffi::CString; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::ptr; pub use warp_options::*; @@ -22,7 +22,7 @@ use crate::DriverManager; use crate::errors::*; use crate::spatial_ref::SpatialRef; -use crate::utils::{_last_cpl_err, _path_to_c_string}; +use crate::utils::{_last_cpl_err, _last_null_pointer_err, _path_to_c_string}; /// Reproject raster dataset into the given [`SpatialRef`] and save result to `dst_file`. pub fn create_and_reproject>( @@ -163,6 +163,79 @@ pub fn reproject_into( Ok(()) } +pub fn warp(source: &Dataset, dest: D, options: &GdalWarpOptions) -> Result +where + D: Into, +{ + warp_multiple(&[source], dest, options) +} + +pub fn warp_multiple(source: &[&Dataset], dest: D, options: &GdalWarpOptions) -> Result +where + D: Into, +{ + let app_opts = GdalWarpAppOptions::default(); + + if true { + todo!("how the hell do you go from {options:?} to GdalWarpAppOptions?"); + } + + let mut source = source.iter().map(|ds| ds.c_dataset()).collect::>(); + + let dest = dest.into(); + match dest { + WarpDestination::Dataset(ds) => { + let ds_c = unsafe { + GDALWarp( + ptr::null_mut(), + ds.c_dataset(), + source.len() as libc::c_int, + source.as_mut_ptr(), + app_opts.as_ptr(), + ptr::null_mut(), + ) + }; + if ds_c.is_null() { + Err(_last_null_pointer_err("GDALWarp")) + } else { + Ok(ds) + } + } + WarpDestination::Path(p) => { + let path = _path_to_c_string(&p)?; + let ds_c = unsafe { + GDALWarp( + path.as_ptr(), + ptr::null_mut(), + source.len() as libc::c_int, + source.as_ptr() as *mut GDALDatasetH, + app_opts.as_ptr(), + ptr::null_mut(), + ) + }; + Ok(unsafe { Dataset::from_c_dataset(ds_c) }) + } + } +} + +#[derive(Debug)] +pub enum WarpDestination { + Dataset(Dataset), + Path(PathBuf), +} + +impl From for WarpDestination { + fn from(ds: Dataset) -> Self { + WarpDestination::Dataset(ds) + } +} + +impl From for WarpDestination { + fn from(path: PathBuf) -> Self { + WarpDestination::Path(path) + } +} + #[cfg(test)] mod tests { use super::*; @@ -251,4 +324,21 @@ mod tests { Ok(()) } + + #[test] + #[ignore] + fn test_warp() -> Result<()> { + let source = TempFixture::fixture("labels.tif"); + let source_ds = Dataset::open(&source)?; + let dest = Path::new("target").join("labels-warp.tif"); + + let mut options = GdalWarpOptions::default(); + options + .with_band_count(source_ds.raster_count()) + .with_initial_value(InitValue::NoData) + .with_resampling_alg(WarpResampleAlg::NearestNeighbour); + + warp(&source_ds, dest, &options)?; + Ok(()) + } } diff --git a/src/raster/warp/warp_options.rs b/src/raster/warp/warp_options.rs index b70a6b90..fc9625bc 100644 --- a/src/raster/warp/warp_options.rs +++ b/src/raster/warp/warp_options.rs @@ -1,4 +1,5 @@ use std::fmt::{Debug, Display, Formatter}; +use std::ptr; use std::ptr::NonNull; use crate::cpl::CslStringList; @@ -9,9 +10,9 @@ use crate::utils::_last_null_pointer_err; use crate::xml::GdalXmlNode; use gdal_sys::{ GDALCloneWarpOptions, GDALCreateWarpOptions, GDALDeserializeWarpOptions, - GDALDestroyWarpOptions, GDALSerializeWarpOptions, GDALWarpInitDefaultBandMapping, - GDALWarpInitDstNoDataReal, GDALWarpInitSrcNoDataReal, GDALWarpOptions, - GDALWarpResolveWorkingDataType, + GDALDestroyWarpOptions, GDALSerializeWarpOptions, GDALWarpAppOptions, GDALWarpAppOptionsFree, + GDALWarpAppOptionsNew, GDALWarpInitDefaultBandMapping, GDALWarpInitDstNoDataReal, + GDALWarpInitSrcNoDataReal, GDALWarpOptions, GDALWarpResolveWorkingDataType, }; use libc::c_char; @@ -246,6 +247,47 @@ impl Debug for GdalWarpOptions { } } +#[derive(Debug)] +pub struct GdalWarpAppOptions(NonNull); + +impl GdalWarpAppOptions { + /// Instatiate and empty set of warp options. + pub fn new() -> Self { + unsafe { Self::from_ptr(GDALWarpAppOptionsNew(ptr::null_mut(), ptr::null_mut())) } + } + + /// Create Self from a raw pointer. + /// + /// # Safety + /// Caller is responsible for ensuring `ptr` is not null, and + /// ownership of `ptr` is properly transferred + pub unsafe fn from_ptr(ptr: *mut GDALWarpAppOptions) -> Self { + Self(NonNull::new_unchecked(ptr)) + } + + /// Get a immutable pointer to C API options. + pub fn as_ptr(&self) -> *const GDALWarpAppOptions { + self.0.as_ptr() + } + + /// Get a mutable pointer to C API options. + pub fn as_ptr_mut(&mut self) -> *mut GDALWarpAppOptions { + self.0.as_ptr() + } +} + +impl Drop for GdalWarpAppOptions { + fn drop(&mut self) { + unsafe { GDALWarpAppOptionsFree(self.as_ptr_mut()) } + } +} + +impl Default for GdalWarpAppOptions { + fn default() -> Self { + Self::new() + } +} + /// Specifies the initial value cells in the destination dataset during a warp operation. /// /// See [`GdalWarpOptions::with_initial_value`].