From 9ddbec30b0ae2d7eb6ef6ccd7e03b004d8c14e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dr=C3=B6nner?= Date: Mon, 15 Aug 2022 16:51:29 +0200 Subject: [PATCH 1/5] add raster mask methods --- src/raster/rasterband.rs | 68 ++++++++++++++++++++++++++++++++++++++++ src/raster/tests.rs | 32 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index d0e28ffc2..4fee19ecd 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -50,6 +50,37 @@ fn map_resample_alg(alg: &ResampleAlg) -> u32 { } } +/// Wrapper type for gdal mask flags. +/// From the GDAL docs: +/// - `GMF_ALL_VALID`(0x01): There are no invalid pixels, all mask values will be 255. When used this will normally be the only flag set. +/// - `GMF_PER_DATASET`(0x02): The mask band is shared between all bands on the dataset. +/// - `GMF_ALPHA`(0x04): The mask band is actually an alpha band and may have values other than 0 and 255. +/// - `GMF_NODATA`(0x08): Indicates the mask is actually being generated from nodata values. (mutually exclusive of `GMF_ALPHA`) +pub struct GdalMaskFlags(i32); + +impl GdalMaskFlags { + const GMF_ALL_VALID: i32 = 0x01; + const GMF_PER_DATASET: i32 = 0x02; + const GMF_ALPHA: i32 = 0x04; + const GMF_NODATA: i32 = 0x08; + + pub fn is_all_valid(&self) -> bool { + self.0 & Self::GMF_ALL_VALID != 0 + } + + pub fn is_per_dataset(&self) -> bool { + self.0 & Self::GMF_PER_DATASET != 0 + } + + pub fn is_alpha(&self) -> bool { + self.0 & Self::GMF_ALPHA != 0 + } + + pub fn is_nodata(&self) -> bool { + self.0 & Self::GMF_NODATA != 0 + } +} + /// Extra options used to read a raster. /// /// For documentation, see `gdal_sys::GDALRasterIOExtraArg`. @@ -498,6 +529,43 @@ impl<'a> RasterBand<'a> { _string(str_ptr) } + + /// Read the band mask flags for a GDAL `RasterBand`. + pub fn mask_flags(&self) -> Result { + let band_mask_flags = unsafe { gdal_sys::GDALGetMaskFlags(self.c_rasterband) }; + + Ok(GdalMaskFlags(band_mask_flags)) + } + + /// Create a new mask band for the layer. + /// `shared_between_all_bands` indicates if all bands of the dataset use the same mask. + pub fn create_mask_band(&mut self, shared_between_all_bands: bool) -> Result<()> { + let flags = if shared_between_all_bands { + GdalMaskFlags::GMF_PER_DATASET // It is the only valid flag here. + } else { + 0x00 + }; + + unsafe { + let rv = gdal_sys::GDALCreateMaskBand(self.c_rasterband, flags); + if rv != 0 { + return Err(_last_cpl_err(rv)); + } + Ok(()) + } + } + + /// Open the mask-`Rasterband` + pub fn open_mask_band(&self) -> Result { + unsafe { + let mask_band_ptr = gdal_sys::GDALGetMaskBand(self.c_rasterband); + if mask_band_ptr.is_null() { + return Err(_last_null_pointer_err("GDALGetMaskBand")); + } + let mask_band = RasterBand::from_c_rasterband(self.dataset, mask_band_ptr); + Ok(mask_band) + } + } } impl<'a> MajorObject for RasterBand<'a> { diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 43ffdd37a..01d42d863 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -406,6 +406,38 @@ fn test_read_raster_as() { assert_eq!(rb.band_type(), GDALDataType::GDT_Byte); } +#[test] +fn mask_flags() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rb = dataset.rasterband(1).unwrap(); + let mask_flags = rb.mask_flags().unwrap(); + assert_eq!(mask_flags.is_nodata(), false); + assert_eq!(mask_flags.is_alpha(), false); + assert_eq!(mask_flags.is_per_dataset(), false); + assert_eq!(mask_flags.is_all_valid(), true); +} + +#[test] +fn open_mask_band() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rb = dataset.rasterband(1).unwrap(); + let mb = rb.open_mask_band().unwrap(); + let mask_values = mb.read_as::((20, 30), (2, 3), (2, 3), None).unwrap(); + assert_eq!(mask_values.data, [255u8; 6]) +} + +#[test] +fn create_mask_band() { + let driver = Driver::get_by_name("MEM").unwrap(); + let dataset = driver.create("", 20, 10, 1).unwrap(); + let mut rb = dataset.rasterband(1).unwrap(); + rb.create_mask_band(false).unwrap(); + + let mb = rb.open_mask_band().unwrap(); + let mask_values = mb.read_as::((0, 0), (20, 10), (20, 10), None).unwrap(); + assert_eq!(mask_values.data, [0; 200]) +} + #[test] #[cfg(feature = "ndarray")] fn test_read_raster_as_array() { From 7ae3f55f43dd3fe7bb316cc27bc4bc6efb4909a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dr=C3=B6nner?= Date: Mon, 15 Aug 2022 16:56:02 +0200 Subject: [PATCH 2/5] add changes --- CHANGES.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4c02b0944..da98e15b6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,7 +17,7 @@ - Implemented wrapper for `OGR_L_SetFeature` - - + - Add `programs::raster::build_vrt` - Add `GeoTransformEx` extension trait with `apply` and `invert` @@ -47,7 +47,7 @@ - -- Add `gdal::srs::CoordTransform::transform_bounds` as wrapper for `OCTTransformBounds` for GDAL 3.4 +- Add `gdal::srs::CoordTransform::transform_bounds` as wrapper for `OCTTransformBounds` for GDAL 3.4 - @@ -59,6 +59,10 @@ - +- Add methods to access raster masks and get raster mask flags. (`open_mask_band`, `create_mask_band`, and `mask_flags`). + + - + ## 0.12 - Bump Rust edition to 2021 From d1d392a180754b59172788edc90b8568af28a883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dr=C3=B6nner?= Date: Tue, 16 Aug 2022 13:59:55 +0200 Subject: [PATCH 3/5] clippy assert --- src/raster/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 01d42d863..9d7be8fde 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -411,10 +411,10 @@ fn mask_flags() { let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); let rb = dataset.rasterband(1).unwrap(); let mask_flags = rb.mask_flags().unwrap(); - assert_eq!(mask_flags.is_nodata(), false); - assert_eq!(mask_flags.is_alpha(), false); - assert_eq!(mask_flags.is_per_dataset(), false); - assert_eq!(mask_flags.is_all_valid(), true); + assert!(!mask_flags.is_nodata()); + assert!(!mask_flags.is_alpha()); + assert!(!mask_flags.is_per_dataset()); + assert!(mask_flags.is_all_valid()); } #[test] From 6f4fbea032bf5afa7b2477867c80a8e96ddd3c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dr=C3=B6nner?= Date: Tue, 16 Aug 2022 15:49:28 +0200 Subject: [PATCH 4/5] smaller unsafe block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laurențiu Nicola --- src/raster/rasterband.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index 28765ad31..7f06edf28 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -547,7 +547,7 @@ impl<'a> RasterBand<'a> { }; unsafe { - let rv = gdal_sys::GDALCreateMaskBand(self.c_rasterband, flags); + let rv = unsafe { gdal_sys::GDALCreateMaskBand(self.c_rasterband, flags) }; if rv != 0 { return Err(_last_cpl_err(rv)); } From cc4dbb68b4f2f40d96216242a873e43876a26808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dr=C3=B6nner?= Date: Tue, 16 Aug 2022 16:03:47 +0200 Subject: [PATCH 5/5] fix unsafe block --- src/raster/rasterband.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index 7f06edf28..0c8002202 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -546,13 +546,11 @@ impl<'a> RasterBand<'a> { 0x00 }; - unsafe { - let rv = unsafe { gdal_sys::GDALCreateMaskBand(self.c_rasterband, flags) }; - if rv != 0 { - return Err(_last_cpl_err(rv)); - } - Ok(()) - } + let rv = unsafe { gdal_sys::GDALCreateMaskBand(self.c_rasterband, flags) }; + if rv != 0 { + return Err(_last_cpl_err(rv)); + }; + Ok(()) } /// Open the mask-`Rasterband`