Skip to content

Commit

Permalink
Added unioning of GDALDataType.
Browse files Browse the repository at this point in the history
  • Loading branch information
metasim committed Oct 18, 2022
1 parent 55093e7 commit 962f79d
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 58 deletions.
7 changes: 7 additions & 0 deletions script/doctest-output.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

# Runs doc-tests without capturing output.

# Source: <https://github.com/rust-lang/cargo/pull/9705>

RUSTDOCFLAGS="-Z unstable-options --nocapture" cargo +nightly test --doc
39 changes: 38 additions & 1 deletion src/raster/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 = <f32>::descriptor();
let f64d = <f64>::descriptor();

let u8d = <u8>::descriptor();
let u16d = <u16>::descriptor();
let i16d = <i16>::descriptor();
let u32d = <u32>::descriptor();
let i32d = <i32>::descriptor();

#[cfg(all(major_ge_3, minor_ge_5))]
let i64d = <i64>::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);
}
174 changes: 117 additions & 57 deletions src/raster/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
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<String> {
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))
}
Expand Down Expand Up @@ -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!(<f32>::descriptor().union(<i32>::descriptor()), <f64>::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::*;
Expand All @@ -112,14 +92,27 @@ 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),
]
}
}

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())
Expand All @@ -140,3 +133,70 @@ impl TryFrom<GDALDataType::Type> 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
}
}

0 comments on commit 962f79d

Please sign in to comment.