use arrow_schema::{DataType, Field, FieldRef, Fields};
#[derive(Debug, PartialEq, Clone)]
pub struct RasterSchema;
impl RasterSchema {
pub fn fields() -> Fields {
Fields::from(vec![
Field::new(column::METADATA, Self::metadata_type(), false),
Field::new(column::CRS, Self::crs_type(), true), Field::new(column::BANDS, Self::bands_type(), true),
])
}
pub fn metadata_type() -> DataType {
DataType::Struct(Fields::from(vec![
Field::new(column::WIDTH, DataType::UInt64, false),
Field::new(column::HEIGHT, DataType::UInt64, false),
Field::new(column::UPPERLEFT_X, DataType::Float64, false),
Field::new(column::UPPERLEFT_Y, DataType::Float64, false),
Field::new(column::SCALE_X, DataType::Float64, false),
Field::new(column::SCALE_Y, DataType::Float64, false),
Field::new(column::SKEW_X, DataType::Float64, false),
Field::new(column::SKEW_Y, DataType::Float64, false),
]))
}
pub fn bands_type() -> DataType {
DataType::List(FieldRef::new(Field::new(
column::BAND,
Self::band_type(),
false,
)))
}
pub fn band_type() -> DataType {
DataType::Struct(Fields::from(vec![
Field::new(column::METADATA, Self::band_metadata_type(), false),
Field::new(column::DATA, Self::band_data_type(), false),
]))
}
pub fn band_metadata_type() -> DataType {
DataType::Struct(Fields::from(vec![
Field::new(column::NODATAVALUE, DataType::Binary, true), Field::new(column::STORAGE_TYPE, DataType::UInt32, false),
Field::new(column::DATATYPE, DataType::UInt32, false),
Field::new(column::OUTDB_URL, DataType::Utf8, true),
Field::new(column::OUTDB_BAND_ID, DataType::UInt32, true),
]))
}
pub fn band_data_type() -> DataType {
DataType::BinaryView
}
pub fn crs_type() -> DataType {
DataType::Utf8View
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
pub enum BandDataType {
UInt8 = 1,
UInt16 = 2,
Int16 = 3,
UInt32 = 4,
Int32 = 5,
Float32 = 6,
Float64 = 7,
UInt64 = 8,
Int64 = 9,
Int8 = 10,
}
impl BandDataType {
pub fn byte_size(&self) -> usize {
match self {
BandDataType::UInt8 | BandDataType::Int8 => 1,
BandDataType::UInt16 | BandDataType::Int16 => 2,
BandDataType::UInt32 | BandDataType::Int32 | BandDataType::Float32 => 4,
BandDataType::UInt64 | BandDataType::Int64 | BandDataType::Float64 => 8,
}
}
pub fn pixel_type_name(&self) -> &'static str {
match self {
BandDataType::UInt8 => "UNSIGNED_8BITS",
BandDataType::UInt16 => "UNSIGNED_16BITS",
BandDataType::Int16 => "SIGNED_16BITS",
BandDataType::Int32 => "SIGNED_32BITS",
BandDataType::Float32 => "REAL_32BITS",
BandDataType::Float64 => "REAL_64BITS",
BandDataType::UInt32 => "UNSIGNED_32BITS",
BandDataType::UInt64 => "UNSIGNED_64BITS",
BandDataType::Int64 => "SIGNED_64BITS",
BandDataType::Int8 => "SIGNED_8BITS",
}
}
}
#[repr(u16)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
pub enum StorageType {
InDb = 0,
OutDbRef = 1,
}
pub mod metadata_indices {
pub const WIDTH: usize = 0;
pub const HEIGHT: usize = 1;
pub const UPPERLEFT_X: usize = 2;
pub const UPPERLEFT_Y: usize = 3;
pub const SCALE_X: usize = 4;
pub const SCALE_Y: usize = 5;
pub const SKEW_X: usize = 6;
pub const SKEW_Y: usize = 7;
}
pub mod band_metadata_indices {
pub const NODATAVALUE: usize = 0;
pub const STORAGE_TYPE: usize = 1;
pub const DATATYPE: usize = 2;
pub const OUTDB_URL: usize = 3;
pub const OUTDB_BAND_ID: usize = 4;
}
pub mod band_indices {
pub const METADATA: usize = 0;
pub const DATA: usize = 1;
}
pub mod raster_indices {
pub const METADATA: usize = 0;
pub const CRS: usize = 1;
pub const BANDS: usize = 2;
}
pub mod column {
pub const METADATA: &str = "metadata";
pub const BANDS: &str = "bands";
pub const BAND: &str = "band";
pub const DATA: &str = "data";
pub const WIDTH: &str = "width";
pub const HEIGHT: &str = "height";
pub const UPPERLEFT_X: &str = "upperleft_x";
pub const UPPERLEFT_Y: &str = "upperleft_y";
pub const SCALE_X: &str = "scale_x";
pub const SCALE_Y: &str = "scale_y";
pub const SKEW_X: &str = "skew_x";
pub const SKEW_Y: &str = "skew_y";
pub const CRS: &str = "crs";
pub const NODATAVALUE: &str = "nodata_value";
pub const STORAGE_TYPE: &str = "storage_type";
pub const DATATYPE: &str = "data_type";
pub const OUTDB_URL: &str = "outdb_url";
pub const OUTDB_BAND_ID: &str = "outdb_band_id";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_raster_schema_fields() {
let fields = RasterSchema::fields();
assert_eq!(fields.len(), 3);
assert_eq!(fields[0].name(), column::METADATA);
assert_eq!(fields[1].name(), column::CRS);
assert_eq!(fields[2].name(), column::BANDS);
}
#[test]
fn test_hardcoded_indices_match_schema() {
let raster_fields = RasterSchema::fields();
assert_eq!(raster_fields.len(), 3, "Expected exactly 3 raster fields");
assert_eq!(
raster_fields[raster_indices::METADATA].name(),
column::METADATA,
"Raster metadata index mismatch"
);
assert_eq!(
raster_fields[raster_indices::CRS].name(),
column::CRS,
"Raster CRS index mismatch"
);
assert_eq!(
raster_fields[raster_indices::BANDS].name(),
column::BANDS,
"Raster BANDS index mismatch"
);
let metadata_type = RasterSchema::metadata_type();
if let DataType::Struct(metadata_fields) = metadata_type {
assert_eq!(
metadata_fields.len(),
8,
"Expected exactly 8 metadata fields"
);
assert_eq!(
metadata_fields[metadata_indices::WIDTH].name(),
column::WIDTH,
"Metadata width index mismatch"
);
assert_eq!(
metadata_fields[metadata_indices::HEIGHT].name(),
column::HEIGHT,
"Metadata height index mismatch"
);
assert_eq!(
metadata_fields[metadata_indices::UPPERLEFT_X].name(),
column::UPPERLEFT_X,
"Metadata upperleft_x index mismatch"
);
assert_eq!(
metadata_fields[metadata_indices::UPPERLEFT_Y].name(),
column::UPPERLEFT_Y,
"Metadata upperleft_y index mismatch"
);
assert_eq!(
metadata_fields[metadata_indices::SCALE_X].name(),
column::SCALE_X,
"Metadata scale_x index mismatch"
);
assert_eq!(
metadata_fields[metadata_indices::SCALE_Y].name(),
column::SCALE_Y,
"Metadata scale_y index mismatch"
);
assert_eq!(
metadata_fields[metadata_indices::SKEW_X].name(),
column::SKEW_X,
"Metadata skew_x index mismatch"
);
assert_eq!(
metadata_fields[metadata_indices::SKEW_Y].name(),
column::SKEW_Y,
"Metadata skew_y index mismatch"
);
} else {
panic!("Expected Struct type for metadata");
}
let band_metadata_type = RasterSchema::band_metadata_type();
if let DataType::Struct(band_metadata_fields) = band_metadata_type {
assert_eq!(
band_metadata_fields.len(),
5,
"Expected exactly 5 band metadata fields"
);
assert_eq!(
band_metadata_fields[band_metadata_indices::NODATAVALUE].name(),
column::NODATAVALUE,
"Band metadata nodatavalue index mismatch"
);
assert_eq!(
band_metadata_fields[band_metadata_indices::STORAGE_TYPE].name(),
column::STORAGE_TYPE,
"Band metadata storage_type index mismatch"
);
assert_eq!(
band_metadata_fields[band_metadata_indices::DATATYPE].name(),
column::DATATYPE,
"Band metadata datatype index mismatch"
);
assert_eq!(
band_metadata_fields[band_metadata_indices::OUTDB_URL].name(),
column::OUTDB_URL,
"Band metadata outdb_url index mismatch"
);
assert_eq!(
band_metadata_fields[band_metadata_indices::OUTDB_BAND_ID].name(),
column::OUTDB_BAND_ID,
"Band metadata outdb_band_id index mismatch"
);
} else {
panic!("Expected Struct type for band metadata");
}
let band_type = RasterSchema::band_type();
if let DataType::Struct(band_fields) = band_type {
assert_eq!(band_fields.len(), 2, "Expected exactly 2 band fields");
assert_eq!(
band_fields[band_indices::METADATA].name(),
column::METADATA,
"Band metadata index mismatch"
);
assert_eq!(
band_fields[band_indices::DATA].name(),
column::DATA,
"Band data index mismatch"
);
} else {
panic!("Expected Struct type for band");
}
}
#[test]
fn test_band_data_type_byte_size() {
assert_eq!(BandDataType::UInt8.byte_size(), 1);
assert_eq!(BandDataType::Int8.byte_size(), 1);
assert_eq!(BandDataType::UInt16.byte_size(), 2);
assert_eq!(BandDataType::Int16.byte_size(), 2);
assert_eq!(BandDataType::UInt32.byte_size(), 4);
assert_eq!(BandDataType::Int32.byte_size(), 4);
assert_eq!(BandDataType::Float32.byte_size(), 4);
assert_eq!(BandDataType::UInt64.byte_size(), 8);
assert_eq!(BandDataType::Int64.byte_size(), 8);
assert_eq!(BandDataType::Float64.byte_size(), 8);
}
#[test]
fn test_band_data_type_pixel_type_name() {
assert_eq!(BandDataType::UInt8.pixel_type_name(), "UNSIGNED_8BITS");
assert_eq!(BandDataType::Int8.pixel_type_name(), "SIGNED_8BITS");
assert_eq!(BandDataType::UInt16.pixel_type_name(), "UNSIGNED_16BITS");
assert_eq!(BandDataType::Int16.pixel_type_name(), "SIGNED_16BITS");
assert_eq!(BandDataType::UInt32.pixel_type_name(), "UNSIGNED_32BITS");
assert_eq!(BandDataType::Int32.pixel_type_name(), "SIGNED_32BITS");
assert_eq!(BandDataType::Float32.pixel_type_name(), "REAL_32BITS");
assert_eq!(BandDataType::UInt64.pixel_type_name(), "UNSIGNED_64BITS");
assert_eq!(BandDataType::Int64.pixel_type_name(), "SIGNED_64BITS");
assert_eq!(BandDataType::Float64.pixel_type_name(), "REAL_64BITS");
}
}