diff --git a/examples/metadata.rs b/examples/metadata.rs index 4cd71397..27558f17 100644 --- a/examples/metadata.rs +++ b/examples/metadata.rs @@ -6,15 +6,15 @@ fn main() { use gdal::metadata::Metadata; let driver = gdal::raster::driver::Driver::get("mem").unwrap(); - println!("driver description: {:?}", driver.get_description()); + println!("driver description: {:?}", driver.description()); let path = Path::new("./fixtures/tinymarble.png"); let dataset = Dataset::open(path).unwrap(); - println!("dataset description: {:?}", dataset.get_description()); + println!("dataset description: {:?}", dataset.description()); let key = "INTERLEAVE"; let domain = "IMAGE_STRUCTURE"; - let meta = dataset.get_metadata_item(key, domain); + let meta = dataset.metadata_item(key, domain); println!("domain: {:?} key: {:?} -> value: {:?}", domain, key, meta); } diff --git a/examples/rasterband.rs b/examples/rasterband.rs new file mode 100644 index 00000000..40b72b1c --- /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.description()); + + let rasterband: RasterBand = dataset.rasterband(1).unwrap(); + println!("rasterband description: {:?}", rasterband.description()); + println!("rasterband no_data_value: {:?}", rasterband.no_data_value()); + println!("rasterband type: {:?}", rasterband.band_type()); + println!("rasterband scale: {:?}", rasterband.scale()); + println!("rasterband offset: {:?}", rasterband.offset()); + let rv = rasterband.read_as::( + (20, 30), + (2, 3), + (2, 3) + ); + println!("{:?}", rv.data); +} diff --git a/src/gdal_major_object.rs b/src/gdal_major_object.rs index c59b99da..09caecb0 100644 --- a/src/gdal_major_object.rs +++ b/src/gdal_major_object.rs @@ -1,5 +1,5 @@ use libc::{c_void}; pub trait MajorObject { - unsafe fn get_gdal_object_ptr(&self) -> *const c_void; + unsafe fn gdal_object_ptr(&self) -> *const c_void; } diff --git a/src/metadata.rs b/src/metadata.rs index d63f9bd2..79cf7336 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -12,8 +12,8 @@ extern { pub trait Metadata: MajorObject { - fn get_description(&self) -> Option{ - let c_res = unsafe { GDALGetDescription(self.get_gdal_object_ptr())}; + fn description(&self) -> Option{ + let c_res = unsafe { GDALGetDescription(self.gdal_object_ptr())}; if c_res.is_null() { None } else { @@ -21,10 +21,10 @@ pub trait Metadata: MajorObject { } } - fn get_metadata_item(&self, key: &str, domain: &str) -> Option { + fn metadata_item(&self, key: &str, domain: &str) -> Option { if let Ok(c_key) = CString::new(key.to_owned()) { if let Ok(c_domain) = CString::new(domain.to_owned()){ - let c_res = unsafe { GDALGetMetadataItem(self.get_gdal_object_ptr(), c_key.as_ptr(), c_domain.as_ptr())}; + let c_res = unsafe { GDALGetMetadataItem(self.gdal_object_ptr(), c_key.as_ptr(), c_domain.as_ptr())}; if !c_res.is_null() { return Some(_string(c_res)); } @@ -37,7 +37,7 @@ pub trait Metadata: MajorObject { if let Ok(c_key) = CString::new(key.to_owned()){ if let Ok(c_domain) = CString::new(domain.to_owned()){ if let Ok(c_value) = CString::new(value.to_owned()){ - let c_res = unsafe { GDALSetMetadataItem(self.get_gdal_object_ptr(), c_key.as_ptr(), c_value.as_ptr(), c_domain.as_ptr())}; + let c_res = unsafe { GDALSetMetadataItem(self.gdal_object_ptr(), c_key.as_ptr(), c_value.as_ptr(), c_domain.as_ptr())}; if c_res == 0 { return Ok(()); } diff --git a/src/raster/dataset.rs b/src/raster/dataset.rs index 8bb2f405..64bb26bb 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; @@ -16,14 +16,13 @@ pub struct Dataset { } impl MajorObject for Dataset { - unsafe fn get_gdal_object_ptr(&self) -> *const c_void { + unsafe fn gdal_object_ptr(&self) -> *const c_void { self.c_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 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 band_type(&self, band_index: isize) -> Option { + self.rasterband(band_index).map(|band| band.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.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.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.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/driver.rs b/src/raster/driver.rs index e659d9f5..2ba9dfbd 100644 --- a/src/raster/driver.rs +++ b/src/raster/driver.rs @@ -97,7 +97,7 @@ impl Driver { } impl MajorObject for Driver { - unsafe fn get_gdal_object_ptr(&self) -> *const c_void { + unsafe fn gdal_object_ptr(&self) -> *const c_void { return self.c_driver; } } 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..06136b40 --- /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 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 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 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 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 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..2460eeef 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); } @@ -118,7 +118,7 @@ fn test_get_dataset_driver() { fn test_get_description() { let driver = Driver::get("mem").unwrap(); - assert_eq!(driver.get_description(), Some("MEM".to_owned())); + assert_eq!(driver.description(), Some("MEM".to_owned())); } #[test] @@ -126,12 +126,12 @@ fn test_get_metadata_item() { let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); let key = "None"; let domain = "None"; - let meta = dataset.get_metadata_item(key, domain); + let meta = dataset.metadata_item(key, domain); assert_eq!(meta, None); let key = "INTERLEAVE"; let domain = "IMAGE_STRUCTURE"; - let meta = dataset.get_metadata_item(key, domain); + let meta = dataset.metadata_item(key, domain); assert_eq!(meta, Some(String::from("PIXEL"))); } @@ -146,7 +146,7 @@ fn test_set_metadata_item() { let result = dataset.set_metadata_item(key, value, domain); assert_eq!(result, Ok(())); - let result = dataset.get_metadata_item(key, domain); + let result = dataset.metadata_item(key, domain); assert_eq!(Some(value.to_owned()), result); } @@ -166,7 +166,7 @@ fn test_create_with_band_type() { assert_eq!(dataset.size(), (10, 20)); assert_eq!(dataset.count(), 3); assert_eq!(dataset.driver().short_name(), "MEM"); - assert_eq!(dataset.get_band_type(1), Some(GDALDataType::GDT_Float32)) + assert_eq!(dataset.band_type(1), Some(GDALDataType::GDT_Float32)) } #[test] @@ -209,27 +209,62 @@ 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); - assert_eq!(dataset.get_band_type(1), Some(GDALDataType::GDT_Byte)); + assert_eq!(dataset.band_type(1), Some(GDALDataType::GDT_Byte)); } #[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] fn test_get_band_type() { let driver = Driver::get("MEM").unwrap(); let dataset = driver.create("", 20, 10, 1).unwrap(); - assert_eq!(dataset.get_band_type(1), Some(GDALDataType::GDT_Byte)); - assert_eq!(dataset.get_band_type(2), None); + assert_eq!(dataset.band_type(1), Some(GDALDataType::GDT_Byte)); + assert_eq!(dataset.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.rasterband(1); + assert!(rasterband.is_some()) +} + +#[test] +fn test_get_no_data_value() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rasterband = dataset.rasterband(1).unwrap(); + let no_data_value = rasterband.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.rasterband(1).unwrap(); + let scale = rasterband.scale(); + assert_eq!(scale, Some(1.0)); +} + +#[test] +fn test_get_offset() { + let dataset = Dataset::open(fixture!("tinymarble.png")).unwrap(); + let rasterband = dataset.rasterband(1).unwrap(); + let offset = rasterband.offset(); + assert_eq!(offset, Some(0.0)); } diff --git a/src/vector/dataset.rs b/src/vector/dataset.rs index 69724cb3..28281ab2 100644 --- a/src/vector/dataset.rs +++ b/src/vector/dataset.rs @@ -22,7 +22,7 @@ pub struct Dataset { } impl MajorObject for Dataset { - unsafe fn get_gdal_object_ptr(&self) -> *const c_void { + unsafe fn gdal_object_ptr(&self) -> *const c_void { self.c_dataset } } diff --git a/src/vector/layer.rs b/src/vector/layer.rs index 83c1e868..8e29d8d8 100644 --- a/src/vector/layer.rs +++ b/src/vector/layer.rs @@ -23,7 +23,7 @@ pub struct Layer { } impl MajorObject for Layer { - unsafe fn get_gdal_object_ptr(&self) -> *const c_void { + unsafe fn gdal_object_ptr(&self) -> *const c_void { self.c_layer } }