Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add raster mask methods #285

Merged
merged 6 commits into from Aug 27, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Expand Up @@ -59,6 +59,10 @@

- <https://github.com/georust/gdal/pull/265>

- Add methods to access raster masks and get raster mask flags. (`open_mask_band`, `create_mask_band`, and `mask_flags`).

- <https://github.com/georust/gdal/pull/285>

- Remove `PartialEq` from `GdalError`

- <https://github.com/georust/gdal/pull/286>
Expand Down
68 changes: 68 additions & 0 deletions src/raster/rasterband.rs
Expand Up @@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit surprised we don't have these in gdal-sys.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it is a define not a const variable in the cpp code. I guess bindgen does not pick that up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm not sure what's up, I think it should generate them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is bindgen able to handle defines? i can't find any examples.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so what is wrong with our bindgen config? And i guess we would need to regenerate all bindings -_-

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`.
Expand Down Expand Up @@ -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<GdalMaskFlags> {
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);
jdroenner marked this conversation as resolved.
Show resolved Hide resolved
if rv != 0 {
return Err(_last_cpl_err(rv));
}
Ok(())
}
}

/// Open the mask-`Rasterband`
pub fn open_mask_band(&self) -> Result<RasterBand> {
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> {
Expand Down
32 changes: 32 additions & 0 deletions src/raster/tests.rs
Expand Up @@ -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::<u8>((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::<u8>((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() {
Expand Down