Skip to content
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 Wrapper for GDALVectorTranslate(ogr2ogr) #536

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/programs/destination.rs
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but maybe DestinationDataset sounds a little better?

Path(CString),
Dataset {
dataset: ManuallyDrop<Dataset>,
drop: bool,
},
}

impl TryFrom<&str> for DatasetDestination {
type Error = GdalError;

fn try_from(path: &str) -> crate::errors::Result<Self> {
Self::path(path)
}
}

impl TryFrom<&Path> for DatasetDestination {
type Error = GdalError;

fn try_from(path: &Path) -> crate::errors::Result<Self> {
Self::path(path)
}
}

impl TryFrom<PathBuf> for DatasetDestination {
type Error = GdalError;

fn try_from(path: PathBuf) -> crate::errors::Result<Self> {
Self::path(path)
}
}

impl From<Dataset> 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<P: AsRef<Path>>(path: P) -> crate::errors::Result<Self> {
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;
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: missing trailing newline.

2 changes: 2 additions & 0 deletions src/programs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Rust wrappers for the [GDAL Programs](https://gdal.org/programs/index.html)

pub mod raster;
pub mod destination;
mod vector;
79 changes: 4 additions & 75 deletions src/programs/raster/mdimtranslate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -80,81 +84,6 @@ impl TryFrom<Vec<&str>> for MultiDimTranslateOptions {
}
}

pub enum MultiDimTranslateDestination {
Path(CString),
Dataset {
dataset: ManuallyDrop<Dataset>,
drop: bool,
},
}

impl TryFrom<&str> for MultiDimTranslateDestination {
type Error = GdalError;

fn try_from(path: &str) -> Result<Self> {
Self::path(path)
}
}

impl TryFrom<&Path> for MultiDimTranslateDestination {
type Error = GdalError;

fn try_from(path: &Path) -> Result<Self> {
Self::path(path)
}
}

impl TryFrom<PathBuf> for MultiDimTranslateDestination {
type Error = GdalError;

fn try_from(path: PathBuf) -> Result<Self> {
Self::path(path)
}
}

impl From<Dataset> 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<P: AsRef<Path>>(path: P) -> Result<Self> {
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.
///
Expand Down
2 changes: 1 addition & 1 deletion src/programs/raster/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
8 changes: 8 additions & 0 deletions src/programs/vector/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[cfg(all(major_ge_3, minor_ge_1))]
mod vector_translate;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is available since 2.1, not 3.1, isn't it?


#[cfg(all(major_ge_3, minor_ge_1))]
pub use vector_translate::{
vector_translate,
VectorTranslateOptions
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing trailing newline.

150 changes: 150 additions & 0 deletions src/programs/vector/vector_translate.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be pub use, otherwise you can't call vector_translate.


/// 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<S:Into<Vec<u8>>,I:IntoIterator<Item=S>>(args:I)->Result<Self>{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please run cargo fmt.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you mirrored multi_dim_translate, but https://docs.rs/gdal/latest/gdal/cpl/struct.CslStringList.html might be nicer to use here, especially since fn new<S:Into<Vec<u8>>,I:IntoIterator<Item=S>>(args:I)->Result<Self> can look a little scary.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It‘s excellent advice to use CslString instead of fn new<S:Into<Vec<u8>>,I:IntoIterator<Item=S>>(args:I)->Result<Self> , and I will make some modifications to this section.

let cstr_args = args
.into_iter()
.map(CString::new)
.collect::<std::result::Result<Vec<_>,_>>()?;
let mut c_args = cstr_args
.iter()
.map(|x| x.as_ptr() as *mut c_char)
.chain(std::iter::once(null_mut()))
.collect::<Vec<_>>();

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<Vec<&str>> for VectorTranslateOptions {
type Error = GdalError;

fn try_from(value: Vec<&str>) -> Result<Self> {
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<D:Borrow<Dataset>>(src: &[D], dest: DatasetDestination, options: Option<VectorTranslateOptions>) ->Result<Dataset> {
_vector_translate(
&src
.iter()
.map(|x|x.borrow())
.collect::<Vec<&Dataset>>()
,dest,
options
)
}

fn _vector_translate(datasets: &[&Dataset], mut dest: DatasetDestination,options:Option<VectorTranslateOptions>)->Result<Dataset>{

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<gdal_sys::GDALDatasetH> =
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);
}
}
Loading