From 66d3485c0c932970fff3c037bfee137534cdc3dc Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Thu, 1 Feb 2024 14:21:37 -0500 Subject: [PATCH] Unified raster creation options over `CplStringList`. (#519) --- CHANGES.md | 5 +++ src/dataset.rs | 13 +++---- src/driver.rs | 28 +++++---------- src/raster/create_options.rs | 7 ++++ src/raster/mod.rs | 12 ++----- src/raster/rasterband.rs | 23 ++++--------- src/raster/tests.rs | 67 +++++++++--------------------------- 7 files changed, 49 insertions(+), 106 deletions(-) create mode 100644 src/raster/create_options.rs diff --git a/CHANGES.md b/CHANGES.md index cba870e2..a5a31b79 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ # Changes ## Unreleased + +- **Breaking** Removed `RasterCreationOption`and replaced usages of `[RasterCreationOption]` with `RasterCreationOptions`, a type alias for `CplStringList`. + + - + - Add `DriverIterator` format to iterate through drivers, as well as `DriverManager::all()` method that provides the iterator. - diff --git a/src/dataset.rs b/src/dataset.rs index 55c2da9e..857728d9 100644 --- a/src/dataset.rs +++ b/src/dataset.rs @@ -5,7 +5,7 @@ use gdal_sys::{self, CPLErr, GDALDatasetH, GDALMajorObjectH}; use crate::cpl::CslStringList; use crate::errors::*; use crate::options::DatasetOptions; -use crate::raster::RasterCreationOption; +use crate::raster::RasterCreationOptions; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _path_to_c_string, _string}; use crate::{ gdal_major_object::MajorObject, spatial_ref::SpatialRef, Driver, GeoTransform, Metadata, @@ -238,28 +238,23 @@ impl Dataset { &self, driver: &Driver, filename: P, - options: &[RasterCreationOption], + options: &RasterCreationOptions, ) -> Result { fn _create_copy( ds: &Dataset, driver: &Driver, filename: &Path, - options: &[RasterCreationOption], + options: &CslStringList, ) -> Result { let c_filename = _path_to_c_string(filename)?; - let mut c_options = CslStringList::new(); - for option in options { - c_options.set_name_value(option.key, option.value)?; - } - let c_dataset = unsafe { gdal_sys::GDALCreateCopy( driver.c_driver(), c_filename.as_ptr(), ds.c_dataset, 0, - c_options.as_ptr(), + options.as_ptr(), None, ptr::null_mut(), ) diff --git a/src/driver.rs b/src/driver.rs index 50e9d302..346e39dd 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -4,11 +4,10 @@ use std::sync::Once; use gdal_sys::{self, CPLErr, GDALDriverH, GDALMajorObjectH}; -use crate::cpl::CslStringList; use crate::dataset::Dataset; use crate::gdal_major_object::MajorObject; use crate::metadata::Metadata; -use crate::raster::{GdalDataType, GdalType, RasterCreationOption}; +use crate::raster::{GdalDataType, GdalType, RasterCreationOptions}; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _path_to_c_string, _string}; use crate::errors::*; @@ -137,7 +136,7 @@ impl Driver { size_y: usize, bands: usize, ) -> Result { - let options = []; + let options = Default::default(); self.create_with_band_type_with_options::(filename, size_x, size_y, bands, &options) } @@ -153,16 +152,11 @@ impl Driver { /// ```rust, no_run /// # fn main() -> gdal::errors::Result<()> { /// use gdal::DriverManager; - /// use gdal::raster::RasterCreationOption; + /// use gdal::raster::RasterCreationOptions; /// use gdal::raster::GdalDataType; /// use gdal::spatial_ref::SpatialRef; /// let d = DriverManager::get_driver_by_name("BMP")?; - /// let options = [ - /// RasterCreationOption { - /// key: "WORLDFILE", - /// value: "YES" - /// } - /// ]; + /// let options = RasterCreationOptions::from_iter(["WORLD_FILE=YES"]); /// let mut ds = d.create_with_band_type_with_options::("/tmp/foo.bmp", 64, 64, 1, &options)?; /// ds.set_spatial_ref(&SpatialRef::from_epsg(4326)?)?; /// assert_eq!(ds.raster_count(), 1); @@ -178,7 +172,7 @@ impl Driver { size_x: usize, size_y: usize, bands: usize, - options: &[RasterCreationOption], + options: &RasterCreationOptions, ) -> Result { Self::_create_with_band_type_with_options( self, @@ -198,13 +192,8 @@ impl Driver { size_y: usize, bands: usize, data_type: GdalDataType, - options: &[RasterCreationOption], + options: &RasterCreationOptions, ) -> Result { - let mut options_c = CslStringList::new(); - for option in options { - options_c.set_name_value(option.key, option.value)?; - } - let size_x = libc::c_int::try_from(size_x)?; let size_y = libc::c_int::try_from(size_y)?; let bands = libc::c_int::try_from(bands)?; @@ -218,7 +207,7 @@ impl Driver { size_y, bands, data_type as u32, - options_c.as_ptr(), + options.as_ptr(), ) }; @@ -232,14 +221,13 @@ impl Driver { /// Convenience for creating a vector-only dataset from a compatible driver. /// [Details](https://gdal.org/api/gdaldriver_cpp.html#_CPPv4N10GDALDriver6CreateEPKciii12GDALDataType12CSLConstList) pub fn create_vector_only>(&self, filename: P) -> Result { - let options = []; self._create_with_band_type_with_options( filename.as_ref(), 0, 0, 0, GdalDataType::Unknown, - &options, + &RasterCreationOptions::default(), ) } diff --git a/src/raster/create_options.rs b/src/raster/create_options.rs new file mode 100644 index 00000000..2c7d55bf --- /dev/null +++ b/src/raster/create_options.rs @@ -0,0 +1,7 @@ +use crate::cpl::CslStringList; + +/// Key/value pairs of options for passing driver-specific creation flags to +/// [`Driver::create_with_band_type_with_options`](crate::Driver::create_with_band_type_with_options`). +/// +/// See `papszOptions` in [GDAL's `Create(...)` API documentation](https://gdal.org/api/gdaldriver_cpp.html#_CPPv4N10GDALDriver6CreateEPKciii12GDALDataType12CSLConstList). +pub type RasterCreationOptions = CslStringList; diff --git a/src/raster/mod.rs b/src/raster/mod.rs index f0401637..1fd9947f 100644 --- a/src/raster/mod.rs +++ b/src/raster/mod.rs @@ -75,6 +75,7 @@ //! ``` pub use buffer::{Buffer, ByteBuffer}; +pub use create_options::RasterCreationOptions; #[cfg(all(major_ge_3, minor_ge_1))] pub use mdarray::{ Attribute, Dimension, ExtendedDataType, ExtendedDataTypeClass, Group, MDArray, MdStatisticsAll, @@ -88,6 +89,7 @@ pub use types::{AdjustedValue, GdalDataType, GdalType}; pub use warp::reproject; mod buffer; +mod create_options; #[cfg(all(major_ge_3, minor_ge_1))] mod mdarray; pub mod processing; @@ -97,13 +99,3 @@ mod rasterize; mod tests; mod types; mod warp; - -/// Key/value pair for passing driver-specific creation options to -/// [`Driver::create_with_band_type_wth_options`](crate::Driver::create_with_band_type_with_options`). -/// -/// See `papszOptions` in [GDAL's `Create(...)` API documentation](https://gdal.org/api/gdaldriver_cpp.html#_CPPv4N10GDALDriver6CreateEPKciii12GDALDataType12CSLConstList). -#[derive(Debug)] -pub struct RasterCreationOption<'a> { - pub key: &'a str, - pub value: &'a str, -} diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs index c9081a4e..105dab87 100644 --- a/src/raster/rasterband.rs +++ b/src/raster/rasterband.rs @@ -578,23 +578,12 @@ impl<'a> RasterBand<'a> { /// ```rust, no_run /// # fn main() -> gdal::errors::Result<()> { /// use gdal::DriverManager; - /// use gdal::raster::{Buffer, RasterCreationOption}; + /// use gdal::raster::{Buffer, RasterCreationOptions }; /// /// let driver = DriverManager::get_driver_by_name("GTiff").unwrap(); - /// let options = [ - /// RasterCreationOption { - /// key: "TILED", - /// value: "YES", - /// }, - /// RasterCreationOption { - /// key: "BLOCKXSIZE", - /// value: "16", - /// }, - /// RasterCreationOption { - /// key: "BLOCKYSIZE", - /// value: "16", - /// }, - /// ]; + /// let options = RasterCreationOptions::from_iter([ + /// "TILED=YES", "BLOCKXSIZE=16", "BLOCKYSIZE=16" + /// ]); /// let dataset = driver /// .create_with_band_type_with_options::( /// "/vsimem/test_write_block.tif", @@ -1434,7 +1423,7 @@ impl Debug for ColorEntry { /// /// // Create in-memory copy to mutate /// let mem_driver = DriverManager::get_driver_by_name("MEM")?; -/// let ds = ds.create_copy(&mem_driver, "", &[])?; +/// let ds = ds.create_copy(&mem_driver, "", &Default::default())?; /// let mut band = ds.rasterband(1)?; /// assert!(band.color_table().is_none()); /// @@ -1448,7 +1437,7 @@ impl Debug for ColorEntry { /// /// // Render a PNG /// let png_driver = DriverManager::get_driver_by_name("PNG")?; -/// ds.create_copy(&png_driver, "/tmp/labels.png", &[])?; +/// ds.create_copy(&png_driver, "/tmp/labels.png", &Default::default())?; /// /// # Ok(()) /// # } diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 3839664b..23094c81 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, GdalDataType, RasterCreationOption, + ByteBuffer, ColorEntry, ColorInterpretation, ColorTable, GdalDataType, RasterCreationOptions, StatisticsAll, StatisticsMinMax, }; use crate::test_utils::{fixture, TempFixture}; @@ -127,7 +127,9 @@ fn test_rename_remove_raster() { let driver = DriverManager::get_driver_by_name("GTiff").unwrap(); - dataset.create_copy(&driver, mem_file_path_a, &[]).unwrap(); + dataset + .create_copy(&driver, mem_file_path_a, &Default::default()) + .unwrap(); driver.rename(mem_file_path_b, mem_file_path_a).unwrap(); @@ -166,28 +168,13 @@ fn test_create_with_band_type() { #[test] fn test_create_with_band_type_with_options() { let driver = DriverManager::get_driver_by_name("GTiff").unwrap(); - let options = [ - RasterCreationOption { - key: "TILED", - value: "YES", - }, - RasterCreationOption { - key: "BLOCKXSIZE", - value: "128", - }, - RasterCreationOption { - key: "BLOCKYSIZE", - value: "64", - }, - RasterCreationOption { - key: "COMPRESS", - value: "LZW", - }, - RasterCreationOption { - key: "INTERLEAVE", - value: "BAND", - }, - ]; + let options = RasterCreationOptions::from_iter([ + "TILED=YES", + "BLOCKXSIZE=128", + "BLOCKYSIZE=64", + "COMPRESS=LZW", + "INTERLEAVE=BAND", + ]); let tmp_filename = TempFixture::empty("test.tif"); { @@ -214,7 +201,9 @@ fn test_create_with_band_type_with_options() { fn test_create_copy() { let driver = DriverManager::get_driver_by_name("MEM").unwrap(); let dataset = Dataset::open(fixture("tinymarble.tif")).unwrap(); - let copy = dataset.create_copy(&driver, "", &[]).unwrap(); + let copy = dataset + .create_copy(&driver, "", &Default::default()) + .unwrap(); assert_eq!(copy.raster_size(), (100, 50)); assert_eq!(copy.raster_count(), 3); } @@ -234,16 +223,7 @@ fn test_create_copy_with_options() { .create_copy( &DriverManager::get_driver_by_name("GTiff").unwrap(), mem_file_path, - &[ - RasterCreationOption { - key: "INTERLEAVE", - value: "BAND", - }, - RasterCreationOption { - key: "COMPRESS", - value: "LZW", - }, - ], + &RasterCreationOptions::from_iter(["INTERLEAVE=BAND", "COMPRESS=LZW"]), ) .unwrap(); @@ -391,20 +371,7 @@ fn test_read_block_data() { #[cfg(feature = "ndarray")] fn test_write_block() { let driver = DriverManager::get_driver_by_name("GTiff").unwrap(); - let options = [ - RasterCreationOption { - key: "TILED", - value: "YES", - }, - RasterCreationOption { - key: "BLOCKXSIZE", - value: "16", - }, - RasterCreationOption { - key: "BLOCKYSIZE", - value: "16", - }, - ]; + let options = RasterCreationOptions::from_iter(["TILED=YES", "BLOCKXSIZE=16", "BLOCKYSIZE=16"]); let dataset = driver .create_with_band_type_with_options::( "/vsimem/test_write_block.tif", @@ -724,7 +691,7 @@ fn test_create_color_table() { // Create a new file to put color table in let dataset = dataset - .create_copy(&dataset.driver(), &outfile, &[]) + .create_copy(&dataset.driver(), &outfile, &Default::default()) .unwrap(); dataset .rasterband(1)