diff --git a/CHANGES.md b/CHANGES.md index 472dde6a0..3ede7919d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -63,6 +63,10 @@ - +- Add methods to access raster masks and get raster mask flags. (`open_mask_band`, `create_mask_band`, and `mask_flags`). + + - + - Remove `PartialEq` from `GdalError` - diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index 76c387b9a..0c8002202 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,41 @@ 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 + }; + + let rv = unsafe { 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..9d7be8fde 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!(!mask_flags.is_nodata()); + assert!(!mask_flags.is_alpha()); + assert!(!mask_flags.is_per_dataset()); + assert!(mask_flags.is_all_valid()); +} + +#[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() {