From 5f44a249ee91c6f57cc8a1c34ca313302161d530 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Sun, 24 Dec 2023 23:27:10 -0500 Subject: [PATCH 01/23] Add DriverManager::get_drivers_for_filename --- src/driver.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index 83630ca0..a73b890e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -11,7 +11,7 @@ use crate::metadata::Metadata; use crate::raster::{GdalDataType, GdalType, RasterCreationOption}; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _path_to_c_string, _string}; -use crate::errors::*; +use crate::{errors::*, GdalOpenFlags}; static START: Once = Once::new(); @@ -410,6 +410,73 @@ impl DriverManager { Ok(Driver { c_driver }) } + /// Get the Driver based on the file extension + /// + /// Searches for the available extensions in the registered + /// drivers and returns the first match + // it's not a good implementation but should work for now + pub fn get_drivers_for_filename(filename: &str, options: &GdalOpenFlags) -> Vec { + let ext = { + let filename = filename.to_ascii_lowercase(); + if let Some(mut ext) = Path::new(&filename).extension().and_then(|s| s.to_str()) { + if ext == "zip" { + // doing .ends_with on the &Path filename doesn't work + if filename.ends_with(".shp.zip") { + ext = "shp.zip"; + } else if filename.ends_with(".gpkg.zip") { + ext = "gpkg.zip"; + } + } + ext.to_string() + } else { + "".to_string() + } + }; + + let mut drivers: Vec = Vec::new(); + for i in 0..DriverManager::count() { + let d = DriverManager::get_driver(i).expect("Index for this loop should be valid"); + let mut supports = false; + if (d.metadata_item("DCAP_CREATE", "").is_some() + || d.metadata_item("DCAP_CREATECOPY", "").is_some()) + && ((options.contains(GdalOpenFlags::GDAL_OF_VECTOR) + && d.metadata_item("DCAP_VECTOR", "").is_some()) + || (options.contains(GdalOpenFlags::GDAL_OF_RASTER) + && d.metadata_item("DCAP_RASTER", "").is_some())) + { + supports = true; + } else if options.contains(GdalOpenFlags::GDAL_OF_VECTOR) + && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some() + { + supports = true; + } + if !supports { + continue; + } + + if let Some(e) = &d.metadata_item("DMD_EXTENSION", "") { + if *e == ext { + drivers.push(d); + continue; + } + } + if let Some(e) = d.metadata_item("DMD_EXTENSIONS", "") { + if e.split(" ").collect::>().contains(&ext.as_str()) { + drivers.push(d); + continue; + } + } + + if let Some(pre) = d.metadata_item("DMD_CONNECTION_PREFIX", "") { + if filename.starts_with(&pre) { + drivers.push(d); + } + } + } + + return drivers; + } + /// Register a driver for use. /// /// Wraps [`GDALRegisterDriver()`](https://gdal.org/api/raster_c_api.html#_CPPv418GDALRegisterDriver11GDALDriverH) @@ -466,4 +533,28 @@ mod tests { assert!(DriverManager::count() > 0); assert!(DriverManager::get_driver(0).is_ok()); } + + #[test] + fn test_driver_extension() { + // convert the driver into short_name for testing purposes + let drivers = |filename, isvec| { + DriverManager::get_drivers_for_filename( + filename, + if isvec { + &GdalOpenFlags::GDAL_OF_VECTOR + } else { + &GdalOpenFlags::GDAL_OF_RASTER + }, + ) + .iter() + .map(|d| d.short_name()) + .collect::>() + }; + + assert_eq!(drivers("test.gpkg", true), vec!["GPKG"]); + assert_eq!(drivers("test.gpkg.zip", true), vec!["GPKG"]); + assert_eq!(drivers("test.tiff", false), vec!["GTiff", "COG"]); + assert_eq!(drivers("test.nc", false), vec!["netCDF"]); + assert_eq!(drivers("ES:test", true), vec!["Elasticsearch"]); + } } From 9fb996b07cab4ca272906bd3f5e88204f8d11a94 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Fri, 29 Dec 2023 14:12:36 -0500 Subject: [PATCH 02/23] Add docstring to DriverManager::get_drivers_for_filename; use match --- CHANGES.md | 2 ++ src/driver.rs | 52 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 25f0e99d..7c4905ba 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ## Unreleased +- Added `DriverManager::get_drivers_for_filename` for the ability to auto detect compatible `Driver`s for writing data. + - Added `Feature::unset_field` - diff --git a/src/driver.rs b/src/driver.rs index a73b890e..3bdb4722 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -410,27 +410,50 @@ impl DriverManager { Ok(Driver { c_driver }) } - /// Get the Driver based on the file extension + /// Get the Driver based on the file extension from filename /// /// Searches for the available extensions in the registered - /// drivers and returns the first match - // it's not a good implementation but should work for now + /// drivers and returns the matches, the `options:&GdalOpenFlags` + /// is only used for determining vector or raster type. + /// + /// See also: [`get_driver_by_name`](Self::get_driver_by_name) + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::{DriverManager,GdalOpenFlags}; + /// # fn main() -> gdal::errors::Result<()> { + /// let compatible_drivers = + /// DriverManager::get_drivers_for_filename("test.gpkg", &GdalOpenFlags::GDAL_OF_VECTOR) + /// .iter() + /// .map(|d| d.short_name()) + /// .collect::>(); + /// println!("{:?}", compatible_drivers); + /// # Ok(()) + /// # } + /// ``` + /// ```text + /// ["GPKG"] + /// ``` pub fn get_drivers_for_filename(filename: &str, options: &GdalOpenFlags) -> Vec { let ext = { let filename = filename.to_ascii_lowercase(); - if let Some(mut ext) = Path::new(&filename).extension().and_then(|s| s.to_str()) { - if ext == "zip" { - // doing .ends_with on the &Path filename doesn't work - if filename.ends_with(".shp.zip") { - ext = "shp.zip"; - } else if filename.ends_with(".gpkg.zip") { - ext = "gpkg.zip"; + let e = match filename.rsplit_once(".") { + Some(("", _)) => "", // hidden file no ext + Some((f, "zip")) => { + // zip files could be zipped shp or gpkg + if f.ends_with(".shp") { + "shp.zip" + } else if f.ends_with(".gpkg") { + "gpkg.zip" + } else { + "zip" } } - ext.to_string() - } else { - "".to_string() - } + Some((_, e)) => e, // normal file with ext + None => "", + }; + e.to_string() }; let mut drivers: Vec = Vec::new(); @@ -550,7 +573,6 @@ mod tests { .map(|d| d.short_name()) .collect::>() }; - assert_eq!(drivers("test.gpkg", true), vec!["GPKG"]); assert_eq!(drivers("test.gpkg.zip", true), vec!["GPKG"]); assert_eq!(drivers("test.tiff", false), vec!["GTiff", "COG"]); From f7d6bcb39f13b94f4abdd5b39c06990665b2f825 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Fri, 29 Dec 2023 14:57:32 -0500 Subject: [PATCH 03/23] Satisfy clippy --- src/driver.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 3bdb4722..cb211761 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -438,7 +438,7 @@ impl DriverManager { pub fn get_drivers_for_filename(filename: &str, options: &GdalOpenFlags) -> Vec { let ext = { let filename = filename.to_ascii_lowercase(); - let e = match filename.rsplit_once(".") { + let e = match filename.rsplit_once('.') { Some(("", _)) => "", // hidden file no ext Some((f, "zip")) => { // zip files could be zipped shp or gpkg @@ -460,16 +460,14 @@ impl DriverManager { for i in 0..DriverManager::count() { let d = DriverManager::get_driver(i).expect("Index for this loop should be valid"); let mut supports = false; - if (d.metadata_item("DCAP_CREATE", "").is_some() + if ((d.metadata_item("DCAP_CREATE", "").is_some() || d.metadata_item("DCAP_CREATECOPY", "").is_some()) && ((options.contains(GdalOpenFlags::GDAL_OF_VECTOR) && d.metadata_item("DCAP_VECTOR", "").is_some()) || (options.contains(GdalOpenFlags::GDAL_OF_RASTER) - && d.metadata_item("DCAP_RASTER", "").is_some())) - { - supports = true; - } else if options.contains(GdalOpenFlags::GDAL_OF_VECTOR) - && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some() + && d.metadata_item("DCAP_RASTER", "").is_some()))) + || (options.contains(GdalOpenFlags::GDAL_OF_VECTOR) + && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some()) { supports = true; } @@ -484,7 +482,7 @@ impl DriverManager { } } if let Some(e) = d.metadata_item("DMD_EXTENSIONS", "") { - if e.split(" ").collect::>().contains(&ext.as_str()) { + if e.split(' ').collect::>().contains(&ext.as_str()) { drivers.push(d); continue; } @@ -497,7 +495,7 @@ impl DriverManager { } } - return drivers; + drivers } /// Register a driver for use. From 3d569e0a2a5f35c54d3d8583a33bae6ccf11f6c2 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Sun, 31 Dec 2023 21:17:15 -0500 Subject: [PATCH 04/23] Improve readability for DriverManager::get_drivers_for_filename --- src/driver.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index cb211761..74eb6dbc 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -414,7 +414,10 @@ impl DriverManager { /// /// Searches for the available extensions in the registered /// drivers and returns the matches, the `options:&GdalOpenFlags` - /// is only used for determining vector or raster type. + /// is only used for determining vector or raster type. The + /// determined driver is checked for writing capabilities as + /// [`Dataset::open`](Dataset::open) can already open datasets + /// with just path. /// /// See also: [`get_driver_by_name`](Self::get_driver_by_name) /// @@ -459,19 +462,15 @@ impl DriverManager { let mut drivers: Vec = Vec::new(); for i in 0..DriverManager::count() { let d = DriverManager::get_driver(i).expect("Index for this loop should be valid"); - let mut supports = false; - if ((d.metadata_item("DCAP_CREATE", "").is_some() - || d.metadata_item("DCAP_CREATECOPY", "").is_some()) - && ((options.contains(GdalOpenFlags::GDAL_OF_VECTOR) - && d.metadata_item("DCAP_VECTOR", "").is_some()) - || (options.contains(GdalOpenFlags::GDAL_OF_RASTER) - && d.metadata_item("DCAP_RASTER", "").is_some()))) - || (options.contains(GdalOpenFlags::GDAL_OF_VECTOR) - && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some()) - { - supports = true; - } - if !supports { + let can_create = d.metadata_item("DCAP_CREATE", "").is_some() + || d.metadata_item("DCAP_CREATECOPY", "").is_some(); + let check_vector = options.contains(GdalOpenFlags::GDAL_OF_VECTOR) + && d.metadata_item("DCAP_VECTOR", "").is_some(); + let check_raster = options.contains(GdalOpenFlags::GDAL_OF_RASTER) + && d.metadata_item("DCAP_RASTER", "").is_some(); + let check_vector_translate = options.contains(GdalOpenFlags::GDAL_OF_VECTOR) + && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some(); + if !((can_create && (check_vector || check_raster)) || (check_vector_translate)) { continue; } From 0b8d80d28955a88048260d710f9ab100639e5440 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Mon, 1 Jan 2024 16:02:12 -0500 Subject: [PATCH 05/23] only test for available drivers in DriverManager::get_driver_by_name --- src/driver.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 74eb6dbc..3ae6ae2f 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -542,6 +542,8 @@ impl DriverManager { #[cfg(test)] mod tests { + use std::collections::HashSet; + use super::*; #[test] @@ -568,12 +570,20 @@ mod tests { ) .iter() .map(|d| d.short_name()) - .collect::>() + .collect::>() }; - assert_eq!(drivers("test.gpkg", true), vec!["GPKG"]); - assert_eq!(drivers("test.gpkg.zip", true), vec!["GPKG"]); - assert_eq!(drivers("test.tiff", false), vec!["GTiff", "COG"]); - assert_eq!(drivers("test.nc", false), vec!["netCDF"]); - assert_eq!(drivers("ES:test", true), vec!["Elasticsearch"]); + if DriverManager::get_driver_by_name("GPKG").is_ok() { + assert!(drivers("test.gpkg", true).contains("GPKG")); + assert!(drivers("test.gpkg.zip", true).contains("GPKG")); + } + if DriverManager::get_driver_by_name("GTiff").is_ok() { + assert!(drivers("test.tiff", false).contains("GTiff")); + } + if DriverManager::get_driver_by_name("netCDF").is_ok() { + assert!(drivers("test.nc", false).contains("netCDF")); + } + if DriverManager::get_driver_by_name("Elasticsearch").is_ok() { + assert!(drivers("ES:test", true).contains("Elasticsearch")); + } } } From 79300cdf2a1510baec39862bc831a6d10a139df7 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Mon, 1 Jan 2024 16:07:25 -0500 Subject: [PATCH 06/23] Add test for ESRI Shapefile for .shp file extension --- src/driver.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/driver.rs b/src/driver.rs index 3ae6ae2f..df59db1d 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -572,6 +572,10 @@ mod tests { .map(|d| d.short_name()) .collect::>() }; + if DriverManager::get_driver_by_name("ESRI Shapefile").is_ok() { + assert!(drivers("test.shp", true).contains("ESRI Shapefile")); + assert!(drivers("test.shp.zip", true).contains("ESRI Shapefile")); + } if DriverManager::get_driver_by_name("GPKG").is_ok() { assert!(drivers("test.gpkg", true).contains("GPKG")); assert!(drivers("test.gpkg.zip", true).contains("GPKG")); From 563eb97667461dce1f21c875cc85fa2a1d3490b6 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Mon, 1 Jan 2024 16:12:21 -0500 Subject: [PATCH 07/23] bool for vector/raster in DriverManager::get_drivers_for_filename --- src/driver.rs | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index df59db1d..fd97b207 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -11,7 +11,7 @@ use crate::metadata::Metadata; use crate::raster::{GdalDataType, GdalType, RasterCreationOption}; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _path_to_c_string, _string}; -use crate::{errors::*, GdalOpenFlags}; +use crate::errors::*; static START: Once = Once::new(); @@ -413,9 +413,8 @@ impl DriverManager { /// Get the Driver based on the file extension from filename /// /// Searches for the available extensions in the registered - /// drivers and returns the matches, the `options:&GdalOpenFlags` - /// is only used for determining vector or raster type. The - /// determined driver is checked for writing capabilities as + /// drivers and returns the matches. The determined driver is + /// checked for writing capabilities as /// [`Dataset::open`](Dataset::open) can already open datasets /// with just path. /// @@ -424,10 +423,10 @@ impl DriverManager { /// # Example /// /// ```rust, no_run - /// use gdal::{DriverManager,GdalOpenFlags}; + /// use gdal::DriverManager; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_drivers = - /// DriverManager::get_drivers_for_filename("test.gpkg", &GdalOpenFlags::GDAL_OF_VECTOR) + /// DriverManager::get_drivers_for_filename("test.gpkg", true) /// .iter() /// .map(|d| d.short_name()) /// .collect::>(); @@ -438,7 +437,7 @@ impl DriverManager { /// ```text /// ["GPKG"] /// ``` - pub fn get_drivers_for_filename(filename: &str, options: &GdalOpenFlags) -> Vec { + pub fn get_drivers_for_filename(filename: &str, is_vector: bool) -> Vec { let ext = { let filename = filename.to_ascii_lowercase(); let e = match filename.rsplit_once('.') { @@ -464,12 +463,10 @@ impl DriverManager { let d = DriverManager::get_driver(i).expect("Index for this loop should be valid"); let can_create = d.metadata_item("DCAP_CREATE", "").is_some() || d.metadata_item("DCAP_CREATECOPY", "").is_some(); - let check_vector = options.contains(GdalOpenFlags::GDAL_OF_VECTOR) - && d.metadata_item("DCAP_VECTOR", "").is_some(); - let check_raster = options.contains(GdalOpenFlags::GDAL_OF_RASTER) - && d.metadata_item("DCAP_RASTER", "").is_some(); - let check_vector_translate = options.contains(GdalOpenFlags::GDAL_OF_VECTOR) - && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some(); + let check_vector = is_vector && d.metadata_item("DCAP_VECTOR", "").is_some(); + let check_raster = !is_vector && d.metadata_item("DCAP_RASTER", "").is_some(); + let check_vector_translate = + is_vector && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some(); if !((can_create && (check_vector || check_raster)) || (check_vector_translate)) { continue; } @@ -559,18 +556,11 @@ mod tests { #[test] fn test_driver_extension() { // convert the driver into short_name for testing purposes - let drivers = |filename, isvec| { - DriverManager::get_drivers_for_filename( - filename, - if isvec { - &GdalOpenFlags::GDAL_OF_VECTOR - } else { - &GdalOpenFlags::GDAL_OF_RASTER - }, - ) - .iter() - .map(|d| d.short_name()) - .collect::>() + let drivers = |filename, is_vector| { + DriverManager::get_drivers_for_filename(filename, is_vector) + .iter() + .map(|d| d.short_name()) + .collect::>() }; if DriverManager::get_driver_by_name("ESRI Shapefile").is_ok() { assert!(drivers("test.shp", true).contains("ESRI Shapefile")); From 62f7ccd23839d4a28a53bd113aab6fa555867dc5 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Tue, 2 Jan 2024 16:02:47 -0500 Subject: [PATCH 08/23] Fix: modify test for gpkg.zip only for gdal v 3.7 onwards --- src/driver.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index fd97b207..5ba9627e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -568,7 +568,12 @@ mod tests { } if DriverManager::get_driver_by_name("GPKG").is_ok() { assert!(drivers("test.gpkg", true).contains("GPKG")); - assert!(drivers("test.gpkg.zip", true).contains("GPKG")); + // `gpkg.zip` only supported from gdal version 3.7 + // https://gdal.org/drivers/vector/gpkg.html#compressed-files + let gdal_version: i64 = crate::version::version_info("VERSION_NUM").parse().unwrap(); + if gdal_version >= 3070000 { + assert!(drivers("test.gpkg.zip", true).contains("GPKG")); + } } if DriverManager::get_driver_by_name("GTiff").is_ok() { assert!(drivers("test.tiff", false).contains("GTiff")); From 3dbc42febb251fc5430e720b6b10e7cdd8c18b5e Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Tue, 2 Jan 2024 16:45:16 -0500 Subject: [PATCH 09/23] Fix: modify test for shp.zip only for gdal v 3.1 onwards --- src/driver.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 5ba9627e..0f33da34 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -562,15 +562,19 @@ mod tests { .map(|d| d.short_name()) .collect::>() }; + let gdal_version: i64 = crate::version::version_info("VERSION_NUM").parse().unwrap(); if DriverManager::get_driver_by_name("ESRI Shapefile").is_ok() { assert!(drivers("test.shp", true).contains("ESRI Shapefile")); - assert!(drivers("test.shp.zip", true).contains("ESRI Shapefile")); + // `shp.zip` only supported from gdal version 3.1 + // https://gdal.org/drivers/vector/shapefile.html#compressed-files + if gdal_version >= 3010000 { + assert!(drivers("test.shp.zip", true).contains("ESRI Shapefile")); + } } if DriverManager::get_driver_by_name("GPKG").is_ok() { assert!(drivers("test.gpkg", true).contains("GPKG")); // `gpkg.zip` only supported from gdal version 3.7 // https://gdal.org/drivers/vector/gpkg.html#compressed-files - let gdal_version: i64 = crate::version::version_info("VERSION_NUM").parse().unwrap(); if gdal_version >= 3070000 { assert!(drivers("test.gpkg.zip", true).contains("GPKG")); } From 05f46c4555bb3b7e918080f76e98867fd2dc7231 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Tue, 2 Jan 2024 17:18:59 -0500 Subject: [PATCH 10/23] Fixed the test failed due to Elasticsearch name capitalization change --- src/driver.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index 0f33da34..10eb3171 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -586,7 +586,12 @@ mod tests { assert!(drivers("test.nc", false).contains("netCDF")); } if DriverManager::get_driver_by_name("Elasticsearch").is_ok() { - assert!(drivers("ES:test", true).contains("Elasticsearch")); + // Elasticsearch short name was ElasticSearch in older versions + if gdal_version >= 3010000 { + assert!(drivers("ES:test", true).contains("Elasticsearch")); + } else { + assert!(drivers("ES:test", true).contains("ElasticSearch")); + } } } } From 7c90f627c4f726e30821d0ab6ca811facc2f268f Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Wed, 3 Jan 2024 09:53:52 -0500 Subject: [PATCH 11/23] Rename the function and minor changes --- CHANGES.md | 2 +- src/driver.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7c4905ba..49e748d7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ## Unreleased -- Added `DriverManager::get_drivers_for_filename` for the ability to auto detect compatible `Driver`s for writing data. +- Added `DriverManager::guess_drivers_for_write` for the ability to auto detect compatible `Driver`s for writing data. - Added `Feature::unset_field` - diff --git a/src/driver.rs b/src/driver.rs index 10eb3171..8a36bbc3 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -426,7 +426,7 @@ impl DriverManager { /// use gdal::DriverManager; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_drivers = - /// DriverManager::get_drivers_for_filename("test.gpkg", true) + /// DriverManager::guess_drivers_for_write("test.gpkg", true) /// .iter() /// .map(|d| d.short_name()) /// .collect::>(); @@ -437,7 +437,7 @@ impl DriverManager { /// ```text /// ["GPKG"] /// ``` - pub fn get_drivers_for_filename(filename: &str, is_vector: bool) -> Vec { + pub fn guess_drivers_for_write(filename: &str, is_vector: bool) -> Vec { let ext = { let filename = filename.to_ascii_lowercase(); let e = match filename.rsplit_once('.') { @@ -557,7 +557,7 @@ mod tests { fn test_driver_extension() { // convert the driver into short_name for testing purposes let drivers = |filename, is_vector| { - DriverManager::get_drivers_for_filename(filename, is_vector) + DriverManager::guess_drivers_for_write(filename, is_vector) .iter() .map(|d| d.short_name()) .collect::>() @@ -565,33 +565,33 @@ mod tests { let gdal_version: i64 = crate::version::version_info("VERSION_NUM").parse().unwrap(); if DriverManager::get_driver_by_name("ESRI Shapefile").is_ok() { assert!(drivers("test.shp", true).contains("ESRI Shapefile")); + assert!(drivers("my.test.shp", true).contains("ESRI Shapefile")); // `shp.zip` only supported from gdal version 3.1 // https://gdal.org/drivers/vector/shapefile.html#compressed-files if gdal_version >= 3010000 { assert!(drivers("test.shp.zip", true).contains("ESRI Shapefile")); + assert!(drivers("my.test.shp.zip", true).contains("ESRI Shapefile")); } } if DriverManager::get_driver_by_name("GPKG").is_ok() { assert!(drivers("test.gpkg", true).contains("GPKG")); + assert!(drivers("my.test.gpkg", true).contains("GPKG")); // `gpkg.zip` only supported from gdal version 3.7 // https://gdal.org/drivers/vector/gpkg.html#compressed-files if gdal_version >= 3070000 { assert!(drivers("test.gpkg.zip", true).contains("GPKG")); + assert!(drivers("my.test.gpkg.zip", true).contains("GPKG")); } } if DriverManager::get_driver_by_name("GTiff").is_ok() { assert!(drivers("test.tiff", false).contains("GTiff")); + assert!(drivers("my.test.tiff", false).contains("GTiff")); } if DriverManager::get_driver_by_name("netCDF").is_ok() { assert!(drivers("test.nc", false).contains("netCDF")); } - if DriverManager::get_driver_by_name("Elasticsearch").is_ok() { - // Elasticsearch short name was ElasticSearch in older versions - if gdal_version >= 3010000 { - assert!(drivers("ES:test", true).contains("Elasticsearch")); - } else { - assert!(drivers("ES:test", true).contains("ElasticSearch")); - } + if DriverManager::get_driver_by_name("PostgreSQL").is_ok() { + assert!(drivers("PG:test", true).contains("PostgreSQL")); } } } From 2feaf29b30563d04fe122f6d31d482934de131e1 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Fri, 5 Jan 2024 16:03:30 -0500 Subject: [PATCH 12/23] Use DriverIterator for looping through drivers --- src/driver.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 33befd08..c97d22af 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -459,18 +459,15 @@ impl DriverManager { }; let mut drivers: Vec = Vec::new(); - for i in 0..DriverManager::count() { - let d = DriverManager::get_driver(i).expect("Index for this loop should be valid"); + for d in DriverManager::all().filter(|d| { let can_create = d.metadata_item("DCAP_CREATE", "").is_some() || d.metadata_item("DCAP_CREATECOPY", "").is_some(); let check_vector = is_vector && d.metadata_item("DCAP_VECTOR", "").is_some(); let check_raster = !is_vector && d.metadata_item("DCAP_RASTER", "").is_some(); let check_vector_translate = is_vector && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some(); - if !((can_create && (check_vector || check_raster)) || (check_vector_translate)) { - continue; - } - + (can_create && (check_vector || check_raster)) || (check_vector_translate) + }) { if let Some(e) = &d.metadata_item("DMD_EXTENSION", "") { if *e == ext { drivers.push(d); From 5c32a2cbc641c8eeb5991f02dc3cb6f14ee8b13d Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Fri, 5 Jan 2024 16:13:43 -0500 Subject: [PATCH 13/23] Make `DriverManager::all()` return an Iterator --- src/driver.rs | 59 +++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index c97d22af..30148049 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -427,7 +427,6 @@ impl DriverManager { /// # fn main() -> gdal::errors::Result<()> { /// let compatible_drivers = /// DriverManager::guess_drivers_for_write("test.gpkg", true) - /// .iter() /// .map(|d| d.short_name()) /// .collect::>(); /// println!("{:?}", compatible_drivers); @@ -437,7 +436,10 @@ impl DriverManager { /// ```text /// ["GPKG"] /// ``` - pub fn guess_drivers_for_write(filename: &str, is_vector: bool) -> Vec { + pub fn guess_drivers_for_write( + filename: &str, + is_vector: bool, + ) -> impl Iterator + '_ { let ext = { let filename = filename.to_ascii_lowercase(); let e = match filename.rsplit_once('.') { @@ -458,37 +460,35 @@ impl DriverManager { e.to_string() }; - let mut drivers: Vec = Vec::new(); - for d in DriverManager::all().filter(|d| { - let can_create = d.metadata_item("DCAP_CREATE", "").is_some() - || d.metadata_item("DCAP_CREATECOPY", "").is_some(); - let check_vector = is_vector && d.metadata_item("DCAP_VECTOR", "").is_some(); - let check_raster = !is_vector && d.metadata_item("DCAP_RASTER", "").is_some(); - let check_vector_translate = - is_vector && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some(); - (can_create && (check_vector || check_raster)) || (check_vector_translate) - }) { - if let Some(e) = &d.metadata_item("DMD_EXTENSION", "") { - if *e == ext { - drivers.push(d); - continue; + DriverManager::all() + .filter(move |d| { + let can_create = d.metadata_item("DCAP_CREATE", "").is_some() + || d.metadata_item("DCAP_CREATECOPY", "").is_some(); + let check_vector = is_vector && d.metadata_item("DCAP_VECTOR", "").is_some(); + let check_raster = !is_vector && d.metadata_item("DCAP_RASTER", "").is_some(); + let check_vector_translate = + is_vector && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some(); + (can_create && (check_vector || check_raster)) || (check_vector_translate) + }) + .filter(move |d| { + if let Some(e) = &d.metadata_item("DMD_EXTENSION", "") { + if *e == ext { + return true; + } } - } - if let Some(e) = d.metadata_item("DMD_EXTENSIONS", "") { - if e.split(' ').collect::>().contains(&ext.as_str()) { - drivers.push(d); - continue; + if let Some(e) = d.metadata_item("DMD_EXTENSIONS", "") { + if e.split(' ').collect::>().contains(&ext.as_str()) { + return true; + } } - } - if let Some(pre) = d.metadata_item("DMD_CONNECTION_PREFIX", "") { - if filename.starts_with(&pre) { - drivers.push(d); + if let Some(pre) = d.metadata_item("DMD_CONNECTION_PREFIX", "") { + if filename.starts_with(&pre) { + return true; + } } - } - } - - drivers + false + }) } /// Register a driver for use. @@ -582,7 +582,6 @@ mod tests { // convert the driver into short_name for testing purposes let drivers = |filename, is_vector| { DriverManager::guess_drivers_for_write(filename, is_vector) - .iter() .map(|d| d.short_name()) .collect::>() }; From ed5a9913f3b81d931641cba80af8e4f05672c389 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Mon, 8 Jan 2024 17:42:29 -0500 Subject: [PATCH 14/23] Add function to get a single driver based on file extension --- CHANGES.md | 2 +- src/driver.rs | 125 ++++++++++++++++++++++++++++++++++++++++++-------- src/lib.rs | 2 +- 3 files changed, 107 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 97fa63a0..e08d4fb7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,7 +10,7 @@ - -- Added `DriverManager::guess_drivers_for_write` for the ability to auto detect compatible `Driver`s for writing data. +- Added `DriverManager::guess_driver_for_write` and `DriverManager::guess_drivers_for_write` for the ability to auto detect compatible `Driver`(s) for writing data. - Added `Feature::unset_field` diff --git a/src/driver.rs b/src/driver.rs index 30148049..e0e5a06a 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -410,7 +410,7 @@ impl DriverManager { Ok(Driver { c_driver }) } - /// Get the Driver based on the file extension from filename + /// Get a Driver based on the file extension from filename /// /// Searches for the available extensions in the registered /// drivers and returns the matches. The determined driver is @@ -423,10 +423,49 @@ impl DriverManager { /// # Example /// /// ```rust, no_run - /// use gdal::DriverManager; + /// use gdal::{DriverManager,DriverProperties}; + /// # fn main() -> gdal::errors::Result<()> { + /// let compatible_driver = + /// DriverManager::guess_driver_for_write("test.gpkg", DriverProperties::Vector).unwrap(); + /// println!("{}", compatible_driver.short_name()); + /// # Ok(()) + /// # } + /// ``` + /// ```text + /// "GPKG" + /// ``` + pub fn guess_driver_for_write(filename: &str, properties: DriverProperties) -> Option { + let mut drivers = Self::guess_drivers_for_write(filename, properties); + drivers + .next() + .map(|d| match d.short_name().to_uppercase().as_str() { + "GMT" => drivers + .find(|d| d.short_name().to_lowercase() == "netcdf") + .unwrap_or(d), + + "COG" => drivers + .find(|d| d.short_name().to_lowercase() == "gtiff") + .unwrap_or(d), + _ => d, + }) + } + /// Get the supported Drivers based on the file extension from filename + /// + /// Searches for the available extensions in the registered + /// drivers and returns the matches. The determined driver is + /// checked for writing capabilities as + /// [`Dataset::open`](Dataset::open) can already open datasets + /// with just path. + /// + /// See also: [`get_driver_by_name`](Self::get_driver_by_name) + /// + /// # Example + /// + /// ```rust, no_run + /// use gdal::{DriverManager,DriverProperties}; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_drivers = - /// DriverManager::guess_drivers_for_write("test.gpkg", true) + /// DriverManager::guess_drivers_for_write("test.gpkg", DriverProperties::Vector) /// .map(|d| d.short_name()) /// .collect::>(); /// println!("{:?}", compatible_drivers); @@ -438,11 +477,11 @@ impl DriverManager { /// ``` pub fn guess_drivers_for_write( filename: &str, - is_vector: bool, + properties: DriverProperties, ) -> impl Iterator + '_ { let ext = { let filename = filename.to_ascii_lowercase(); - let e = match filename.rsplit_once('.') { + match filename.rsplit_once('.') { Some(("", _)) => "", // hidden file no ext Some((f, "zip")) => { // zip files could be zipped shp or gpkg @@ -456,19 +495,23 @@ impl DriverManager { } Some((_, e)) => e, // normal file with ext None => "", - }; - e.to_string() + } + .to_string() }; DriverManager::all() .filter(move |d| { let can_create = d.metadata_item("DCAP_CREATE", "").is_some() || d.metadata_item("DCAP_CREATECOPY", "").is_some(); - let check_vector = is_vector && d.metadata_item("DCAP_VECTOR", "").is_some(); - let check_raster = !is_vector && d.metadata_item("DCAP_RASTER", "").is_some(); - let check_vector_translate = - is_vector && d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some(); - (can_create && (check_vector || check_raster)) || (check_vector_translate) + match properties { + DriverProperties::Raster => { + can_create && d.metadata_item("DCAP_RASTER", "").is_some() + } + DriverProperties::Vector => { + (can_create && d.metadata_item("DCAP_VECTOR", "").is_some()) + || d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some() + } + } }) .filter(move |d| { if let Some(e) = &d.metadata_item("DMD_EXTENSION", "") { @@ -477,7 +520,7 @@ impl DriverManager { } } if let Some(e) = d.metadata_item("DMD_EXTENSIONS", "") { - if e.split(' ').collect::>().contains(&ext.as_str()) { + if e.split(' ').any(|s| s == ext) { return true; } } @@ -542,6 +585,11 @@ impl DriverManager { } } +pub enum DriverProperties { + Vector, + Raster, +} + /// Iterator for the registered [`Driver`]s in [`DriverManager`] pub struct DriverIterator { current: usize, @@ -578,20 +626,57 @@ mod tests { } #[test] - fn test_driver_extension() { + fn test_driver_by_extension() { + fn test_driver(d: &Driver, filename: &str, properties: DriverProperties) { + assert_eq!( + DriverManager::guess_driver_for_write(filename, properties) + .unwrap() + .short_name(), + d.short_name() + ); + } + + if let Ok(d) = DriverManager::get_driver_by_name("ESRI Shapefile") { + test_driver(&d, "test.shp", DriverProperties::Vector); + test_driver(&d, "my.test.shp", DriverProperties::Vector); + // `shp.zip` only supported from gdal version 3.1 + // https://gdal.org/drivers/vector/shapefile.html#compressed-files + if cfg!(all(major_ge_3, minor_ge_1)) { + test_driver(&d, "test.shp.zip", DriverProperties::Vector); + test_driver(&d, "my.test.shp.zip", DriverProperties::Vector); + } + } + + if let Ok(d) = DriverManager::get_driver_by_name("GTiff") { + test_driver(&d, "test.tiff", DriverProperties::Raster); + test_driver(&d, "my.test.tiff", DriverProperties::Raster); + } + if let Ok(d) = DriverManager::get_driver_by_name("netCDF") { + test_driver(&d, "test.nc", DriverProperties::Raster); + } + } + + #[test] + fn test_drivers_by_extension() { // convert the driver into short_name for testing purposes let drivers = |filename, is_vector| { - DriverManager::guess_drivers_for_write(filename, is_vector) - .map(|d| d.short_name()) - .collect::>() + DriverManager::guess_drivers_for_write( + filename, + if is_vector { + DriverProperties::Vector + } else { + DriverProperties::Raster + }, + ) + .map(|d| d.short_name()) + .collect::>() }; - let gdal_version: i64 = crate::version::version_info("VERSION_NUM").parse().unwrap(); if DriverManager::get_driver_by_name("ESRI Shapefile").is_ok() { assert!(drivers("test.shp", true).contains("ESRI Shapefile")); assert!(drivers("my.test.shp", true).contains("ESRI Shapefile")); // `shp.zip` only supported from gdal version 3.1 // https://gdal.org/drivers/vector/shapefile.html#compressed-files - if gdal_version >= 3010000 { + if cfg!(all(major_ge_3, minor_ge_1)) { assert!(drivers("test.shp.zip", true).contains("ESRI Shapefile")); assert!(drivers("my.test.shp.zip", true).contains("ESRI Shapefile")); } @@ -601,7 +686,7 @@ mod tests { assert!(drivers("my.test.gpkg", true).contains("GPKG")); // `gpkg.zip` only supported from gdal version 3.7 // https://gdal.org/drivers/vector/gpkg.html#compressed-files - if gdal_version >= 3070000 { + if cfg!(all(major_ge_3, minor_ge_7)) { assert!(drivers("test.gpkg.zip", true).contains("GPKG")); assert!(drivers("my.test.gpkg.zip", true).contains("GPKG")); } diff --git a/src/lib.rs b/src/lib.rs index 337c65b3..e7e274d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,7 +129,7 @@ pub use dataset::Dataset; pub use geo_transform::{GeoTransform, GeoTransformEx}; pub use options::{DatasetOptions, GdalOpenFlags}; -pub use driver::{Driver, DriverManager}; +pub use driver::{Driver, DriverManager, DriverProperties}; pub use gcp::{Gcp, GcpRef}; #[cfg(any(major_ge_4, all(major_is_3, minor_ge_6)))] pub use gdal_sys::ArrowArrayStream; From 45473a6d32ec38151759ac1db745ad863c1b8cf4 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Tue, 9 Jan 2024 15:46:54 -0500 Subject: [PATCH 15/23] Use `AsRef` instead of `&str` in guess_driver(s)_for_write --- src/driver.rs | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index e0e5a06a..5ada2b10 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -434,8 +434,11 @@ impl DriverManager { /// ```text /// "GPKG" /// ``` - pub fn guess_driver_for_write(filename: &str, properties: DriverProperties) -> Option { - let mut drivers = Self::guess_drivers_for_write(filename, properties); + pub fn guess_driver_for_write>( + filepath: P, + properties: DriverProperties, + ) -> Option { + let mut drivers = Self::guess_drivers_for_write(filepath, properties); drivers .next() .map(|d| match d.short_name().to_uppercase().as_str() { @@ -475,25 +478,30 @@ impl DriverManager { /// ```text /// ["GPKG"] /// ``` - pub fn guess_drivers_for_write( - filename: &str, + pub fn guess_drivers_for_write>( + filepath: P, properties: DriverProperties, - ) -> impl Iterator + '_ { + ) -> impl Iterator { let ext = { - let filename = filename.to_ascii_lowercase(); - match filename.rsplit_once('.') { - Some(("", _)) => "", // hidden file no ext - Some((f, "zip")) => { + let ext = filepath + .as_ref() + .extension() + .map(|e| e.to_string_lossy().to_string().to_ascii_lowercase()); + match ext.as_deref() { + Some("zip") => { // zip files could be zipped shp or gpkg - if f.ends_with(".shp") { - "shp.zip" - } else if f.ends_with(".gpkg") { - "gpkg.zip" - } else { - "zip" + let iext = filepath + .as_ref() + .with_extension("") + .extension() + .map(|f| f.to_string_lossy().to_string()); + match iext.as_deref() { + Some("shp") => "shp.zip", + Some("gpkg") => "gpkg.zip", + _ => "zip", } } - Some((_, e)) => e, // normal file with ext + Some(e) => e, // normal file with ext None => "", } .to_string() @@ -526,7 +534,7 @@ impl DriverManager { } if let Some(pre) = d.metadata_item("DMD_CONNECTION_PREFIX", "") { - if filename.starts_with(&pre) { + if filepath.as_ref().to_string_lossy().starts_with(&pre) { return true; } } From fc0361ad5ce8402c3dbf1ad4969dca9b0424a919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 10 Jan 2024 21:22:06 +0200 Subject: [PATCH 16/23] Small cleanups --- src/driver.rs | 110 +++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 5ada2b10..7ee98c0e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -410,15 +410,18 @@ impl DriverManager { Ok(Driver { c_driver }) } - /// Get a Driver based on the file extension from filename + /// Get one [`Driver`] that can create a file with the given name. /// - /// Searches for the available extensions in the registered - /// drivers and returns the matches. The determined driver is - /// checked for writing capabilities as - /// [`Dataset::open`](Dataset::open) can already open datasets - /// with just path. + /// Searches for registered drivers that can create files and support + /// the file extension or the connection prefix. /// /// See also: [`get_driver_by_name`](Self::get_driver_by_name) + /// and [`Dataset::open`](Dataset::open). + /// + /// # Note + /// + /// This functionality is implemented natively in GDAL 3.9, but this crate + /// emulates it in previous versions. /// /// # Example /// @@ -426,7 +429,7 @@ impl DriverManager { /// use gdal::{DriverManager,DriverProperties}; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_driver = - /// DriverManager::guess_driver_for_write("test.gpkg", DriverProperties::Vector).unwrap(); + /// DriverManager::get_output_driver_for_name("test.gpkg", DriverProperties::Vector).unwrap(); /// println!("{}", compatible_driver.short_name()); /// # Ok(()) /// # } @@ -434,33 +437,33 @@ impl DriverManager { /// ```text /// "GPKG" /// ``` - pub fn guess_driver_for_write>( + pub fn get_output_driver_for_name>( filepath: P, properties: DriverProperties, ) -> Option { - let mut drivers = Self::guess_drivers_for_write(filepath, properties); - drivers - .next() - .map(|d| match d.short_name().to_uppercase().as_str() { - "GMT" => drivers - .find(|d| d.short_name().to_lowercase() == "netcdf") - .unwrap_or(d), - - "COG" => drivers - .find(|d| d.short_name().to_lowercase() == "gtiff") - .unwrap_or(d), - _ => d, - }) + let mut drivers = Self::get_output_drivers_for_name(filepath, properties); + drivers.next().map(|d| match d.short_name().as_str() { + "GMT" => drivers + .find(|d| d.short_name().to_lowercase() == "NETCDF") + .unwrap_or(d), + + "COG" => drivers.find(|d| d.short_name() == "GTiff").unwrap_or(d), + _ => d, + }) } - /// Get the supported Drivers based on the file extension from filename + + /// Get the [`Driver`]s that can create a file with the given name. /// - /// Searches for the available extensions in the registered - /// drivers and returns the matches. The determined driver is - /// checked for writing capabilities as - /// [`Dataset::open`](Dataset::open) can already open datasets - /// with just path. + /// Searches for registered drivers that can create files and support + /// the file extension or the connection prefix. /// /// See also: [`get_driver_by_name`](Self::get_driver_by_name) + /// and [`Dataset::open`](Dataset::open). + /// + /// # Note + /// + /// This functionality is implemented natively in GDAL 3.9, but this crate + /// emulates it in previous versions. /// /// # Example /// @@ -468,7 +471,7 @@ impl DriverManager { /// use gdal::{DriverManager,DriverProperties}; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_drivers = - /// DriverManager::guess_drivers_for_write("test.gpkg", DriverProperties::Vector) + /// DriverManager::get_output_drivers_for_name("test.gpkg", DriverProperties::Vector) /// .map(|d| d.short_name()) /// .collect::>(); /// println!("{:?}", compatible_drivers); @@ -478,33 +481,28 @@ impl DriverManager { /// ```text /// ["GPKG"] /// ``` - pub fn guess_drivers_for_write>( - filepath: P, + pub fn get_output_drivers_for_name>( + path: P, properties: DriverProperties, ) -> impl Iterator { - let ext = { - let ext = filepath - .as_ref() - .extension() - .map(|e| e.to_string_lossy().to_string().to_ascii_lowercase()); - match ext.as_deref() { - Some("zip") => { - // zip files could be zipped shp or gpkg - let iext = filepath - .as_ref() - .with_extension("") - .extension() - .map(|f| f.to_string_lossy().to_string()); - match iext.as_deref() { - Some("shp") => "shp.zip", - Some("gpkg") => "gpkg.zip", - _ => "zip", - } - } - Some(e) => e, // normal file with ext - None => "", + let path = path.as_ref(); + let path_lower = path.to_string_lossy().to_ascii_lowercase(); + + // NOTE: this isn't exactly correct for e.g. `.gpkg.zip` + // (which is not a GPKG), but this code is going away. + let ext = if path_lower.ends_with(".zip") { + if path_lower.ends_with(".shp.zip") { + "shp.zip".to_string() + } else if path_lower.ends_with(".gpkg.zip") { + "gpkg.zip".to_string() + } else { + "zip".to_string() } - .to_string() + } else { + Path::new(&path_lower) + .extension() + .map(|e| e.to_string_lossy().into_owned()) + .unwrap_or_default() }; DriverManager::all() @@ -534,7 +532,7 @@ impl DriverManager { } if let Some(pre) = d.metadata_item("DMD_CONNECTION_PREFIX", "") { - if filepath.as_ref().to_string_lossy().starts_with(&pre) { + if path_lower.starts_with(&pre.to_ascii_lowercase()) { return true; } } @@ -637,7 +635,7 @@ mod tests { fn test_driver_by_extension() { fn test_driver(d: &Driver, filename: &str, properties: DriverProperties) { assert_eq!( - DriverManager::guess_driver_for_write(filename, properties) + DriverManager::get_output_driver_for_name(filename, properties) .unwrap() .short_name(), d.short_name() @@ -668,7 +666,7 @@ mod tests { fn test_drivers_by_extension() { // convert the driver into short_name for testing purposes let drivers = |filename, is_vector| { - DriverManager::guess_drivers_for_write( + DriverManager::get_output_drivers_for_name( filename, if is_vector { DriverProperties::Vector @@ -707,7 +705,7 @@ mod tests { assert!(drivers("test.nc", false).contains("netCDF")); } if DriverManager::get_driver_by_name("PostgreSQL").is_ok() { - assert!(drivers("PG:test", true).contains("PostgreSQL")); + assert!(dbg!(drivers("PG:test", true)).contains("PostgreSQL")); } } From 2c1fc764fe7d45eb4058507443b8ce3d6945fe26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 10 Jan 2024 21:26:15 +0200 Subject: [PATCH 17/23] Fix test --- src/driver.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 7ee98c0e..7514b21e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -443,10 +443,7 @@ impl DriverManager { ) -> Option { let mut drivers = Self::get_output_drivers_for_name(filepath, properties); drivers.next().map(|d| match d.short_name().as_str() { - "GMT" => drivers - .find(|d| d.short_name().to_lowercase() == "NETCDF") - .unwrap_or(d), - + "GMT" => drivers.find(|d| d.short_name() == "netCDF").unwrap_or(d), "COG" => drivers.find(|d| d.short_name() == "GTiff").unwrap_or(d), _ => d, }) From 773872d6995445d8164bd8d7994a3007477acc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 10 Jan 2024 21:30:28 +0200 Subject: [PATCH 18/23] Try to debug test --- src/driver.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 7514b21e..831e146a 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -441,7 +441,12 @@ impl DriverManager { filepath: P, properties: DriverProperties, ) -> Option { - let mut drivers = Self::get_output_drivers_for_name(filepath, properties); + let drivers = Self::get_output_drivers_for_name(filepath, properties); + let drivers = drivers.collect::>(); + for d in &drivers { + dbg!(d.short_name()); + } + let mut drivers = drivers.into_iter(); drivers.next().map(|d| match d.short_name().as_str() { "GMT" => drivers.find(|d| d.short_name() == "netCDF").unwrap_or(d), "COG" => drivers.find(|d| d.short_name() == "GTiff").unwrap_or(d), @@ -702,7 +707,7 @@ mod tests { assert!(drivers("test.nc", false).contains("netCDF")); } if DriverManager::get_driver_by_name("PostgreSQL").is_ok() { - assert!(dbg!(drivers("PG:test", true)).contains("PostgreSQL")); + assert!(drivers("PG:test", true).contains("PostgreSQL")); } } From a187d381cdbd0b6e29a5e43761ffaee0a5960fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 10 Jan 2024 21:48:51 +0200 Subject: [PATCH 19/23] Remove debugging code --- src/driver.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 831e146a..8b4f1e83 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -441,12 +441,7 @@ impl DriverManager { filepath: P, properties: DriverProperties, ) -> Option { - let drivers = Self::get_output_drivers_for_name(filepath, properties); - let drivers = drivers.collect::>(); - for d in &drivers { - dbg!(d.short_name()); - } - let mut drivers = drivers.into_iter(); + let mut drivers = Self::get_output_drivers_for_name(filepath, properties); drivers.next().map(|d| match d.short_name().as_str() { "GMT" => drivers.find(|d| d.short_name() == "netCDF").unwrap_or(d), "COG" => drivers.find(|d| d.short_name() == "GTiff").unwrap_or(d), From f54cd81ebd5394f775a5044a357c981288abc357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 11 Jan 2024 20:41:45 +0200 Subject: [PATCH 20/23] Rename methods and ignore case --- src/driver.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 8b4f1e83..b1cf5fb3 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -426,10 +426,10 @@ impl DriverManager { /// # Example /// /// ```rust, no_run - /// use gdal::{DriverManager,DriverProperties}; + /// use gdal::{DriverManager, DriverProperties}; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_driver = - /// DriverManager::get_output_driver_for_name("test.gpkg", DriverProperties::Vector).unwrap(); + /// DriverManager::get_output_driver_for_dataset_name("test.gpkg", DriverProperties::Vector).unwrap(); /// println!("{}", compatible_driver.short_name()); /// # Ok(()) /// # } @@ -437,14 +437,18 @@ impl DriverManager { /// ```text /// "GPKG" /// ``` - pub fn get_output_driver_for_name>( + pub fn get_output_driver_for_dataset_name>( filepath: P, properties: DriverProperties, ) -> Option { - let mut drivers = Self::get_output_drivers_for_name(filepath, properties); + let mut drivers = Self::get_output_drivers_for_dataset_name(filepath, properties); drivers.next().map(|d| match d.short_name().as_str() { - "GMT" => drivers.find(|d| d.short_name() == "netCDF").unwrap_or(d), - "COG" => drivers.find(|d| d.short_name() == "GTiff").unwrap_or(d), + "GMT" => drivers + .find(|d| d.short_name().eq_ignore_ascii_case("netCDF")) + .unwrap_or(d), + "COG" => drivers + .find(|d| d.short_name().eq_ignore_ascii_case("GTiff")) + .unwrap_or(d), _ => d, }) } @@ -465,10 +469,10 @@ impl DriverManager { /// # Example /// /// ```rust, no_run - /// use gdal::{DriverManager,DriverProperties}; + /// use gdal::{DriverManager, DriverProperties}; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_drivers = - /// DriverManager::get_output_drivers_for_name("test.gpkg", DriverProperties::Vector) + /// DriverManager::get_output_drivers_for_dataset_name("test.gpkg", DriverProperties::Vector) /// .map(|d| d.short_name()) /// .collect::>(); /// println!("{:?}", compatible_drivers); @@ -478,7 +482,7 @@ impl DriverManager { /// ```text /// ["GPKG"] /// ``` - pub fn get_output_drivers_for_name>( + pub fn get_output_drivers_for_dataset_name>( path: P, properties: DriverProperties, ) -> impl Iterator { @@ -632,7 +636,7 @@ mod tests { fn test_driver_by_extension() { fn test_driver(d: &Driver, filename: &str, properties: DriverProperties) { assert_eq!( - DriverManager::get_output_driver_for_name(filename, properties) + DriverManager::get_output_driver_for_dataset_name(filename, properties) .unwrap() .short_name(), d.short_name() @@ -663,7 +667,7 @@ mod tests { fn test_drivers_by_extension() { // convert the driver into short_name for testing purposes let drivers = |filename, is_vector| { - DriverManager::get_output_drivers_for_name( + DriverManager::get_output_drivers_for_dataset_name( filename, if is_vector { DriverProperties::Vector From 326feda604c004e28ce86a1f7b850090c90b5485 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Mon, 15 Jan 2024 12:54:29 -0500 Subject: [PATCH 21/23] Add PR link to CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index e08d4fb7..e262a1d6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## Unreleased - Add `DriverIterator` format to iterate through drivers, as well as `DriverManager::all()` method that provides the iterator. + - - **Breaking**: `Feature::set_field_xxx` now take `&mut self` - From 04a549c111a30342d0465f8fd9aa1dae8f934794 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Mon, 15 Jan 2024 12:56:43 -0500 Subject: [PATCH 22/23] Rename DriverProperties to DriverType --- src/driver.rs | 38 +++++++++++++++++++------------------- src/lib.rs | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index b1cf5fb3..50e9d302 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -426,10 +426,10 @@ impl DriverManager { /// # Example /// /// ```rust, no_run - /// use gdal::{DriverManager, DriverProperties}; + /// use gdal::{DriverManager, DriverType}; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_driver = - /// DriverManager::get_output_driver_for_dataset_name("test.gpkg", DriverProperties::Vector).unwrap(); + /// DriverManager::get_output_driver_for_dataset_name("test.gpkg", DriverType::Vector).unwrap(); /// println!("{}", compatible_driver.short_name()); /// # Ok(()) /// # } @@ -439,7 +439,7 @@ impl DriverManager { /// ``` pub fn get_output_driver_for_dataset_name>( filepath: P, - properties: DriverProperties, + properties: DriverType, ) -> Option { let mut drivers = Self::get_output_drivers_for_dataset_name(filepath, properties); drivers.next().map(|d| match d.short_name().as_str() { @@ -469,10 +469,10 @@ impl DriverManager { /// # Example /// /// ```rust, no_run - /// use gdal::{DriverManager, DriverProperties}; + /// use gdal::{DriverManager, DriverType}; /// # fn main() -> gdal::errors::Result<()> { /// let compatible_drivers = - /// DriverManager::get_output_drivers_for_dataset_name("test.gpkg", DriverProperties::Vector) + /// DriverManager::get_output_drivers_for_dataset_name("test.gpkg", DriverType::Vector) /// .map(|d| d.short_name()) /// .collect::>(); /// println!("{:?}", compatible_drivers); @@ -484,7 +484,7 @@ impl DriverManager { /// ``` pub fn get_output_drivers_for_dataset_name>( path: P, - properties: DriverProperties, + properties: DriverType, ) -> impl Iterator { let path = path.as_ref(); let path_lower = path.to_string_lossy().to_ascii_lowercase(); @@ -511,10 +511,10 @@ impl DriverManager { let can_create = d.metadata_item("DCAP_CREATE", "").is_some() || d.metadata_item("DCAP_CREATECOPY", "").is_some(); match properties { - DriverProperties::Raster => { + DriverType::Raster => { can_create && d.metadata_item("DCAP_RASTER", "").is_some() } - DriverProperties::Vector => { + DriverType::Vector => { (can_create && d.metadata_item("DCAP_VECTOR", "").is_some()) || d.metadata_item("DCAP_VECTOR_TRANSLATE_FROM", "").is_some() } @@ -592,7 +592,7 @@ impl DriverManager { } } -pub enum DriverProperties { +pub enum DriverType { Vector, Raster, } @@ -634,7 +634,7 @@ mod tests { #[test] fn test_driver_by_extension() { - fn test_driver(d: &Driver, filename: &str, properties: DriverProperties) { + fn test_driver(d: &Driver, filename: &str, properties: DriverType) { assert_eq!( DriverManager::get_output_driver_for_dataset_name(filename, properties) .unwrap() @@ -644,22 +644,22 @@ mod tests { } if let Ok(d) = DriverManager::get_driver_by_name("ESRI Shapefile") { - test_driver(&d, "test.shp", DriverProperties::Vector); - test_driver(&d, "my.test.shp", DriverProperties::Vector); + test_driver(&d, "test.shp", DriverType::Vector); + test_driver(&d, "my.test.shp", DriverType::Vector); // `shp.zip` only supported from gdal version 3.1 // https://gdal.org/drivers/vector/shapefile.html#compressed-files if cfg!(all(major_ge_3, minor_ge_1)) { - test_driver(&d, "test.shp.zip", DriverProperties::Vector); - test_driver(&d, "my.test.shp.zip", DriverProperties::Vector); + test_driver(&d, "test.shp.zip", DriverType::Vector); + test_driver(&d, "my.test.shp.zip", DriverType::Vector); } } if let Ok(d) = DriverManager::get_driver_by_name("GTiff") { - test_driver(&d, "test.tiff", DriverProperties::Raster); - test_driver(&d, "my.test.tiff", DriverProperties::Raster); + test_driver(&d, "test.tiff", DriverType::Raster); + test_driver(&d, "my.test.tiff", DriverType::Raster); } if let Ok(d) = DriverManager::get_driver_by_name("netCDF") { - test_driver(&d, "test.nc", DriverProperties::Raster); + test_driver(&d, "test.nc", DriverType::Raster); } } @@ -670,9 +670,9 @@ mod tests { DriverManager::get_output_drivers_for_dataset_name( filename, if is_vector { - DriverProperties::Vector + DriverType::Vector } else { - DriverProperties::Raster + DriverType::Raster }, ) .map(|d| d.short_name()) diff --git a/src/lib.rs b/src/lib.rs index e7e274d5..fecb2413 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,7 +129,7 @@ pub use dataset::Dataset; pub use geo_transform::{GeoTransform, GeoTransformEx}; pub use options::{DatasetOptions, GdalOpenFlags}; -pub use driver::{Driver, DriverManager, DriverProperties}; +pub use driver::{Driver, DriverManager, DriverType}; pub use gcp::{Gcp, GcpRef}; #[cfg(any(major_ge_4, all(major_is_3, minor_ge_6)))] pub use gdal_sys::ArrowArrayStream; From 98fbc2350dc6e08c044e8e1cfe952753380f4db5 Mon Sep 17 00:00:00 2001 From: Gaurav Atreya Date: Mon, 15 Jan 2024 12:59:14 -0500 Subject: [PATCH 23/23] Fix: wrong PR link location --- CHANGES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e262a1d6..cba870e2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ## Unreleased - Add `DriverIterator` format to iterate through drivers, as well as `DriverManager::all()` method that provides the iterator. - - + - - **Breaking**: `Feature::set_field_xxx` now take `&mut self` - @@ -11,7 +11,8 @@ - -- Added `DriverManager::guess_driver_for_write` and `DriverManager::guess_drivers_for_write` for the ability to auto detect compatible `Driver`(s) for writing data. +- Added `DriverManager::get_output_driver_for_dataset_name` and `DriverManager::get_output_drivers_for_dataset_name` for the ability to auto detect compatible `Driver`(s) for writing data. + - - Added `Feature::unset_field`