From ddc7c9c566ae9ae129e3da76433abc419a78bb39 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sun, 16 Oct 2022 16:54:17 -0400 Subject: [PATCH 01/15] Created GdalDataTypeDescriptor. --- src/raster/mod.rs | 2 +- src/raster/tests.rs | 16 +++++++--- src/raster/types.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/raster/mod.rs b/src/raster/mod.rs index 82330400..1eb7f4be 100644 --- a/src/raster/mod.rs +++ b/src/raster/mod.rs @@ -91,7 +91,7 @@ pub use rasterband::{ StatisticsMinMax, }; pub use rasterize::{rasterize, BurnSource, MergeAlgorithm, OptimizeMode, RasterizeOptions}; -pub use types::{GDALDataType, GdalType}; +pub use types::{GDALDataType, GdalType, GdalTypeDescriptor}; pub use warp::reproject; /// Key/value pair for passing driver-specific creation options to diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 17879f31..2cb12790 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -1,10 +1,7 @@ use crate::dataset::Dataset; use crate::metadata::Metadata; use crate::raster::rasterband::ResampleAlg; -use crate::raster::{ - ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, RasterCreationOption, StatisticsAll, - StatisticsMinMax, -}; +use crate::raster::{ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, GdalTypeDescriptor, RasterCreationOption, StatisticsAll, StatisticsMinMax}; use crate::test_utils::TempFixture; use crate::vsi::unlink_mem_file; use crate::DriverManager; @@ -892,3 +889,14 @@ fn test_raster_stats() { } ); } + +#[test] +fn test_gdal_data_type() { + for t in GdalTypeDescriptor::available_types() { + // Test converting from GDALDataType:Type + let t2: GdalTypeDescriptor = t.gdal_type().try_into().unwrap(); + assert_eq!(t, &t2, "{}", t); + assert!(t.bits() > 0, "{}", t); + assert_eq!(t.bits(), t.bytes() * 8, "{}", t); + } +} diff --git a/src/raster/types.rs b/src/raster/types.rs index 18a75108..993ac180 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -1,7 +1,17 @@ +use std::fmt::{Display, Formatter}; pub use gdal_sys::GDALDataType; +use gdal_sys::{GDALGetDataTypeName, GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes}; +use crate::errors::{GdalError, Result}; +use crate::utils::{_last_null_pointer_err, _string}; +/// Type-level constraint for limiting which primitive numeric values can be passed +/// to functions needing target data type. pub trait GdalType { fn gdal_type() -> GDALDataType::Type; + fn descriptor() -> GdalTypeDescriptor { + // We can call `unwrap` because existence is guaranteed in this case. + Self::gdal_type().try_into().unwrap() + } } impl GdalType for u8 { @@ -9,33 +19,101 @@ impl GdalType for u8 { GDALDataType::GDT_Byte } } + impl GdalType for u16 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_UInt16 } } + impl GdalType for u32 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_UInt32 } } + impl GdalType for i16 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Int16 } } + impl GdalType for i32 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Int32 } } + impl GdalType for f32 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Float32 } } + impl GdalType for f64 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Float64 } } + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct GdalTypeDescriptor(GDALDataType::Type); + +impl GdalTypeDescriptor { + pub fn gdal_type(&self) -> GDALDataType::Type { + self.0 + } + pub fn name(&self) -> Result { + let c_str = unsafe { GDALGetDataTypeName(self.gdal_type()) }; + if c_str.is_null() { + return Err(_last_null_pointer_err("GDALGetDescription")); + } + Ok(_string(c_str)) + } + + /// Get the gdal type size in **bits**. + pub fn bits(&self) -> u8 { + unsafe { GDALGetDataTypeSizeBits(self.gdal_type()) }.try_into().unwrap() + } + + /// Get the gdal type size in **bytes**. + pub fn bytes(&self) -> u8 { + unsafe { GDALGetDataTypeSizeBytes(self.gdal_type()) }.try_into().unwrap() + } + + /// Subset of the GDAL data types supported by Rust bindings. + pub fn available_types() -> &'static [GdalTypeDescriptor] { + use GDALDataType::*; + &[ + GdalTypeDescriptor(GDT_Byte), + GdalTypeDescriptor(GDT_UInt16), + GdalTypeDescriptor(GDT_Int16), + GdalTypeDescriptor(GDT_UInt32), + GdalTypeDescriptor(GDT_Int32), + GdalTypeDescriptor(GDT_UInt64), + GdalTypeDescriptor(GDT_Int64), + GdalTypeDescriptor(GDT_Float32), + GdalTypeDescriptor(GDT_Float64) + ] + } +} + +impl Display for GdalTypeDescriptor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.name().unwrap()) + } +} + +impl TryFrom for GdalTypeDescriptor { + type Error = GdalError; + + fn try_from(value: GDALDataType::Type) -> std::result::Result { + let wrapped = GdalTypeDescriptor(value); + if !GdalTypeDescriptor::available_types().contains(&wrapped) { + Err(GdalError::BadArgument(format!("unknown GDALDataType {value}"))) + } else { + Ok(wrapped) + } + } +} + From 8446544e4039146a66999809b55755868db69efa Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Mon, 17 Oct 2022 20:03:08 -0400 Subject: [PATCH 02/15] Added unit tests. --- src/raster/tests.rs | 28 +++++++++++++++++++++++++++- src/raster/types.rs | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 2cb12790..31acd221 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -1,13 +1,17 @@ use crate::dataset::Dataset; use crate::metadata::Metadata; use crate::raster::rasterband::ResampleAlg; -use crate::raster::{ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, GdalTypeDescriptor, RasterCreationOption, StatisticsAll, StatisticsMinMax}; +use crate::raster::{ + ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, GdalTypeDescriptor, + RasterCreationOption, StatisticsAll, StatisticsMinMax, +}; use crate::test_utils::TempFixture; use crate::vsi::unlink_mem_file; use crate::DriverManager; use gdal_sys::GDALDataType; use std::path::Path; +use gdal_sys::GDALDataType::*; #[cfg(feature = "ndarray")] use ndarray::arr2; @@ -891,6 +895,7 @@ fn test_raster_stats() { } #[test] +#[allow(non_upper_case_globals)] fn test_gdal_data_type() { for t in GdalTypeDescriptor::available_types() { // Test converting from GDALDataType:Type @@ -898,5 +903,26 @@ fn test_gdal_data_type() { assert_eq!(t, &t2, "{}", t); assert!(t.bits() > 0, "{}", t); assert_eq!(t.bits(), t.bytes() * 8, "{}", t); + let name = t.name().unwrap(); + match t.gdal_type() { + GDT_Byte | GDT_UInt16 | GDT_Int16 | GDT_UInt32 | GDT_Int32 | GDT_UInt64 | GDT_Int64 => { + assert!(t.is_integer(), "{}", &name); + assert!(!t.is_floating(), "{}", &name); + } + GDT_Float32 | GDT_Float64 => { + assert!(!t.is_integer(), "{}", &name); + assert!(t.is_floating(), "{}", &name); + } + o => panic!("unknown type ordinal '{}'", o), + } + match t.gdal_type() { + GDT_Byte | GDT_UInt16 | GDT_UInt32 | GDT_UInt64 => { + assert!(!t.is_signed(), "{}", &name); + } + GDT_Int16 | GDT_Int32 | GDT_Int64 | GDT_Float32 | GDT_Float64 => { + assert!(t.is_signed(), "{}", &name); + } + o => panic!("unknown type ordinal '{}'", o), + } } } diff --git a/src/raster/types.rs b/src/raster/types.rs index 993ac180..5cc51e69 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -1,8 +1,11 @@ -use std::fmt::{Display, Formatter}; -pub use gdal_sys::GDALDataType; -use gdal_sys::{GDALGetDataTypeName, GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes}; use crate::errors::{GdalError, Result}; use crate::utils::{_last_null_pointer_err, _string}; +pub use gdal_sys::GDALDataType; +use gdal_sys::{ + GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, GDALGetDataTypeName, + GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, +}; +use std::fmt::{Display, Formatter}; /// Type-level constraint for limiting which primitive numeric values can be passed /// to functions needing target data type. @@ -73,12 +76,31 @@ impl GdalTypeDescriptor { /// Get the gdal type size in **bits**. pub fn bits(&self) -> u8 { - unsafe { GDALGetDataTypeSizeBits(self.gdal_type()) }.try_into().unwrap() + unsafe { GDALGetDataTypeSizeBits(self.gdal_type()) } + .try_into() + .unwrap() } /// Get the gdal type size in **bytes**. pub fn bytes(&self) -> u8 { - unsafe { GDALGetDataTypeSizeBytes(self.gdal_type()) }.try_into().unwrap() + unsafe { GDALGetDataTypeSizeBytes(self.gdal_type()) } + .try_into() + .unwrap() + } + + /// Returns `true` if data type is integral (non-floating point) + pub fn is_integer(&self) -> bool { + (unsafe { GDALDataTypeIsInteger(self.gdal_type()) }) > 0 + } + + /// Returns `true` if data type is floating point (non-integral) + pub fn is_floating(&self) -> bool { + (unsafe { GDALDataTypeIsFloating(self.gdal_type()) }) > 0 + } + + /// Returns `true` if data type supports negative values. + pub fn is_signed(&self) -> bool { + (unsafe { GDALDataTypeIsSigned(self.gdal_type()) }) > 0 } /// Subset of the GDAL data types supported by Rust bindings. @@ -93,7 +115,7 @@ impl GdalTypeDescriptor { GdalTypeDescriptor(GDT_UInt64), GdalTypeDescriptor(GDT_Int64), GdalTypeDescriptor(GDT_Float32), - GdalTypeDescriptor(GDT_Float64) + GdalTypeDescriptor(GDT_Float64), ] } } @@ -110,10 +132,11 @@ impl TryFrom for GdalTypeDescriptor { fn try_from(value: GDALDataType::Type) -> std::result::Result { let wrapped = GdalTypeDescriptor(value); if !GdalTypeDescriptor::available_types().contains(&wrapped) { - Err(GdalError::BadArgument(format!("unknown GDALDataType {value}"))) + Err(GdalError::BadArgument(format!( + "unknown GDALDataType {value}" + ))) } else { Ok(wrapped) } } } - From c062a1146d817e47a85e78e57c45b2b82f975d8a Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 18 Oct 2022 10:01:00 -0400 Subject: [PATCH 03/15] Added unioning of GDALDataType. --- src/raster/tests.rs | 39 +++++++++- src/raster/types.rs | 174 +++++++++++++++++++++++++++++--------------- 2 files changed, 155 insertions(+), 58 deletions(-) diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 31acd221..db427134 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::rasterband::ResampleAlg; use crate::raster::{ - ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, GdalTypeDescriptor, + ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, GdalType, GdalTypeDescriptor, RasterCreationOption, StatisticsAll, StatisticsMinMax, }; use crate::test_utils::TempFixture; @@ -926,3 +926,40 @@ fn test_gdal_data_type() { } } } + +#[test] +fn test_data_type_from_name() { + assert!(GdalTypeDescriptor::from_name("foobar").is_err()); + + for t in GdalTypeDescriptor::available_types() { + let name = t.name().unwrap(); + let t2 = GdalTypeDescriptor::from_name(&name); + assert!(t2.is_ok()); + } +} + +#[test] +fn test_data_type_union() { + let f32d = ::descriptor(); + let f64d = ::descriptor(); + + let u8d = ::descriptor(); + let u16d = ::descriptor(); + let i16d = ::descriptor(); + let u32d = ::descriptor(); + let i32d = ::descriptor(); + + #[cfg(all(major_ge_3, minor_ge_5))] + let i64d = ::descriptor(); + + // reflexivity + assert_eq!(i16d.union(i16d), i16d); + // symmetry + assert_eq!(i16d.union(f32d), f32d); + assert_eq!(f32d.union(i16d), f32d); + // widening + assert_eq!(u8d.union(u16d), u16d); + assert_eq!(f32d.union(i32d), f64d); + #[cfg(all(major_ge_3, minor_ge_5))] + assert_eq!(i16d.union(u32d), i64d); +} diff --git a/src/raster/types.rs b/src/raster/types.rs index 5cc51e69..7789d435 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -2,74 +2,40 @@ use crate::errors::{GdalError, Result}; use crate::utils::{_last_null_pointer_err, _string}; pub use gdal_sys::GDALDataType; use gdal_sys::{ - GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, GDALGetDataTypeName, - GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, + GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, GDALDataTypeUnion, + GDALGetDataTypeByName, GDALGetDataTypeName, GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, }; -use std::fmt::{Display, Formatter}; +use std::ffi::CString; +use std::fmt::{Debug, Display, Formatter}; -/// Type-level constraint for limiting which primitive numeric values can be passed -/// to functions needing target data type. -pub trait GdalType { - fn gdal_type() -> GDALDataType::Type; - fn descriptor() -> GdalTypeDescriptor { - // We can call `unwrap` because existence is guaranteed in this case. - Self::gdal_type().try_into().unwrap() - } -} - -impl GdalType for u8 { - fn gdal_type() -> GDALDataType::Type { - GDALDataType::GDT_Byte - } -} - -impl GdalType for u16 { - fn gdal_type() -> GDALDataType::Type { - GDALDataType::GDT_UInt16 - } -} - -impl GdalType for u32 { - fn gdal_type() -> GDALDataType::Type { - GDALDataType::GDT_UInt32 - } -} - -impl GdalType for i16 { - fn gdal_type() -> GDALDataType::Type { - GDALDataType::GDT_Int16 - } -} - -impl GdalType for i32 { - fn gdal_type() -> GDALDataType::Type { - GDALDataType::GDT_Int32 - } -} - -impl GdalType for f32 { - fn gdal_type() -> GDALDataType::Type { - GDALDataType::GDT_Float32 - } -} - -impl GdalType for f64 { - fn gdal_type() -> GDALDataType::Type { - GDALDataType::GDT_Float64 - } -} - -#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] pub struct GdalTypeDescriptor(GDALDataType::Type); impl GdalTypeDescriptor { + /// Find `GDALDataType` by name, as would be returned by [`GdalTypeDescriptor.name()`]. + #[allow(non_snake_case)] + pub fn from_name(name: &str) -> Result { + let c_name = CString::new(name.to_owned())?; + let gdal_type = unsafe { GDALGetDataTypeByName(c_name.as_ptr()) }; + match gdal_type { + GDALDataType::GDT_Unknown => Err(GdalError::BadArgument(format!( + "unable to find datatype with name '{}'", + name + ))), + _ => gdal_type.try_into(), + } + } + + /// Get the `GDALDataType` ordinal value pub fn gdal_type(&self) -> GDALDataType::Type { self.0 } + + /// Get the name of the `GDALDataType`. pub fn name(&self) -> Result { let c_str = unsafe { GDALGetDataTypeName(self.gdal_type()) }; if c_str.is_null() { - return Err(_last_null_pointer_err("GDALGetDescription")); + return Err(_last_null_pointer_err("GDALGetDataTypeName")); } Ok(_string(c_str)) } @@ -103,6 +69,20 @@ impl GdalTypeDescriptor { (unsafe { GDALDataTypeIsSigned(self.gdal_type()) }) > 0 } + /// Return the smallest data type that can fully express both `self` and + /// `other` data types. + /// + /// # Example + /// + /// ```rust + /// use gdal::raster::GdalType; + /// assert_eq!(::descriptor().union(::descriptor()), ::descriptor()); + /// ``` + pub fn union(&self, other: Self) -> Self { + let gdal_type = unsafe { GDALDataTypeUnion(self.gdal_type(), other.gdal_type()) }; + Self(gdal_type) + } + /// Subset of the GDAL data types supported by Rust bindings. pub fn available_types() -> &'static [GdalTypeDescriptor] { use GDALDataType::*; @@ -112,7 +92,9 @@ impl GdalTypeDescriptor { GdalTypeDescriptor(GDT_Int16), GdalTypeDescriptor(GDT_UInt32), GdalTypeDescriptor(GDT_Int32), + #[cfg(all(major_ge_3, minor_ge_5))] GdalTypeDescriptor(GDT_UInt64), + #[cfg(all(major_ge_3, minor_ge_5))] GdalTypeDescriptor(GDT_Int64), GdalTypeDescriptor(GDT_Float32), GdalTypeDescriptor(GDT_Float64), @@ -120,6 +102,17 @@ impl GdalTypeDescriptor { } } +impl Debug for GdalTypeDescriptor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GdalTypeDescriptor") + .field("name", &self.name().unwrap_or_else(|e| format!("{e:?}"))) + .field("bits", &self.bits()) + .field("signed", &self.is_signed()) + .field("gdal_ordinal", &self.gdal_type()) + .finish() + } +} + impl Display for GdalTypeDescriptor { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&self.name().unwrap()) @@ -140,3 +133,70 @@ impl TryFrom for GdalTypeDescriptor { } } } + +/// Type-level constraint for bounding primitive numeric values passed +/// to functions requiring a data type. See [`GdalTypeDescriptor`] for access to +/// metadata describing the data type. +pub trait GdalType { + fn gdal_type() -> GDALDataType::Type; + fn descriptor() -> GdalTypeDescriptor { + // We can call `unwrap` because existence is guaranteed in this case. + Self::gdal_type().try_into().unwrap() + } +} + +impl GdalType for u8 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_Byte + } +} + +impl GdalType for u16 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_UInt16 + } +} + +impl GdalType for u32 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_UInt32 + } +} + +#[cfg(all(major_ge_3, minor_ge_5))] +impl GdalType for u64 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_UInt64 + } +} + +impl GdalType for i16 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_Int16 + } +} + +impl GdalType for i32 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_Int32 + } +} + +#[cfg(all(major_ge_3, minor_ge_5))] +impl GdalType for i64 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_Int64 + } +} + +impl GdalType for f32 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_Float32 + } +} + +impl GdalType for f64 { + fn gdal_type() -> GDALDataType::Type { + GDALDataType::GDT_Float64 + } +} From 708a2f595a38d81e1335daf1f98f98d341285d68 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Wed, 19 Oct 2022 15:09:56 -0400 Subject: [PATCH 04/15] Clippy-foo --- src/raster/tests.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/raster/tests.rs b/src/raster/tests.rs index db427134..1af665da 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -905,7 +905,7 @@ fn test_gdal_data_type() { assert_eq!(t.bits(), t.bytes() * 8, "{}", t); let name = t.name().unwrap(); match t.gdal_type() { - GDT_Byte | GDT_UInt16 | GDT_Int16 | GDT_UInt32 | GDT_Int32 | GDT_UInt64 | GDT_Int64 => { + GDT_Byte | GDT_UInt16 | GDT_Int16 | GDT_UInt32 | GDT_Int32 => { assert!(t.is_integer(), "{}", &name); assert!(!t.is_floating(), "{}", &name); } @@ -916,10 +916,10 @@ fn test_gdal_data_type() { o => panic!("unknown type ordinal '{}'", o), } match t.gdal_type() { - GDT_Byte | GDT_UInt16 | GDT_UInt32 | GDT_UInt64 => { + GDT_Byte | GDT_UInt16 | GDT_UInt32 => { assert!(!t.is_signed(), "{}", &name); } - GDT_Int16 | GDT_Int32 | GDT_Int64 | GDT_Float32 | GDT_Float64 => { + GDT_Int16 | GDT_Int32 | GDT_Float32 | GDT_Float64 => { assert!(t.is_signed(), "{}", &name); } o => panic!("unknown type ordinal '{}'", o), @@ -946,12 +946,8 @@ fn test_data_type_union() { let u8d = ::descriptor(); let u16d = ::descriptor(); let i16d = ::descriptor(); - let u32d = ::descriptor(); let i32d = ::descriptor(); - #[cfg(all(major_ge_3, minor_ge_5))] - let i64d = ::descriptor(); - // reflexivity assert_eq!(i16d.union(i16d), i16d); // symmetry @@ -960,6 +956,13 @@ fn test_data_type_union() { // widening assert_eq!(u8d.union(u16d), u16d); assert_eq!(f32d.union(i32d), f64d); + + #[cfg(all(major_ge_3, minor_ge_5))] - assert_eq!(i16d.union(u32d), i64d); + { + let u32d = ::descriptor(); + let i64d = ::descriptor(); + assert_eq!(i16d.union(u32d), i64d); + } } + From 18323c2cd25314ea9aec6f48690ea7ea0dc629bd Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sun, 23 Oct 2022 09:41:22 -0400 Subject: [PATCH 05/15] Added `for_value`, examples, documentation. --- src/raster/tests.rs | 78 +-------------- src/raster/types.rs | 228 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 217 insertions(+), 89 deletions(-) diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 1af665da..17879f31 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -2,8 +2,8 @@ use crate::dataset::Dataset; use crate::metadata::Metadata; use crate::raster::rasterband::ResampleAlg; use crate::raster::{ - ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, GdalType, GdalTypeDescriptor, - RasterCreationOption, StatisticsAll, StatisticsMinMax, + ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, RasterCreationOption, StatisticsAll, + StatisticsMinMax, }; use crate::test_utils::TempFixture; use crate::vsi::unlink_mem_file; @@ -11,7 +11,6 @@ use crate::DriverManager; use gdal_sys::GDALDataType; use std::path::Path; -use gdal_sys::GDALDataType::*; #[cfg(feature = "ndarray")] use ndarray::arr2; @@ -893,76 +892,3 @@ fn test_raster_stats() { } ); } - -#[test] -#[allow(non_upper_case_globals)] -fn test_gdal_data_type() { - for t in GdalTypeDescriptor::available_types() { - // Test converting from GDALDataType:Type - let t2: GdalTypeDescriptor = t.gdal_type().try_into().unwrap(); - assert_eq!(t, &t2, "{}", t); - assert!(t.bits() > 0, "{}", t); - assert_eq!(t.bits(), t.bytes() * 8, "{}", t); - let name = t.name().unwrap(); - match t.gdal_type() { - GDT_Byte | GDT_UInt16 | GDT_Int16 | GDT_UInt32 | GDT_Int32 => { - assert!(t.is_integer(), "{}", &name); - assert!(!t.is_floating(), "{}", &name); - } - GDT_Float32 | GDT_Float64 => { - assert!(!t.is_integer(), "{}", &name); - assert!(t.is_floating(), "{}", &name); - } - o => panic!("unknown type ordinal '{}'", o), - } - match t.gdal_type() { - GDT_Byte | GDT_UInt16 | GDT_UInt32 => { - assert!(!t.is_signed(), "{}", &name); - } - GDT_Int16 | GDT_Int32 | GDT_Float32 | GDT_Float64 => { - assert!(t.is_signed(), "{}", &name); - } - o => panic!("unknown type ordinal '{}'", o), - } - } -} - -#[test] -fn test_data_type_from_name() { - assert!(GdalTypeDescriptor::from_name("foobar").is_err()); - - for t in GdalTypeDescriptor::available_types() { - let name = t.name().unwrap(); - let t2 = GdalTypeDescriptor::from_name(&name); - assert!(t2.is_ok()); - } -} - -#[test] -fn test_data_type_union() { - let f32d = ::descriptor(); - let f64d = ::descriptor(); - - let u8d = ::descriptor(); - let u16d = ::descriptor(); - let i16d = ::descriptor(); - let i32d = ::descriptor(); - - // reflexivity - assert_eq!(i16d.union(i16d), i16d); - // symmetry - assert_eq!(i16d.union(f32d), f32d); - assert_eq!(f32d.union(i16d), f32d); - // widening - assert_eq!(u8d.union(u16d), u16d); - assert_eq!(f32d.union(i32d), f64d); - - - #[cfg(all(major_ge_3, minor_ge_5))] - { - let u32d = ::descriptor(); - let i64d = ::descriptor(); - assert_eq!(i16d.union(u32d), i64d); - } -} - diff --git a/src/raster/types.rs b/src/raster/types.rs index 7789d435..938e361c 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -2,17 +2,39 @@ use crate::errors::{GdalError, Result}; use crate::utils::{_last_null_pointer_err, _string}; pub use gdal_sys::GDALDataType; use gdal_sys::{ - GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, GDALDataTypeUnion, - GDALGetDataTypeByName, GDALGetDataTypeName, GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, + GDALAdjustValueToDataType, GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, + GDALDataTypeUnion, GDALFindDataTypeForValue, GDALGetDataTypeByName, GDALGetDataTypeName, + GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, }; use std::ffi::CString; use std::fmt::{Debug, Display, Formatter}; +/// Provides ergonomic access to functions describing [`GDALDataType`] ordinals. +/// +/// A [`GDALDataType`] indicates the primitive storage value of a cell/pixel in a [`RasterBand`][crate::raster::RasterBand]. +/// +/// # Example +/// ```rust, no_run +/// use gdal::raster::{GdalType, GdalTypeDescriptor}; +/// let td = ::descriptor(); +/// println!("{} is {} and uses {} bits.", +/// td.name(), +/// if td.is_signed() { "signed" } else { "unsigned" }, +/// td.bits() +/// ); +/// ``` #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] pub struct GdalTypeDescriptor(GDALDataType::Type); impl GdalTypeDescriptor { - /// Find `GDALDataType` by name, as would be returned by [`GdalTypeDescriptor.name()`]. + /// Find `GdalTypeDescriptor` by name, as would be returned by [`GdalTypeDescriptor.name()`]. + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::raster::{GdalType, GdalTypeDescriptor}; + /// assert_eq!(GdalTypeDescriptor::from_name("UInt16").unwrap().gdal_type(), ::gdal_type()) + /// ``` #[allow(non_snake_case)] pub fn from_name(name: &str) -> Result { let c_name = CString::new(name.to_owned())?; @@ -26,18 +48,38 @@ impl GdalTypeDescriptor { } } + /// Finds the smallest data type able to support the provided value. + /// + /// See [`GDALFindDataTypeForValue`](https://gdal.org/api/raster_c_api.html#_CPPv424GDALFindDataTypeForValuedi) + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::raster::{GdalType, GdalTypeDescriptor}; + /// assert_eq!(GdalTypeDescriptor::for_value(0), ::descriptor()); + /// assert_eq!(GdalTypeDescriptor::for_value(256), ::descriptor()); + /// assert_eq!(GdalTypeDescriptor::for_value(-1), ::descriptor()); + /// assert_eq!(GdalTypeDescriptor::for_value(::MAX as f64 * -2.0), ::descriptor()); + /// ``` + pub fn for_value>(value: N) -> Self { + let gdal_type = unsafe { GDALFindDataTypeForValue(value.into(), 0) }; + GdalTypeDescriptor(gdal_type) + } + /// Get the `GDALDataType` ordinal value pub fn gdal_type(&self) -> GDALDataType::Type { self.0 } /// Get the name of the `GDALDataType`. - pub fn name(&self) -> Result { + pub fn name(&self) -> String { let c_str = unsafe { GDALGetDataTypeName(self.gdal_type()) }; if c_str.is_null() { - return Err(_last_null_pointer_err("GDALGetDataTypeName")); + // This case shouldn't happen, because `self` only exists for valid + // GDALDataType ordinals. + panic!("{}", _last_null_pointer_err("GDALGetDataTypeName")); } - Ok(_string(c_str)) + _string(c_str) } /// Get the gdal type size in **bits**. @@ -74,9 +116,13 @@ impl GdalTypeDescriptor { /// /// # Example /// - /// ```rust + /// ```rust, no_run /// use gdal::raster::GdalType; - /// assert_eq!(::descriptor().union(::descriptor()), ::descriptor()); + /// println!("To safely store all possible '{}' and '{}' values, you should use '{}'", + /// ::descriptor(), + /// ::descriptor(), + /// ::descriptor().union(::descriptor()) + /// ); /// ``` pub fn union(&self, other: Self) -> Self { let gdal_type = unsafe { GDALDataTypeUnion(self.gdal_type(), other.gdal_type()) }; @@ -105,9 +151,10 @@ impl GdalTypeDescriptor { impl Debug for GdalTypeDescriptor { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("GdalTypeDescriptor") - .field("name", &self.name().unwrap_or_else(|e| format!("{e:?}"))) + .field("name", &self.name()) .field("bits", &self.bits()) .field("signed", &self.is_signed()) + .field("floating", &self.is_floating()) .field("gdal_ordinal", &self.gdal_type()) .finish() } @@ -115,10 +162,19 @@ impl Debug for GdalTypeDescriptor { impl Display for GdalTypeDescriptor { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.name().unwrap()) + f.write_str(&self.name()) } } +/// Converts from a possible [`GDALDataType`] ordinal value to a [`GdalTypeDescriptor`]. +/// +/// # Example +/// +/// ```rust, no_run +/// use gdal::raster::{GdalType, GdalTypeDescriptor}; +/// let gdt: GdalTypeDescriptor = 3.try_into().unwrap(); +/// println!("{gdt:#?}") +/// ``` impl TryFrom for GdalTypeDescriptor { type Error = GdalError; @@ -134,29 +190,66 @@ impl TryFrom for GdalTypeDescriptor { } } -/// Type-level constraint for bounding primitive numeric values passed -/// to functions requiring a data type. See [`GdalTypeDescriptor`] for access to -/// metadata describing the data type. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum AdjustedValue { + Unchanged(f64), + Clamped(f64), + Rounded(f64), + ClampedRounded(f64), +} + +impl From for f64 { + fn from(av: AdjustedValue) -> Self { + match av { + AdjustedValue::Unchanged(v) => v, + AdjustedValue::Clamped(v) => v, + AdjustedValue::Rounded(v) => v, + AdjustedValue::ClampedRounded(v) => v, + } + } +} + +/// Type-level constraint for bounding primitive numeric values for generic +/// functions requiring a data type. +/// +/// See [`GdalTypeDescriptor`] for access to metadata describing the data type. pub trait GdalType { + /// Get the [`GDALDataType`] ordinal value used in `gdal_sys` to represent a GDAL cell/pixel + /// data type. + /// + /// See also: [GDAL API](https://gdal.org/api/raster_c_api.html#_CPPv412GDALDataType) fn gdal_type() -> GDALDataType::Type; + + /// Get the metadata type over a `GdalType`. + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::raster::GdalType; + /// let gdt = ::descriptor(); + /// println!("{gdt:#?}"); + /// ``` fn descriptor() -> GdalTypeDescriptor { // We can call `unwrap` because existence is guaranteed in this case. Self::gdal_type().try_into().unwrap() } } +/// Provides evidence `u8` is a valid [`GDALDataType`]. impl GdalType for u8 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Byte } } +/// Provides evidence `u16` is a valid [`GDALDataType`]. impl GdalType for u16 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_UInt16 } } +/// Provides evidence `u32` is a valid [`GDALDataType`]. impl GdalType for u32 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_UInt32 @@ -164,18 +257,21 @@ impl GdalType for u32 { } #[cfg(all(major_ge_3, minor_ge_5))] +/// Provides evidence `u64` is a valid [`GDALDataType`]. impl GdalType for u64 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_UInt64 } } +/// Provides evidence `i16` is a valid [`GDALDataType`]. impl GdalType for i16 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Int16 } } +/// Provides evidence `i32` is a valid [`GDALDataType`]. impl GdalType for i32 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Int32 @@ -183,20 +279,126 @@ impl GdalType for i32 { } #[cfg(all(major_ge_3, minor_ge_5))] +/// Provides evidence `i64` is a valid [`GDALDataType`]. impl GdalType for i64 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Int64 } } +/// Provides evidence `f32` is a valid [`GDALDataType`]. impl GdalType for f32 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Float32 } } +/// Provides evidence `f64` is a valid [`GDALDataType`]. impl GdalType for f64 { fn gdal_type() -> GDALDataType::Type { GDALDataType::GDT_Float64 } } + +#[cfg(test)] +mod tests { + use super::*; + use gdal_sys::GDALDataType::*; + use crate::raster::types::AdjustedValue::{ClampedRounded, Rounded}; + + #[test] + #[allow(non_upper_case_globals)] + fn test_gdal_data_type() { + for t in GdalTypeDescriptor::available_types() { + // Test converting from GDALDataType:Type + let t2: GdalTypeDescriptor = t.gdal_type().try_into().unwrap(); + assert_eq!(t, &t2, "{}", t); + assert!(t.bits() > 0, "{}", t); + assert_eq!(t.bits(), t.bytes() * 8, "{}", t); + let name = t.name(); + match t.gdal_type() { + GDT_Byte | GDT_UInt16 | GDT_Int16 | GDT_UInt32 | GDT_Int32 => { + assert!(t.is_integer(), "{}", &name); + assert!(!t.is_floating(), "{}", &name); + } + #[cfg(all(major_ge_3, minor_ge_5))] + GDT_UInt64 | GDT_Int64 => { + assert!(t.is_integer(), "{}", &name); + assert!(!t.is_floating(), "{}", &name); + } + GDT_Float32 | GDT_Float64 => { + assert!(!t.is_integer(), "{}", &name); + assert!(t.is_floating(), "{}", &name); + } + + o => panic!("unknown type ordinal '{}'", o), + } + match t.gdal_type() { + GDT_Byte | GDT_UInt16 | GDT_UInt32 => { + assert!(!t.is_signed(), "{}", &name); + } + #[cfg(all(major_ge_3, minor_ge_5))] + GDT_UInt64 => { + assert!(!t.is_signed(), "{}", &name); + } + GDT_Int16 | GDT_Int32 | GDT_Float32 | GDT_Float64 => { + assert!(t.is_signed(), "{}", &name); + } + #[cfg(all(major_ge_3, minor_ge_5))] + GDT_Int64 => { + assert!(t.is_signed(), "{}", &name); + } + o => panic!("unknown type ordinal '{}'", o), + } + } + } + + #[test] + fn test_data_type_from_name() { + assert!(GdalTypeDescriptor::from_name("foobar").is_err()); + + for t in GdalTypeDescriptor::available_types() { + let name = t.name(); + let t2 = GdalTypeDescriptor::from_name(&name); + assert!(t2.is_ok()); + } + } + + #[test] + fn test_data_type_union() { + let f32d = ::descriptor(); + let f64d = ::descriptor(); + + let u8d = ::descriptor(); + let u16d = ::descriptor(); + let i16d = ::descriptor(); + let i32d = ::descriptor(); + + // reflexivity + assert_eq!(i16d.union(i16d), i16d); + // symmetry + assert_eq!(i16d.union(f32d), f32d); + assert_eq!(f32d.union(i16d), f32d); + // widening + assert_eq!(u8d.union(u16d), u16d); + assert_eq!(f32d.union(i32d), f64d); + + #[cfg(all(major_ge_3, minor_ge_5))] + { + let u32d = ::descriptor(); + let i64d = ::descriptor(); + assert_eq!(i16d.union(u32d), i64d); + } + } + + #[test] + fn test_for_value() { + assert_eq!(GdalTypeDescriptor::for_value(0), ::descriptor()); + assert_eq!(GdalTypeDescriptor::for_value(256), ::descriptor()); + assert_eq!(GdalTypeDescriptor::for_value(-1), ::descriptor()); + assert_eq!( + GdalTypeDescriptor::for_value(::MAX as f64 * -2.0), + ::descriptor() + ); + } +} From 97751ce9c4adecee1c06e3c2e40da6c8f63abcff Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sun, 23 Oct 2022 10:29:00 -0400 Subject: [PATCH 06/15] Added `adjust_value`. --- src/raster/mod.rs | 2 +- src/raster/types.rs | 115 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 98 insertions(+), 19 deletions(-) diff --git a/src/raster/mod.rs b/src/raster/mod.rs index 1eb7f4be..92d7b429 100644 --- a/src/raster/mod.rs +++ b/src/raster/mod.rs @@ -91,7 +91,7 @@ pub use rasterband::{ StatisticsMinMax, }; pub use rasterize::{rasterize, BurnSource, MergeAlgorithm, OptimizeMode, RasterizeOptions}; -pub use types::{GDALDataType, GdalType, GdalTypeDescriptor}; +pub use types::{AdjustedValue, GDALDataType, GdalType, GdalTypeDescriptor}; pub use warp::reproject; /// Key/value pair for passing driver-specific creation options to diff --git a/src/raster/types.rs b/src/raster/types.rs index 938e361c..ded597d0 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -2,9 +2,9 @@ use crate::errors::{GdalError, Result}; use crate::utils::{_last_null_pointer_err, _string}; pub use gdal_sys::GDALDataType; use gdal_sys::{ - GDALAdjustValueToDataType, GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, - GDALDataTypeUnion, GDALFindDataTypeForValue, GDALGetDataTypeByName, GDALGetDataTypeName, - GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, + GDALAdjustValueToDataType, GDALDataTypeIsConversionLossy, GDALDataTypeIsFloating, + GDALDataTypeIsInteger, GDALDataTypeIsSigned, GDALDataTypeUnion, GDALFindDataTypeForValue, + GDALGetDataTypeByName, GDALGetDataTypeName, GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, }; use std::ffi::CString; use std::fmt::{Debug, Display, Formatter}; @@ -27,13 +27,13 @@ use std::fmt::{Debug, Display, Formatter}; pub struct GdalTypeDescriptor(GDALDataType::Type); impl GdalTypeDescriptor { - /// Find `GdalTypeDescriptor` by name, as would be returned by [`GdalTypeDescriptor.name()`]. + /// Find `GdalTypeDescriptor` by name, as would be returned by [`name`][Self::name]. /// /// # Example /// /// ```rust, no_run /// use gdal::raster::{GdalType, GdalTypeDescriptor}; - /// assert_eq!(GdalTypeDescriptor::from_name("UInt16").unwrap().gdal_type(), ::gdal_type()) + /// assert_eq!(GdalTypeDescriptor::from_name("UInt16").unwrap(), ::descriptor()) /// ``` #[allow(non_snake_case)] pub fn from_name(name: &str) -> Result { @@ -66,12 +66,19 @@ impl GdalTypeDescriptor { GdalTypeDescriptor(gdal_type) } - /// Get the `GDALDataType` ordinal value + /// Get the [`GDALDataType`] ordinal value pub fn gdal_type(&self) -> GDALDataType::Type { self.0 } - /// Get the name of the `GDALDataType`. + /// Get the name of the [`GDALDataType`]. + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::raster::GdalType; + /// assert_eq!(::descriptor().name(), "UInt16"); + /// ``` pub fn name(&self) -> String { let c_str = unsafe { GDALGetDataTypeName(self.gdal_type()) }; if c_str.is_null() { @@ -82,37 +89,37 @@ impl GdalTypeDescriptor { _string(c_str) } - /// Get the gdal type size in **bits**. + /// Get the [`GDALDataType`] size in **bits**. pub fn bits(&self) -> u8 { unsafe { GDALGetDataTypeSizeBits(self.gdal_type()) } .try_into() .unwrap() } - /// Get the gdal type size in **bytes**. + /// Get the [`GDALDataType`] size in **bytes**. pub fn bytes(&self) -> u8 { unsafe { GDALGetDataTypeSizeBytes(self.gdal_type()) } .try_into() .unwrap() } - /// Returns `true` if data type is integral (non-floating point) + /// Returns `true` if [`GDALDataType`] is integral (non-floating point) pub fn is_integer(&self) -> bool { (unsafe { GDALDataTypeIsInteger(self.gdal_type()) }) > 0 } - /// Returns `true` if data type is floating point (non-integral) + /// Returns `true` if [`GDALDataType`] is floating point (non-integral) pub fn is_floating(&self) -> bool { (unsafe { GDALDataTypeIsFloating(self.gdal_type()) }) > 0 } - /// Returns `true` if data type supports negative values. + /// Returns `true` if [`GDALDataType`] supports negative values. pub fn is_signed(&self) -> bool { (unsafe { GDALDataTypeIsSigned(self.gdal_type()) }) > 0 } - /// Return the smallest data type that can fully express both `self` and - /// `other` data types. + /// Return the descriptor for smallest [`GDALDataType`] fully contains both data types + /// indicated by `self` and `other`. /// /// # Example /// @@ -129,6 +136,54 @@ impl GdalTypeDescriptor { Self(gdal_type) } + /// Change a given value to fit within the constraints of this [`GDALDataType`]. + /// + /// Returns an enum indicating if the wrapped value is unchanged, clamped + /// (to min or max datatype value) or rounded (for integral data types). + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::raster::{GdalType, AdjustedValue::*}; + /// assert_eq!(::descriptor().adjust_value(255), Unchanged(255.)); + /// assert_eq!(::descriptor().adjust_value(1.2334), Rounded(1.)); + /// assert_eq!(::descriptor().adjust_value(1000.2334), Clamped(255.)); + /// ``` + pub fn adjust_value>(&self, value: N) -> AdjustedValue { + let mut is_clamped: libc::c_int = 0; + let mut is_rounded: libc::c_int = 0; + + let result = unsafe { + GDALAdjustValueToDataType( + self.gdal_type(), + value.into(), + &mut is_clamped, + &mut is_rounded, + ) + }; + + match (is_clamped > 0, is_rounded > 0) { + (false, false) => AdjustedValue::Unchanged(result), + (true, false) => AdjustedValue::Clamped(result), + (false, true) => AdjustedValue::Rounded(result), + (true, true) => panic!("Unexpected adjustment result: clamped and rounded."), + } + } + + /// Determine if converting a value from [`GDALDataType`] described by `self` to one + /// described by `other` is potentially lossy. + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::raster::GdalType; + /// assert!(::descriptor().is_conversion_lossy(::descriptor())) + /// ``` + pub fn is_conversion_lossy(&self, other: Self) -> bool { + let r = unsafe { GDALDataTypeIsConversionLossy(self.gdal_type(), other.gdal_type()) }; + r != 0 + } + /// Subset of the GDAL data types supported by Rust bindings. pub fn available_types() -> &'static [GdalTypeDescriptor] { use GDALDataType::*; @@ -190,12 +245,15 @@ impl TryFrom for GdalTypeDescriptor { } } +/// Return type for [`GdalTypeDescriptor::adjust_value`]. #[derive(Debug, Copy, Clone, PartialEq)] pub enum AdjustedValue { + /// Value was not changed Unchanged(f64), + /// Value was clamped to fit within the min/max bounds of data type Clamped(f64), + /// The value was rounded to fit in an integral type Rounded(f64), - ClampedRounded(f64), } impl From for f64 { @@ -204,13 +262,12 @@ impl From for f64 { AdjustedValue::Unchanged(v) => v, AdjustedValue::Clamped(v) => v, AdjustedValue::Rounded(v) => v, - AdjustedValue::ClampedRounded(v) => v, } } } /// Type-level constraint for bounding primitive numeric values for generic -/// functions requiring a data type. +/// functions requiring a [`GDALDataType`]. /// /// See [`GdalTypeDescriptor`] for access to metadata describing the data type. pub trait GdalType { @@ -303,8 +360,8 @@ impl GdalType for f64 { #[cfg(test)] mod tests { use super::*; + use crate::raster::types::AdjustedValue::{Clamped, Rounded, Unchanged}; use gdal_sys::GDALDataType::*; - use crate::raster::types::AdjustedValue::{ClampedRounded, Rounded}; #[test] #[allow(non_upper_case_globals)] @@ -401,4 +458,26 @@ mod tests { ::descriptor() ); } + + #[test] + fn test_adjust_value() { + assert_eq!(::descriptor().adjust_value(255), Unchanged(255.)); + assert_eq!(::descriptor().adjust_value(1.2334), Rounded(1.)); + assert_eq!(::descriptor().adjust_value(1000.2334), Clamped(255.)); + assert_eq!(::descriptor().adjust_value(-1), Clamped(0.)); + assert_eq!( + ::descriptor().adjust_value(-32768), + Unchanged(-32768.0) + ); + assert_eq!( + ::descriptor().adjust_value(-32767.4), + Rounded(-32767.0) + ); + assert_eq!( + ::descriptor().adjust_value(1e300), + Clamped(f32::MAX as f64) + ); + let v: f64 = ::descriptor().adjust_value(-32767.4).into(); + assert_eq!(v, -32767.0); + } } From 72e110ff784634d1b68e5308326da9561a89512f Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sun, 23 Oct 2022 10:38:59 -0400 Subject: [PATCH 07/15] Removed `pub use GDALDataType` from `gdal::raster`. --- CHANGES.md | 9 ++++++++- src/raster/mod.rs | 2 +- src/raster/rasterband.rs | 9 +++++---- src/raster/types.rs | 3 +-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9fd3cc1c..f44ec29e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -36,9 +36,16 @@ - +- Added `GdalTypeDescriptor` to provide access to metadata and supporting routines around `GDALDataType` ordinals. +- **Breaking**: `GDALDataType` is no longer `pub use` in `gdal::raster`, + as `GdalType` and `GdalTypeDescriptor` sufficiently cover use cases in safe code. + Still accessible via `gdal_sys::GDALDataType`. + + - + ## 0.13 -- Add prebuild bindings for GDAL 3.5 +- Add prebuilt bindings for GDAL 3.5 - diff --git a/src/raster/mod.rs b/src/raster/mod.rs index 92d7b429..4c9e76a9 100644 --- a/src/raster/mod.rs +++ b/src/raster/mod.rs @@ -91,7 +91,7 @@ pub use rasterband::{ StatisticsMinMax, }; pub use rasterize::{rasterize, BurnSource, MergeAlgorithm, OptimizeMode, RasterizeOptions}; -pub use types::{AdjustedValue, GDALDataType, GdalType, GdalTypeDescriptor}; +pub use types::{AdjustedValue, GdalType, GdalTypeDescriptor}; pub use warp::reproject; /// Key/value pair for passing driver-specific creation options to diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index 7ba1e246..57a51c22 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -1,13 +1,14 @@ use crate::dataset::Dataset; use crate::gdal_major_object::MajorObject; use crate::metadata::Metadata; -use crate::raster::{GDALDataType, GdalType}; +use crate::raster::GdalType; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string}; use gdal_sys::{ self, CPLErr, GDALColorEntry, GDALColorInterp, GDALColorTableH, GDALComputeRasterMinMax, - GDALCreateColorRamp, GDALCreateColorTable, GDALDestroyColorTable, GDALGetPaletteInterpretation, - GDALGetRasterStatistics, GDALMajorObjectH, GDALPaletteInterp, GDALRIOResampleAlg, GDALRWFlag, - GDALRasterBandH, GDALRasterIOExtraArg, GDALSetColorEntry, GDALSetRasterColorTable, + GDALCreateColorRamp, GDALCreateColorTable, GDALDataType, GDALDestroyColorTable, + GDALGetPaletteInterpretation, GDALGetRasterStatistics, GDALMajorObjectH, GDALPaletteInterp, + GDALRIOResampleAlg, GDALRWFlag, GDALRasterBandH, GDALRasterIOExtraArg, GDALSetColorEntry, + GDALSetRasterColorTable, }; use libc::c_int; use std::ffi::CString; diff --git a/src/raster/types.rs b/src/raster/types.rs index ded597d0..b264715c 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -1,8 +1,7 @@ use crate::errors::{GdalError, Result}; use crate::utils::{_last_null_pointer_err, _string}; -pub use gdal_sys::GDALDataType; use gdal_sys::{ - GDALAdjustValueToDataType, GDALDataTypeIsConversionLossy, GDALDataTypeIsFloating, + GDALAdjustValueToDataType, GDALDataType, GDALDataTypeIsConversionLossy, GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, GDALDataTypeUnion, GDALFindDataTypeForValue, GDALGetDataTypeByName, GDALGetDataTypeName, GDALGetDataTypeSizeBits, GDALGetDataTypeSizeBytes, }; From 6f02b7536c98cf418cbacf2e489c5be952e1d182 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sun, 6 Nov 2022 18:06:33 -0500 Subject: [PATCH 08/15] PR feedback. --- src/raster/types.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/raster/types.rs b/src/raster/types.rs index b264715c..2234145d 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -1,5 +1,5 @@ use crate::errors::{GdalError, Result}; -use crate::utils::{_last_null_pointer_err, _string}; +use crate::utils::_string; use gdal_sys::{ GDALAdjustValueToDataType, GDALDataType, GDALDataTypeIsConversionLossy, GDALDataTypeIsFloating, GDALDataTypeIsInteger, GDALDataTypeIsSigned, GDALDataTypeUnion, GDALFindDataTypeForValue, @@ -34,9 +34,8 @@ impl GdalTypeDescriptor { /// use gdal::raster::{GdalType, GdalTypeDescriptor}; /// assert_eq!(GdalTypeDescriptor::from_name("UInt16").unwrap(), ::descriptor()) /// ``` - #[allow(non_snake_case)] pub fn from_name(name: &str) -> Result { - let c_name = CString::new(name.to_owned())?; + let c_name = CString::new(name)?; let gdal_type = unsafe { GDALGetDataTypeByName(c_name.as_ptr()) }; match gdal_type { GDALDataType::GDT_Unknown => Err(GdalError::BadArgument(format!( @@ -83,7 +82,10 @@ impl GdalTypeDescriptor { if c_str.is_null() { // This case shouldn't happen, because `self` only exists for valid // GDALDataType ordinals. - panic!("{}", _last_null_pointer_err("GDALGetDataTypeName")); + panic!( + "GDALGetDataTypeName unexpectedly returned an empty name for {:?}", + &self + ); } _string(c_str) } @@ -117,7 +119,7 @@ impl GdalTypeDescriptor { (unsafe { GDALDataTypeIsSigned(self.gdal_type()) }) > 0 } - /// Return the descriptor for smallest [`GDALDataType`] fully contains both data types + /// Return the descriptor for smallest [`GDALDataType`] that fully contains both data types /// indicated by `self` and `other`. /// /// # Example From 52c33ae1d18c227e0f5fb9ec40f1f0fbf89af2e7 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Sun, 6 Nov 2022 22:15:11 -0500 Subject: [PATCH 09/15] Rework of GdalDataType as enum. --- src/raster/mod.rs | 2 +- src/raster/types.rs | 153 +++++++++++++++++++++++++------------------- 2 files changed, 88 insertions(+), 67 deletions(-) diff --git a/src/raster/mod.rs b/src/raster/mod.rs index 4c9e76a9..1e266b90 100644 --- a/src/raster/mod.rs +++ b/src/raster/mod.rs @@ -91,7 +91,7 @@ pub use rasterband::{ StatisticsMinMax, }; pub use rasterize::{rasterize, BurnSource, MergeAlgorithm, OptimizeMode, RasterizeOptions}; -pub use types::{AdjustedValue, GdalType, GdalTypeDescriptor}; +pub use types::{AdjustedValue, GdalDataType, GdalType}; pub use warp::reproject; /// Key/value pair for passing driver-specific creation options to diff --git a/src/raster/types.rs b/src/raster/types.rs index 2234145d..0c0d9e4c 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -14,7 +14,7 @@ use std::fmt::{Debug, Display, Formatter}; /// /// # Example /// ```rust, no_run -/// use gdal::raster::{GdalType, GdalTypeDescriptor}; +/// use gdal::raster::{GdalType, GdalDataType}; /// let td = ::descriptor(); /// println!("{} is {} and uses {} bits.", /// td.name(), @@ -23,16 +23,40 @@ use std::fmt::{Debug, Display, Formatter}; /// ); /// ``` #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] -pub struct GdalTypeDescriptor(GDALDataType::Type); +#[repr(u32)] +pub enum GdalDataType { + /// Unknown or unspecified type + Unknown = GDALDataType::GDT_Unknown, + /// Eight bit unsigned integer + UInt8 = GDALDataType::GDT_Byte, + /// Sixteen bit unsigned integer + UInt16 = GDALDataType::GDT_UInt16, + /// Sixteen bit signed integer + Int16 = GDALDataType::GDT_Int16, + /// Thirty two bit unsigned integer + UInt32 = GDALDataType::GDT_UInt32, + /// Thirty two bit signed integer + Int32 = GDALDataType::GDT_Int32, + #[cfg(all(major_ge_3, minor_ge_5))] + /// 64 bit unsigned integer (GDAL >= 3.5) + UInt64 = GDALDataType::GDT_UInt64, + #[cfg(all(major_ge_3, minor_ge_5))] + /// 64 bit signed integer (GDAL >= 3.5) + Int64 = GDALDataType::GDT_Int64, + /// Thirty two bit floating point + Float32 = GDALDataType::GDT_Float32, + /// Sixty four bit floating point + Float64 = GDALDataType::GDT_Float64, +} -impl GdalTypeDescriptor { +impl GdalDataType { /// Find `GdalTypeDescriptor` by name, as would be returned by [`name`][Self::name]. /// /// # Example /// /// ```rust, no_run - /// use gdal::raster::{GdalType, GdalTypeDescriptor}; - /// assert_eq!(GdalTypeDescriptor::from_name("UInt16").unwrap(), ::descriptor()) + /// use gdal::raster::{GdalType, GdalDataType}; + /// assert_eq!(GdalDataType::from_name("UInt16").unwrap(), ::descriptor()) /// ``` pub fn from_name(name: &str) -> Result { let c_name = CString::new(name)?; @@ -53,20 +77,15 @@ impl GdalTypeDescriptor { /// # Example /// /// ```rust, no_run - /// use gdal::raster::{GdalType, GdalTypeDescriptor}; - /// assert_eq!(GdalTypeDescriptor::for_value(0), ::descriptor()); - /// assert_eq!(GdalTypeDescriptor::for_value(256), ::descriptor()); - /// assert_eq!(GdalTypeDescriptor::for_value(-1), ::descriptor()); - /// assert_eq!(GdalTypeDescriptor::for_value(::MAX as f64 * -2.0), ::descriptor()); + /// use gdal::raster::{GdalType, GdalDataType}; + /// assert_eq!(GdalDataType::for_value(0), ::descriptor()); + /// assert_eq!(GdalDataType::for_value(256), ::descriptor()); + /// assert_eq!(GdalDataType::for_value(-1), ::descriptor()); + /// assert_eq!(GdalDataType::for_value(::MAX as f64 * -2.0), ::descriptor()); /// ``` pub fn for_value>(value: N) -> Self { let gdal_type = unsafe { GDALFindDataTypeForValue(value.into(), 0) }; - GdalTypeDescriptor(gdal_type) - } - - /// Get the [`GDALDataType`] ordinal value - pub fn gdal_type(&self) -> GDALDataType::Type { - self.0 + Self::try_from(gdal_type).unwrap() } /// Get the name of the [`GDALDataType`]. @@ -78,7 +97,7 @@ impl GdalTypeDescriptor { /// assert_eq!(::descriptor().name(), "UInt16"); /// ``` pub fn name(&self) -> String { - let c_str = unsafe { GDALGetDataTypeName(self.gdal_type()) }; + let c_str = unsafe { GDALGetDataTypeName(self.gdal_ordinal()) }; if c_str.is_null() { // This case shouldn't happen, because `self` only exists for valid // GDALDataType ordinals. @@ -92,31 +111,31 @@ impl GdalTypeDescriptor { /// Get the [`GDALDataType`] size in **bits**. pub fn bits(&self) -> u8 { - unsafe { GDALGetDataTypeSizeBits(self.gdal_type()) } + unsafe { GDALGetDataTypeSizeBits(self.gdal_ordinal()) } .try_into() .unwrap() } /// Get the [`GDALDataType`] size in **bytes**. pub fn bytes(&self) -> u8 { - unsafe { GDALGetDataTypeSizeBytes(self.gdal_type()) } + unsafe { GDALGetDataTypeSizeBytes(self.gdal_ordinal()) } .try_into() .unwrap() } /// Returns `true` if [`GDALDataType`] is integral (non-floating point) pub fn is_integer(&self) -> bool { - (unsafe { GDALDataTypeIsInteger(self.gdal_type()) }) > 0 + (unsafe { GDALDataTypeIsInteger(self.gdal_ordinal()) }) > 0 } /// Returns `true` if [`GDALDataType`] is floating point (non-integral) pub fn is_floating(&self) -> bool { - (unsafe { GDALDataTypeIsFloating(self.gdal_type()) }) > 0 + (unsafe { GDALDataTypeIsFloating(self.gdal_ordinal()) }) > 0 } /// Returns `true` if [`GDALDataType`] supports negative values. pub fn is_signed(&self) -> bool { - (unsafe { GDALDataTypeIsSigned(self.gdal_type()) }) > 0 + (unsafe { GDALDataTypeIsSigned(self.gdal_ordinal()) }) > 0 } /// Return the descriptor for smallest [`GDALDataType`] that fully contains both data types @@ -133,8 +152,8 @@ impl GdalTypeDescriptor { /// ); /// ``` pub fn union(&self, other: Self) -> Self { - let gdal_type = unsafe { GDALDataTypeUnion(self.gdal_type(), other.gdal_type()) }; - Self(gdal_type) + let gdal_type = unsafe { GDALDataTypeUnion(self.gdal_ordinal(), other.gdal_ordinal()) }; + Self::try_from(gdal_type).unwrap() } /// Change a given value to fit within the constraints of this [`GDALDataType`]. @@ -156,7 +175,7 @@ impl GdalTypeDescriptor { let result = unsafe { GDALAdjustValueToDataType( - self.gdal_type(), + self.gdal_ordinal(), value.into(), &mut is_clamped, &mut is_rounded, @@ -181,42 +200,49 @@ impl GdalTypeDescriptor { /// assert!(::descriptor().is_conversion_lossy(::descriptor())) /// ``` pub fn is_conversion_lossy(&self, other: Self) -> bool { - let r = unsafe { GDALDataTypeIsConversionLossy(self.gdal_type(), other.gdal_type()) }; + let r = unsafe { GDALDataTypeIsConversionLossy(self.gdal_ordinal(), other.gdal_ordinal()) }; r != 0 } /// Subset of the GDAL data types supported by Rust bindings. - pub fn available_types() -> &'static [GdalTypeDescriptor] { - use GDALDataType::*; - &[ - GdalTypeDescriptor(GDT_Byte), - GdalTypeDescriptor(GDT_UInt16), - GdalTypeDescriptor(GDT_Int16), - GdalTypeDescriptor(GDT_UInt32), - GdalTypeDescriptor(GDT_Int32), + pub fn iter() -> impl Iterator { + use GdalDataType::*; + [ + UInt8, + UInt16, + Int16, + UInt32, + Int32, #[cfg(all(major_ge_3, minor_ge_5))] - GdalTypeDescriptor(GDT_UInt64), + UInt64, #[cfg(all(major_ge_3, minor_ge_5))] - GdalTypeDescriptor(GDT_Int64), - GdalTypeDescriptor(GDT_Float32), - GdalTypeDescriptor(GDT_Float64), + Int64, + Float32, + Float64, ] + .iter() + .copied() + } + + #[inline] + pub(crate) fn gdal_ordinal(&self) -> GDALDataType::Type { + *self as GDALDataType::Type } } -impl Debug for GdalTypeDescriptor { +impl Debug for GdalDataType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("GdalTypeDescriptor") .field("name", &self.name()) .field("bits", &self.bits()) .field("signed", &self.is_signed()) .field("floating", &self.is_floating()) - .field("gdal_ordinal", &self.gdal_type()) + .field("gdal_ordinal", &self.gdal_ordinal()) .finish() } } -impl Display for GdalTypeDescriptor { +impl Display for GdalDataType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&self.name()) } @@ -227,22 +253,17 @@ impl Display for GdalTypeDescriptor { /// # Example /// /// ```rust, no_run -/// use gdal::raster::{GdalType, GdalTypeDescriptor}; -/// let gdt: GdalTypeDescriptor = 3.try_into().unwrap(); +/// use gdal::raster::{GdalType, GdalDataType}; +/// let gdt: GdalDataType = 3.try_into().unwrap(); /// println!("{gdt:#?}") /// ``` -impl TryFrom for GdalTypeDescriptor { +impl TryFrom for GdalDataType { type Error = GdalError; - fn try_from(value: GDALDataType::Type) -> std::result::Result { - let wrapped = GdalTypeDescriptor(value); - if !GdalTypeDescriptor::available_types().contains(&wrapped) { - Err(GdalError::BadArgument(format!( - "unknown GDALDataType {value}" - ))) - } else { - Ok(wrapped) - } + fn try_from(value: u32) -> std::result::Result { + Self::iter() + .find(|t| *t as u32 == value) + .ok_or_else(|| GdalError::BadArgument(format!("unknown GDALDataType {value}"))) } } @@ -287,7 +308,7 @@ pub trait GdalType { /// let gdt = ::descriptor(); /// println!("{gdt:#?}"); /// ``` - fn descriptor() -> GdalTypeDescriptor { + fn descriptor() -> GdalDataType { // We can call `unwrap` because existence is guaranteed in this case. Self::gdal_type().try_into().unwrap() } @@ -367,14 +388,14 @@ mod tests { #[test] #[allow(non_upper_case_globals)] fn test_gdal_data_type() { - for t in GdalTypeDescriptor::available_types() { + for t in GdalDataType::iter() { // Test converting from GDALDataType:Type - let t2: GdalTypeDescriptor = t.gdal_type().try_into().unwrap(); - assert_eq!(t, &t2, "{}", t); + let t2: GdalDataType = t.gdal_ordinal().try_into().unwrap(); + assert_eq!(&t, &t2, "{}", t); assert!(t.bits() > 0, "{}", t); assert_eq!(t.bits(), t.bytes() * 8, "{}", t); let name = t.name(); - match t.gdal_type() { + match t.gdal_ordinal() { GDT_Byte | GDT_UInt16 | GDT_Int16 | GDT_UInt32 | GDT_Int32 => { assert!(t.is_integer(), "{}", &name); assert!(!t.is_floating(), "{}", &name); @@ -391,7 +412,7 @@ mod tests { o => panic!("unknown type ordinal '{}'", o), } - match t.gdal_type() { + match t.gdal_ordinal() { GDT_Byte | GDT_UInt16 | GDT_UInt32 => { assert!(!t.is_signed(), "{}", &name); } @@ -413,11 +434,11 @@ mod tests { #[test] fn test_data_type_from_name() { - assert!(GdalTypeDescriptor::from_name("foobar").is_err()); + assert!(GdalDataType::from_name("foobar").is_err()); - for t in GdalTypeDescriptor::available_types() { + for t in GdalDataType::iter() { let name = t.name(); - let t2 = GdalTypeDescriptor::from_name(&name); + let t2 = GdalDataType::from_name(&name); assert!(t2.is_ok()); } } @@ -451,11 +472,11 @@ mod tests { #[test] fn test_for_value() { - assert_eq!(GdalTypeDescriptor::for_value(0), ::descriptor()); - assert_eq!(GdalTypeDescriptor::for_value(256), ::descriptor()); - assert_eq!(GdalTypeDescriptor::for_value(-1), ::descriptor()); + assert_eq!(GdalDataType::for_value(0), ::descriptor()); + assert_eq!(GdalDataType::for_value(256), ::descriptor()); + assert_eq!(GdalDataType::for_value(-1), ::descriptor()); assert_eq!( - GdalTypeDescriptor::for_value(::MAX as f64 * -2.0), + GdalDataType::for_value(::MAX as f64 * -2.0), ::descriptor() ); } From 7ad9b55c837aa87b1498f2a91dc48aac156606fe Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Mon, 7 Nov 2022 12:32:43 -0500 Subject: [PATCH 10/15] Renamed `GdalType::gdal_type` to `GdalType::gdal_ordinal`. --- src/driver.rs | 8 ++-- src/raster/mdarray.rs | 2 +- src/raster/rasterband.rs | 10 ++--- src/raster/types.rs | 94 ++++++++++++++++++++-------------------- 4 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index b8087b84..c8fe5d4a 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -141,7 +141,7 @@ impl Driver { /// let ds = d.create("in-memory", 64, 64, 3)?; /// assert_eq!(ds.raster_count(), 3); /// assert_eq!(ds.raster_size(), (64, 64)); - /// assert_eq!(ds.rasterband(1)?.band_type(), u8::gdal_type()); + /// assert_eq!(ds.rasterband(1)?.band_type(), u8::gdal_ordinal()); /// # Ok(()) /// # } /// ``` @@ -170,7 +170,7 @@ impl Driver { /// let ds = d.create_with_band_type::("in-memory", 64, 64, 3)?; /// assert_eq!(ds.raster_count(), 3); /// assert_eq!(ds.raster_size(), (64, 64)); - /// assert_eq!(ds.rasterband(1)?.band_type(), f64::gdal_type()); + /// assert_eq!(ds.rasterband(1)?.band_type(), f64::gdal_ordinal()); /// # Ok(()) /// # } /// ``` @@ -211,7 +211,7 @@ impl Driver { /// ds.set_spatial_ref(&SpatialRef::from_epsg(4326)?)?; /// assert_eq!(ds.raster_count(), 1); /// assert_eq!(ds.raster_size(), (64, 64)); - /// assert_eq!(ds.rasterband(1)?.band_type(), u8::gdal_type()); + /// assert_eq!(ds.rasterband(1)?.band_type(), u8::gdal_ordinal()); /// assert_eq!(ds.spatial_ref()?.auth_code()?, 4326); /// # Ok(()) /// # } @@ -255,7 +255,7 @@ impl Driver { size_x as c_int, size_y as c_int, bands as c_int, - T::gdal_type(), + T::gdal_ordinal(), options_c.as_ptr(), ) }; diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index ffeb4171..6b6d3697 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -164,7 +164,7 @@ impl<'a> MDArray<'a> { let n_dst_buffer_alloc_size = 0; let rv = unsafe { - let data_type = GDALExtendedDataTypeCreate(T::gdal_type()); + let data_type = GDALExtendedDataTypeCreate(T::gdal_ordinal()); if !self.datatype().class().is_numeric() { return Err(GdalError::UnsupportedMdDataType { diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index 57a51c22..7745ce81 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -233,7 +233,7 @@ impl<'a> RasterBand<'a> { /// use gdal::raster::{GdalType, ResampleAlg}; /// let dataset = Dataset::open("fixtures/m_3607824_se_17_1_20160620_sub.tif")?; /// let band1 = dataset.rasterband(1)?; - /// assert_eq!(band1.band_type(), u8::gdal_type()); + /// assert_eq!(band1.band_type(), u8::gdal_ordinal()); /// let size = 4; /// let mut buf = vec![0; size*size]; /// band1.read_into_slice::((0, 0), band1.size(), (size, size), buf.as_mut_slice(), Some(ResampleAlg::Bilinear))?; @@ -273,7 +273,7 @@ impl<'a> RasterBand<'a> { buffer.as_mut_ptr() as GDALRasterBandH, size.0 as c_int, size.1 as c_int, - T::gdal_type(), + T::gdal_ordinal(), 0, 0, options_ptr, @@ -303,7 +303,7 @@ impl<'a> RasterBand<'a> { /// use gdal::raster::{GdalType, ResampleAlg}; /// let dataset = Dataset::open("fixtures/m_3607824_se_17_1_20160620_sub.tif")?; /// let band1 = dataset.rasterband(1)?; - /// assert_eq!(band1.band_type(), u8::gdal_type()); + /// assert_eq!(band1.band_type(), u8::gdal_ordinal()); /// let size = 4; /// let buf = band1.read_as::((0, 0), band1.size(), (size, size), Some(ResampleAlg::Bilinear))?; /// assert_eq!(buf.size, (size, size)); @@ -347,7 +347,7 @@ impl<'a> RasterBand<'a> { data.as_mut_ptr() as GDALRasterBandH, size.0 as c_int, size.1 as c_int, - T::gdal_type(), + T::gdal_ordinal(), 0, 0, options_ptr, @@ -464,7 +464,7 @@ impl<'a> RasterBand<'a> { buffer.data.as_ptr() as GDALRasterBandH, buffer.size.0 as c_int, buffer.size.1 as c_int, - T::gdal_type(), + T::gdal_ordinal(), 0, 0, ) diff --git a/src/raster/types.rs b/src/raster/types.rs index 0c0d9e4c..cd99af73 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -15,7 +15,7 @@ use std::fmt::{Debug, Display, Formatter}; /// # Example /// ```rust, no_run /// use gdal::raster::{GdalType, GdalDataType}; -/// let td = ::descriptor(); +/// let td = ::datatype(); /// println!("{} is {} and uses {} bits.", /// td.name(), /// if td.is_signed() { "signed" } else { "unsigned" }, @@ -56,7 +56,7 @@ impl GdalDataType { /// /// ```rust, no_run /// use gdal::raster::{GdalType, GdalDataType}; - /// assert_eq!(GdalDataType::from_name("UInt16").unwrap(), ::descriptor()) + /// assert_eq!(GdalDataType::from_name("UInt16").unwrap(), ::datatype()) /// ``` pub fn from_name(name: &str) -> Result { let c_name = CString::new(name)?; @@ -78,10 +78,10 @@ impl GdalDataType { /// /// ```rust, no_run /// use gdal::raster::{GdalType, GdalDataType}; - /// assert_eq!(GdalDataType::for_value(0), ::descriptor()); - /// assert_eq!(GdalDataType::for_value(256), ::descriptor()); - /// assert_eq!(GdalDataType::for_value(-1), ::descriptor()); - /// assert_eq!(GdalDataType::for_value(::MAX as f64 * -2.0), ::descriptor()); + /// assert_eq!(GdalDataType::for_value(0), ::datatype()); + /// assert_eq!(GdalDataType::for_value(256), ::datatype()); + /// assert_eq!(GdalDataType::for_value(-1), ::datatype()); + /// assert_eq!(GdalDataType::for_value(::MAX as f64 * -2.0), ::datatype()); /// ``` pub fn for_value>(value: N) -> Self { let gdal_type = unsafe { GDALFindDataTypeForValue(value.into(), 0) }; @@ -94,7 +94,7 @@ impl GdalDataType { /// /// ```rust, no_run /// use gdal::raster::GdalType; - /// assert_eq!(::descriptor().name(), "UInt16"); + /// assert_eq!(::datatype().name(), "UInt16"); /// ``` pub fn name(&self) -> String { let c_str = unsafe { GDALGetDataTypeName(self.gdal_ordinal()) }; @@ -146,9 +146,9 @@ impl GdalDataType { /// ```rust, no_run /// use gdal::raster::GdalType; /// println!("To safely store all possible '{}' and '{}' values, you should use '{}'", - /// ::descriptor(), - /// ::descriptor(), - /// ::descriptor().union(::descriptor()) + /// ::datatype(), + /// ::datatype(), + /// ::datatype().union(::datatype()) /// ); /// ``` pub fn union(&self, other: Self) -> Self { @@ -165,9 +165,9 @@ impl GdalDataType { /// /// ```rust, no_run /// use gdal::raster::{GdalType, AdjustedValue::*}; - /// assert_eq!(::descriptor().adjust_value(255), Unchanged(255.)); - /// assert_eq!(::descriptor().adjust_value(1.2334), Rounded(1.)); - /// assert_eq!(::descriptor().adjust_value(1000.2334), Clamped(255.)); + /// assert_eq!(::datatype().adjust_value(255), Unchanged(255.)); + /// assert_eq!(::datatype().adjust_value(1.2334), Rounded(1.)); + /// assert_eq!(::datatype().adjust_value(1000.2334), Clamped(255.)); /// ``` pub fn adjust_value>(&self, value: N) -> AdjustedValue { let mut is_clamped: libc::c_int = 0; @@ -197,7 +197,7 @@ impl GdalDataType { /// /// ```rust, no_run /// use gdal::raster::GdalType; - /// assert!(::descriptor().is_conversion_lossy(::descriptor())) + /// assert!(::datatype().is_conversion_lossy(::datatype())) /// ``` pub fn is_conversion_lossy(&self, other: Self) -> bool { let r = unsafe { GDALDataTypeIsConversionLossy(self.gdal_ordinal(), other.gdal_ordinal()) }; @@ -297,7 +297,7 @@ pub trait GdalType { /// data type. /// /// See also: [GDAL API](https://gdal.org/api/raster_c_api.html#_CPPv412GDALDataType) - fn gdal_type() -> GDALDataType::Type; + fn gdal_ordinal() -> GDALDataType::Type; /// Get the metadata type over a `GdalType`. /// @@ -305,32 +305,32 @@ pub trait GdalType { /// /// ```rust, no_run /// use gdal::raster::GdalType; - /// let gdt = ::descriptor(); + /// let gdt = ::datatype(); /// println!("{gdt:#?}"); /// ``` - fn descriptor() -> GdalDataType { + fn datatype() -> GdalDataType { // We can call `unwrap` because existence is guaranteed in this case. - Self::gdal_type().try_into().unwrap() + Self::gdal_ordinal().try_into().unwrap() } } /// Provides evidence `u8` is a valid [`GDALDataType`]. impl GdalType for u8 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_Byte } } /// Provides evidence `u16` is a valid [`GDALDataType`]. impl GdalType for u16 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_UInt16 } } /// Provides evidence `u32` is a valid [`GDALDataType`]. impl GdalType for u32 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_UInt32 } } @@ -338,21 +338,21 @@ impl GdalType for u32 { #[cfg(all(major_ge_3, minor_ge_5))] /// Provides evidence `u64` is a valid [`GDALDataType`]. impl GdalType for u64 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_UInt64 } } /// Provides evidence `i16` is a valid [`GDALDataType`]. impl GdalType for i16 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_Int16 } } /// Provides evidence `i32` is a valid [`GDALDataType`]. impl GdalType for i32 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_Int32 } } @@ -360,21 +360,21 @@ impl GdalType for i32 { #[cfg(all(major_ge_3, minor_ge_5))] /// Provides evidence `i64` is a valid [`GDALDataType`]. impl GdalType for i64 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_Int64 } } /// Provides evidence `f32` is a valid [`GDALDataType`]. impl GdalType for f32 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_Float32 } } /// Provides evidence `f64` is a valid [`GDALDataType`]. impl GdalType for f64 { - fn gdal_type() -> GDALDataType::Type { + fn gdal_ordinal() -> GDALDataType::Type { GDALDataType::GDT_Float64 } } @@ -445,13 +445,13 @@ mod tests { #[test] fn test_data_type_union() { - let f32d = ::descriptor(); - let f64d = ::descriptor(); + let f32d = ::datatype(); + let f64d = ::datatype(); - let u8d = ::descriptor(); - let u16d = ::descriptor(); - let i16d = ::descriptor(); - let i32d = ::descriptor(); + let u8d = ::datatype(); + let u16d = ::datatype(); + let i16d = ::datatype(); + let i32d = ::datatype(); // reflexivity assert_eq!(i16d.union(i16d), i16d); @@ -464,42 +464,42 @@ mod tests { #[cfg(all(major_ge_3, minor_ge_5))] { - let u32d = ::descriptor(); - let i64d = ::descriptor(); + let u32d = ::datatype(); + let i64d = ::datatype(); assert_eq!(i16d.union(u32d), i64d); } } #[test] fn test_for_value() { - assert_eq!(GdalDataType::for_value(0), ::descriptor()); - assert_eq!(GdalDataType::for_value(256), ::descriptor()); - assert_eq!(GdalDataType::for_value(-1), ::descriptor()); + assert_eq!(GdalDataType::for_value(0), ::datatype()); + assert_eq!(GdalDataType::for_value(256), ::datatype()); + assert_eq!(GdalDataType::for_value(-1), ::datatype()); assert_eq!( GdalDataType::for_value(::MAX as f64 * -2.0), - ::descriptor() + ::datatype() ); } #[test] fn test_adjust_value() { - assert_eq!(::descriptor().adjust_value(255), Unchanged(255.)); - assert_eq!(::descriptor().adjust_value(1.2334), Rounded(1.)); - assert_eq!(::descriptor().adjust_value(1000.2334), Clamped(255.)); - assert_eq!(::descriptor().adjust_value(-1), Clamped(0.)); + assert_eq!(::datatype().adjust_value(255), Unchanged(255.)); + assert_eq!(::datatype().adjust_value(1.2334), Rounded(1.)); + assert_eq!(::datatype().adjust_value(1000.2334), Clamped(255.)); + assert_eq!(::datatype().adjust_value(-1), Clamped(0.)); assert_eq!( - ::descriptor().adjust_value(-32768), + ::datatype().adjust_value(-32768), Unchanged(-32768.0) ); assert_eq!( - ::descriptor().adjust_value(-32767.4), + ::datatype().adjust_value(-32767.4), Rounded(-32767.0) ); assert_eq!( - ::descriptor().adjust_value(1e300), + ::datatype().adjust_value(1e300), Clamped(f32::MAX as f64) ); - let v: f64 = ::descriptor().adjust_value(-32767.4).into(); + let v: f64 = ::datatype().adjust_value(-32767.4).into(); assert_eq!(v, -32767.0); } } From 29dc37a304352375f30730a2f59b5e4583bef8da Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Mon, 7 Nov 2022 13:16:39 -0500 Subject: [PATCH 11/15] fmt --- src/raster/types.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/raster/types.rs b/src/raster/types.rs index cd99af73..45dd1eff 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -487,14 +487,8 @@ mod tests { assert_eq!(::datatype().adjust_value(1.2334), Rounded(1.)); assert_eq!(::datatype().adjust_value(1000.2334), Clamped(255.)); assert_eq!(::datatype().adjust_value(-1), Clamped(0.)); - assert_eq!( - ::datatype().adjust_value(-32768), - Unchanged(-32768.0) - ); - assert_eq!( - ::datatype().adjust_value(-32767.4), - Rounded(-32767.0) - ); + assert_eq!(::datatype().adjust_value(-32768), Unchanged(-32768.0)); + assert_eq!(::datatype().adjust_value(-32767.4), Rounded(-32767.0)); assert_eq!( ::datatype().adjust_value(1e300), Clamped(f32::MAX as f64) From 6d6eb326d9630a2d165eef246682fa3d9ed9ba1b Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 8 Nov 2022 09:35:34 -0500 Subject: [PATCH 12/15] Replaced `unwrap` with `expect`. --- src/raster/types.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/raster/types.rs b/src/raster/types.rs index 45dd1eff..408d5a1d 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -85,7 +85,7 @@ impl GdalDataType { /// ``` pub fn for_value>(value: N) -> Self { let gdal_type = unsafe { GDALFindDataTypeForValue(value.into(), 0) }; - Self::try_from(gdal_type).unwrap() + Self::try_from(gdal_type).expect("GDALFindDataTypeForValue") } /// Get the name of the [`GDALDataType`]. @@ -113,14 +113,14 @@ impl GdalDataType { pub fn bits(&self) -> u8 { unsafe { GDALGetDataTypeSizeBits(self.gdal_ordinal()) } .try_into() - .unwrap() + .expect("GDALGetDataTypeSizeBits") } /// Get the [`GDALDataType`] size in **bytes**. pub fn bytes(&self) -> u8 { unsafe { GDALGetDataTypeSizeBytes(self.gdal_ordinal()) } .try_into() - .unwrap() + .expect("GDALGetDataTypeSizeBytes") } /// Returns `true` if [`GDALDataType`] is integral (non-floating point) @@ -153,7 +153,7 @@ impl GdalDataType { /// ``` pub fn union(&self, other: Self) -> Self { let gdal_type = unsafe { GDALDataTypeUnion(self.gdal_ordinal(), other.gdal_ordinal()) }; - Self::try_from(gdal_type).unwrap() + Self::try_from(gdal_type).expect("GDALDataTypeUnion") } /// Change a given value to fit within the constraints of this [`GDALDataType`]. @@ -310,7 +310,7 @@ pub trait GdalType { /// ``` fn datatype() -> GdalDataType { // We can call `unwrap` because existence is guaranteed in this case. - Self::gdal_ordinal().try_into().unwrap() + Self::gdal_ordinal().try_into().expect("GdalDataType") } } From c2f61231c003341826a5737a51ca57fff6e707ab Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 8 Nov 2022 09:40:44 -0500 Subject: [PATCH 13/15] Used explicit match in try_from. --- src/raster/types.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/raster/types.rs b/src/raster/types.rs index 408d5a1d..c28b92bb 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -261,9 +261,25 @@ impl TryFrom for GdalDataType { type Error = GdalError; fn try_from(value: u32) -> std::result::Result { - Self::iter() - .find(|t| *t as u32 == value) - .ok_or_else(|| GdalError::BadArgument(format!("unknown GDALDataType {value}"))) + use GDALDataType::*; + #[allow(non_upper_case_globals)] + match value { + GDT_Unknown => Ok(GdalDataType::Unknown), + GDT_Byte => Ok(GdalDataType::UInt8), + GDT_UInt16 => Ok(GdalDataType::UInt16), + GDT_Int16 => Ok(GdalDataType::Int16), + GDT_UInt32 => Ok(GdalDataType::UInt32), + GDT_Int32 => Ok(GdalDataType::Int32), + #[cfg(all(major_ge_3, minor_ge_5))] + GDT_UInt64 => Ok(GdalDataType::UInt64), + #[cfg(all(major_ge_3, minor_ge_5))] + GDT_Int64 => Ok(GdalDataType::Int64), + GDT_Float32 => Ok(GdalDataType::Float32), + GDT_Float64 => Ok(GdalDataType::Float64), + GDT_CInt16 | GDT_CInt32 | GDT_CFloat32 | GDT_CFloat64 => + Err(GdalError::BadArgument("Complex data types are not available".into())), + o => Err(GdalError::BadArgument(format!("unknown GDALDataType ordinal' {o}'"))) + } } } @@ -430,6 +446,11 @@ mod tests { o => panic!("unknown type ordinal '{}'", o), } } + + for t in [GDT_CInt16, GDT_CInt32, GDT_CFloat32, GDT_CFloat64] { + assert!(TryInto::::try_into(t).is_err()); + } + } #[test] From a7011676d1ed282e1bc89025a552c3dadfae7100 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 8 Nov 2022 09:48:59 -0500 Subject: [PATCH 14/15] Fixed doc warnings. --- src/raster/types.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/raster/types.rs b/src/raster/types.rs index c28b92bb..10871362 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -14,7 +14,7 @@ use std::fmt::{Debug, Display, Formatter}; /// /// # Example /// ```rust, no_run -/// use gdal::raster::{GdalType, GdalDataType}; +/// use gdal::raster::GdalType; /// let td = ::datatype(); /// println!("{} is {} and uses {} bits.", /// td.name(), @@ -253,7 +253,7 @@ impl Display for GdalDataType { /// # Example /// /// ```rust, no_run -/// use gdal::raster::{GdalType, GdalDataType}; +/// use gdal::raster::GdalDataType; /// let gdt: GdalDataType = 3.try_into().unwrap(); /// println!("{gdt:#?}") /// ``` @@ -276,9 +276,12 @@ impl TryFrom for GdalDataType { GDT_Int64 => Ok(GdalDataType::Int64), GDT_Float32 => Ok(GdalDataType::Float32), GDT_Float64 => Ok(GdalDataType::Float64), - GDT_CInt16 | GDT_CInt32 | GDT_CFloat32 | GDT_CFloat64 => - Err(GdalError::BadArgument("Complex data types are not available".into())), - o => Err(GdalError::BadArgument(format!("unknown GDALDataType ordinal' {o}'"))) + GDT_CInt16 | GDT_CInt32 | GDT_CFloat32 | GDT_CFloat64 => Err(GdalError::BadArgument( + "Complex data types are not available".into(), + )), + o => Err(GdalError::BadArgument(format!( + "unknown GDALDataType ordinal' {o}'" + ))), } } } @@ -450,7 +453,6 @@ mod tests { for t in [GDT_CInt16, GDT_CInt32, GDT_CFloat32, GDT_CFloat64] { assert!(TryInto::::try_into(t).is_err()); } - } #[test] From 56d473ef80aef595ff427f3fe092c41e7a78acc4 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 8 Nov 2022 10:37:23 -0500 Subject: [PATCH 15/15] Updated release notes. --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f44ec29e..f7bf4283 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -36,9 +36,9 @@ - -- Added `GdalTypeDescriptor` to provide access to metadata and supporting routines around `GDALDataType` ordinals. +- Added `GdalDataType` to provide access to metadata and supporting routines around `GDALDataType` ordinals. - **Breaking**: `GDALDataType` is no longer `pub use` in `gdal::raster`, - as `GdalType` and `GdalTypeDescriptor` sufficiently cover use cases in safe code. + as `GdalType` and `GdalDataType` sufficiently cover use cases in safe code. Still accessible via `gdal_sys::GDALDataType`. -