use std::slice::Iter;
use crate::{
codetables::SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS,
datatypes::*,
error::*,
helpers::{GribInt, read_as},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Indicator {
pub discipline: u8,
pub total_length: u64,
}
impl Indicator {
pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, ParseError> {
let discipline = slice[6];
let version = slice[7];
if version != 2 {
return Err(ParseError::GRIBVersionMismatch(version));
}
let total_length = read_as!(u64, slice, 8);
Ok(Self {
discipline,
total_length,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Identification {
pub(crate) payload: Box<[u8]>,
}
impl Identification {
pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
let size = slice.len();
if size < 16 {
Err(BuildError::SectionSizeTooSmall(size))
} else {
Ok(Self { payload: slice })
}
}
pub fn iter(&self) -> Iter<'_, u8> {
self.payload.iter()
}
#[inline]
pub fn centre_id(&self) -> u16 {
let payload = &self.payload;
read_as!(u16, payload, 0)
}
#[inline]
pub fn subcentre_id(&self) -> u16 {
let payload = &self.payload;
read_as!(u16, payload, 2)
}
#[inline]
pub fn master_table_version(&self) -> u8 {
self.payload[4]
}
#[inline]
pub fn local_table_version(&self) -> u8 {
self.payload[5]
}
#[inline]
pub fn ref_time_significance(&self) -> u8 {
self.payload[6]
}
pub fn ref_time_unchecked(&self) -> crate::def::grib2::RefTime {
let payload = &self.payload;
crate::def::grib2::RefTime::new(
read_as!(u16, payload, 7),
payload[9],
payload[10],
payload[11],
payload[12],
payload[13],
)
}
#[inline]
pub fn prod_status(&self) -> u8 {
self.payload[14]
}
#[inline]
pub fn data_type(&self) -> u8 {
self.payload[15]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocalUse {
payload: Box<[u8]>,
}
impl LocalUse {
pub fn from_payload(slice: Box<[u8]>) -> Self {
Self { payload: slice }
}
pub fn iter(&self) -> Iter<'_, u8> {
self.payload.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GridDefinition {
pub(crate) payload: Box<[u8]>,
}
impl GridDefinition {
pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
let size = slice.len();
if size < 9 {
Err(BuildError::SectionSizeTooSmall(size))
} else {
Ok(Self { payload: slice })
}
}
pub fn iter(&self) -> Iter<'_, u8> {
self.payload.iter()
}
pub fn num_points(&self) -> u32 {
let payload = &self.payload;
read_as!(u32, payload, 1)
}
pub fn grid_tmpl_num(&self) -> u16 {
let payload = &self.payload;
read_as!(u16, payload, 7)
}
}
const START_OF_PROD_TEMPLATE: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ProdDefinition {
pub(crate) payload: Box<[u8]>,
}
impl ProdDefinition {
pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
let size = slice.len();
if size < START_OF_PROD_TEMPLATE {
Err(BuildError::SectionSizeTooSmall(size))
} else {
Ok(Self { payload: slice })
}
}
pub fn iter(&self) -> Iter<'_, u8> {
self.payload.iter()
}
pub fn num_coordinates(&self) -> u16 {
let payload = &self.payload;
read_as!(u16, payload, 0)
}
pub fn prod_tmpl_num(&self) -> u16 {
let payload = &self.payload;
read_as!(u16, payload, 2)
}
pub(crate) fn template_supported(&self) -> bool {
SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS.contains(&self.prod_tmpl_num())
}
pub fn parameter_category(&self) -> Option<u8> {
if self.template_supported() {
self.payload.get(START_OF_PROD_TEMPLATE).copied()
} else {
None
}
}
pub fn parameter_number(&self) -> Option<u8> {
if self.template_supported() {
self.payload.get(START_OF_PROD_TEMPLATE + 1).copied()
} else {
None
}
}
pub fn generating_process(&self) -> Option<u8> {
if self.template_supported() {
let index = match self.prod_tmpl_num() {
0..=39 => Some(2),
40..=43 => Some(4),
44..=46 => Some(15),
47 => Some(2),
48..=49 => Some(26),
51 => Some(2),
55..=56 => Some(8),
59 => Some(8),
60..=61 => Some(2),
62..=63 => Some(8),
70..=73 => Some(7),
76..=79 => Some(5),
80..=81 => Some(27),
82 => Some(16),
83 => Some(2),
84 => Some(16),
85 => Some(15),
86..=91 => Some(2),
254 => Some(2),
1000..=1101 => Some(2),
_ => None,
}?;
self.payload.get(START_OF_PROD_TEMPLATE + index).copied()
} else {
None
}
}
pub fn forecast_time(&self) -> Option<ForecastTime> {
if self.template_supported() {
let unit_index = match self.prod_tmpl_num() {
0..=15 => Some(8),
32..=34 => Some(8),
40..=43 => Some(10),
44..=47 => Some(21),
48..=49 => Some(32),
51 => Some(8),
55..=56 => Some(14),
59 => Some(14),
60..=61 => Some(8),
62..=63 => Some(14),
70..=73 => Some(13),
76..=79 => Some(11),
80..=81 => Some(33),
82..=84 => Some(22),
85 => Some(21),
86..=87 => Some(8),
88 => Some(26),
91 => Some(8),
1000..=1101 => Some(8),
_ => None,
}?;
let unit_index = START_OF_PROD_TEMPLATE + unit_index;
let unit = self.payload.get(unit_index).copied();
let start = unit_index + 1;
let end = unit_index + 5;
let time = u32::from_be_bytes(self.payload[start..end].try_into().unwrap());
unit.map(|v| ForecastTime::from_numbers(v, time))
} else {
None
}
}
pub fn fixed_surfaces(&self) -> Option<(FixedSurface, FixedSurface)> {
if self.template_supported() {
let index = match self.prod_tmpl_num() {
0..=15 => Some(13),
40..=43 => Some(15),
44 => Some(24),
45..=47 => Some(26),
48..=49 => Some(37),
51 => Some(13),
55..=56 => Some(19),
59 => Some(19),
60..=61 => Some(13),
62..=63 => Some(19),
70..=73 => Some(18),
76..=79 => Some(16),
80..=81 => Some(38),
82..=84 => Some(27),
85 => Some(26),
86..=87 => Some(13),
88 => Some(5),
91 => Some(13),
1100..=1101 => Some(13),
_ => None,
}?;
let first_surface = self.read_surface_from(index);
let second_surface = self.read_surface_from(index + 6);
first_surface.zip(second_surface)
} else {
None
}
}
fn read_surface_from(&self, index: usize) -> Option<FixedSurface> {
let index = START_OF_PROD_TEMPLATE + index;
let surface_type = self.payload.get(index).copied();
let scale_factor = self.payload.get(index + 1).map(|v| (*v).as_grib_int());
let start = index + 2;
let end = index + 6;
let scaled_value =
u32::from_be_bytes(self.payload[start..end].try_into().unwrap()).as_grib_int();
surface_type
.zip(scale_factor)
.map(|(stype, factor)| FixedSurface::new(stype, factor, scaled_value))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ReprDefinition {
pub(crate) payload: Box<[u8]>,
}
impl ReprDefinition {
pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
let size = slice.len();
if size < 6 {
Err(BuildError::SectionSizeTooSmall(size))
} else {
Ok(Self { payload: slice })
}
}
pub fn iter(&self) -> Iter<'_, u8> {
self.payload.iter()
}
pub fn num_points(&self) -> u32 {
let payload = &self.payload;
read_as!(u32, payload, 0)
}
pub fn repr_tmpl_num(&self) -> u16 {
let payload = &self.payload;
read_as!(u16, payload, 4)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BitMap {
pub bitmap_indicator: u8,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prod_definition_parameters() {
let data = ProdDefinition::from_payload(
vec![
0, 0, 0, 0, 193, 0, 2, 153, 255, 0, 0, 0, 0, 0, 0, 0, 40, 1, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255,
]
.into_boxed_slice(),
)
.unwrap();
assert_eq!(data.parameter_category(), Some(193));
assert_eq!(data.parameter_number(), Some(0));
assert_eq!(
data.forecast_time(),
Some(ForecastTime::from_numbers(0, 40))
);
assert_eq!(
data.fixed_surfaces(),
Some((
FixedSurface::new(1, -127, -2147483647),
FixedSurface::new(255, -127, -2147483647)
))
);
}
}