From a255cdcd45030670e5f68c2f8ecfed2760ce8d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padarian?= Date: Wed, 6 Jan 2021 15:46:58 +1100 Subject: [PATCH 1/4] Added parameter to select resampling algo when reading a raster --- examples/rasterband.rs | 2 +- src/raster/rasterband.rs | 92 ++++++++++++++++++++++++++++++++++++++-- src/raster/tests.rs | 45 +++++++++++++++++--- 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/examples/rasterband.rs b/examples/rasterband.rs index 75470261..c7187067 100644 --- a/examples/rasterband.rs +++ b/examples/rasterband.rs @@ -13,7 +13,7 @@ fn main() { println!("rasterband type: {:?}", rasterband.band_type()); println!("rasterband scale: {:?}", rasterband.scale()); println!("rasterband offset: {:?}", rasterband.offset()); - if let Ok(rv) = rasterband.read_as::((20, 30), (2, 3), (2, 3)) { + if let Ok(rv) = rasterband.read_as::((20, 30), (2, 3), (2, 3), None) { println!("{:?}", rv.data); } } diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index c227810d..670124ec 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -3,7 +3,10 @@ use crate::gdal_major_object::MajorObject; use crate::metadata::Metadata; use crate::raster::{GDALDataType, GdalType}; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string}; -use gdal_sys::{self, CPLErr, GDALColorInterp, GDALMajorObjectH, GDALRWFlag, GDALRasterBandH}; +use gdal_sys::{ + self, CPLErr, GDALColorInterp, GDALMajorObjectH, GDALRIOResampleAlg, GDALRWFlag, + GDALRasterBandH, GDALRasterIOExtraArg, +}; use libc::c_int; use std::ffi::CString; @@ -12,6 +15,68 @@ use ndarray::Array2; use crate::errors::*; +/// Extra options used to read a raster. +/// +/// For documentation, see `gdal_sys::GDALRasterIOExtraArg`. +#[derive(Debug)] +pub struct RasterIOExtraArg { + pub n_version: libc::c_int, + pub e_resample_alg: GDALRIOResampleAlg::Type, + pub pfn_progress: gdal_sys::GDALProgressFunc, + pub p_progress_data: *mut libc::c_void, + pub b_floating_point_window_validity: libc::c_int, + pub df_x_off: f64, + pub df_y_off: f64, + pub df_x_size: f64, + pub df_y_size: f64, +} + +impl Default for RasterIOExtraArg { + fn default() -> Self { + Self { + n_version: 1, + pfn_progress: None, + p_progress_data: std::ptr::null_mut(), + e_resample_alg: GDALRIOResampleAlg::GRIORA_NearestNeighbour, + b_floating_point_window_validity: 0, + df_x_off: 0.0, + df_y_off: 0.0, + df_x_size: 0.0, + df_y_size: 0.0, + } + } +} + +impl Into for RasterIOExtraArg { + fn into(self) -> GDALRasterIOExtraArg { + // let opts: GDALRasterIOExtraArg = unsafe { std::mem::transmute(self) }; + // opts + let RasterIOExtraArg { + n_version, + e_resample_alg, + pfn_progress, + p_progress_data, + b_floating_point_window_validity, + df_x_off, + df_y_off, + df_x_size, + df_y_size, + } = self; + + GDALRasterIOExtraArg { + nVersion: n_version, + eResampleAlg: e_resample_alg, + pfnProgress: pfn_progress, + pProgressData: p_progress_data, + bFloatingPointWindowValidity: b_floating_point_window_validity, + dfXOff: df_x_off, + dfYOff: df_y_off, + dfXSize: df_x_size, + dfYSize: df_y_size, + } + } +} + /// Represents a single band of a dataset. /// /// This object carries the lifetime of the dataset that @@ -73,19 +138,32 @@ impl<'a> RasterBand<'a> { /// * window_size - the window size (GDAL will interpolate data if window_size != buffer_size) /// * size - the desired size to read /// * buffer - a slice to hold the data (length must equal product of size parameter) + /// * e_resample_alg - the resample algorithm used for the interpolation pub fn read_into_slice( &self, window: (isize, isize), window_size: (usize, usize), size: (usize, usize), buffer: &mut [T], + e_resample_alg: Option, ) -> Result<()> { let pixels = (size.0 * size.1) as usize; assert_eq!(buffer.len(), pixels); + let resample_alg = e_resample_alg.unwrap_or(GDALRIOResampleAlg::GRIORA_NearestNeighbour); + + // let options: RasterIOExtraArg = Default::default(); + let mut options: GDALRasterIOExtraArg = RasterIOExtraArg { + e_resample_alg: resample_alg, + ..Default::default() + } + .into(); + + let options_ptr: *mut GDALRasterIOExtraArg = &mut options; + //let no_data: let rv = unsafe { - gdal_sys::GDALRasterIO( + gdal_sys::GDALRasterIOEx( self.c_rasterband, GDALRWFlag::GF_Read, window.0 as c_int, @@ -98,6 +176,7 @@ impl<'a> RasterBand<'a> { T::gdal_type(), 0, 0, + options_ptr, ) }; if rv != CPLErr::CE_None { @@ -113,11 +192,13 @@ impl<'a> RasterBand<'a> { /// * window - the window position from top left /// * window_size - the window size (GDAL will interpolate data if window_size != buffer_size) /// * buffer_size - the desired size of the 'Buffer' + /// * e_resample_alg - the resample algorithm used for the interpolation pub fn read_as( &self, window: (isize, isize), window_size: (usize, usize), size: (usize, usize), + e_resample_alg: Option, ) -> Result> { let pixels = (size.0 * size.1) as usize; @@ -131,7 +212,7 @@ impl<'a> RasterBand<'a> { unsafe { data.set_len(pixels); }; - self.read_into_slice(window, window_size, size, &mut data)?; + self.read_into_slice(window, window_size, size, &mut data, e_resample_alg)?; Ok(Buffer { size, data }) } @@ -143,6 +224,7 @@ impl<'a> RasterBand<'a> { /// * window - the window position from top left /// * window_size - the window size (GDAL will interpolate data if window_size != array_size) /// * array_size - the desired size of the 'Array' + /// * e_resample_alg - the resample algorithm used for the interpolation /// # Docs /// The Matrix shape is (rows, cols) and raster shape is (cols in x-axis, rows in y-axis). pub fn read_as_array( @@ -150,8 +232,9 @@ impl<'a> RasterBand<'a> { window: (isize, isize), window_size: (usize, usize), array_size: (usize, usize), + e_resample_alg: Option, ) -> Result> { - let data = self.read_as::(window, window_size, array_size)?; + let data = self.read_as::(window, window_size, array_size, e_resample_alg)?; // Matrix shape is (rows, cols) and raster shape is (cols in x-axis, rows in y-axis) Ok(Array2::from_shape_vec( @@ -169,6 +252,7 @@ impl<'a> RasterBand<'a> { (0, 0), (size.0 as usize, size.1 as usize), (size.0 as usize, size.1 as usize), + None, ) } diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 0ade16c9..146087d4 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -2,7 +2,7 @@ use crate::dataset::Dataset; use crate::metadata::Metadata; use crate::raster::{ByteBuffer, ColorInterpretation}; use crate::Driver; -use gdal_sys::GDALDataType; +use gdal_sys::{GDALDataType, GDALRIOResampleAlg}; use std::path::Path; #[cfg(feature = "ndarray")] @@ -73,17 +73,50 @@ fn test_get_projection() { fn test_read_raster() { let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); let rb = dataset.rasterband(1).unwrap(); - let rv = rb.read_as::((20, 30), (2, 3), (2, 3)).unwrap(); + let rv = rb.read_as::((20, 30), (2, 3), (2, 3), None).unwrap(); assert_eq!(rv.size.0, 2); assert_eq!(rv.size.1, 3); assert_eq!(rv.data, vec!(7, 7, 7, 10, 8, 12)); let mut buf = rv; - rb.read_into_slice((20, 30), (2, 3), (2, 3), &mut buf.data) + rb.read_into_slice((20, 30), (2, 3), (2, 3), &mut buf.data, None) .unwrap(); assert_eq!(buf.data, vec!(7, 7, 7, 10, 8, 12)); } +#[test] +fn test_read_raster_with_default_resample() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rb = dataset.rasterband(1).unwrap(); + let rv = rb.read_as::((20, 30), (4, 4), (2, 2), None).unwrap(); + assert_eq!(rv.size.0, 2); + assert_eq!(rv.size.1, 2); + assert_eq!(rv.data, vec!(10, 4, 6, 11)); + + let mut buf = rv; + rb.read_into_slice((20, 30), (4, 4), (2, 2), &mut buf.data, None) + .unwrap(); + assert_eq!(buf.data, vec!(10, 4, 6, 11)); +} + +#[test] +fn test_read_raster_with_average_resample() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rb = dataset.rasterband(1).unwrap(); + let resample_alg = Some(GDALRIOResampleAlg::GRIORA_Average); + let rv = rb + .read_as::((20, 30), (4, 4), (2, 2), resample_alg) + .unwrap(); + assert_eq!(rv.size.0, 2); + assert_eq!(rv.size.1, 2); + assert_eq!(rv.data, vec!(8, 6, 8, 12)); + + let mut buf = rv; + rb.read_into_slice((20, 30), (4, 4), (2, 2), &mut buf.data, resample_alg) + .unwrap(); + assert_eq!(buf.data, vec!(8, 6, 8, 12)); +} + #[test] fn test_write_raster() { let driver = Driver::get("MEM").unwrap(); @@ -103,11 +136,11 @@ fn test_write_raster() { assert!(res.is_ok()); // read a pixel from the left side - let left = rb.read_as::((5, 5), (1, 1), (1, 1)).unwrap(); + let left = rb.read_as::((5, 5), (1, 1), (1, 1), None).unwrap(); assert_eq!(left.data[0], 50u8); // read a pixel from the right side - let right = rb.read_as::((15, 5), (1, 1), (1, 1)).unwrap(); + let right = rb.read_as::((15, 5), (1, 1), (1, 1), None).unwrap(); assert_eq!(right.data[0], 20u8); } @@ -240,7 +273,7 @@ fn test_get_driver_by_name() { fn test_read_raster_as() { let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); let rb = dataset.rasterband(1).unwrap(); - let rv = rb.read_as::((20, 30), (2, 3), (2, 3)).unwrap(); + let rv = rb.read_as::((20, 30), (2, 3), (2, 3), None).unwrap(); assert_eq!(rv.data, vec!(7, 7, 7, 10, 8, 12)); assert_eq!(rv.size.0, 2); assert_eq!(rv.size.1, 3); From b104c30aacbdc5fe3ac4b0147c06e78b703a8493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padarian?= Date: Wed, 6 Jan 2021 16:00:00 +1100 Subject: [PATCH 2/4] Added entry to CHANGES --- CHANGES.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2a0d188c..62ca2873 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,14 @@ # Changes ## Unreleased + +* **Breaking**: Add support to select a resampling algorithm when reading a raster + * + + Now, it is necessary to provide a `Option` when reading a raster. + If `None`, it uses `ResampleAlg::NearestNeighbour` which was the + default behavior. + * Implement wrapper for `OGR_L_TestCapability` * * **Breaking**: Use `DatasetOptions` to pass as `Dataset::open_ex` parameters and @@ -76,8 +84,11 @@ `SpatialRef::axis_mapping_strategy` instead. * Add support for reading and setting rasterband colour interpretations * +<<<<<<< HEAD * Fixed memory leak in `Geometry::from_wkt` * +======= +>>>>>>> Added entry to CHANGES ## 0.7.1 * fix docs.rs build for gdal-sys From 468af22e698bc5e272f89542d9f479509d8086fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padarian?= Date: Wed, 3 Mar 2021 12:51:29 +1100 Subject: [PATCH 3/4] Added extra parameter in test_read_raster_as_array --- src/raster/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 146087d4..4bc7384b 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -294,6 +294,7 @@ fn test_read_raster_as_array() { (left, top), (window_size_x, window_size_y), (array_size_x, array_size_y), + None, ) .unwrap(); From 969a187a8b598e276bb9908d8d11fc10721bad3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padarian?= Date: Wed, 24 Mar 2021 10:10:44 +1100 Subject: [PATCH 4/4] Changed resample algorithm to enum --- src/raster/rasterband.rs | 66 +++++++++++++++++++++++++++++----------- src/raster/tests.rs | 9 +++--- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index 670124ec..498701be 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -4,8 +4,8 @@ use crate::metadata::Metadata; use crate::raster::{GDALDataType, GdalType}; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string}; use gdal_sys::{ - self, CPLErr, GDALColorInterp, GDALMajorObjectH, GDALRIOResampleAlg, GDALRWFlag, - GDALRasterBandH, GDALRasterIOExtraArg, + self, CPLErr, GDALColorInterp, GDALMajorObjectH, GDALRWFlag, GDALRasterBandH, + GDALRasterIOExtraArg, }; use libc::c_int; use std::ffi::CString; @@ -15,16 +15,50 @@ use ndarray::Array2; use crate::errors::*; +/// Resampling algorithms, map GDAL defines +#[derive(Debug, Copy, Clone)] +pub enum ResampleAlg { + /// Nearest neighbour + NearestNeighbour, + /// Bilinear (2x2 kernel) + Bilinear, + /// Cubic Convolution Approximation (4x4 kernel) + Cubic, + /// Cubic B-Spline Approximation (4x4 kernel) + CubicSpline, + /// Lanczos windowed sinc interpolation (6x6 kernel) + Lanczos, + /// Average + Average, + /// Mode (selects the value which appears most often of all the sampled points) + Mode, + /// Gauss blurring + Gauss, +} + +fn map_resample_alg(alg: &ResampleAlg) -> u32 { + match alg { + ResampleAlg::NearestNeighbour => 0, + ResampleAlg::Bilinear => 1, + ResampleAlg::Cubic => 2, + ResampleAlg::CubicSpline => 3, + ResampleAlg::Lanczos => 4, + ResampleAlg::Average => 5, + ResampleAlg::Mode => 6, + ResampleAlg::Gauss => 7, + } +} + /// Extra options used to read a raster. /// /// For documentation, see `gdal_sys::GDALRasterIOExtraArg`. #[derive(Debug)] pub struct RasterIOExtraArg { - pub n_version: libc::c_int, - pub e_resample_alg: GDALRIOResampleAlg::Type, + pub n_version: usize, + pub e_resample_alg: ResampleAlg, pub pfn_progress: gdal_sys::GDALProgressFunc, - pub p_progress_data: *mut libc::c_void, - pub b_floating_point_window_validity: libc::c_int, + p_progress_data: *mut libc::c_void, + pub b_floating_point_window_validity: usize, pub df_x_off: f64, pub df_y_off: f64, pub df_x_size: f64, @@ -37,7 +71,7 @@ impl Default for RasterIOExtraArg { n_version: 1, pfn_progress: None, p_progress_data: std::ptr::null_mut(), - e_resample_alg: GDALRIOResampleAlg::GRIORA_NearestNeighbour, + e_resample_alg: ResampleAlg::NearestNeighbour, b_floating_point_window_validity: 0, df_x_off: 0.0, df_y_off: 0.0, @@ -49,8 +83,6 @@ impl Default for RasterIOExtraArg { impl Into for RasterIOExtraArg { fn into(self) -> GDALRasterIOExtraArg { - // let opts: GDALRasterIOExtraArg = unsafe { std::mem::transmute(self) }; - // opts let RasterIOExtraArg { n_version, e_resample_alg, @@ -64,11 +96,11 @@ impl Into for RasterIOExtraArg { } = self; GDALRasterIOExtraArg { - nVersion: n_version, - eResampleAlg: e_resample_alg, + nVersion: n_version as c_int, + eResampleAlg: map_resample_alg(&e_resample_alg), pfnProgress: pfn_progress, pProgressData: p_progress_data, - bFloatingPointWindowValidity: b_floating_point_window_validity, + bFloatingPointWindowValidity: b_floating_point_window_validity as c_int, dfXOff: df_x_off, dfYOff: df_y_off, dfXSize: df_x_size, @@ -145,14 +177,13 @@ impl<'a> RasterBand<'a> { window_size: (usize, usize), size: (usize, usize), buffer: &mut [T], - e_resample_alg: Option, + e_resample_alg: Option, ) -> Result<()> { let pixels = (size.0 * size.1) as usize; assert_eq!(buffer.len(), pixels); - let resample_alg = e_resample_alg.unwrap_or(GDALRIOResampleAlg::GRIORA_NearestNeighbour); + let resample_alg = e_resample_alg.unwrap_or(ResampleAlg::NearestNeighbour); - // let options: RasterIOExtraArg = Default::default(); let mut options: GDALRasterIOExtraArg = RasterIOExtraArg { e_resample_alg: resample_alg, ..Default::default() @@ -161,7 +192,6 @@ impl<'a> RasterBand<'a> { let options_ptr: *mut GDALRasterIOExtraArg = &mut options; - //let no_data: let rv = unsafe { gdal_sys::GDALRasterIOEx( self.c_rasterband, @@ -198,7 +228,7 @@ impl<'a> RasterBand<'a> { window: (isize, isize), window_size: (usize, usize), size: (usize, usize), - e_resample_alg: Option, + e_resample_alg: Option, ) -> Result> { let pixels = (size.0 * size.1) as usize; @@ -232,7 +262,7 @@ impl<'a> RasterBand<'a> { window: (isize, isize), window_size: (usize, usize), array_size: (usize, usize), - e_resample_alg: Option, + e_resample_alg: Option, ) -> Result> { let data = self.read_as::(window, window_size, array_size, e_resample_alg)?; diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 4bc7384b..92e62915 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -1,8 +1,9 @@ use crate::dataset::Dataset; use crate::metadata::Metadata; +use crate::raster::rasterband::ResampleAlg; use crate::raster::{ByteBuffer, ColorInterpretation}; use crate::Driver; -use gdal_sys::{GDALDataType, GDALRIOResampleAlg}; +use gdal_sys::GDALDataType; use std::path::Path; #[cfg(feature = "ndarray")] @@ -103,16 +104,16 @@ fn test_read_raster_with_default_resample() { fn test_read_raster_with_average_resample() { let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); let rb = dataset.rasterband(1).unwrap(); - let resample_alg = Some(GDALRIOResampleAlg::GRIORA_Average); + let resample_alg = ResampleAlg::Average; let rv = rb - .read_as::((20, 30), (4, 4), (2, 2), resample_alg) + .read_as::((20, 30), (4, 4), (2, 2), Some(resample_alg)) .unwrap(); assert_eq!(rv.size.0, 2); assert_eq!(rv.size.1, 2); assert_eq!(rv.data, vec!(8, 6, 8, 12)); let mut buf = rv; - rb.read_into_slice((20, 30), (4, 4), (2, 2), &mut buf.data, resample_alg) + rb.read_into_slice((20, 30), (4, 4), (2, 2), &mut buf.data, Some(resample_alg)) .unwrap(); assert_eq!(buf.data, vec!(8, 6, 8, 12)); }