Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into add_OCTTransformB…
Browse files Browse the repository at this point in the history
…ounds
brendan-ward committed May 20, 2022
2 parents 9061914 + 16230bd commit 1ce3a5e
Showing 19 changed files with 1,273 additions and 36 deletions.
18 changes: 17 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@

- https://github.com/georust/gdal/pull/267

- **Breaking**: Rename `Driver::get` to `Driver::get_by_name`, add `Driver::get(usize)` and `Driver::count`

- <https://github.com/georust/gdal/pull/251>

- Add `programs::raster::build_vrt`
- Add `GeoTransformEx` extension trait with `apply` and `invert`

@@ -23,6 +27,18 @@

- <https://github.com/georust/gdal/pull/271>

- Add `gdal::vsi::read_dir` function.

- <https://github.com/georust/gdal/pull/257>

- Add a `ColorTable` struct and `RasterBand::color_table` method

- <https://github.com/georust/gdal/pull/246>

- Add support for MDArray API

- <https://github.com/georust/gdal/pull/273>

- Add `gdal::srs::CoordTransform::transform_bounds` as wrapper for `OCTTransformBounds` for GDAL 3.4

- <https://github.com/georust/gdal/pull/272>
@@ -84,7 +100,7 @@
- <https://github.com/georust/gdal/pull/193>

```rust
let driver = Driver::get("GTiff").unwrap();
let driver = Driver::get_by_name("GTiff").unwrap();
let options = &[
RasterCreationOption {
key: "COMPRESS",
2 changes: 1 addition & 1 deletion examples/metadata.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ fn main() {
use gdal::{Dataset, Metadata};
use std::path::Path;

let driver = gdal::Driver::get("mem").unwrap();
let driver = gdal::Driver::get_by_name("mem").unwrap();
println!("driver description: {:?}", driver.description());

let path = Path::new("./fixtures/tinymarble.png");
2 changes: 1 addition & 1 deletion examples/read_write_ogr.rs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ fn run() -> Result<()> {
// Create a new dataset:
let path = std::env::temp_dir().join("abcde.shp");
let _ = fs::remove_file(&path);
let drv = Driver::get("ESRI Shapefile")?;
let drv = Driver::get_by_name("ESRI Shapefile")?;
let mut ds = drv.create_vector_only(path.to_str().unwrap())?;
let lyr = ds.create_layer(Default::default())?;

2 changes: 1 addition & 1 deletion examples/read_write_ogr_datetime.rs
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ fn run() -> gdal::errors::Result<()> {
// Create a new dataset:
let path = std::env::temp_dir().join("later.geojson");
let _ = std::fs::remove_file(&path);
let drv = Driver::get("GeoJSON")?;
let drv = Driver::get_by_name("GeoJSON")?;
let mut ds = drv.create_vector_only(path.to_str().unwrap())?;
let lyr = ds.create_layer(Default::default())?;

4 changes: 2 additions & 2 deletions examples/write_ogr.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ use std::fs;
fn example_1() -> Result<()> {
let path = std::env::temp_dir().join("output1.geojson");
let _ = fs::remove_file(&path);
let drv = Driver::get("GeoJSON")?;
let drv = Driver::get_by_name("GeoJSON")?;
let mut ds = drv.create_vector_only(path.to_str().unwrap())?;

let lyr = ds.create_layer(Default::default())?;
@@ -52,7 +52,7 @@ fn example_1() -> Result<()> {
fn example_2() -> Result<()> {
let path = std::env::temp_dir().join("output2.geojson");
let _ = fs::remove_file(&path);
let driver = Driver::get("GeoJSON")?;
let driver = Driver::get_by_name("GeoJSON")?;
let mut ds = driver.create_vector_only(path.to_str().unwrap())?;
let mut layer = ds.create_layer(Default::default())?;

Binary file added fixtures/alldatatypes.nc
Binary file not shown.
Binary file added fixtures/byte_no_cf.nc
Binary file not shown.
Binary file added fixtures/cf_nasa_4326.nc
Binary file not shown.
Binary file added fixtures/test_color_table.tif
Binary file not shown.
Binary file added fixtures/test_vsi_read_dir.zip
Binary file not shown.
27 changes: 24 additions & 3 deletions src/dataset.rs
Original file line number Diff line number Diff line change
@@ -17,12 +17,16 @@ use crate::{
gdal_major_object::MajorObject, raster::RasterBand, spatial_ref::SpatialRef, vector::Layer,
Driver, Metadata,
};

use gdal_sys::OGRGeometryH;
use gdal_sys::{
self, CPLErr, GDALAccess, GDALDatasetH, GDALMajorObjectH, OGRErr, OGRLayerH, OGRwkbGeometryType,
};
use libc::{c_double, c_int, c_uint};

#[cfg(all(major_ge_3, minor_ge_1))]
use crate::raster::Group;

use bitflags::bitflags;

/// A 2-D affine transform mapping pixel coordiates to world
@@ -415,6 +419,23 @@ impl Dataset {
}
}

/// Opens the root group of a multi-dim GDAL raster
///
/// # Note
/// You must have opened the dataset with the `GdalOpenFlags::GDAL_OF_MULTIDIM_RASTER`
/// flag in order for it to work.
///
#[cfg(all(major_ge_3, minor_ge_1))]
pub fn root_group(&self) -> Result<Group> {
unsafe {
let c_group = gdal_sys::GDALDatasetGetRootGroup(self.c_dataset());
if c_group.is_null() {
return Err(_last_null_pointer_err("GDALDatasetGetRootGroup"));
}
Ok(Group::from_c_group(self, c_group))
}
}

/// Builds overviews for the current `Dataset`. See [`GDALBuildOverviews`].
///
/// # Arguments
@@ -531,7 +552,7 @@ impl Dataset {
///
/// ```
/// # use gdal::Driver;
/// # let driver = Driver::get("GPKG").unwrap();
/// # let driver = Driver::get_by_name("GPKG").unwrap();
/// # let mut dataset = driver.create_vector_only("/vsimem/example.gpkg").unwrap();
/// let blank_layer = dataset.create_layer(Default::default()).unwrap();
/// ```
@@ -541,7 +562,7 @@ impl Dataset {
/// ```
/// # use gdal::{Driver, LayerOptions};
/// # use gdal::spatial_ref::SpatialRef;
/// # let driver = Driver::get("GPKG").unwrap();
/// # let driver = Driver::get_by_name("GPKG").unwrap();
/// # let mut dataset = driver.create_vector_only("/vsimem/example.gpkg").unwrap();
/// let roads = dataset.create_layer(LayerOptions {
/// name: "roads",
@@ -699,7 +720,7 @@ impl Dataset {
/// }
/// #
/// # fn main() -> gdal::errors::Result<()> {
/// # let driver = gdal::Driver::get("SQLite")?;
/// # let driver = gdal::Driver::get_by_name("SQLite")?;
/// # let mut dataset = driver.create_vector_only(":memory:")?;
/// # create_point_grid(&mut dataset)?;
/// # assert_eq!(dataset.layer(0)?.features().count(), 10000);
21 changes: 20 additions & 1 deletion src/driver.rs
Original file line number Diff line number Diff line change
@@ -30,7 +30,8 @@ pub struct Driver {
}

impl Driver {
pub fn get(name: &str) -> Result<Driver> {
/// Returns the driver with the given short name.
pub fn get_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()) };
@@ -40,6 +41,24 @@ impl Driver {
Ok(Driver { c_driver })
}

/// Returns the driver with the given index, which must be less than the value returned by
/// `Driver::count()`.
pub fn get(index: usize) -> Result<Driver> {
_register_drivers();
let c_driver = unsafe { gdal_sys::GDALGetDriver(index.try_into().unwrap()) };
if c_driver.is_null() {
return Err(_last_null_pointer_err("GDALGetDriver"));
}
Ok(Driver { c_driver })
}

/// Returns the number of registered drivers.
pub fn count() -> usize {
_register_drivers();
let count = unsafe { gdal_sys::GDALGetDriverCount() };
count.try_into().unwrap()
}

/// Creates a new Driver object by wrapping a C pointer
///
/// # Safety
904 changes: 904 additions & 0 deletions src/raster/mdarray.rs

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion src/raster/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
//! GDAL Raster Data
#[cfg(all(major_ge_3, minor_ge_1))]
mod mdarray;
mod rasterband;
mod rasterize;
mod types;
mod warp;

pub use rasterband::{Buffer, ByteBuffer, ColorInterpretation, RasterBand, ResampleAlg};
#[cfg(all(major_ge_3, minor_ge_1))]
pub use mdarray::{Group, MDArray};
pub use rasterband::{
Buffer, ByteBuffer, CmykEntry, ColorEntry, ColorInterpretation, ColorTable, GrayEntry,
HlsEntry, PaletteInterpretation, RasterBand, ResampleAlg, RgbaEntry,
};
pub use rasterize::{rasterize, BurnSource, MergeAlgorithm, OptimizeMode, RasterizeOptions};
pub use types::{GDALDataType, GdalType};
pub use warp::reproject;
155 changes: 153 additions & 2 deletions src/raster/rasterband.rs
Original file line number Diff line number Diff line change
@@ -4,11 +4,12 @@ use crate::metadata::Metadata;
use crate::raster::{GDALDataType, GdalType};
use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string};
use gdal_sys::{
self, CPLErr, GDALColorInterp, GDALMajorObjectH, GDALRWFlag, GDALRasterBandH,
GDALRasterIOExtraArg,
self, CPLErr, GDALColorEntry, GDALColorInterp, GDALColorTableH, GDALMajorObjectH,
GDALPaletteInterp, GDALRWFlag, GDALRasterBandH, GDALRasterIOExtraArg,
};
use libc::c_int;
use std::ffi::CString;
use std::marker::PhantomData;

#[cfg(feature = "ndarray")]
use ndarray::Array2;
@@ -421,6 +422,15 @@ impl<'a> RasterBand<'a> {
Ok(())
}

/// Get the color table for this band if it has one.
pub fn color_table(&self) -> Option<ColorTable> {
let c_color_table = unsafe { gdal_sys::GDALGetRasterColorTable(self.c_rasterband) };
if c_color_table.is_null() {
return None;
}
Some(ColorTable::from_c_color_table(c_color_table))
}

/// Returns the scale of this band if set.
pub fn scale(&self) -> Option<f64> {
let mut pb_success = 1;
@@ -612,3 +622,144 @@ impl ColorInterpretation {
_string(rv)
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PaletteInterpretation {
Gray,
Rgba,
Cmyk,
Hls,
}

impl PaletteInterpretation {
fn from_c_int(palette_interpretation: GDALPaletteInterp::Type) -> Self {
match palette_interpretation {
GDALPaletteInterp::GPI_Gray => Self::Gray,
GDALPaletteInterp::GPI_RGB => Self::Rgba,
GDALPaletteInterp::GPI_CMYK => Self::Cmyk,
GDALPaletteInterp::GPI_HLS => Self::Hls,
_ => unreachable!("GDAL has implemented a new type of `GDALPaletteInterp`"),
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct GrayEntry {
pub g: i16,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct RgbaEntry {
pub r: i16,
pub g: i16,
pub b: i16,
pub a: i16,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CmykEntry {
pub c: i16,
pub m: i16,
pub y: i16,
pub k: i16,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HlsEntry {
pub h: i16,
pub l: i16,
pub s: i16,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ColorEntry {
Gray(GrayEntry),
Rgba(RgbaEntry),
Cmyk(CmykEntry),
Hls(HlsEntry),
}

/// Color table for raster bands that use the PaletteIndex color interpretation.
///
/// This object carries the lifetime of the raster band that
/// contains it. This is necessary to prevent the raster band
/// from being dropped before the color table.
pub struct ColorTable<'a> {
palette_interpretation: PaletteInterpretation,
c_color_table: GDALColorTableH,
phantom_raster_band: PhantomData<&'a RasterBand<'a>>,
}

impl<'a> ColorTable<'a> {
fn from_c_color_table(c_color_table: GDALColorTableH) -> Self {
let interp_index = unsafe { gdal_sys::GDALGetPaletteInterpretation(c_color_table) };
ColorTable {
palette_interpretation: PaletteInterpretation::from_c_int(interp_index),
c_color_table,
phantom_raster_band: PhantomData,
}
}

/// How the values of this color table are interpreted.
pub fn palette_interpretation(&self) -> PaletteInterpretation {
self.palette_interpretation
}

/// Get the number of color entries in this color table.
pub fn entry_count(&self) -> usize {
unsafe { gdal_sys::GDALGetColorEntryCount(self.c_color_table) as usize }
}

/// Get a color entry.
pub fn entry(&self, index: usize) -> Option<ColorEntry> {
let color_entry = unsafe {
let c_color_entry = gdal_sys::GDALGetColorEntry(self.c_color_table, index as i32);
if c_color_entry.is_null() {
return None;
}
*c_color_entry
};
match self.palette_interpretation {
PaletteInterpretation::Gray => Some(ColorEntry::Gray(GrayEntry { g: color_entry.c1 })),
PaletteInterpretation::Rgba => Some(ColorEntry::Rgba(RgbaEntry {
r: color_entry.c1,
g: color_entry.c2,
b: color_entry.c3,
a: color_entry.c4,
})),
PaletteInterpretation::Cmyk => Some(ColorEntry::Cmyk(CmykEntry {
c: color_entry.c1,
m: color_entry.c2,
y: color_entry.c3,
k: color_entry.c4,
})),
PaletteInterpretation::Hls => Some(ColorEntry::Hls(HlsEntry {
h: color_entry.c1,
l: color_entry.c2,
s: color_entry.c3,
})),
}
}

/// Get a color entry as RGB.
pub fn entry_as_rgb(&self, index: usize) -> Option<RgbaEntry> {
let mut color_entry = GDALColorEntry {
c1: 0,
c2: 0,
c3: 0,
c4: 0,
};
if unsafe {
gdal_sys::GDALGetColorEntryAsRGB(self.c_color_table, index as i32, &mut color_entry)
} == 0
{
return None;
}
Some(RgbaEntry {
r: color_entry.c1,
g: color_entry.c2,
b: color_entry.c3,
a: color_entry.c4,
})
}
}
76 changes: 58 additions & 18 deletions src/raster/tests.rs
Original file line number Diff line number Diff line change
@@ -121,7 +121,7 @@ fn test_read_raster_with_average_resample() {

#[test]
fn test_write_raster() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver.create("", 20, 10, 1).unwrap();

// create a 2x1 raster
@@ -153,7 +153,7 @@ fn test_rename_remove_raster() {
let mem_file_path_a = Path::new("/vsimem/030bd1d1-8955-4604-8e37-177dade13863");
let mem_file_path_b = Path::new("/vsimem/c7bfce32-2474-48fa-a907-2af95f83c824");

let driver = Driver::get("GTiff").unwrap();
let driver = Driver::get_by_name("GTiff").unwrap();

dataset.create_copy(&driver, &mem_file_path_a, &[]).unwrap();

@@ -179,7 +179,7 @@ fn test_get_dataset_driver() {

#[test]
fn test_get_description() {
let driver = Driver::get("mem").unwrap();
let driver = Driver::get_by_name("mem").unwrap();
assert_eq!(driver.description().unwrap(), "MEM".to_string());
}

@@ -230,7 +230,7 @@ fn test_get_metadata_item() {

#[test]
fn test_set_metadata_item() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let mut dataset = driver.create("", 1, 1, 1).unwrap();

let key = "Test_Key";
@@ -245,7 +245,7 @@ fn test_set_metadata_item() {

#[test]
fn test_set_description() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver.create("", 1, 1, 1).unwrap();
let mut band = dataset.rasterband(1).unwrap();

@@ -258,7 +258,7 @@ fn test_set_description() {

#[test]
fn test_create() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver.create("", 10, 20, 3).unwrap();
assert_eq!(dataset.raster_size(), (10, 20));
assert_eq!(dataset.raster_count(), 3);
@@ -267,7 +267,7 @@ fn test_create() {

#[test]
fn test_create_with_band_type() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver
.create_with_band_type::<f32, _>("", 10, 20, 3)
.unwrap();
@@ -280,7 +280,7 @@ fn test_create_with_band_type() {

#[test]
fn test_create_with_band_type_with_options() {
let driver = Driver::get("GTiff").unwrap();
let driver = Driver::get_by_name("GTiff").unwrap();
let options = [
RasterCreationOption {
key: "TILED",
@@ -327,7 +327,7 @@ fn test_create_with_band_type_with_options() {

#[test]
fn test_create_copy() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap();
let copy = dataset.create_copy(&driver, "", &[]).unwrap();
assert_eq!(copy.raster_size(), (100, 50));
@@ -347,7 +347,7 @@ fn test_create_copy_with_options() {

let copy = dataset
.create_copy(
&Driver::get("GTiff").unwrap(),
&Driver::get_by_name("GTiff").unwrap(),
mem_file_path,
&[
RasterCreationOption {
@@ -376,7 +376,7 @@ fn test_create_copy_with_options() {
#[test]
#[allow(clippy::float_cmp)]
fn test_geo_transform() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let mut dataset = driver.create("", 20, 10, 1).unwrap();
let transform = [0., 1., 0., 0., 0., 1.];
assert!(dataset.set_geo_transform(&transform).is_ok());
@@ -385,10 +385,10 @@ fn test_geo_transform() {

#[test]
fn test_get_driver_by_name() {
let missing_driver = Driver::get("wtf");
let missing_driver = Driver::get_by_name("wtf");
assert!(missing_driver.is_err());

let ok_driver = Driver::get("GTiff");
let ok_driver = Driver::get_by_name("GTiff");
assert!(ok_driver.is_ok());
let driver = ok_driver.unwrap();
assert_eq!(driver.short_name(), "GTiff");
@@ -486,15 +486,15 @@ fn test_read_block_data() {

#[test]
fn test_get_band_type() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver.create("", 20, 10, 1).unwrap();
let rb = dataset.rasterband(1).unwrap();
assert_eq!(rb.band_type(), GDALDataType::GDT_Byte);
}

#[test]
fn test_get_rasterband() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver.create("", 20, 10, 1).unwrap();
let rasterband = dataset.rasterband(1);
assert!(rasterband.is_ok());
@@ -518,7 +518,7 @@ fn test_get_no_data_value() {
#[test]
#[allow(clippy::float_cmp)]
fn test_set_no_data_value() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver.create("", 20, 10, 1).unwrap();
let mut rasterband = dataset.rasterband(1).unwrap();
assert_eq!(rasterband.no_data_value(), None);
@@ -648,7 +648,7 @@ fn test_get_rasterband_color_interp() {

#[test]
fn test_set_rasterband_color_interp() {
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let dataset = driver.create("", 1, 1, 1).unwrap();
let mut rasterband = dataset.rasterband(1).unwrap();
rasterband
@@ -678,7 +678,7 @@ fn test_rasterize() {

let rows = 5;
let cols = 5;
let driver = Driver::get("MEM").unwrap();
let driver = Driver::get_by_name("MEM").unwrap();
let mut dataset = driver.create("", rows, cols, 1).unwrap();

let bands = [1];
@@ -706,3 +706,43 @@ fn test_rasterband_unit() {

assert_eq!(rasterband.unit(), "m".to_string());
}

#[test]
fn test_color_table() {
use crate::raster::rasterband::{ColorEntry, PaletteInterpretation};

// Raster containing one band.
let dataset = Dataset::open(fixture!("test_color_table.tif")).expect("open failure");
assert_eq!(dataset.raster_count(), 1);

// Band is PaletteIndex.
let band = dataset.rasterband(1).expect("rasterband failure");
assert_eq!(
band.color_interpretation(),
ColorInterpretation::PaletteIndex
);

// Color table is RGB.
let color_table = band.color_table().unwrap();
assert_eq!(
color_table.palette_interpretation(),
PaletteInterpretation::Rgba
);

// Color table has 256 entries.
let entry_count = color_table.entry_count();
assert_eq!(entry_count, 256);

// Check that entry and entry_as_rgb are the same.
for index in 0..entry_count {
if let ColorEntry::Rgba(entry) = color_table.entry(index).unwrap() {
let rgb_entry = color_table.entry_as_rgb(index).unwrap();
assert_eq!(entry.r, rgb_entry.r);
assert_eq!(entry.g, rgb_entry.g);
assert_eq!(entry.b, rgb_entry.b);
assert_eq!(entry.a, rgb_entry.a);
} else {
panic!();
}
}
}
22 changes: 19 additions & 3 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use gdal_sys::{self, CPLErr};
use libc::c_char;
use std::ffi::{CStr, CString};
use std::path::Path;
use std::path::{Path, PathBuf};

use crate::errors::*;

@@ -11,7 +11,23 @@ pub fn _string(raw_ptr: *const c_char) -> String {
}

pub fn _string_array(raw_ptr: *mut *mut c_char) -> Vec<String> {
let mut ret_val: Vec<String> = vec![];
_convert_raw_ptr_array(raw_ptr, _string)
}

pub fn _pathbuf(raw_ptr: *const c_char) -> PathBuf {
let c_str = unsafe { CStr::from_ptr(raw_ptr) };
c_str.to_string_lossy().into_owned().into()
}

pub fn _pathbuf_array(raw_ptr: *mut *mut c_char) -> Vec<PathBuf> {
_convert_raw_ptr_array(raw_ptr, _pathbuf)
}

fn _convert_raw_ptr_array<F, R>(raw_ptr: *mut *mut c_char, convert: F) -> Vec<R>
where
F: Fn(*const c_char) -> R,
{
let mut ret_val = Vec::new();
let mut i = 0;
unsafe {
loop {
@@ -23,7 +39,7 @@ pub fn _string_array(raw_ptr: *mut *mut c_char) -> Vec<String> {
if next.is_null() {
break;
}
let value = _string(next);
let value = convert(next);
i += 1;
ret_val.push(value);
}
2 changes: 1 addition & 1 deletion src/vector/vector_tests/mod.rs
Original file line number Diff line number Diff line change
@@ -672,7 +672,7 @@ mod tests {
use std::fs;

{
let driver = Driver::get("GeoJSON").unwrap();
let driver = Driver::get_by_name("GeoJSON").unwrap();
let mut ds = driver
.create_vector_only(&fixture!("output.geojson"))
.unwrap();
65 changes: 64 additions & 1 deletion src/vsi.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,31 @@ use std::path::{Path, PathBuf};
use gdal_sys::{VSIFCloseL, VSIFileFromMemBuffer, VSIFree, VSIGetMemFileBuffer, VSIUnlink};

use crate::errors::{GdalError, Result};
use crate::utils::{_last_null_pointer_err, _path_to_c_string};
use crate::utils::{_last_null_pointer_err, _path_to_c_string, _pathbuf_array};

/// Read the file names from a virtual file system with optional recursion.
pub fn read_dir<P: AsRef<Path>>(path: P, recursive: bool) -> Result<Vec<PathBuf>> {
_read_dir(path.as_ref(), recursive)
}

fn _read_dir(path: &Path, recursive: bool) -> Result<Vec<PathBuf>> {
let path = _path_to_c_string(path)?;
let data = if recursive {
let data = unsafe { gdal_sys::VSIReadDirRecursive(path.as_ptr()) };
if data.is_null() {
return Err(_last_null_pointer_err("VSIReadDirRecursive"));
}
data
} else {
let data = unsafe { gdal_sys::VSIReadDir(path.as_ptr()) };
if data.is_null() {
return Err(_last_null_pointer_err("VSIReadDir"));
}
data
};

Ok(_pathbuf_array(data))
}

/// Creates a new VSIMemFile from a given buffer.
pub fn create_mem_file<P: AsRef<Path>>(file_name: P, data: Vec<u8>) -> Result<()> {
@@ -277,4 +301,43 @@ mod tests {
})
);
}

#[test]
fn test_vsi_read_dir() {
use std::path::Path;
let zip_path = Path::new(file!())
.parent()
.unwrap()
.parent()
.unwrap()
.join("fixtures")
.join("test_vsi_read_dir.zip");

// Concatenate "/vsizip/" prefix.
let path = ["/vsizip/", zip_path.to_str().unwrap()].concat();

// Read without recursion.
let expected = [
Path::new("folder"),
Path::new("File 1.txt"),
Path::new("File 2.txt"),
Path::new("File 3.txt"),
];
let files = read_dir(path.as_str(), false).unwrap();
assert_eq!(files, expected);

// Read with recursion.
let expected = [
Path::new("folder/"),
Path::new("folder/File 4.txt"),
Path::new("File 1.txt"),
Path::new("File 2.txt"),
Path::new("File 3.txt"),
];
let files = read_dir(path.as_str(), true).unwrap();
assert_eq!(files, expected);

// Attempting to read without VSI prefix returns error.
assert!(read_dir(zip_path, false).is_err());
}
}

0 comments on commit 1ce3a5e

Please sign in to comment.