use std::ffi::CString;
use std::path::Path;
use std::sync::Once;
use gdal_sys::{self, CPLErr, GDALDriverH, GDALMajorObjectH};
use libc::c_int;
use crate::cpl::CslStringList;
use crate::dataset::Dataset;
use crate::gdal_major_object::MajorObject;
use crate::metadata::Metadata;
use crate::raster::{GdalType, RasterCreationOption};
use crate::utils::{_last_cpl_err, _last_null_pointer_err, _path_to_c_string, _string};
use crate::errors::*;
static START: Once = Once::new();
pub fn _register_drivers() {
START.call_once(DriverManager::register_all);
}
#[allow(missing_copy_implementations)]
pub struct Driver {
c_driver: GDALDriverH,
}
impl Driver {
#[deprecated(note = "Please use `DriverManager::get_driver_by_name()` instead")]
pub fn get_by_name(name: &str) -> Result<Driver> {
DriverManager::get_driver_by_name(name)
}
#[deprecated(note = "Please use `DriverManager::get_driver()` instead")]
pub fn get(index: usize) -> Result<Driver> {
DriverManager::get_driver(index)
}
#[deprecated(note = "Please use `DriverManager::count()` instead")]
pub fn count() -> usize {
DriverManager::count()
}
pub fn short_name(&self) -> String {
let rv = unsafe { gdal_sys::GDALGetDriverShortName(self.c_driver) };
_string(rv)
}
pub fn long_name(&self) -> String {
let rv = unsafe { gdal_sys::GDALGetDriverLongName(self.c_driver) };
_string(rv)
}
pub fn create<P: AsRef<Path>>(
&self,
filename: P,
size_x: isize,
size_y: isize,
bands: isize,
) -> Result<Dataset> {
self.create_with_band_type::<u8, _>(filename, size_x, size_y, bands)
}
pub fn create_with_band_type<T: GdalType, P: AsRef<Path>>(
&self,
filename: P,
size_x: isize,
size_y: isize,
bands: isize,
) -> Result<Dataset> {
let options = [];
self.create_with_band_type_with_options::<T, _>(filename, size_x, size_y, bands, &options)
}
pub fn create_with_band_type_with_options<T: GdalType, P: AsRef<Path>>(
&self,
filename: P,
size_x: isize,
size_y: isize,
bands: isize,
options: &[RasterCreationOption],
) -> Result<Dataset> {
Self::_create_with_band_type_with_options::<T>(
self,
filename.as_ref(),
size_x,
size_y,
bands,
options,
)
}
fn _create_with_band_type_with_options<T: GdalType>(
&self,
filename: &Path,
size_x: isize,
size_y: isize,
bands: isize,
options: &[RasterCreationOption],
) -> Result<Dataset> {
let mut options_c = CslStringList::new();
for option in options {
options_c.set_name_value(option.key, option.value)?;
}
let c_filename = _path_to_c_string(filename)?;
let c_dataset = unsafe {
gdal_sys::GDALCreate(
self.c_driver,
c_filename.as_ptr(),
size_x as c_int,
size_y as c_int,
bands as c_int,
T::gdal_ordinal(),
options_c.as_ptr(),
)
};
if c_dataset.is_null() {
return Err(_last_null_pointer_err("GDALCreate"));
};
Ok(unsafe { Dataset::from_c_dataset(c_dataset) })
}
pub fn create_vector_only<P: AsRef<Path>>(&self, filename: P) -> Result<Dataset> {
self.create_with_band_type::<u8, _>(filename, 0, 0, 0)
}
pub fn delete<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
Self::_delete(self, filename.as_ref())
}
fn _delete(&self, filename: &Path) -> Result<()> {
let c_filename = _path_to_c_string(filename)?;
let rv = unsafe { gdal_sys::GDALDeleteDataset(self.c_driver, c_filename.as_ptr()) };
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn rename<P1: AsRef<Path>, P2: AsRef<Path>>(
&self,
new_filename: P1,
old_filename: P2,
) -> Result<()> {
Self::_rename(self, new_filename.as_ref(), old_filename.as_ref())
}
fn _rename(&self, new_filename: &Path, old_filename: &Path) -> Result<()> {
let c_old_filename = _path_to_c_string(old_filename)?;
let c_new_filename = _path_to_c_string(new_filename)?;
let rv = unsafe {
gdal_sys::GDALRenameDataset(
self.c_driver,
c_new_filename.as_ptr(),
c_old_filename.as_ptr(),
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub unsafe fn from_c_driver(c_driver: GDALDriverH) -> Driver {
Driver { c_driver }
}
pub unsafe fn c_driver(&self) -> GDALDriverH {
self.c_driver
}
}
impl MajorObject for Driver {
unsafe fn gdal_object_ptr(&self) -> GDALMajorObjectH {
self.c_driver
}
}
impl Metadata for Driver {}
pub struct DriverManager;
impl DriverManager {
pub fn count() -> usize {
_register_drivers();
let count = unsafe { gdal_sys::GDALGetDriverCount() };
count
.try_into()
.expect("The returned count should be zero or positive")
}
pub fn get_driver(index: usize) -> Result<Driver> {
_register_drivers();
let c_driver = unsafe { gdal_sys::GDALGetDriver(index.try_into().unwrap()) };
if c_driver.is_null() {
return Err(GdalError::NullPointer {
method_name: "GDALGetDriver",
msg: "Unable to find driver".to_string(),
});
}
Ok(Driver { c_driver })
}
pub fn get_driver_by_name(name: &str) -> Result<Driver> {
_register_drivers();
let c_name = CString::new(name)?;
let c_driver = unsafe { gdal_sys::GDALGetDriverByName(c_name.as_ptr()) };
if c_driver.is_null() {
return Err(GdalError::NullPointer {
method_name: "GDALGetDriverByName",
msg: "Unable to find driver".to_string(),
});
};
Ok(Driver { c_driver })
}
pub fn register_driver(driver: &Driver) -> usize {
let index = unsafe { gdal_sys::GDALRegisterDriver(driver.c_driver) };
index
.try_into()
.expect("The returned index should be zero or positive")
}
pub fn deregister_driver(driver: &Driver) {
unsafe {
gdal_sys::GDALDeregisterDriver(driver.c_driver);
}
}
pub fn register_all() {
unsafe {
gdal_sys::GDALAllRegister();
}
}
pub fn prevent_auto_registration() {
START.call_once(|| {});
}
pub fn destroy() {
unsafe {
gdal_sys::GDALDestroyDriverManager();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_driver_access() {
let driver = DriverManager::get_driver_by_name("GTiff").unwrap();
assert_eq!(driver.short_name(), "GTiff");
assert_eq!(driver.long_name(), "GeoTIFF");
assert!(DriverManager::count() > 0);
assert!(DriverManager::get_driver(0).is_ok());
}
}