From c89d8b4fb5986c25feb464b00797b0cf992ee880 Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Wed, 24 Aug 2022 16:32:20 +0200 Subject: [PATCH 1/8] allow reading dimensions from md groups --- src/raster/mdarray.rs | 76 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index d9a809e1d..1260e8341 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -10,12 +10,12 @@ use gdal_sys::{ GDALAttributeRelease, GDALDataType, GDALDimensionGetIndexingVariable, GDALDimensionGetName, GDALDimensionGetSize, GDALDimensionHS, GDALDimensionRelease, GDALExtendedDataTypeClass, GDALExtendedDataTypeGetClass, GDALExtendedDataTypeGetNumericDataType, GDALExtendedDataTypeH, - GDALExtendedDataTypeRelease, GDALGroupGetAttribute, GDALGroupGetGroupNames, - GDALGroupGetMDArrayNames, GDALGroupGetName, GDALGroupH, GDALGroupOpenGroup, - GDALGroupOpenMDArray, GDALGroupRelease, GDALMDArrayGetAttribute, GDALMDArrayGetDataType, - GDALMDArrayGetDimensionCount, GDALMDArrayGetDimensions, GDALMDArrayGetNoDataValueAsDouble, - GDALMDArrayGetSpatialRef, GDALMDArrayGetTotalElementsCount, GDALMDArrayGetUnit, GDALMDArrayH, - GDALMDArrayRelease, OSRDestroySpatialReference, VSIFree, + GDALExtendedDataTypeRelease, GDALGroupGetAttribute, GDALGroupGetDimensions, + GDALGroupGetGroupNames, GDALGroupGetMDArrayNames, GDALGroupGetName, GDALGroupH, + GDALGroupOpenGroup, GDALGroupOpenMDArray, GDALGroupRelease, GDALMDArrayGetAttribute, + GDALMDArrayGetDataType, GDALMDArrayGetDimensionCount, GDALMDArrayGetDimensions, + GDALMDArrayGetNoDataValueAsDouble, GDALMDArrayGetSpatialRef, GDALMDArrayGetTotalElementsCount, + GDALMDArrayGetUnit, GDALMDArrayH, GDALMDArrayRelease, OSRDestroySpatialReference, VSIFree, }; use libc::c_void; use std::ffi::CString; @@ -37,11 +37,17 @@ pub struct MDArray<'a> { } #[derive(Debug)] -enum GroupOrDimension<'a> { +pub enum GroupOrDimension<'a> { Group { _group: &'a Group<'a> }, Dimension { _dimension: &'a Dimension<'a> }, } +#[derive(Debug)] +pub enum GroupOrArray<'a> { + Group { _group: &'a Group<'a> }, + MDArray { _md_array: &'a MDArray<'a> }, +} + impl Drop for MDArray<'_> { fn drop(&mut self) { unsafe { @@ -95,7 +101,10 @@ impl<'a> MDArray<'a> { let mut dimensions: Vec = Vec::with_capacity(num_dimensions); for c_dimension in dimensions_ref { - let dimension = Dimension::from_c_dimension(self, *c_dimension); + let dimension = Dimension::from_c_dimension( + GroupOrArray::MDArray { _md_array: self }, + *c_dimension, + ); dimensions.push(dimension); } @@ -424,13 +433,43 @@ impl<'a> Group<'a> { Ok(Attribute::from_c_attribute(c_attribute)) } } + + pub fn dimensions(&self, options: CslStringList) -> Result> { + unsafe { + let mut num_dimensions: usize = 0; + let c_dimensions = GDALGroupGetDimensions( + self.c_group, + std::ptr::addr_of_mut!(num_dimensions), + options.as_ptr(), + ); + + if c_dimensions.is_null() { + return Err(_last_null_pointer_err("GDALGroupGetDimensions")); + } + + let dimensions_ref = std::slice::from_raw_parts_mut(c_dimensions, num_dimensions); + + let mut dimensions: Vec = Vec::with_capacity(num_dimensions); + + for c_dimension in dimensions_ref { + let dimension = + Dimension::from_c_dimension(GroupOrArray::Group { _group: self }, *c_dimension); + dimensions.push(dimension); + } + + // only free the array, not the dimensions themselves + VSIFree(c_dimensions as *mut c_void); + + Ok(dimensions) + } + } } /// A `GDALDimension` with name and size #[derive(Debug)] pub struct Dimension<'a> { c_dimension: *mut GDALDimensionHS, - _md_array: &'a MDArray<'a>, + _parent: GroupOrArray<'a>, } impl Drop for Dimension<'_> { @@ -446,10 +485,13 @@ impl<'a> Dimension<'a> { /// /// # Safety /// This method operates on a raw C pointer - pub fn from_c_dimension(_md_array: &'a MDArray<'a>, c_dimension: *mut GDALDimensionHS) -> Self { + pub unsafe fn from_c_dimension( + _parent: GroupOrArray<'a>, + c_dimension: *mut GDALDimensionHS, + ) -> Self { Self { c_dimension, - _md_array, + _parent, } } pub fn size(&self) -> usize { @@ -692,6 +734,17 @@ mod tests { }; let dataset = Dataset::open_ex("fixtures/byte_no_cf.nc", dataset_options).unwrap(); let root_group = dataset.root_group().unwrap(); + + // group dimensions + let group_dimensions = root_group.dimensions(CslStringList::new()).unwrap(); + let group_dimensions_names: Vec = group_dimensions + .into_iter() + .map(|dimensions| dimensions.name()) + .collect(); + assert_eq!(group_dimensions_names, vec!["x", "y"]); + + // array dimensions + let array_name = "Band1".to_string(); let options = CslStringList::new(); //Driver specific options determining how the array should be opened. Pass nullptr for default behavior. let md_array = root_group.open_md_array(&array_name, options).unwrap(); @@ -702,6 +755,7 @@ mod tests { } assert_eq!(dimension_names, vec!["y".to_string(), "x".to_string()]) } + #[test] fn test_dimension_size() { let dataset_options = DatasetOptions { From 07dbe432e750a9c5906377e22a51d845f3fcf6ca Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Wed, 24 Aug 2022 16:34:03 +0200 Subject: [PATCH 2/8] changelog addition for PR --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 9b65bb5a4..a4d3ee2df 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ## Unreleased - Add prebuild bindings for GDAL 3.5 + - - **Breaking**: Add `gdal::vector::OwnedLayer`, `gdal::vector::LayerAccess` and `gdal::vector::layer::OwnedFeatureIterator`. This requires importing `gdal::vector::LayerAccess` for using most vector layer methods. @@ -67,6 +68,10 @@ - +- Allow reading `Dimension`s from `Group`s in multimensional `Dataset`s. + + - + ## 0.12 - Bump Rust edition to 2021 From 62a42fcf44b9b10863a0e83ae1e0da9ade9594bb Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Wed, 24 Aug 2022 17:40:49 +0200 Subject: [PATCH 3/8] pub use structs from `mdarray` --- src/raster/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raster/mod.rs b/src/raster/mod.rs index fb71dd04a..830e15c5c 100644 --- a/src/raster/mod.rs +++ b/src/raster/mod.rs @@ -8,7 +8,7 @@ mod types; mod warp; #[cfg(all(major_ge_3, minor_ge_1))] -pub use mdarray::{Group, MDArray}; +pub use mdarray::{Attribute, Dimension, ExtendedDataType, Group, MDArray}; pub use rasterband::{ Buffer, ByteBuffer, CmykEntry, ColorEntry, ColorInterpretation, ColorTable, GrayEntry, HlsEntry, PaletteInterpretation, RasterBand, ResampleAlg, RgbaEntry, From 3392acafefce6420ff663b727eaaddbda31e74f3 Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Wed, 24 Aug 2022 19:02:43 +0200 Subject: [PATCH 4/8] lifetime independence of groups --- src/raster/mdarray.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index 1260e8341..b879b0763 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -406,7 +406,7 @@ impl<'a> Group<'a> { } } - pub fn open_group(&self, name: &str, options: CslStringList) -> Result { + pub fn open_group(&'_ self, name: &str, options: CslStringList) -> Result> { let name = CString::new(name)?; unsafe { @@ -945,6 +945,9 @@ mod tests { let group_grids = group_science .open_group("grids", CslStringList::new()) .unwrap(); + + drop(group_science); // check that `Group`s do not borrow each other + let group_data = group_grids .open_group("data", CslStringList::new()) .unwrap(); From fbe0e09f9291607d19604e03b88280d787a396bd Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Thu, 25 Aug 2022 17:13:37 +0200 Subject: [PATCH 5/8] allow empty dimensions --- src/raster/mdarray.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index b879b0763..4f3b88cf3 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -96,6 +96,12 @@ impl<'a> MDArray<'a> { let c_dimensions = GDALMDArrayGetDimensions(self.c_mdarray, std::ptr::addr_of_mut!(num_dimensions)); + // `num_dimensions` is `0`, we can safely return an empty vector + // `GDALMDArrayGetDimensions` does not state that errors can occur + if num_dimensions > 0 && c_dimensions.is_null() { + return Err(_last_null_pointer_err("GDALGroupGetDimensions")); + } + let dimensions_ref = std::slice::from_raw_parts_mut(c_dimensions, num_dimensions); let mut dimensions: Vec = Vec::with_capacity(num_dimensions); @@ -443,7 +449,9 @@ impl<'a> Group<'a> { options.as_ptr(), ); - if c_dimensions.is_null() { + // `num_dimensions` is `0`, we can safely return an empty vector + // `GDALGroupGetDimensions` does not state that errors can occur + if num_dimensions > 0 && c_dimensions.is_null() { return Err(_last_null_pointer_err("GDALGroupGetDimensions")); } @@ -827,6 +835,7 @@ mod tests { let dataset = Dataset::open_ex("fixtures/byte_no_cf.nc", dataset_options).unwrap(); let root_group = dataset.root_group().unwrap(); + let md_array = root_group .open_md_array("Band1", CslStringList::new()) .unwrap(); @@ -897,6 +906,12 @@ mod tests { let group_science = root_group .open_group("science", CslStringList::new()) .unwrap(); + + assert!(group_science + .dimensions(Default::default()) + .unwrap() + .is_empty()); + let group_grids = group_science .open_group("grids", CslStringList::new()) .unwrap(); From 952eb0639407f5ed5402561d79268d82c1b3a4f3 Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Sat, 27 Aug 2022 17:34:02 +0200 Subject: [PATCH 6/8] fixed wrong error method name --- src/raster/mdarray.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index 299fbed7c..2aa3063f3 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -99,7 +99,7 @@ impl<'a> MDArray<'a> { // `num_dimensions` is `0`, we can safely return an empty vector // `GDALMDArrayGetDimensions` does not state that errors can occur if num_dimensions > 0 && c_dimensions.is_null() { - return Err(_last_null_pointer_err("GDALGroupGetDimensions")); + return Err(_last_null_pointer_err("GDALMDArrayGetDimensions")); } let dimensions_ref = std::slice::from_raw_parts_mut(c_dimensions, num_dimensions); From c4824127bcb594f53e4c23b2de03b32504fe1d7a Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Sat, 27 Aug 2022 17:36:53 +0200 Subject: [PATCH 7/8] make unsafe regions smaller --- src/raster/mdarray.rs | 44 ++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index 2aa3063f3..7ca1ab4bc 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -91,34 +91,36 @@ impl<'a> MDArray<'a> { } pub fn dimensions(&self) -> Result> { - unsafe { - let mut num_dimensions: usize = 0; - let c_dimensions = - GDALMDArrayGetDimensions(self.c_mdarray, std::ptr::addr_of_mut!(num_dimensions)); + let mut num_dimensions: usize = 0; - // `num_dimensions` is `0`, we can safely return an empty vector - // `GDALMDArrayGetDimensions` does not state that errors can occur - if num_dimensions > 0 && c_dimensions.is_null() { - return Err(_last_null_pointer_err("GDALMDArrayGetDimensions")); - } + let c_dimensions = unsafe { + GDALMDArrayGetDimensions(self.c_mdarray, std::ptr::addr_of_mut!(num_dimensions)) + }; - let dimensions_ref = std::slice::from_raw_parts_mut(c_dimensions, num_dimensions); + // `num_dimensions` is `0`, we can safely return an empty vector + // `GDALMDArrayGetDimensions` does not state that errors can occur + if num_dimensions > 0 && c_dimensions.is_null() { + return Err(_last_null_pointer_err("GDALMDArrayGetDimensions")); + } - let mut dimensions: Vec = Vec::with_capacity(num_dimensions); + let dimensions_ref = + unsafe { std::slice::from_raw_parts_mut(c_dimensions, num_dimensions) }; - for c_dimension in dimensions_ref { - let dimension = Dimension::from_c_dimension( - GroupOrArray::MDArray { _md_array: self }, - *c_dimension, - ); - dimensions.push(dimension); - } + let mut dimensions: Vec = Vec::with_capacity(num_dimensions); - // only free the array, not the dimensions themselves - VSIFree(c_dimensions as *mut c_void); + for c_dimension in dimensions_ref { + let dimension = unsafe { + Dimension::from_c_dimension(GroupOrArray::MDArray { _md_array: self }, *c_dimension) + }; + dimensions.push(dimension); + } - Ok(dimensions) + // only free the array, not the dimensions themselves + unsafe { + VSIFree(c_dimensions as *mut c_void); } + + Ok(dimensions) } pub fn datatype(&self) -> ExtendedDataType { From ccfa51d913579b45473c530b051b1249bf807a32 Mon Sep 17 00:00:00 2001 From: Christian Beilschmidt Date: Sat, 27 Aug 2022 17:44:39 +0200 Subject: [PATCH 8/8] remove uses of `addr_of_mut` --- src/raster/mdarray.rs | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index 7ca1ab4bc..55d0042e9 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -93,9 +93,7 @@ impl<'a> MDArray<'a> { pub fn dimensions(&self) -> Result> { let mut num_dimensions: usize = 0; - let c_dimensions = unsafe { - GDALMDArrayGetDimensions(self.c_mdarray, std::ptr::addr_of_mut!(num_dimensions)) - }; + let c_dimensions = unsafe { GDALMDArrayGetDimensions(self.c_mdarray, &mut num_dimensions) }; // `num_dimensions` is `0`, we can safely return an empty vector // `GDALMDArrayGetDimensions` does not state that errors can occur @@ -321,9 +319,8 @@ impl<'a> MDArray<'a> { pub fn no_data_value_as_double(&self) -> Option { let mut has_nodata = 0; - let no_data_value = unsafe { - GDALMDArrayGetNoDataValueAsDouble(self.c_mdarray, std::ptr::addr_of_mut!(has_nodata)) - }; + let no_data_value = + unsafe { GDALMDArrayGetNoDataValueAsDouble(self.c_mdarray, &mut has_nodata) }; if has_nodata == 0 { None @@ -457,11 +454,8 @@ impl<'a> Group<'a> { pub fn dimensions(&self, options: CslStringList) -> Result> { unsafe { let mut num_dimensions: usize = 0; - let c_dimensions = GDALGroupGetDimensions( - self.c_group, - std::ptr::addr_of_mut!(num_dimensions), - options.as_ptr(), - ); + let c_dimensions = + GDALGroupGetDimensions(self.c_group, &mut num_dimensions, options.as_ptr()); // `num_dimensions` is `0`, we can safely return an empty vector // `GDALGroupGetDimensions` does not state that errors can occur @@ -596,10 +590,8 @@ impl Attribute { unsafe { let mut num_dimensions = 0; - let c_dimension_sizes = GDALAttributeGetDimensionsSize( - self.c_attribute, - std::ptr::addr_of_mut!(num_dimensions), - ); + let c_dimension_sizes = + GDALAttributeGetDimensionsSize(self.c_attribute, &mut num_dimensions); let dimension_sizes = std::slice::from_raw_parts(c_dimension_sizes, num_dimensions) .iter() @@ -647,8 +639,7 @@ impl Attribute { pub fn read_as_i64_array(&self) -> Vec { unsafe { let mut array_len = 0; - let c_int_array = - GDALAttributeReadAsIntArray(self.c_attribute, std::ptr::addr_of_mut!(array_len)); + let c_int_array = GDALAttributeReadAsIntArray(self.c_attribute, &mut array_len); let int_array = std::slice::from_raw_parts(c_int_array, array_len).to_vec(); @@ -665,8 +656,7 @@ impl Attribute { pub fn read_as_f64_array(&self) -> Vec { unsafe { let mut array_len = 0; - let c_int_array = - GDALAttributeReadAsDoubleArray(self.c_attribute, std::ptr::addr_of_mut!(array_len)); + let c_int_array = GDALAttributeReadAsDoubleArray(self.c_attribute, &mut array_len); let float_array = std::slice::from_raw_parts(c_int_array, array_len).to_vec();