use crate::dataset::Dataset;
use crate::gdal_major_object::MajorObject;
use crate::metadata::Metadata;
use crate::raster::GdalType;
use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string};
use gdal_sys::{
self, CPLErr, GDALColorEntry, GDALColorInterp, GDALColorTableH, GDALComputeRasterMinMax,
GDALCreateColorRamp, GDALCreateColorTable, GDALDataType, GDALDestroyColorTable,
GDALGetPaletteInterpretation, GDALGetRasterStatistics, GDALMajorObjectH, GDALPaletteInterp,
GDALRIOResampleAlg, GDALRWFlag, GDALRasterBandH, GDALRasterIOExtraArg, GDALSetColorEntry,
GDALSetRasterColorTable,
};
use libc::c_int;
use std::ffi::CString;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
#[cfg(feature = "ndarray")]
use ndarray::Array2;
use crate::errors::*;
#[derive(Debug, Copy, Clone)]
#[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 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 libc::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,
}
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,
}
}
}
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,
} = 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,
}
}
}
pub struct RasterBand<'a> {
c_rasterband: GDALRasterBandH,
dataset: &'a Dataset,
}
impl<'a> RasterBand<'a> {
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) as usize;
assert_eq!(buffer.len(), 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 as c_int,
window.1 as c_int,
window_size.0 as c_int,
window_size.1 as c_int,
buffer.as_mut_ptr() as GDALRasterBandH,
size.0 as c_int,
size.1 as c_int,
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),
size: (usize, usize),
e_resample_alg: Option<ResampleAlg>,
) -> Result<Buffer<T>> {
let pixels = (size.0 * size.1) as usize;
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 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 GDALRasterBandH,
size.0 as c_int,
size.1 as c_int,
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 { size, data })
}
#[cfg(feature = "ndarray")]
#[cfg_attr(docsrs, doc(cfg(feature = "array")))]
pub fn read_as_array<T: Copy + GdalType>(
&self,
window: (isize, isize),
window_size: (usize, usize),
array_size: (usize, usize),
e_resample_alg: Option<ResampleAlg>,
) -> Result<Array2<T>> {
let data = self.read_as::<T>(window, window_size, array_size, e_resample_alg)?;
Ok(Array2::from_shape_vec(
(array_size.1, array_size.0),
data.data,
)?)
}
pub fn read_band_as<T: Copy + GdalType>(&self) -> Result<Buffer<T>> {
let size = self.size();
self.read_as::<T>(
(0, 0),
(size.0 as usize, size.1 as usize),
(size.0 as usize, size.1 as usize),
None,
)
}
#[cfg(feature = "ndarray")]
#[cfg_attr(docsrs, doc(cfg(feature = "array")))]
pub fn read_block<T: Copy + GdalType>(&self, block_index: (usize, usize)) -> Result<Array2<T>> {
let size = self.block_size();
let pixels = (size.0 * size.1) as usize;
let mut data: Vec<T> = Vec::with_capacity(pixels);
let rv = unsafe {
gdal_sys::GDALReadBlock(
self.c_rasterband,
block_index.0 as c_int,
block_index.1 as c_int,
data.as_mut_ptr() as GDALRasterBandH,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
unsafe {
data.set_len(pixels);
};
Array2::from_shape_vec((size.1, size.0), data).map_err(Into::into)
}
pub fn write<T: GdalType + Copy>(
&mut self,
window: (isize, isize),
window_size: (usize, usize),
buffer: &Buffer<T>,
) -> Result<()> {
assert_eq!(buffer.data.len(), buffer.size.0 * buffer.size.1);
let rv = unsafe {
gdal_sys::GDALRasterIO(
self.c_rasterband,
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 GDALRasterBandH,
buffer.size.0 as c_int,
buffer.size.1 as c_int,
T::gdal_ordinal(),
0,
0,
)
};
if rv != CPLErr::CE_None {
return Err(_last_cpl_err(rv));
}
Ok(())
}
pub fn band_type(&self) -> GDALDataType::Type {
unsafe { gdal_sys::GDALGetRasterDataType(self.c_rasterband) }
}
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 as f64);
}
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(())
}
}
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 as f64);
}
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 as f64);
}
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(())
}
#[cfg(any(all(major_is_2, minor_ge_2), major_ge_3))] pub fn actual_block_size(&self, offset: (isize, isize)) -> Result<(usize, usize)> {
let mut block_size_x = 0;
let mut block_size_y = 0;
let rv = unsafe {
gdal_sys::GDALGetActualBlockSize(
self.c_rasterband,
offset.0 as libc::c_int,
offset.1 as libc::c_int,
&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: isize) -> Result<RasterBand<'a>> {
unsafe {
let c_band = self.c_rasterband;
let overview = gdal_sys::GDALGetOverview(c_band, overview_index as libc::c_int);
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 str_ptr = unsafe { gdal_sys::GDALGetRasterUnitType(self.c_rasterband) };
if str_ptr.is_null() {
return String::new();
}
_string(str_ptr)
}
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,
libc::c_int::from(is_approx_ok),
libc::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,
libc::c_int::from(is_approx_ok),
&mut min_max as *mut f64,
)
};
Ok(StatisticsMinMax {
min: min_max[0],
max: min_max[1],
})
}
}
#[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,
}
impl<'a> MajorObject for RasterBand<'a> {
unsafe fn gdal_object_ptr(&self) -> GDALMajorObjectH {
self.c_rasterband
}
}
impl<'a> Metadata for RasterBand<'a> {}
pub struct Buffer<T: GdalType> {
pub size: (usize, usize),
pub data: Vec<T>,
}
impl<T: GdalType> Buffer<T> {
pub fn new(size: (usize, usize), data: Vec<T>) -> Buffer<T> {
Buffer { size, data }
}
}
pub type ByteBuffer = Buffer<u8>;
#[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 rv = unsafe { gdal_sys::GDALGetColorInterpretationName(self.c_int()) };
_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`"),
}
}
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()
}
}