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

Added no_data_value_u64, set_no_data_value_u64, no_data_value_i64 and set_no_data_value_i64 to RasterBand #520

Merged
merged 5 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Added `no_data_value_u64`, `set_no_data_value_u64`, `no_data_value_i64` and `set_no_data_value_i64` to `RasterBand`.
- <https://github.com/georust/gdal/pull/520>

- **Breaking** Removed `RasterCreationOption`and replaced usages of `[RasterCreationOption]` with `RasterCreationOptions`, a type alias for `CplStringList`.

- <https://github.com/georust/gdal/pull/519>
Expand Down
97 changes: 96 additions & 1 deletion src/raster/rasterband.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ impl<'a> RasterBand<'a> {
None
}

/// Set the no data value of this band.
/// Sets the no-data value of this band.
///
/// If `no_data` is `None`, any existing no-data value is deleted.
pub fn set_no_data_value(&mut self, no_data: Option<f64>) -> Result<()> {
Expand All @@ -702,6 +702,101 @@ impl<'a> RasterBand<'a> {
}
}

/// Fetch the no-data value for this band.
///
/// This method should ONLY be called on bands whose data type is `UInt64`.
///
/// The no data value returned is 'raw', meaning that it has no offset and scale applied.
///
/// # Returns
/// No-data value as `Some(i64)` if no-data value exists, `None` otherwise.
///
/// # Notes
/// See also: [`GDALGetRasterNoDataValueAsUInt64`](https://gdal.org/api/raster_c_api.html#_CPPv432GDALGetRasterNoDataValueAsUInt6415GDALRasterBandHPi)
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn no_data_value_u64(&self) -> Option<u64> {
let mut pb_success = 1;
let no_data = unsafe {
gdal_sys::GDALGetRasterNoDataValueAsUInt64(self.c_rasterband, &mut pb_success)
};
if pb_success == 1 {
return Some(no_data);
}
None
}

/// Sets the no-data value for a `UInt64` band.
///
/// This method should ONLY be called on bands whose data type is `UInt64`.
///
/// If `no_data` is `None`, any existing no-data value is deleted.
///
/// # Notes
/// See also:
/// [`GDALSetRasterNoDataValueAsUInt64`](https://gdal.org/api/raster_c_api.html#_CPPv432GDALSetRasterNoDataValueAsUInt6415GDALRasterBandH8uint64_t),
/// [`GDALDeleteRasterNoDataValue`](https://gdal.org/api/raster_c_api.html#_CPPv427GDALDeleteRasterNoDataValue15GDALRasterBandH)
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn set_no_data_value_u64(&mut self, no_data: Option<u64>) -> Result<()> {
let rv = if let Some(no_data) = no_data {
unsafe { gdal_sys::GDALSetRasterNoDataValueAsUInt64(self.c_rasterband, no_data) }
} else {
unsafe { gdal_sys::GDALDeleteRasterNoDataValue(self.c_rasterband) }
};

if rv != CPLErr::CE_None {
Err(_last_cpl_err(rv))
} else {
Ok(())
}
}

/// Fetch the no-data value for this band.
///
/// This method should ONLY be called on bands whose data type is `Int64`.
///
/// The no data value returned is 'raw', meaning that it has no offset and scale applied.
///
/// # Returns
/// No-data value as `Some(i64)` if no-data value exists, `None` otherwise.
///
/// # Notes
/// See also: [`GDALGetRasterNoDataValueAsInt64`](https://gdal.org/api/gdalrasterband_cpp.html#_CPPv4N14GDALRasterBand21GetNoDataValueAsInt64EPi)
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn no_data_value_i64(&self) -> Option<i64> {
let mut pb_success = 1;
let no_data = unsafe {
gdal_sys::GDALGetRasterNoDataValueAsInt64(self.c_rasterband, &mut pb_success)
};
if pb_success == 1 {
return Some(no_data);
}
None
}

/// Sets the no-data value for a `Int64` band.
///
/// This method should ONLY be called on bands whose data type is `Int64`.
///
/// If `no_data` is `None`, any existing no-data value is deleted.
///
/// # Notes
/// See also:
/// [`GDALSetRasterNoDataValueAsInt64`](https://gdal.org/api/raster_c_api.html#_CPPv431GDALSetRasterNoDataValueAsInt6415GDALRasterBandH7int64_t),
/// [`GDALDeleteRasterNoDataValue`](https://gdal.org/api/raster_c_api.html#_CPPv427GDALDeleteRasterNoDataValue15GDALRasterBandH)
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn set_no_data_value_i64(&mut self, no_data: Option<i64>) -> Result<()> {
let rv = if let Some(no_data) = no_data {
Copy link
Member

@lnicola lnicola Feb 1, 2024

Choose a reason for hiding this comment

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

Well, isn't this awkward...

Taking an Option seemed like a good idea at the time. Now we have three ways to clear it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I still think there's value in keeping them consistent...

The alternative is to add GDALDeleteRasterNoDataValue

Copy link
Member

Choose a reason for hiding this comment

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

And now I think we should go for that (but in a separate PR).

unsafe { gdal_sys::GDALSetRasterNoDataValueAsInt64(self.c_rasterband, no_data) }
} else {
unsafe { gdal_sys::GDALDeleteRasterNoDataValue(self.c_rasterband) }
};

if rv != CPLErr::CE_None {
Err(_last_cpl_err(rv))
} else {
Ok(())
}
}
/// Returns the color interpretation of this band.
pub fn color_interpretation(&self) -> ColorInterpretation {
let interp_index = unsafe { gdal_sys::GDALGetRasterColorInterpretation(self.c_rasterband) };
Expand Down
42 changes: 35 additions & 7 deletions src/raster/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::dataset::Dataset;
use crate::errors::Result;
use crate::metadata::Metadata;
use crate::raster::rasterband::ResampleAlg;
use crate::raster::{
Expand Down Expand Up @@ -424,16 +425,43 @@ fn test_get_rasterband() {
}

#[test]
fn test_get_no_data_value() {
let dataset = Dataset::open(fixture("tinymarble.tif")).unwrap();
let rasterband = dataset.rasterband(1).unwrap();
fn test_get_no_data_value() -> Result<()> {
let dataset = Dataset::open(fixture("tinymarble.tif"))?;
let rasterband = dataset.rasterband(1)?;
let no_data_value = rasterband.no_data_value();
assert!(no_data_value.is_none());

// let dataset = Dataset::open(fixture!("bluemarble.tif")).unwrap();
// let rasterband = dataset.get_rasterband(1).unwrap();
// let no_data_value = rasterband.get_no_data_value();
// assert_eq!(no_data_value, Some(0.0));
let dataset = Dataset::open(fixture("labels.tif"))?;
let rasterband = dataset.rasterband(1)?;
let no_data_value = rasterband.no_data_value();
assert_eq!(no_data_value, Some(255.0));
Ok(())
}

#[test]
#[cfg(all(major_ge_3, minor_ge_5))]
fn test_no_data_value_i64() -> Result<()> {
let driver = DriverManager::get_driver_by_name("MEM")?;
let ds = driver.create_with_band_type::<i64, _>("test_no_data_value_i64", 1, 1, 1)?;
let mut rasterband = ds.rasterband(1)?;
assert_eq!(rasterband.no_data_value_i64(), None);
rasterband.set_no_data_value_i64(Some(i64::MIN))?;
assert_eq!(rasterband.no_data_value_i64(), Some(i64::MIN));

Ok(())
}

#[test]
#[cfg(all(major_ge_3, minor_ge_5))]
fn test_no_data_value_u64() -> Result<()> {
let driver = DriverManager::get_driver_by_name("MEM")?;
let ds = driver.create_with_band_type::<u64, _>("test_no_data_value_u64", 1, 1, 1)?;
let mut rasterband = ds.rasterband(1)?;
assert_eq!(rasterband.no_data_value_u64(), None);
rasterband.set_no_data_value_u64(Some(u64::MAX))?;
assert_eq!(rasterband.no_data_value_u64(), Some(u64::MAX));

Ok(())
}

#[test]
Expand Down
Loading