-
Notifications
You must be signed in to change notification settings - Fork 92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add DriverManager::get_drivers_for_filename
for the ability to auto detect compatible Driver
s for writing data
#510
Changes from 21 commits
5f44a24
9fb996b
f7d6bcb
3d569e0
0b8d80d
79300cd
563eb97
62f7ccd
3dbc42f
05f46c4
7c90f62
223b846
2feaf29
5c32a2c
ed5a991
45473a6
fc0361a
2c1fc76
773872d
a187d38
f54cd81
326feda
04a549c
98fbc23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -410,6 +410,137 @@ impl DriverManager { | |
Ok(Driver { c_driver }) | ||
} | ||
|
||
/// Get one [`Driver`] that can create a file with the given name. | ||
/// | ||
/// 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 | ||
/// | ||
/// ```rust, no_run | ||
/// use gdal::{DriverManager, DriverProperties}; | ||
/// # fn main() -> gdal::errors::Result<()> { | ||
/// let compatible_driver = | ||
/// DriverManager::get_output_driver_for_dataset_name("test.gpkg", DriverProperties::Vector).unwrap(); | ||
/// println!("{}", compatible_driver.short_name()); | ||
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
/// ```text | ||
/// "GPKG" | ||
/// ``` | ||
pub fn get_output_driver_for_dataset_name<P: AsRef<Path>>( | ||
filepath: P, | ||
properties: DriverProperties, | ||
) -> Option<Driver> { | ||
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().eq_ignore_ascii_case("netCDF")) | ||
.unwrap_or(d), | ||
"COG" => drivers | ||
.find(|d| d.short_name().eq_ignore_ascii_case("GTiff")) | ||
.unwrap_or(d), | ||
_ => d, | ||
}) | ||
} | ||
|
||
/// Get the [`Driver`]s that can create a file with the given name. | ||
/// | ||
/// 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 | ||
/// | ||
/// ```rust, no_run | ||
/// use gdal::{DriverManager, DriverProperties}; | ||
/// # fn main() -> gdal::errors::Result<()> { | ||
/// let compatible_drivers = | ||
/// DriverManager::get_output_drivers_for_dataset_name("test.gpkg", DriverProperties::Vector) | ||
/// .map(|d| d.short_name()) | ||
/// .collect::<Vec<String>>(); | ||
/// println!("{:?}", compatible_drivers); | ||
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
/// ```text | ||
/// ["GPKG"] | ||
/// ``` | ||
pub fn get_output_drivers_for_dataset_name<P: AsRef<Path>>( | ||
path: P, | ||
properties: DriverProperties, | ||
) -> impl Iterator<Item = Driver> { | ||
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() | ||
} | ||
} else { | ||
Path::new(&path_lower) | ||
.extension() | ||
.map(|e| e.to_string_lossy().into_owned()) | ||
.unwrap_or_default() | ||
}; | ||
|
||
DriverManager::all() | ||
.filter(move |d| { | ||
let can_create = d.metadata_item("DCAP_CREATE", "").is_some() | ||
|| d.metadata_item("DCAP_CREATECOPY", "").is_some(); | ||
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", "") { | ||
if *e == ext { | ||
return true; | ||
} | ||
} | ||
if let Some(e) = d.metadata_item("DMD_EXTENSIONS", "") { | ||
if e.split(' ').any(|s| s == ext) { | ||
return true; | ||
} | ||
} | ||
|
||
if let Some(pre) = d.metadata_item("DMD_CONNECTION_PREFIX", "") { | ||
if path_lower.starts_with(&pre.to_ascii_lowercase()) { | ||
return true; | ||
} | ||
} | ||
false | ||
}) | ||
} | ||
|
||
/// Register a driver for use. | ||
/// | ||
/// Wraps [`GDALRegisterDriver()`](https://gdal.org/api/raster_c_api.html#_CPPv418GDALRegisterDriver11GDALDriverH) | ||
|
@@ -461,6 +592,11 @@ impl DriverManager { | |
} | ||
} | ||
|
||
pub enum DriverProperties { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider the name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you, I was initially thinking that properties might be weird. It's renamed now. |
||
Vector, | ||
Raster, | ||
} | ||
|
||
/// Iterator for the registered [`Driver`]s in [`DriverManager`] | ||
pub struct DriverIterator { | ||
current: usize, | ||
|
@@ -496,6 +632,84 @@ mod tests { | |
assert!(DriverManager::get_driver(0).is_ok()); | ||
} | ||
|
||
#[test] | ||
fn test_driver_by_extension() { | ||
fn test_driver(d: &Driver, filename: &str, properties: DriverProperties) { | ||
assert_eq!( | ||
DriverManager::get_output_driver_for_dataset_name(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::get_output_drivers_for_dataset_name( | ||
filename, | ||
if is_vector { | ||
DriverProperties::Vector | ||
} else { | ||
DriverProperties::Raster | ||
}, | ||
) | ||
.map(|d| d.short_name()) | ||
.collect::<HashSet<String>>() | ||
}; | ||
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 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")); | ||
} | ||
} | ||
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 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")); | ||
} | ||
} | ||
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("PostgreSQL").is_ok() { | ||
assert!(drivers("PG:test", true).contains("PostgreSQL")); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_driver_iterator() { | ||
assert_eq!(DriverManager::count(), DriverManager::all().count()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to add the PR link here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. Done.