use std::ffi::{c_int, c_void, CString};
use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData;
use std::str::FromStr;
use gdal_sys::{
self, CPLErr, GDALColorEntry, GDALColorInterp, GDALColorTableH, GDALComputeRasterMinMax,
GDALCreateColorRamp, GDALCreateColorTable, GDALDestroyColorTable, GDALGetDefaultHistogramEx,
GDALGetPaletteInterpretation, GDALGetRasterHistogramEx, GDALGetRasterStatistics,
GDALMajorObjectH, GDALPaletteInterp, GDALRIOResampleAlg, GDALRWFlag, GDALRasterBandH,
GDALRasterIOExtraArg, GDALSetColorEntry, GDALSetDefaultHistogramEx, GDALSetRasterColorTable,
};
use crate::dataset::Dataset;
use crate::errors::*;
use crate::gdal_major_object::MajorObject;
use crate::metadata::Metadata;
use crate::raster::buffer::Buffer;
use crate::raster::ResampleAlg::{
Average, Bilinear, Cubic, CubicSpline, Gauss, Lanczos, Mode, NearestNeighbour,
};
use crate::raster::{GdalDataType, GdalType};
use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string};
impl Dataset {
pub fn rasterband(&self, band_index: usize) -> Result<RasterBand<'_>> {
let band_index = c_int::try_from(band_index)?;
unsafe {
let c_band = gdal_sys::GDALGetRasterBand(self.c_dataset(), band_index);
if c_band.is_null() {
return Err(_last_null_pointer_err("GDALGetRasterBand"));
}
Ok(RasterBand::from_c_rasterband(self, c_band))
}
}
pub fn rasterbands(&self) -> impl Iterator<Item = Result<RasterBand<'_>>> {
(1..=self.raster_count()).map(|idx| self.rasterband(idx))
}
pub fn build_overviews(
&mut self,
resampling: &str,
overviews: &[i32],
bands: &[i32],
) -> Result<()> {
let c_resampling = CString::new(resampling)?;
let rv = unsafe {
gdal_sys::GDALBuildOverviews(
self.c_dataset(),
c_resampling.as_ptr(),
overviews.len() as i32,
overviews.as_ptr() as *mut i32,
bands.len() as i32,
bands.as_ptr() as *mut i32,
None,
std::ptr::null_mut(),
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn raster_count(&self) -> usize {
(unsafe { gdal_sys::GDALGetRasterCount(self.c_dataset()) }) as usize
}
pub fn raster_size(&self) -> (usize, usize) {
let size_x = unsafe { gdal_sys::GDALGetRasterXSize(self.c_dataset()) } as usize;
let size_y = unsafe { gdal_sys::GDALGetRasterYSize(self.c_dataset()) } as usize;
(size_x, size_y)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u32)]
pub enum ResampleAlg {
NearestNeighbour = GDALRIOResampleAlg::GRIORA_NearestNeighbour,
Bilinear = GDALRIOResampleAlg::GRIORA_Bilinear,
Cubic = GDALRIOResampleAlg::GRIORA_Cubic,
CubicSpline = GDALRIOResampleAlg::GRIORA_CubicSpline,
Lanczos = GDALRIOResampleAlg::GRIORA_Lanczos,
Average = GDALRIOResampleAlg::GRIORA_Average,
Mode = GDALRIOResampleAlg::GRIORA_Mode,
Gauss = GDALRIOResampleAlg::GRIORA_Gauss,
}
impl ResampleAlg {
pub fn to_gdal(&self) -> GDALRIOResampleAlg::Type {
*self as GDALRIOResampleAlg::Type
}
pub fn iter() -> impl Iterator<Item = ResampleAlg> {
use ResampleAlg::*;
[
NearestNeighbour,
Bilinear,
Cubic,
CubicSpline,
Lanczos,
Average,
Mode,
Gauss,
]
.into_iter()
}
}
impl Display for ResampleAlg {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
}
}
impl FromStr for ResampleAlg {
type Err = GdalError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"nearestneighbour" => Ok(NearestNeighbour),
"bilinear" => Ok(Bilinear),
"cubic" => Ok(Cubic),
"cubicspline" => Ok(CubicSpline),
"lanczos" => Ok(Lanczos),
"average" => Ok(Average),
"mode" => Ok(Mode),
"gauss" => Ok(Gauss),
o => Err(GdalError::BadArgument(format!(
"'{}' does not match one of {:?}",
o,
Self::iter().map(|e| e.to_string()).collect::<Vec<_>>()
))),
}
}
}
pub struct GdalMaskFlags(i32);
impl GdalMaskFlags {
const GMF_ALL_VALID: i32 = 0x01;
const GMF_PER_DATASET: i32 = 0x02;
const GMF_ALPHA: i32 = 0x04;
const GMF_NODATA: i32 = 0x08;
pub fn is_all_valid(&self) -> bool {
self.0 & Self::GMF_ALL_VALID != 0
}
pub fn is_per_dataset(&self) -> bool {
self.0 & Self::GMF_PER_DATASET != 0
}
pub fn is_alpha(&self) -> bool {
self.0 & Self::GMF_ALPHA != 0
}
pub fn is_nodata(&self) -> bool {
self.0 & Self::GMF_NODATA != 0
}
}
#[derive(Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct RasterIOExtraArg {
pub n_version: usize,
pub e_resample_alg: ResampleAlg,
pub pfn_progress: gdal_sys::GDALProgressFunc,
p_progress_data: *mut c_void,
pub b_floating_point_window_validity: usize,
pub df_x_off: f64,
pub df_y_off: f64,
pub df_x_size: f64,
pub df_y_size: f64,
pub b_use_only_this_scale: bool,
}
impl Default for RasterIOExtraArg {
fn default() -> Self {
Self {
n_version: 1,
pfn_progress: None,
p_progress_data: std::ptr::null_mut(),
e_resample_alg: ResampleAlg::NearestNeighbour,
b_floating_point_window_validity: 0,
df_x_off: 0.0,
df_y_off: 0.0,
df_x_size: 0.0,
df_y_size: 0.0,
b_use_only_this_scale: false,
}
}
}
impl From<RasterIOExtraArg> for GDALRasterIOExtraArg {
fn from(arg: RasterIOExtraArg) -> Self {
let RasterIOExtraArg {
n_version,
e_resample_alg,
pfn_progress,
p_progress_data,
b_floating_point_window_validity,
df_x_off,
df_y_off,
df_x_size,
df_y_size,
#[allow(unused)]
b_use_only_this_scale,
} = arg;
GDALRasterIOExtraArg {
nVersion: n_version as c_int,
eResampleAlg: e_resample_alg.to_gdal(),
pfnProgress: pfn_progress,
pProgressData: p_progress_data,
bFloatingPointWindowValidity: b_floating_point_window_validity as c_int,
dfXOff: df_x_off,
dfYOff: df_y_off,
dfXSize: df_x_size,
dfYSize: df_y_size,
#[cfg(all(major_ge_3, minor_ge_12))]
bUseOnlyThisScale: if b_use_only_this_scale { 1 } else { 0 },
}
}
}
pub struct RasterBand<'a> {
c_rasterband: GDALRasterBandH,
dataset: &'a Dataset,
}
impl<'a> RasterBand<'a> {
pub unsafe fn c_rasterband(&self) -> GDALRasterBandH {
self.c_rasterband
}
pub unsafe fn from_c_rasterband(dataset: &'a Dataset, c_rasterband: GDALRasterBandH) -> Self {
RasterBand {
c_rasterband,
dataset,
}
}
pub fn block_size(&self) -> (usize, usize) {
let mut size_x = 0;
let mut size_y = 0;
unsafe { gdal_sys::GDALGetBlockSize(self.c_rasterband, &mut size_x, &mut size_y) };
(size_x as usize, size_y as usize)
}
pub fn x_size(&self) -> usize {
let out;
unsafe {
out = gdal_sys::GDALGetRasterBandXSize(self.c_rasterband);
}
out as usize
}
pub fn y_size(&self) -> usize {
let out;
unsafe { out = gdal_sys::GDALGetRasterBandYSize(self.c_rasterband) }
out as usize
}
pub fn size(&self) -> (usize, usize) {
(self.x_size(), self.y_size())
}
pub fn read_into_slice<T: Copy + GdalType>(
&self,
window: (isize, isize),
window_size: (usize, usize),
size: (usize, usize),
buffer: &mut [T],
e_resample_alg: Option<ResampleAlg>,
) -> Result<()> {
let pixels = size.0 * size.1;
if buffer.len() != pixels {
return Err(GdalError::BufferSizeMismatch(buffer.len(), size));
}
let resample_alg = e_resample_alg.unwrap_or(ResampleAlg::NearestNeighbour);
let mut options: GDALRasterIOExtraArg = RasterIOExtraArg {
e_resample_alg: resample_alg,
..Default::default()
}
.into();
let options_ptr: *mut GDALRasterIOExtraArg = &mut options;
let rv = unsafe {
gdal_sys::GDALRasterIOEx(
self.c_rasterband,
GDALRWFlag::GF_Read,
window.0.try_into()?,
window.1.try_into()?,
window_size.0.try_into()?,
window_size.1.try_into()?,
buffer.as_mut_ptr() as *mut c_void,
size.0.try_into()?,
size.1.try_into()?,
T::gdal_ordinal(),
0,
0,
options_ptr,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn read_as<T: Copy + GdalType>(
&self,
window: (isize, isize),
window_size: (usize, usize),
shape: (usize, usize),
e_resample_alg: Option<ResampleAlg>,
) -> Result<Buffer<T>> {
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);
let mut options: GDALRasterIOExtraArg = RasterIOExtraArg {
e_resample_alg: resample_alg,
..Default::default()
}
.into();
let options_ptr: *mut GDALRasterIOExtraArg = &mut options;
let rv = unsafe {
gdal_sys::GDALRasterIOEx(
self.c_rasterband,
GDALRWFlag::GF_Read,
window.0.try_into()?,
window.1.try_into()?,
window_size.0.try_into()?,
window_size.1.try_into()?,
data.as_mut_ptr() as *mut c_void,
shape.0.try_into()?,
shape.1.try_into()?,
T::gdal_ordinal(),
0,
0,
options_ptr,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
unsafe {
data.set_len(pixels);
};
Ok(Buffer::new(shape, data))
}
pub fn read_band_as<T: Copy + GdalType>(&self) -> Result<Buffer<T>> {
let size = self.size();
self.read_as::<T>((0, 0), size, size, None)
}
pub fn read_block<T: Copy + GdalType>(&self, block_index: (usize, usize)) -> Result<Buffer<T>> {
if T::gdal_ordinal() != self.band_type() as u32 {
return Err(GdalError::BadArgument(
"result array type must match band data type".to_string(),
));
}
let size = self.block_size();
let pixels = size.0 * size.1;
let mut data: Vec<T> = Vec::with_capacity(pixels);
let rv = unsafe {
gdal_sys::GDALReadBlock(
self.c_rasterband,
block_index.0.try_into()?,
block_index.1.try_into()?,
data.as_mut_ptr() as *mut c_void,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
unsafe {
data.set_len(pixels);
};
Ok(Buffer::new(size, data))
}
pub fn write_block<T: Copy + GdalType>(
&mut self,
block_index: (usize, usize),
block: &mut Buffer<T>,
) -> Result<()> {
if T::gdal_ordinal() != self.band_type() as u32 {
return Err(GdalError::BadArgument(
"array type must match band data type".to_string(),
));
}
let rv = unsafe {
gdal_sys::GDALWriteBlock(
self.c_rasterband,
block_index.0.try_into()?,
block_index.1.try_into()?,
block.data_mut().as_mut_ptr() as *mut c_void,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn write<T: GdalType + Copy>(
&mut self,
window: (isize, isize),
window_size: (usize, usize),
buffer: &mut Buffer<T>,
) -> Result<()> {
let shape = buffer.shape();
if buffer.len() != shape.0 * shape.1 {
return Err(GdalError::BufferSizeMismatch(buffer.len(), shape));
}
let rv = unsafe {
gdal_sys::GDALRasterIO(
self.c_rasterband,
GDALRWFlag::GF_Write,
window.0.try_into()?,
window.1.try_into()?,
window_size.0.try_into()?,
window_size.1.try_into()?,
buffer.data_mut().as_mut_ptr() as *mut c_void,
shape.0.try_into()?,
shape.1.try_into()?,
T::gdal_ordinal(),
0,
0,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn band_type(&self) -> GdalDataType {
let ordinal = unsafe { gdal_sys::GDALGetRasterDataType(self.c_rasterband) };
ordinal.try_into().unwrap_or(GdalDataType::Unknown)
}
pub fn no_data_value(&self) -> Option<f64> {
let mut pb_success = 1;
let no_data =
unsafe { gdal_sys::GDALGetRasterNoDataValue(self.c_rasterband, &mut pb_success) };
if pb_success == 1 {
return Some(no_data);
}
None
}
pub fn set_no_data_value(&mut self, no_data: Option<f64>) -> Result<()> {
let rv = if let Some(no_data) = no_data {
unsafe { gdal_sys::GDALSetRasterNoDataValue(self.c_rasterband, no_data) }
} else {
unsafe { gdal_sys::GDALDeleteRasterNoDataValue(self.c_rasterband) }
};
if rv != CPLErr::CE_None {
Err(_last_cpl_err(rv))
} else {
Ok(())
}
}
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn no_data_value_u64(&self) -> Option<u64> {
let mut pb_success = 1;
let no_data = unsafe {
gdal_sys::GDALGetRasterNoDataValueAsUInt64(self.c_rasterband, &mut pb_success)
};
if pb_success == 1 {
return Some(no_data);
}
None
}
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn set_no_data_value_u64(&mut self, no_data: Option<u64>) -> Result<()> {
let rv = if let Some(no_data) = no_data {
unsafe { gdal_sys::GDALSetRasterNoDataValueAsUInt64(self.c_rasterband, no_data) }
} else {
unsafe { gdal_sys::GDALDeleteRasterNoDataValue(self.c_rasterband) }
};
if rv != CPLErr::CE_None {
Err(_last_cpl_err(rv))
} else {
Ok(())
}
}
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn no_data_value_i64(&self) -> Option<i64> {
let mut pb_success = 1;
let no_data = unsafe {
gdal_sys::GDALGetRasterNoDataValueAsInt64(self.c_rasterband, &mut pb_success)
};
if pb_success == 1 {
return Some(no_data);
}
None
}
#[cfg(all(major_ge_3, minor_ge_5))]
pub fn set_no_data_value_i64(&mut self, no_data: Option<i64>) -> Result<()> {
let rv = if let Some(no_data) = no_data {
unsafe { gdal_sys::GDALSetRasterNoDataValueAsInt64(self.c_rasterband, no_data) }
} else {
unsafe { gdal_sys::GDALDeleteRasterNoDataValue(self.c_rasterband) }
};
if rv != CPLErr::CE_None {
Err(_last_cpl_err(rv))
} else {
Ok(())
}
}
pub fn fill(&mut self, real_value: f64, imaginary_value: Option<f64>) -> Result<()> {
let rv = unsafe {
gdal_sys::GDALFillRaster(
self.c_rasterband,
real_value,
imaginary_value.unwrap_or(0.0),
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn color_interpretation(&self) -> ColorInterpretation {
let interp_index = unsafe { gdal_sys::GDALGetRasterColorInterpretation(self.c_rasterband) };
ColorInterpretation::from_c_int(interp_index).unwrap()
}
pub fn set_color_interpretation(&mut self, interp: ColorInterpretation) -> Result<()> {
let interp_index = interp.c_int();
let rv =
unsafe { gdal_sys::GDALSetRasterColorInterpretation(self.c_rasterband, interp_index) };
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
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))
}
pub fn set_color_table(&mut self, colors: &ColorTable) {
unsafe { GDALSetRasterColorTable(self.c_rasterband, colors.c_color_table) };
}
pub fn scale(&self) -> Option<f64> {
let mut pb_success = 1;
let scale = unsafe { gdal_sys::GDALGetRasterScale(self.c_rasterband, &mut pb_success) };
if pb_success == 1 {
return Some(scale);
}
None
}
pub fn set_scale(&mut self, scale: f64) -> Result<()> {
let rv = unsafe { gdal_sys::GDALSetRasterScale(self.c_rasterband, scale) };
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn offset(&self) -> Option<f64> {
let mut pb_success = 1;
let offset = unsafe { gdal_sys::GDALGetRasterOffset(self.c_rasterband, &mut pb_success) };
if pb_success == 1 {
return Some(offset);
}
None
}
pub fn set_offset(&mut self, offset: f64) -> Result<()> {
let rv = unsafe { gdal_sys::GDALSetRasterOffset(self.c_rasterband, offset) };
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn actual_block_size(&self, x: usize, y: usize) -> Result<(usize, usize)> {
let offset_x = x.try_into().expect("`x` offset must fit in `c_int`");
let offset_y = y.try_into().expect("`y` offset must fit in `c_int`");
let mut block_size_x = 0;
let mut block_size_y = 0;
let rv = unsafe {
gdal_sys::GDALGetActualBlockSize(
self.c_rasterband,
offset_x,
offset_y,
&mut block_size_x,
&mut block_size_y,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok((block_size_x as usize, block_size_y as usize))
}
pub fn overview_count(&self) -> Result<i32> {
unsafe { Ok(gdal_sys::GDALGetOverviewCount(self.c_rasterband)) }
}
pub fn overview(&self, overview_index: usize) -> Result<RasterBand<'a>> {
let overview_index = c_int::try_from(overview_index)?;
unsafe {
let c_band = self.c_rasterband;
let overview = gdal_sys::GDALGetOverview(c_band, overview_index);
if overview.is_null() {
return Err(_last_null_pointer_err("GDALGetOverview"));
}
Ok(RasterBand::from_c_rasterband(self.dataset, overview))
}
}
pub fn unit(&self) -> String {
let c_ptr = unsafe { gdal_sys::GDALGetRasterUnitType(self.c_rasterband) };
_string(c_ptr).unwrap_or_default()
}
pub fn mask_flags(&self) -> Result<GdalMaskFlags> {
let band_mask_flags = unsafe { gdal_sys::GDALGetMaskFlags(self.c_rasterband) };
Ok(GdalMaskFlags(band_mask_flags))
}
pub fn create_mask_band(&mut self, shared_between_all_bands: bool) -> Result<()> {
let flags = if shared_between_all_bands {
GdalMaskFlags::GMF_PER_DATASET } else {
0x00
};
let rv = unsafe { gdal_sys::GDALCreateMaskBand(self.c_rasterband, flags) };
if rv != 0 {
return Err(_last_cpl_err(rv));
};
Ok(())
}
pub fn open_mask_band(&self) -> Result<RasterBand<'_>> {
unsafe {
let mask_band_ptr = gdal_sys::GDALGetMaskBand(self.c_rasterband);
if mask_band_ptr.is_null() {
return Err(_last_null_pointer_err("GDALGetMaskBand"));
}
let mask_band = RasterBand::from_c_rasterband(self.dataset, mask_band_ptr);
Ok(mask_band)
}
}
pub fn get_statistics(&self, force: bool, is_approx_ok: bool) -> Result<Option<StatisticsAll>> {
let mut statistics = StatisticsAll {
min: 0.,
max: 0.,
mean: 0.,
std_dev: 0.,
};
let rv = unsafe {
GDALGetRasterStatistics(
self.c_rasterband,
c_int::from(is_approx_ok),
c_int::from(force),
&mut statistics.min,
&mut statistics.max,
&mut statistics.mean,
&mut statistics.std_dev,
)
};
match CplErrType::from(rv) {
CplErrType::None => Ok(Some(statistics)),
CplErrType::Warning => Ok(None),
_ => Err(_last_cpl_err(rv)),
}
}
pub fn compute_raster_min_max(&self, is_approx_ok: bool) -> Result<StatisticsMinMax> {
let mut min_max = [0., 0.];
unsafe {
GDALComputeRasterMinMax(
self.c_rasterband,
c_int::from(is_approx_ok),
&mut min_max as *mut f64,
)
};
Ok(StatisticsMinMax {
min: min_max[0],
max: min_max[1],
})
}
pub fn default_histogram(&self, force: bool) -> Result<Option<Histogram>> {
let mut counts = std::ptr::null_mut();
let mut min = 0.0;
let mut max = 0.0;
let mut n_buckets = 0i32;
let rv = unsafe {
GDALGetDefaultHistogramEx(
self.c_rasterband,
&mut min,
&mut max,
&mut n_buckets,
&mut counts as *mut *mut u64,
c_int::from(force),
None,
std::ptr::null_mut(),
)
};
match CplErrType::from(rv) {
CplErrType::None => Ok(Some(Histogram {
min,
max,
counts: HistogramCounts::GdalAllocated(counts, n_buckets as usize),
})),
CplErrType::Warning => Ok(None),
_ => Err(_last_cpl_err(rv)),
}
}
pub fn set_default_histogram(&self, min: f64, max: f64, counts: &mut [u64]) -> Result<()> {
let n_buckets = c_int::try_from(counts.len())?;
let rv = unsafe {
GDALSetDefaultHistogramEx(self.c_rasterband, min, max, n_buckets, counts.as_mut_ptr())
};
match CplErrType::from(rv) {
CplErrType::None => Ok(()),
_ => Err(_last_cpl_err(rv)),
}
}
pub fn histogram(
&self,
min: f64,
max: f64,
n_buckets: usize,
include_out_of_range: bool,
is_approx_ok: bool,
) -> Result<Histogram> {
if n_buckets == 0 {
return Err(GdalError::BadArgument(
"n_buckets should be > 0".to_string(),
));
}
let n_buckets = c_int::try_from(n_buckets)?;
let mut counts = vec![0; n_buckets as usize];
let rv = unsafe {
GDALGetRasterHistogramEx(
self.c_rasterband,
min,
max,
n_buckets,
counts.as_mut_ptr(),
c_int::from(include_out_of_range),
c_int::from(is_approx_ok),
None,
std::ptr::null_mut(),
)
};
match CplErrType::from(rv) {
CplErrType::None => Ok(Histogram {
min,
max,
counts: HistogramCounts::RustAllocated(counts),
}),
_ => Err(_last_cpl_err(rv)),
}
}
pub fn checksum(&self, window: (isize, isize), window_size: (usize, usize)) -> Result<u16> {
let rv = unsafe {
gdal_sys::GDALChecksumImage(
self.c_rasterband,
window.0.try_into()?,
window.1.try_into()?,
window_size.0.try_into()?,
window_size.1.try_into()?,
)
};
if rv == -1 {
let cpl_err = unsafe { gdal_sys::CPLGetLastErrorType() };
Err(_last_cpl_err(cpl_err))
} else {
Ok(rv as u16)
}
}
}
#[derive(Debug, PartialEq)]
pub struct StatisticsMinMax {
pub min: f64,
pub max: f64,
}
#[derive(Debug, PartialEq)]
pub struct StatisticsAll {
pub min: f64,
pub max: f64,
pub mean: f64,
pub std_dev: f64,
}
#[derive(Debug)]
pub struct Histogram {
min: f64,
max: f64,
counts: HistogramCounts,
}
impl Histogram {
pub fn min(&self) -> f64 {
self.min
}
pub fn max(&self) -> f64 {
self.max
}
pub fn counts(&self) -> &[u64] {
self.counts.as_slice()
}
pub fn n_buckets(&self) -> usize {
self.counts().len()
}
pub fn bucket_size(&self) -> f64 {
(self.max - self.min) / self.counts().len() as f64
}
}
enum HistogramCounts {
GdalAllocated(*mut u64, usize),
RustAllocated(Vec<u64>),
}
impl HistogramCounts {
fn as_slice(&self) -> &[u64] {
match &self {
Self::GdalAllocated(p, n) => unsafe { std::slice::from_raw_parts(*p, *n) },
Self::RustAllocated(v) => v.as_slice(),
}
}
}
impl Debug for HistogramCounts {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.as_slice().fmt(f)
}
}
impl Drop for HistogramCounts {
fn drop(&mut self) {
match self {
HistogramCounts::GdalAllocated(p, _) => unsafe {
gdal_sys::VSIFree(*p as *mut c_void);
},
HistogramCounts::RustAllocated(_) => {}
}
}
}
impl MajorObject for RasterBand<'_> {
fn gdal_object_ptr(&self) -> GDALMajorObjectH {
self.c_rasterband
}
}
impl Metadata for RasterBand<'_> {}
#[derive(Debug, PartialEq, Eq)]
pub enum ColorInterpretation {
Undefined,
GrayIndex,
PaletteIndex,
RedBand,
GreenBand,
BlueBand,
AlphaBand,
HueBand,
SaturationBand,
LightnessBand,
CyanBand,
MagentaBand,
YellowBand,
BlackBand,
YCbCrSpaceYBand,
YCbCrSpaceCbBand,
YCbCrSpaceCrBand,
}
impl ColorInterpretation {
pub fn from_c_int(color_interpretation: GDALColorInterp::Type) -> Option<Self> {
match color_interpretation {
GDALColorInterp::GCI_Undefined => Some(Self::Undefined),
GDALColorInterp::GCI_GrayIndex => Some(Self::GrayIndex),
GDALColorInterp::GCI_PaletteIndex => Some(Self::PaletteIndex),
GDALColorInterp::GCI_RedBand => Some(Self::RedBand),
GDALColorInterp::GCI_GreenBand => Some(Self::GreenBand),
GDALColorInterp::GCI_BlueBand => Some(Self::BlueBand),
GDALColorInterp::GCI_AlphaBand => Some(Self::AlphaBand),
GDALColorInterp::GCI_HueBand => Some(Self::HueBand),
GDALColorInterp::GCI_SaturationBand => Some(Self::SaturationBand),
GDALColorInterp::GCI_LightnessBand => Some(Self::LightnessBand),
GDALColorInterp::GCI_CyanBand => Some(Self::CyanBand),
GDALColorInterp::GCI_MagentaBand => Some(Self::MagentaBand),
GDALColorInterp::GCI_YellowBand => Some(Self::YellowBand),
GDALColorInterp::GCI_BlackBand => Some(Self::BlackBand),
GDALColorInterp::GCI_YCbCr_YBand => Some(Self::YCbCrSpaceYBand),
GDALColorInterp::GCI_YCbCr_CbBand => Some(Self::YCbCrSpaceCbBand),
GDALColorInterp::GCI_YCbCr_CrBand => Some(Self::YCbCrSpaceCrBand),
_ => None,
}
}
pub fn c_int(&self) -> GDALColorInterp::Type {
match self {
Self::Undefined => GDALColorInterp::GCI_Undefined,
Self::GrayIndex => GDALColorInterp::GCI_GrayIndex,
Self::PaletteIndex => GDALColorInterp::GCI_PaletteIndex,
Self::RedBand => GDALColorInterp::GCI_RedBand,
Self::GreenBand => GDALColorInterp::GCI_GreenBand,
Self::BlueBand => GDALColorInterp::GCI_BlueBand,
Self::AlphaBand => GDALColorInterp::GCI_AlphaBand,
Self::HueBand => GDALColorInterp::GCI_HueBand,
Self::SaturationBand => GDALColorInterp::GCI_SaturationBand,
Self::LightnessBand => GDALColorInterp::GCI_LightnessBand,
Self::CyanBand => GDALColorInterp::GCI_CyanBand,
Self::MagentaBand => GDALColorInterp::GCI_MagentaBand,
Self::YellowBand => GDALColorInterp::GCI_YellowBand,
Self::BlackBand => GDALColorInterp::GCI_BlackBand,
Self::YCbCrSpaceYBand => GDALColorInterp::GCI_YCbCr_YBand,
Self::YCbCrSpaceCbBand => GDALColorInterp::GCI_YCbCr_CbBand,
Self::YCbCrSpaceCrBand => GDALColorInterp::GCI_YCbCr_CrBand,
}
}
pub fn from_name(name: &str) -> Result<Self> {
let c_str_interp_name = CString::new(name)?;
let interp_index =
unsafe { gdal_sys::GDALGetColorInterpretationByName(c_str_interp_name.as_ptr()) };
Ok(Self::from_c_int(interp_index).unwrap())
}
pub fn name(&self) -> String {
let c_ptr = unsafe { gdal_sys::GDALGetColorInterpretationName(self.c_int()) };
_string(c_ptr).unwrap_or_default()
}
}
#[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`"),
}
}
pub fn c_int(&self) -> GDALPaletteInterp::Type {
match self {
Self::Gray => GDALPaletteInterp::GPI_Gray,
Self::Rgba => GDALPaletteInterp::GPI_RGB,
Self::Cmyk => GDALPaletteInterp::GPI_CMYK,
Self::Hls => GDALPaletteInterp::GPI_HLS,
}
}
}
#[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, PartialEq, Eq)]
pub enum ColorEntry {
Gray(GrayEntry),
Rgba(RgbaEntry),
Cmyk(CmykEntry),
Hls(HlsEntry),
}
impl ColorEntry {
pub fn grey(g: i16) -> Self {
Self::Gray(GrayEntry { g })
}
pub fn rgba(r: i16, g: i16, b: i16, a: i16) -> Self {
Self::Rgba(RgbaEntry { r, g, b, a })
}
pub fn cmyk(c: i16, m: i16, y: i16, k: i16) -> Self {
Self::Cmyk(CmykEntry { c, m, y, k })
}
pub fn hls(h: i16, l: i16, s: i16) -> Self {
Self::Hls(HlsEntry { h, l, s })
}
pub fn palette_interpretation(&self) -> PaletteInterpretation {
match self {
ColorEntry::Gray(_) => PaletteInterpretation::Gray,
ColorEntry::Rgba(_) => PaletteInterpretation::Rgba,
ColorEntry::Cmyk(_) => PaletteInterpretation::Cmyk,
ColorEntry::Hls(_) => PaletteInterpretation::Hls,
}
}
fn from(e: GDALColorEntry, interp: PaletteInterpretation) -> ColorEntry {
match interp {
PaletteInterpretation::Gray => ColorEntry::Gray(GrayEntry { g: e.c1 }),
PaletteInterpretation::Rgba => ColorEntry::Rgba(RgbaEntry {
r: e.c1,
g: e.c2,
b: e.c3,
a: e.c4,
}),
PaletteInterpretation::Cmyk => ColorEntry::Cmyk(CmykEntry {
c: e.c1,
m: e.c2,
y: e.c3,
k: e.c4,
}),
PaletteInterpretation::Hls => ColorEntry::Hls(HlsEntry {
h: e.c1,
l: e.c2,
s: e.c3,
}),
}
}
}
impl From<&ColorEntry> for GDALColorEntry {
fn from(e: &ColorEntry) -> Self {
match e {
ColorEntry::Gray(e) => GDALColorEntry {
c1: e.g,
c2: 0,
c3: 0,
c4: 0,
},
ColorEntry::Rgba(e) => GDALColorEntry {
c1: e.r,
c2: e.g,
c3: e.b,
c4: e.a,
},
ColorEntry::Cmyk(e) => GDALColorEntry {
c1: e.c,
c2: e.m,
c3: e.y,
c4: e.k,
},
ColorEntry::Hls(e) => GDALColorEntry {
c1: e.h,
c2: e.l,
c3: e.s,
c4: 0,
},
}
}
}
impl Debug for ColorEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ColorEntry::Gray(e) => e.fmt(f),
ColorEntry::Rgba(e) => e.fmt(f),
ColorEntry::Cmyk(e) => e.fmt(f),
ColorEntry::Hls(e) => e.fmt(f),
}
}
}
pub struct ColorTable<'a> {
palette_interpretation: PaletteInterpretation,
c_color_table: GDALColorTableH,
rust_owned: bool,
phantom_raster_band: PhantomData<&'a RasterBand<'a>>,
}
impl<'a> ColorTable<'a> {
pub fn new(interp: PaletteInterpretation) -> Self {
let c_color_table = unsafe { GDALCreateColorTable(interp.c_int()) };
Self {
palette_interpretation: interp,
c_color_table,
rust_owned: true,
phantom_raster_band: PhantomData,
}
}
pub fn color_ramp(
start_index: u8,
start_color: &ColorEntry,
end_index: u8,
end_color: &ColorEntry,
) -> Result<ColorTable<'a>> {
if start_color.palette_interpretation() != end_color.palette_interpretation() {
Err(GdalError::BadArgument(
"start_color and end_color must have the same palette_interpretation".into(),
))
} else {
let ct = ColorTable::new(start_color.palette_interpretation());
unsafe {
GDALCreateColorRamp(
ct.c_color_table,
start_index as c_int,
&start_color.into(),
end_index as c_int,
&end_color.into(),
);
}
Ok(ct)
}
}
fn from_c_color_table(c_color_table: GDALColorTableH) -> Self {
let interp_index = unsafe { GDALGetPaletteInterpretation(c_color_table) };
ColorTable {
palette_interpretation: PaletteInterpretation::from_c_int(interp_index),
c_color_table,
rust_owned: false,
phantom_raster_band: PhantomData,
}
}
pub fn palette_interpretation(&self) -> PaletteInterpretation {
self.palette_interpretation
}
pub fn entry_count(&self) -> usize {
unsafe { gdal_sys::GDALGetColorEntryCount(self.c_color_table) as usize }
}
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
};
Some(ColorEntry::from(color_entry, self.palette_interpretation))
}
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,
})
}
pub fn set_color_entry(&mut self, index: u16, entry: &ColorEntry) {
unsafe { GDALSetColorEntry(self.c_color_table, index as c_int, &entry.into()) }
}
}
impl Drop for ColorTable<'_> {
fn drop(&mut self) {
if self.rust_owned {
unsafe { GDALDestroyColorTable(self.c_color_table) }
}
}
}
impl Default for ColorTable<'_> {
fn default() -> Self {
Self::new(PaletteInterpretation::Rgba)
}
}
impl Debug for ColorTable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let entries = (0..self.entry_count())
.filter_map(|i| self.entry(i))
.collect::<Vec<_>>();
f.debug_struct("ColorTable")
.field("palette_interpretation", &self.palette_interpretation)
.field("entries", &entries)
.finish()
}
}