-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
289: wrapper for gdalmdimtranslate r=lnicola a=ChristianBeilschmidt - [X] I agree to follow the project's [code of conduct](https://github.com/georust/gdal/blob/master/CODE_OF_CONDUCT.md). - [X] I added an entry to `CHANGES.md` if knowledge of this change could be valuable to users. --- I created a wrapper around `GDALMultiDimTranslate`. I used part of the logic for the wrapper for the VRT program. Co-authored-by: Christian Beilschmidt <[email protected]>
- Loading branch information
Showing
3 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
use crate::{ | ||
errors::*, | ||
utils::{_last_null_pointer_err, _path_to_c_string}, | ||
Dataset, | ||
}; | ||
use gdal_sys::{GDALMultiDimTranslate, GDALMultiDimTranslateOptions}; | ||
use libc::{c_char, c_int}; | ||
use std::{ | ||
borrow::Borrow, | ||
ffi::CString, | ||
mem::ManuallyDrop, | ||
path::{Path, PathBuf}, | ||
ptr::{null, null_mut}, | ||
}; | ||
|
||
/// Wraps a [GDALMultiDimTranslateOptions] object. | ||
/// | ||
/// [GDALMultiDimTranslateOptions]: https://gdal.org/api/gdal_utils.html#_CPPv428GDALMultiDimTranslateOptions | ||
/// | ||
pub struct MultiDimTranslateOptions { | ||
c_options: *mut GDALMultiDimTranslateOptions, | ||
} | ||
|
||
impl MultiDimTranslateOptions { | ||
/// See [GDALMultiDimTranslateOptionsNew]. | ||
/// | ||
/// [GDALMultiDimTranslateOptionsNew]: https://gdal.org/api/gdal_utils.html#_CPPv431GDALMultiDimTranslateOptionsNewPPcP37GDALMultiDimTranslateOptionsForBinary | ||
/// | ||
pub fn new<S: Into<Vec<u8>>, I: IntoIterator<Item = S>>(args: I) -> Result<Self> { | ||
// Convert args to CStrings to add terminating null bytes | ||
let cstr_args = args | ||
.into_iter() | ||
.map(CString::new) | ||
.collect::<std::result::Result<Vec<_>, _>>()?; | ||
|
||
Self::_new(&cstr_args) | ||
} | ||
|
||
fn _new(cstr_args: &[CString]) -> Result<Self> { | ||
// Get pointers to the strings | ||
let mut c_args = cstr_args | ||
.iter() | ||
.map(|x| x.as_ptr() as *mut c_char) // These strings don't actually get modified, the C API is just not const-correct | ||
.chain(std::iter::once(null_mut())) // Null-terminate the list | ||
.collect::<Vec<_>>(); | ||
|
||
unsafe { | ||
Ok(Self { | ||
c_options: gdal_sys::GDALMultiDimTranslateOptionsNew( | ||
c_args.as_mut_ptr(), | ||
null_mut(), | ||
), | ||
}) | ||
} | ||
} | ||
|
||
/// Returns the wrapped C pointer | ||
/// | ||
/// # Safety | ||
/// This method returns a raw C pointer | ||
/// | ||
pub unsafe fn c_options(&self) -> *mut GDALMultiDimTranslateOptions { | ||
self.c_options | ||
} | ||
} | ||
|
||
impl Drop for MultiDimTranslateOptions { | ||
fn drop(&mut self) { | ||
unsafe { | ||
gdal_sys::GDALMultiDimTranslateOptionsFree(self.c_options); | ||
} | ||
} | ||
} | ||
|
||
impl TryFrom<Vec<&str>> for MultiDimTranslateOptions { | ||
type Error = GdalError; | ||
|
||
fn try_from(value: Vec<&str>) -> Result<Self> { | ||
MultiDimTranslateOptions::new(value) | ||
} | ||
} | ||
|
||
pub enum MultiDimTranslateDestination { | ||
Path(CString), | ||
Dataset { | ||
dataset: ManuallyDrop<Dataset>, | ||
drop: bool, | ||
}, | ||
} | ||
|
||
impl TryFrom<&str> for MultiDimTranslateDestination { | ||
type Error = GdalError; | ||
|
||
fn try_from(path: &str) -> Result<Self> { | ||
Self::path(path) | ||
} | ||
} | ||
|
||
impl TryFrom<&Path> for MultiDimTranslateDestination { | ||
type Error = GdalError; | ||
|
||
fn try_from(path: &Path) -> Result<Self> { | ||
Self::path(path) | ||
} | ||
} | ||
|
||
impl TryFrom<PathBuf> for MultiDimTranslateDestination { | ||
type Error = GdalError; | ||
|
||
fn try_from(path: PathBuf) -> Result<Self> { | ||
Self::path(path) | ||
} | ||
} | ||
|
||
impl From<Dataset> for MultiDimTranslateDestination { | ||
fn from(dataset: Dataset) -> Self { | ||
Self::dataset(dataset) | ||
} | ||
} | ||
|
||
impl Drop for MultiDimTranslateDestination { | ||
fn drop(&mut self) { | ||
match self { | ||
Self::Path(_) => {} | ||
Self::Dataset { dataset, drop } => { | ||
if *drop { | ||
unsafe { | ||
ManuallyDrop::drop(dataset); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl MultiDimTranslateDestination { | ||
pub fn dataset(dataset: Dataset) -> Self { | ||
Self::Dataset { | ||
dataset: ManuallyDrop::new(dataset), | ||
drop: true, | ||
} | ||
} | ||
|
||
pub fn path<P: AsRef<Path>>(path: P) -> Result<Self> { | ||
let c_path = _path_to_c_string(path.as_ref())?; | ||
Ok(Self::Path(c_path)) | ||
} | ||
|
||
unsafe fn do_no_drop_dataset(&mut self) { | ||
match self { | ||
Self::Path(_) => {} | ||
Self::Dataset { dataset: _, drop } => { | ||
*drop = false; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Converts raster data between different formats. | ||
/// | ||
/// Wraps [GDALMultiDimTranslate]. | ||
/// See the [program docs] for more details. | ||
/// | ||
/// [GDALMultiDimTranslate]: https://gdal.org/api/gdal_utils.html#_CPPv421GDALMultiDimTranslatePKc12GDALDatasetHiP12GDALDatasetHPK28GDALMultiDimTranslateOptionsPi | ||
/// [program docs]: https://gdal.org/programs/gdalmdimtranslate.html | ||
/// | ||
pub fn multi_dim_translate<D: Borrow<Dataset>>( | ||
input: &[D], | ||
destination: MultiDimTranslateDestination, | ||
options: Option<MultiDimTranslateOptions>, | ||
) -> Result<Dataset> { | ||
_multi_dim_translate( | ||
&input.iter().map(|x| x.borrow()).collect::<Vec<&Dataset>>(), | ||
destination, | ||
options, | ||
) | ||
} | ||
|
||
fn _multi_dim_translate( | ||
input: &[&Dataset], | ||
mut destination: MultiDimTranslateDestination, | ||
options: Option<MultiDimTranslateOptions>, | ||
) -> Result<Dataset> { | ||
let (psz_dest_option, h_dst_ds) = match &destination { | ||
MultiDimTranslateDestination::Path(c_path) => (Some(c_path), null_mut()), | ||
MultiDimTranslateDestination::Dataset { dataset, .. } => { | ||
(None, unsafe { dataset.c_dataset() }) | ||
} | ||
}; | ||
|
||
let psz_dest = psz_dest_option.map(|x| x.as_ptr()).unwrap_or_else(null); | ||
|
||
let mut pah_src_ds: Vec<gdal_sys::GDALDatasetH> = | ||
input.iter().map(|x| unsafe { x.c_dataset() }).collect(); | ||
|
||
let ps_options = options | ||
.as_ref() | ||
.map(|x| x.c_options as *const GDALMultiDimTranslateOptions) | ||
.unwrap_or(null()); | ||
|
||
let mut pb_usage_error: c_int = 0; | ||
|
||
let dataset_out = unsafe { | ||
let data = GDALMultiDimTranslate( | ||
psz_dest, | ||
h_dst_ds, | ||
pah_src_ds.len() as c_int, | ||
pah_src_ds.as_mut_ptr(), | ||
ps_options, | ||
&mut pb_usage_error as *mut c_int, | ||
); | ||
|
||
// GDAL takes the ownership of `h_dst_ds` | ||
destination.do_no_drop_dataset(); | ||
|
||
data | ||
}; | ||
|
||
if dataset_out.is_null() { | ||
return Err(_last_null_pointer_err("GDALMultiDimTranslate")); | ||
} | ||
|
||
let result = unsafe { Dataset::from_c_dataset(dataset_out) }; | ||
|
||
Ok(result) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
use crate::{DatasetOptions, Driver, GdalOpenFlags}; | ||
|
||
#[test] | ||
fn test_build_tiff_from_path() { | ||
let dataset_options = DatasetOptions { | ||
open_flags: GdalOpenFlags::GDAL_OF_MULTIDIM_RASTER, | ||
allowed_drivers: None, | ||
open_options: None, | ||
sibling_files: None, | ||
}; | ||
let dataset = Dataset::open_ex("fixtures/cf_nasa_4326.nc", dataset_options).unwrap(); | ||
|
||
let mem_file_path = "/vsimem/2d3e9124-a7a0-413e-97b5-e79d46e50ff8"; | ||
|
||
let dataset = multi_dim_translate( | ||
&[dataset], | ||
mem_file_path.try_into().unwrap(), | ||
Some( | ||
vec![ | ||
"-array", | ||
"name=/science/grids/imagingGeometry/lookAngle,view=[2,:,:]", | ||
] | ||
.try_into() | ||
.unwrap(), | ||
), | ||
) | ||
.unwrap(); | ||
|
||
assert_eq!(dataset.raster_size(), (5, 7)); | ||
assert_eq!(dataset.raster_count(), 1); | ||
} | ||
|
||
#[test] | ||
fn test_build_tiff_from_dataset() { | ||
let dataset_options = DatasetOptions { | ||
open_flags: GdalOpenFlags::GDAL_OF_MULTIDIM_RASTER, | ||
allowed_drivers: None, | ||
open_options: None, | ||
sibling_files: None, | ||
}; | ||
let dataset = Dataset::open_ex("fixtures/cf_nasa_4326.nc", dataset_options).unwrap(); | ||
|
||
let driver = Driver::get_by_name("MEM").unwrap(); | ||
let output_dataset = driver.create("", 5, 7, 1).unwrap(); | ||
|
||
let error = multi_dim_translate( | ||
&[output_dataset], | ||
dataset.into(), | ||
Some( | ||
MultiDimTranslateOptions::new(vec![ | ||
"-array", | ||
"name=/science/grids/imagingGeometry/lookAngle,view=[2,:,:]", | ||
]) | ||
.unwrap(), | ||
), | ||
) | ||
.unwrap_err(); | ||
|
||
assert_eq!( | ||
error.to_string(), | ||
"GDAL method 'GDALMultiDimTranslate' returned a NULL pointer. Error msg: 'Update of existing file not supported yet'" | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,9 @@ | ||
#[cfg(all(major_ge_3, minor_ge_1))] | ||
mod mdimtranslate; | ||
mod vrt; | ||
|
||
#[cfg(all(major_ge_3, minor_ge_1))] | ||
pub use mdimtranslate::{ | ||
multi_dim_translate, MultiDimTranslateDestination, MultiDimTranslateOptions, | ||
}; | ||
pub use vrt::*; |