diff --git a/src/programs/destination.rs b/src/programs/destination.rs new file mode 100644 index 000000000..429503b43 --- /dev/null +++ b/src/programs/destination.rs @@ -0,0 +1,83 @@ +use std::ffi::CString; +use std::mem::ManuallyDrop; +use std::path::{Path, PathBuf}; +use crate::Dataset; +use crate::errors::GdalError; +use crate::utils::_path_to_c_string; + +/// Path or Dataset to store Vectors or Raster +pub enum DatasetDestination { + Path(CString), + Dataset { + dataset: ManuallyDrop, + drop: bool, + }, +} + +impl TryFrom<&str> for DatasetDestination { + type Error = GdalError; + + fn try_from(path: &str) -> crate::errors::Result { + Self::path(path) + } +} + +impl TryFrom<&Path> for DatasetDestination { + type Error = GdalError; + + fn try_from(path: &Path) -> crate::errors::Result { + Self::path(path) + } +} + +impl TryFrom for DatasetDestination { + type Error = GdalError; + + fn try_from(path: PathBuf) -> crate::errors::Result { + Self::path(path) + } +} + +impl From for DatasetDestination { + fn from(dataset: Dataset) -> Self { + Self::dataset(dataset) + } +} + +impl Drop for DatasetDestination { + fn drop(&mut self) { + match self { + Self::Path(_) => {} + Self::Dataset { dataset, drop } => { + if *drop { + unsafe { + ManuallyDrop::drop(dataset); + } + } + } + } + } +} + +impl DatasetDestination { + pub fn dataset(dataset: Dataset) -> Self { + Self::Dataset { + dataset: ManuallyDrop::new(dataset), + drop: true, + } + } + + pub fn path>(path: P) -> crate::errors::Result { + let c_path = _path_to_c_string(path.as_ref())?; + Ok(Self::Path(c_path)) + } + + pub unsafe fn do_no_drop_dataset(&mut self) { + match self { + Self::Path(_) => {} + Self::Dataset { dataset: _, drop } => { + *drop = false; + } + } + } +} \ No newline at end of file diff --git a/src/programs/mod.rs b/src/programs/mod.rs index 153e2a16b..8b546bd42 100644 --- a/src/programs/mod.rs +++ b/src/programs/mod.rs @@ -1,3 +1,5 @@ //! Rust wrappers for the [GDAL Programs](https://gdal.org/programs/index.html) pub mod raster; +pub mod destination; +mod vector; diff --git a/src/programs/raster/mdimtranslate.rs b/src/programs/raster/mdimtranslate.rs index d90bd6363..2692c318b 100644 --- a/src/programs/raster/mdimtranslate.rs +++ b/src/programs/raster/mdimtranslate.rs @@ -13,6 +13,10 @@ use std::{ ptr::{null, null_mut}, }; +use crate::programs::destination::DatasetDestination; + +type MultiDimTranslateDestination = DatasetDestination; + /// Wraps a [GDALMultiDimTranslateOptions] object. /// /// [GDALMultiDimTranslateOptions]: https://gdal.org/api/gdal_utils.html#_CPPv428GDALMultiDimTranslateOptions @@ -80,81 +84,6 @@ impl TryFrom> for MultiDimTranslateOptions { } } -pub enum MultiDimTranslateDestination { - Path(CString), - Dataset { - dataset: ManuallyDrop, - drop: bool, - }, -} - -impl TryFrom<&str> for MultiDimTranslateDestination { - type Error = GdalError; - - fn try_from(path: &str) -> Result { - Self::path(path) - } -} - -impl TryFrom<&Path> for MultiDimTranslateDestination { - type Error = GdalError; - - fn try_from(path: &Path) -> Result { - Self::path(path) - } -} - -impl TryFrom for MultiDimTranslateDestination { - type Error = GdalError; - - fn try_from(path: PathBuf) -> Result { - Self::path(path) - } -} - -impl From 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>(path: P) -> Result { - 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. /// diff --git a/src/programs/raster/mod.rs b/src/programs/raster/mod.rs index b3a53e11f..30cd04da6 100644 --- a/src/programs/raster/mod.rs +++ b/src/programs/raster/mod.rs @@ -4,6 +4,6 @@ mod vrt; #[cfg(all(major_ge_3, minor_ge_1))] pub use mdimtranslate::{ - multi_dim_translate, MultiDimTranslateDestination, MultiDimTranslateOptions, + multi_dim_translate, MultiDimTranslateOptions, }; pub use vrt::*; diff --git a/src/programs/vector/mod.rs b/src/programs/vector/mod.rs new file mode 100644 index 000000000..82a6aca8b --- /dev/null +++ b/src/programs/vector/mod.rs @@ -0,0 +1,8 @@ +#[cfg(all(major_ge_3, minor_ge_1))] +mod vector_translate; + +#[cfg(all(major_ge_3, minor_ge_1))] +pub use vector_translate::{ + vector_translate, + VectorTranslateOptions +}; \ No newline at end of file diff --git a/src/programs/vector/vector_translate.rs b/src/programs/vector/vector_translate.rs new file mode 100644 index 000000000..f1f7ce2e9 --- /dev/null +++ b/src/programs/vector/vector_translate.rs @@ -0,0 +1,150 @@ +use std::borrow::Borrow; +use std::ffi::CString; +use std::ptr::{null, null_mut}; + +use crate::{ + errors::*, + utils::{_last_null_pointer_err}, + Dataset, +}; +use gdal_sys::{GDALVectorTranslate, GDALVectorTranslateOptions, GDALVectorTranslateOptionsFree}; +use libc::c_char; +use crate::programs::destination::DatasetDestination; + +/// Wraps a [GDALVectorTranslateOptions] object. +/// +/// [GDALVectorTranslateOptions]: https://gdal.org/api/gdal_utils.html#_CPPv426GDALVectorTranslateOptions +pub struct VectorTranslateOptions{ + c_options: *mut GDALVectorTranslateOptions +} + +impl VectorTranslateOptions{ + /// See [GDALVectorTranslateOptionsNew]. + /// + /// [GDALVectorTranslateOptionsNew]: https://gdal.org/api/gdal_utils.html#_CPPv429GDALVectorTranslateOptionsNewPPcP35GDALVectorTranslateOptionsForBinary + pub fn new>,I:IntoIterator>(args:I)->Result{ + let cstr_args = args + .into_iter() + .map(CString::new) + .collect::,_>>()?; + let mut c_args = cstr_args + .iter() + .map(|x| x.as_ptr() as *mut c_char) + .chain(std::iter::once(null_mut())) + .collect::>(); + + unsafe { + Ok(Self { + c_options: gdal_sys::GDALVectorTranslateOptionsNew(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 GDALVectorTranslateOptions { + self.c_options + } +} + +impl Drop for VectorTranslateOptions { + fn drop(&mut self) { + unsafe { + GDALVectorTranslateOptionsFree(self.c_options); + } + } +} + +impl TryFrom> for VectorTranslateOptions { + type Error = GdalError; + + fn try_from(value: Vec<&str>) -> Result { + VectorTranslateOptions::new(value) + } +} + + + +/// Converts simple features data between file formats. +/// +/// Wraps [GDALVectorTranslate]. +/// See the [program docs] for more details. +/// +/// [GDALVectorTranslate]: https://gdal.org/api/gdal_utils.html#_CPPv419GDALVectorTranslatePKc12GDALDatasetHiP12GDALDatasetHPK26GDALVectorTranslateOptionsPi +/// [program docs]: https://gdal.org/programs/ogr2ogr.html +/// +pub fn vector_translate>(src: &[D], dest: DatasetDestination, options: Option) ->Result { + _vector_translate( + &src + .iter() + .map(|x|x.borrow()) + .collect::>() + ,dest, + options + ) +} + +fn _vector_translate(datasets: &[&Dataset], mut dest: DatasetDestination,options:Option)->Result{ + + let (psz_dest_option, h_dst_ds) = match &dest { + DatasetDestination::Path(c_path) => (Some(c_path), null_mut()), + DatasetDestination::Dataset { dataset, .. } => (None, dataset.c_dataset()), + }; + + let psz_dest = psz_dest_option.map(|x| x.as_ptr()).unwrap_or_else(null); + + let c_options = options + .as_ref() + .map(|x| x.c_options as *const GDALVectorTranslateOptions) + .unwrap_or(null()); + + let dataset_out = unsafe { + // Get raw handles to the datasets + let mut datasets_raw: Vec = + datasets.iter().map(|x| x.c_dataset()).collect(); + + let data = GDALVectorTranslate( + psz_dest, + h_dst_ds, + // only 1 supported currently + 1, + datasets_raw.as_mut_ptr(), + c_options, + null_mut(), + ); + + // GDAL takes the ownership of `h_dst_ds` + dest.do_no_drop_dataset(); + + data + + }; + + if dataset_out.is_null() { + return Err(_last_null_pointer_err("GDALVectorTranslate")); + } + + let result = unsafe { Dataset::from_c_dataset(dataset_out) }; + + Ok(result) +} + + + + +#[cfg(test)] +mod tests { + use std::path::Path; + use crate::Dataset; + use crate::programs::vector::vector_translate::vector_translate; + + #[test] + fn test_vector_translate(){ + let path = "fixtures/roads.geojson"; + let dataset = &Dataset::open(Path::new(path)).unwrap(); + let out = "fixtures/roads.sql"; + let dest = out.try_into().unwrap(); + let dst = vector_translate(&[dataset], dest, None); + } +}