Skip to content

Commit

Permalink
Implemented IntoIterator, Index and IndexMut for Buffer<T>
Browse files Browse the repository at this point in the history
Made fields of `Buffer<T>` private.
  • Loading branch information
metasim committed Dec 18, 2023
1 parent caf5aad commit 9801492
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 65 deletions.
8 changes: 6 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
## Unreleased

- Added ability to convert between `Buffer<T>` and `ndarray::Array2<T>`.
- Implemented `IntoIterator`, `Index` and `IndexMut` for `Buffer<T>`.
- **Breaking**: `Buffer<T>::size` is now private and accessed via `Buffer<T>::shape().
- **Breaking**: `Buffer<T>::data` is now private and accessed via `Buffer<T>::data().
- **Breaking**: Removed `Rasterband::read_as_array`, changed signature of `Rasterband::read_block` to return a `Buffer<T>`.
- **Breaking**: `Rasterband::write_block` now takes a `&Buffer<T>`.
- **Breaking**: `Rasterband::write` and `Rasterband::write_block` now require a `&mut Buffer<T>` to handle possible case of drivers temporarily mutating input buffer.

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

- Defers the gdal_i.lib missing message until after the pkg-config check and outputs pkg-config metadata in case of a static build.
- Defers the `gdal_i.lib` missing message until after the `pkg-config` check and outputs `pkg-config` metadata in case of a static build.

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

Expand Down
2 changes: 1 addition & 1 deletion examples/rasterband.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ fn main() {
println!("rasterband scale: {:?}", rasterband.scale());
println!("rasterband offset: {:?}", rasterband.offset());
if let Ok(rv) = rasterband.read_as::<u8>((20, 30), (2, 3), (2, 3), None) {
println!("{:?}", rv.data);
println!("{:?}", rv.data());
}
}
131 changes: 118 additions & 13 deletions src/raster/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,114 @@
use crate::raster::GdalType;
use std::ops::{Index, IndexMut};
use std::slice::Iter;

#[cfg(feature = "ndarray")]
use ndarray::Array2;

/// A 2-D array backed by it's `size` (cols, rows) and a row-major `Vec<T>` and it's dimensions.
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct Buffer<T> {
pub size: (usize, usize),
pub data: Vec<T>,
shape: (usize, usize),
data: Vec<T>,
}

impl<T: GdalType> Buffer<T> {
/// Construct a new buffer from `size` (`(cols, rows)`) and `Vec<T>`.
///
/// # Panic
/// # Panics
/// Will panic if `size.0 * size.1 != data.len()`.
pub fn new(size: (usize, usize), data: Vec<T>) -> Self {
pub fn new(shape: (usize, usize), data: Vec<T>) -> Self {
assert_eq!(
size.0 * size.1,
shape.0 * shape.1,
data.len(),
"size {:?} does not match length {}",
size,
shape,
data.len()
);
Buffer { size, data }
Buffer { shape, data }
}

/// Destructures `self` into constituent parts.
pub fn into_shape_and_vec(self) -> ((usize, usize), Vec<T>) {
(self.shape, self.data)
}

/// Gets the 2-d shape of the buffer.
///
/// Returns `(cols, rows)`
pub fn shape(&self) -> (usize, usize) {
self.shape
}

/// Get a slice over the buffer contents.
pub fn data(&self) -> &[T] {
self.data.as_slice()
}

/// Get a mutable slice over the buffer contents.
pub fn data_mut(&mut self) -> &mut [T] {
self.data.as_mut_slice()
}

/// Get the number of elements in the buffer
pub fn len(&self) -> usize {
self.data.len()
}

/// Determine if the buffer has no elements.
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}

#[cfg(feature = "ndarray")]
/// Convert `self` into an [`ndarray::Array2`].
pub fn to_array(self) -> crate::errors::Result<Array2<T>> {
// Array2 shape is (rows, cols) and Buffer shape is (cols in x-axis, rows in y-axis)
Ok(Array2::from_shape_vec(
(self.size.1, self.size.0),
(self.shape.1, self.shape.0),
self.data,
)?)
}

#[inline]
pub(self) fn vec_index_for(&self, coord: (usize, usize)) -> usize {
if coord.0 >= self.shape.0 {
panic!(
"index out of bounds: buffer has {} columns but row {} was requested",
self.shape.0, coord.0
);
}
if coord.1 >= self.shape.1 {
panic!(
"index out of bounds: buffer has {} rows but row {} was requested",
self.shape.1, coord.1
);
}
coord.0 * self.shape.0 + coord.1
}
}

impl<T: GdalType> Index<(usize, usize)> for Buffer<T> {
type Output = T;
fn index(&self, index: (usize, usize)) -> &Self::Output {
&self.data[self.vec_index_for(index)]
}
}

impl<T: GdalType> IndexMut<(usize, usize)> for Buffer<T> {
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
let idx = self.vec_index_for(index);
&mut self.data[idx]
}
}

impl<'a, T: GdalType> IntoIterator for &'a Buffer<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;

fn into_iter(self) -> Self::IntoIter {
self.data.iter()
}
}

pub type ByteBuffer = Buffer<u8>;
Expand All @@ -54,11 +128,12 @@ impl<T: GdalType + Copy> From<Array2<T>> for Buffer<T> {
// Array2 shape is (rows, cols) and Buffer shape is (cols in x-axis, rows in y-axis)
let shape = value.shape();
let (rows, cols) = (shape[0], shape[1]);
let data = value
.as_standard_layout()
.iter()
.copied()
.collect::<Vec<T>>();
let data: Vec<T> = if value.is_standard_layout() {
value.into_raw_vec()
} else {
value.iter().copied().collect()
};

Buffer::new((cols, rows), data)
}
}
Expand All @@ -84,4 +159,34 @@ mod tests {
let a2 = b.to_array().unwrap();
assert_eq!(a, a2);
}

#[test]
fn index() {
let b = Buffer::new((5, 7), (0..5 * 7).collect());
assert_eq!(b[(0, 0)], 0);
assert_eq!(b[(1, 1)], 5 + 1);
assert_eq!(b[(4, 5)], 4 * 5 + 5);

let mut b = b;
b[(2, 2)] = 99;
assert_eq!(b[(2, 1)], 2 * 5 + 1);
assert_eq!(b[(2, 2)], 99);
assert_eq!(b[(2, 3)], 2 * 5 + 3);
}

#[test]
fn iter() {
let b = Buffer::new((5, 7), (0..5 * 7).collect());
let mut iter = b.into_iter();
let _ = iter.next().unwrap();
let v = iter.next().unwrap();
assert_eq!(*v, b[(0, 1)]);
}

#[test]
#[should_panic]
fn index_bounds() {
let b = Buffer::new((5, 7), (0..5 * 7).collect());
let _ = b[(5, 0)];
}
}
4 changes: 2 additions & 2 deletions src/raster/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
//! let rv = band.read_as::<u8>(window, window_size, size, resample_alg)?;
//! // `Rasterband::read_as` returns a `Buffer` struct, which contains the shape of the output
//! // `(cols, rows)` and a `Vec<_>` containing the pixel values.
//! println!(" Data size: {:?}", rv.size);
//! println!(" Data values: {:?}", rv.data);
//! println!(" Data size: {:?}", rv.shape());
//! println!(" Data values: {:?}", rv.data());
//! }
//! # Ok(())
//! # }
Expand Down
28 changes: 14 additions & 14 deletions src/raster/rasterband.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl Dataset {
/// // Down-sample a image using cubic-spline interpolation
/// let buf = band1.read_as::<f64>((0, 0), ds.raster_size(), (2, 2), Some(ResampleAlg::CubicSpline))?;
/// // In this particular image, resulting data should be close to the overall average.
/// assert!(buf.data.iter().all(|c| (c - stats.mean).abs() < stats.std_dev / 2.0));
/// assert!(buf.data().iter().all(|c| (c - stats.mean).abs() < stats.std_dev / 2.0));
/// # Ok(())
/// # }
/// ```
Expand Down Expand Up @@ -428,19 +428,19 @@ impl<'a> RasterBand<'a> {
/// assert_eq!(band1.band_type(), GdalDataType::UInt8);
/// let size = 4;
/// let buf = band1.read_as::<u8>((0, 0), band1.size(), (size, size), Some(ResampleAlg::Bilinear))?;
/// assert_eq!(buf.size, (size, size));
/// assert_eq!(buf.data, [101u8, 119, 94, 87, 92, 110, 92, 87, 91, 90, 89, 87, 92, 91, 88, 88]);
/// assert_eq!(buf.shape(), (size, size));
/// assert_eq!(buf.data(), [101u8, 119, 94, 87, 92, 110, 92, 87, 91, 90, 89, 87, 92, 91, 88, 88]);
/// # Ok(())
/// # }
/// ```
pub fn read_as<T: Copy + GdalType>(
&self,
window: (isize, isize),
window_size: (usize, usize),
size: (usize, usize),
shape: (usize, usize),
e_resample_alg: Option<ResampleAlg>,
) -> Result<Buffer<T>> {
let pixels = size.0 * size.1;
let pixels = shape.0 * shape.1;
let mut data: Vec<T> = Vec::with_capacity(pixels);

let resample_alg = e_resample_alg.unwrap_or(ResampleAlg::NearestNeighbour);
Expand All @@ -467,8 +467,8 @@ impl<'a> RasterBand<'a> {
window_size.0 as c_int,
window_size.1 as c_int,
data.as_mut_ptr() as *mut c_void,
size.0 as c_int,
size.1 as c_int,
shape.0 as c_int,
shape.1 as c_int,
T::gdal_ordinal(),
0,
0,
Expand All @@ -483,7 +483,7 @@ impl<'a> RasterBand<'a> {
data.set_len(pixels);
};

Ok(Buffer { size, data })
Ok(Buffer::new(shape, data))
}

/// Read the full band as a [`Buffer<T>`], where `T` implements [`GdalType`].
Expand Down Expand Up @@ -518,7 +518,7 @@ impl<'a> RasterBand<'a> {
/// let dataset = Dataset::open("fixtures/m_3607824_se_17_1_20160620_sub.tif")?;
/// let band1 = dataset.rasterband(1)?;
/// let arr = band1.read_block::<u8>((0, 0))?;
/// assert_eq!(arr.size, (300, 6));
/// assert_eq!(arr.shape(), (300, 6));
/// # Ok(())
/// # }
/// ```
Expand Down Expand Up @@ -626,7 +626,7 @@ impl<'a> RasterBand<'a> {
self.c_rasterband,
block_index.0 as c_int,
block_index.1 as c_int,
block.data.as_mut_ptr() as *mut c_void,
block.data_mut().as_mut_ptr() as *mut c_void,
)
};
if rv != CPLErr::CE_None {
Expand All @@ -653,7 +653,7 @@ impl<'a> RasterBand<'a> {
window_size: (usize, usize),
buffer: &mut Buffer<T>,
) -> Result<()> {
assert_eq!(buffer.data.len(), buffer.size.0 * buffer.size.1);
let shape = buffer.shape();
let rv = unsafe {
gdal_sys::GDALRasterIO(
self.c_rasterband,
Expand All @@ -662,9 +662,9 @@ impl<'a> RasterBand<'a> {
window.1 as c_int,
window_size.0 as c_int,
window_size.1 as c_int,
buffer.data.as_mut_ptr() as *mut c_void,
buffer.size.0 as c_int,
buffer.size.1 as c_int,
buffer.data_mut().as_mut_ptr() as *mut c_void,
shape.0 as c_int,
shape.1 as c_int,
T::gdal_ordinal(),
0,
0,
Expand Down
Loading

0 comments on commit 9801492

Please sign in to comment.