From b0baa92706198a6fbe01de0335a16e2f48ee9d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dr=C3=B6nner?= Date: Wed, 15 Jun 2016 21:04:20 +0200 Subject: [PATCH] Added rasterband.rs and moved all band functions from dataset.rs to rasterband.rs. Added more rasterband functions and tests. --- examples/rasterband.rs | 25 ++++++ src/raster/dataset.rs | 122 ++++++++--------------------- src/raster/gdal.rs | 16 ++-- src/raster/mod.rs | 6 +- src/raster/rasterband.rs | 160 +++++++++++++++++++++++++++++++++++++++ src/raster/tests.rs | 51 +++++++++++-- 6 files changed, 276 insertions(+), 104 deletions(-) create mode 100644 examples/rasterband.rs create mode 100644 src/raster/rasterband.rs diff --git a/examples/rasterband.rs b/examples/rasterband.rs new file mode 100644 index 00000000..16671d90 --- /dev/null +++ b/examples/rasterband.rs @@ -0,0 +1,25 @@ +extern crate gdal; + +use std::path::Path; +use gdal::raster::{Dataset, RasterBand}; +use gdal::metadata::Metadata; + +fn main() { + + let path = Path::new("./fixtures/tinymarble.png"); + let dataset = Dataset::open(path).unwrap(); + println!("dataset description: {:?}", dataset.get_description()); + + let rasterband: RasterBand = dataset.get_rasterband(1).unwrap(); + println!("rasterband description: {:?}", rasterband.get_description()); + println!("rasterband no_data_value: {:?}", rasterband.get_no_data_value()); + println!("rasterband type: {:?}", rasterband.get_band_type()); + println!("rasterband scale: {:?}", rasterband.get_scale()); + println!("rasterband offset: {:?}", rasterband.get_offset()); + let rv = rasterband.read_as::( + (20, 30), + (2, 3), + (2, 3) + ); + println!("{:?}", rv.data); +} diff --git a/src/raster/dataset.rs b/src/raster/dataset.rs index 8bb2f405..9bd2dcc9 100644 --- a/src/raster/dataset.rs +++ b/src/raster/dataset.rs @@ -2,9 +2,9 @@ use libc::{c_int, c_double, c_void}; use std::ffi::CString; use std::path::Path; use utils::_string; -use raster::{gdal, Driver}; +use raster::{gdal, Driver, RasterBand}; use raster::driver::_register_drivers; -use raster::gdal_enums::{GDALRWFlag, GDALAccess, GDALDataType}; +use raster::gdal_enums::{GDALAccess, GDALDataType}; use raster::types::GdalType; use gdal_major_object::MajorObject; use metadata::Metadata; @@ -22,8 +22,7 @@ impl MajorObject for Dataset { } impl Metadata for Dataset {} - - + impl Drop for Dataset { fn drop(&mut self) { unsafe { gdal::GDALClose(self.c_dataset); } @@ -49,11 +48,22 @@ impl Dataset { pub unsafe fn _c_ptr(&self) -> *const c_void { return self.c_dataset; - } - - pub fn size(&self) -> (isize, isize) { - let size_x = unsafe { gdal::GDALGetRasterXSize(self.c_dataset) } as isize; - let size_y = unsafe { gdal::GDALGetRasterYSize(self.c_dataset) } as isize; + } + + + pub fn get_rasterband<'a>(&'a self, band_index: isize) -> Option> { + unsafe { + let c_band = gdal::GDALGetRasterBand(self.c_dataset, band_index as c_int); + if c_band.is_null() { + return None; + } + Some(RasterBand::_with_c_ptr(c_band, self)) + } + } + + pub fn size(&self) -> (usize, usize) { + let size_x = unsafe { gdal::GDALGetRasterXSize(self.c_dataset) } as usize; + let size_y = unsafe { gdal::GDALGetRasterYSize(self.c_dataset) } as usize; return (size_x, size_y); } @@ -122,7 +132,11 @@ impl Dataset { true => None, false => Some(Dataset{c_dataset: c_dataset}), }; - } + } + + pub fn get_band_type(&self, band_index: isize) -> Option { + self.get_rasterband(band_index).map(|band| band.get_band_type()) + } /// Read a 'Buffer' from a 'Dataset'. /// # Arguments @@ -135,7 +149,7 @@ impl Dataset { window: (isize, isize), window_size: (usize, usize), size: (usize, usize) - ) -> ByteBuffer + ) -> Option { self.read_raster_as::( band_index, @@ -151,23 +165,10 @@ impl Dataset { pub fn read_full_raster_as( &self, band_index: isize, - ) -> Buffer + ) -> Option> { - let size_x; - let size_y; - - unsafe{ - size_x = gdal::GDALGetRasterXSize(self.c_dataset) as usize; - size_y = gdal::GDALGetRasterYSize(self.c_dataset) as usize; - } - - self.read_raster_as::( - band_index, - (0, 0), - (size_x, size_y), - (size_y, size_y) - ) - } + self.get_rasterband(band_index).map(|band| band.read_band_as()) + } /// Read a 'Buffer' from a 'Dataset'. T implements 'GdalType' /// # Arguments @@ -181,34 +182,9 @@ impl Dataset { window: (isize, isize), window_size: (usize, usize), size: (usize, usize), - ) -> Buffer + ) -> Option> { - let pixels = (size.0 * size.1) as usize; - let mut data: Vec = Vec::with_capacity(pixels); - //let no_data: - unsafe { - let c_band = gdal::GDALGetRasterBand(self.c_dataset, band_index as c_int); - let rv = gdal::GDALRasterIO( - c_band, - GDALRWFlag::GF_Read, - window.0 as c_int, - window.1 as c_int, - window_size.0 as c_int, - window_size.1 as c_int, - data.as_mut_ptr() as *const c_void, - size.0 as c_int, - size.1 as c_int, - T::gdal_type(), - 0, - 0 - ) as isize; - assert!(rv == 0); - data.set_len(pixels); - }; - Buffer{ - size: size, - data: data, - } + self.get_rasterband(band_index).map(|band| band.read_as(window, window_size, size)) } /// Write a 'Buffer' into a 'Dataset'. @@ -223,41 +199,9 @@ impl Dataset { window_size: (usize, usize), buffer: Buffer ) { - assert_eq!(buffer.data.len(), buffer.size.0 * buffer.size.1); - unsafe { - let c_band = gdal::GDALGetRasterBand(self.c_dataset, band_index as c_int); - let rv = gdal::GDALRasterIO( - c_band, - GDALRWFlag::GF_Write, - window.0 as c_int, - window.1 as c_int, - window_size.0 as c_int, - window_size.1 as c_int, - buffer.data.as_ptr() as *const c_void, - buffer.size.0 as c_int, - buffer.size.1 as c_int, - T::gdal_type(), - 0, - 0 - ) as isize; - assert!(rv == 0); - }; - } - - - pub fn get_band_type(&self, band_index: isize) -> Option { - - let band_count = self.count(); - if band_index < 1 || band_count < band_index { - return None - } - - let gdal_type: c_int; - unsafe{ - gdal_type = gdal::GDALGetRasterDataType(gdal::GDALGetRasterBand(self.c_dataset, band_index as c_int)); - } - Some(GDALDataType::from_c_int(gdal_type)) + self.get_rasterband(band_index).expect("Invalid RasterBand").write(window, window_size, buffer) } + } pub struct Buffer { @@ -271,4 +215,4 @@ impl Buffer { } } -pub type ByteBuffer = Buffer; +pub type ByteBuffer = Buffer; diff --git a/src/raster/gdal.rs b/src/raster/gdal.rs index 0dfbab43..f9893eed 100644 --- a/src/raster/gdal.rs +++ b/src/raster/gdal.rs @@ -3,6 +3,7 @@ use super::gdal_enums::*; #[link(name="gdal")] extern { + // driver pub fn GDALAllRegister(); pub fn GDALGetDriverByName(pszName: *const c_char) -> *const c_void; pub fn GDALGetDriverShortName(hDriver: *const c_void) -> *const c_char; @@ -26,17 +27,22 @@ extern { pProgressData: *const c_void ) -> *const c_void; pub fn GDALOpen(pszFilename: *const c_char, eAccess: GDALAccess) -> *const c_void; + // dataset pub fn GDALClose(hDS: *const c_void); pub fn GDALGetDatasetDriver(hDataset: *const c_void) -> *const c_void; pub fn GDALGetRasterXSize(hDataset: *const c_void) -> c_int; pub fn GDALGetRasterYSize(hDataset: *const c_void) -> c_int; pub fn GDALGetRasterCount(hDataset: *const c_void) -> c_int; + pub fn GDALGetProjectionRef(hDataset: *const c_void) -> *const c_char; + pub fn GDALSetProjection(hDataset: *const c_void, pszProjection: *const c_char) -> c_int; + pub fn GDALSetGeoTransform(hDataset: *const c_void, padfTransform: *const c_double) -> c_int; + pub fn GDALGetGeoTransform(hDataset: *const c_void, padfTransform: *mut c_double) -> c_int; + pub fn GDALGetRasterBand(hDataset: *const c_void, nBandId: c_int) -> *const c_void; + // band pub fn GDALGetRasterDataType(hBand: *const c_void) -> c_int; - pub fn GDALGetProjectionRef(hDS: *const c_void) -> *const c_char; - pub fn GDALSetProjection(hDS: *const c_void, pszProjection: *const c_char) -> c_int; - pub fn GDALSetGeoTransform(hDS: *const c_void, padfTransform: *const c_double) -> c_int; - pub fn GDALGetGeoTransform(hDS: *const c_void, padfTransform: *mut c_double) -> c_int; - pub fn GDALGetRasterBand(hDS: *const c_void, nBandId: c_int) -> *const c_void; + pub fn GDALGetRasterNoDataValue(hBand: *const c_void, pbSuccess: *mut c_int) -> c_double; + pub fn GDALGetRasterOffset(hBand: *const c_void, pbSuccess: *mut c_int) -> c_double; + pub fn GDALGetRasterScale(hBand: *const c_void, pbSuccess: *mut c_int) -> c_double; pub fn GDALRasterIO( hBand: *const c_void, eRWFlag: GDALRWFlag, diff --git a/src/raster/mod.rs b/src/raster/mod.rs index 8dcba1c6..30a8adf0 100644 --- a/src/raster/mod.rs +++ b/src/raster/mod.rs @@ -2,14 +2,16 @@ pub use raster::dataset::{Dataset, Buffer, ByteBuffer}; pub use raster::driver::Driver; -pub use raster::warp::reproject; +pub use raster::warp::reproject; +pub use raster::rasterband::{RasterBand}; mod gdal; mod types; mod gdal_enums; pub mod dataset; pub mod driver; -pub mod warp; +pub mod warp; +pub mod rasterband; #[cfg(test)] mod tests; diff --git a/src/raster/rasterband.rs b/src/raster/rasterband.rs new file mode 100644 index 00000000..ed1c717b --- /dev/null +++ b/src/raster/rasterband.rs @@ -0,0 +1,160 @@ +use libc::{c_int, c_void}; +use raster::{gdal, Dataset, Buffer}; +use raster::types::{GdalType}; +use raster::gdal_enums; +use gdal_major_object::MajorObject; +use metadata::Metadata; + +pub struct RasterBand<'a> { + c_rasterband: *const c_void, + owning_dataset: &'a Dataset, +} + +impl <'a> RasterBand<'a> { + pub fn owning_dataset(&self) -> &'a Dataset { + self.owning_dataset + } + + pub unsafe fn _with_c_ptr(c_rasterband: *const c_void, owning_dataset: &'a Dataset) -> Self { + RasterBand { c_rasterband: c_rasterband, owning_dataset: owning_dataset } + } + + /// Read a 'Buffer' from a 'Dataset'. T implements 'GdalType' + /// # Arguments + /// * band_index - the band_index + /// * window - the window position from top left + /// * window_size - the window size (GDAL will interpolate data if window_size != buffer_size) + /// * buffer_size - the desired size of the 'Buffer' + pub fn read_as( + &self, + window: (isize, isize), + window_size: (usize, usize), + size: (usize, usize), + ) -> Buffer + { + let pixels = (size.0 * size.1) as usize; + let mut data: Vec = Vec::with_capacity(pixels); + //let no_data: + unsafe { + let rv = gdal::GDALRasterIO( + self.c_rasterband, + gdal_enums::GDALRWFlag::GF_Read, + window.0 as c_int, + window.1 as c_int, + window_size.0 as c_int, + window_size.1 as c_int, + data.as_mut_ptr() as *const c_void, + size.0 as c_int, + size.1 as c_int, + T::gdal_type(), + 0, + 0 + ) as isize; + assert!(rv == 0); + data.set_len(pixels); + }; + Buffer{ + size: size, + data: data, + } + } + + /// Read a full 'Dataset' as 'Buffer'. + /// # Arguments + /// * band_index - the band_index + pub fn read_band_as( + &self, + ) -> Buffer + { + let size = self.owning_dataset.size(); + self.read_as::( + (0, 0), + (size.0 as usize, size.1 as usize), + (size.0 as usize, size.1 as usize) + ) + } + + // Write a 'Buffer' into a 'Dataset'. + /// # Arguments + /// * band_index - the band_index + /// * window - the window position from top left + /// * window_size - the window size (GDAL will interpolate data if window_size != Buffer.size) + pub fn write( + &self, + window: (isize, isize), + window_size: (usize, usize), + buffer: Buffer + ) { + assert_eq!(buffer.data.len(), buffer.size.0 * buffer.size.1); + unsafe { + let rv = gdal::GDALRasterIO( + self.c_rasterband, + gdal_enums::GDALRWFlag::GF_Write, + window.0 as c_int, + window.1 as c_int, + window_size.0 as c_int, + window_size.1 as c_int, + buffer.data.as_ptr() as *const c_void, + buffer.size.0 as c_int, + buffer.size.1 as c_int, + T::gdal_type(), + 0, + 0 + ) as isize; + assert!(rv == 0); + }; + } + + pub fn get_band_type(&self) -> gdal_enums::GDALDataType { + + let gdal_type: c_int; + unsafe{ + gdal_type = gdal::GDALGetRasterDataType(self.c_rasterband); + } + gdal_enums::GDALDataType::from_c_int(gdal_type) + } + + pub fn get_no_data_value(&self) ->Option { + unsafe { + let mut pb_success: c_int = 1; + let raw_pb_success = &mut pb_success as *mut c_int; + let no_data = gdal::GDALGetRasterNoDataValue(self.c_rasterband, raw_pb_success); + if pb_success == 1 { + return Some(no_data as f64); + } + } + None + } + + pub fn get_scale(&self) ->Option { + unsafe { + let mut pb_success: c_int = 1; + let raw_pb_success = &mut pb_success as *mut c_int; + let scale = gdal::GDALGetRasterScale(self.c_rasterband, raw_pb_success); + if pb_success == 1 { + return Some(scale as f64); + } + } + None + } + + pub fn get_offset(&self) ->Option { + unsafe { + let mut pb_success: c_int = 1; + let raw_pb_success = &mut pb_success as *mut c_int; + let offset = gdal::GDALGetRasterOffset(self.c_rasterband, raw_pb_success); + if pb_success == 1 { + return Some(offset as f64); + } + } + None + } +} + +impl<'a> MajorObject for RasterBand<'a> { + unsafe fn get_gdal_object_ptr(&self) -> *const c_void { + self.c_rasterband + } +} + +impl<'a> Metadata for RasterBand<'a> {} diff --git a/src/raster/tests.rs b/src/raster/tests.rs index 84fb211b..155b0739 100644 --- a/src/raster/tests.rs +++ b/src/raster/tests.rs @@ -60,7 +60,7 @@ fn test_read_raster() { (20, 30), (2, 3), (2, 3) - ); + ).unwrap(); assert_eq!(rv.size.0, 2); assert_eq!(rv.size.1, 3); assert_eq!(rv.data, vec!(7, 7, 7, 10, 8, 12)); @@ -92,7 +92,7 @@ fn test_write_raster() { (5, 5), (1, 1), (1, 1) - ); + ).unwrap(); assert_eq!(left.data[0], 50u8); // read a pixel from the right side @@ -101,7 +101,7 @@ fn test_write_raster() { (15, 5), (1, 1), (1, 1) - ); + ).unwrap(); assert_eq!(right.data[0], 20u8); } @@ -209,7 +209,7 @@ fn test_read_raster_as() { (20, 30), (2, 3), (2, 3) - ); + ).unwrap(); assert_eq!(rv.data, vec!(7, 7, 7, 10, 8, 12)); assert_eq!(rv.size.0, 2); assert_eq!(rv.size.1, 3); @@ -219,11 +219,9 @@ fn test_read_raster_as() { #[test] fn test_read_full_raster_as() { let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); - let rv = dataset.read_full_raster_as::(1); - assert_eq!(rv.size.0, 50); + let rv = dataset.read_full_raster_as::(1).unwrap(); + assert_eq!(rv.size.0, 100); assert_eq!(rv.size.1, 50); - assert_eq!(dataset.get_band_type(1), Some(GDALDataType::GDT_Byte)); - //TODO: find a value to assert? } #[test] @@ -233,3 +231,40 @@ fn test_get_band_type() { assert_eq!(dataset.get_band_type(1), Some(GDALDataType::GDT_Byte)); assert_eq!(dataset.get_band_type(2), None); } + +#[test] +fn test_get_rasterband() { + let driver = Driver::get("MEM").unwrap(); + let dataset = driver.create("", 20, 10, 1).unwrap(); + let rasterband = dataset.get_rasterband(1); + assert!(rasterband.is_some()) +} + +#[test] +fn test_get_no_data_value() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rasterband = dataset.get_rasterband(1).unwrap(); + let no_data_value = rasterband.get_no_data_value(); + assert!(no_data_value.is_none()); + + // let dataset = Dataset::open(fixture!("bluemarble.tif")).unwrap(); + // let rasterband = dataset.get_rasterband(1).unwrap(); + // let no_data_value = rasterband.get_no_data_value(); + // assert_eq!(no_data_value, Some(0.0)); +} + +#[test] +fn test_get_scale() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rasterband = dataset.get_rasterband(1).unwrap(); + let scale = rasterband.get_scale(); + assert_eq!(scale, Some(1.0)); +} + +#[test] +fn test_get_offset() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rasterband = dataset.get_rasterband(1).unwrap(); + let offset = rasterband.get_offset(); + assert_eq!(offset, Some(0.0)); +}