use crate::error::StaticError;
use crate::grid::Grid;
use crate::model::model::Georef;
use crate::model::pipeline::areal_lattice;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct ValueRange {
pub min: f64,
pub max: f64,
}
impl ValueRange {
pub(crate) fn of(it: impl Iterator<Item = f64>) -> Self {
let (mut min, mut max) = (f64::INFINITY, f64::NEG_INFINITY);
for v in it {
if v.is_finite() {
min = min.min(v);
max = max.max(v);
}
}
if min <= max {
Self { min, max }
} else {
Self {
min: f64::NAN,
max: f64::NAN,
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct GridFrame {
pub origin_x: f64,
pub origin_y: f64,
pub spacing_x: f64,
pub spacing_y: f64,
pub ncol: usize,
pub nrow: usize,
}
impl GridFrame {
pub(crate) fn of_grid(grid: &Grid, georef: Option<Georef>) -> Result<Self, StaticError> {
if let Some(g) = georef {
let dims = grid.dims();
let (ncol, nrow) = (dims.ni, dims.nj);
if ncol < 2 || nrow < 2 {
return Err(StaticError::InvalidInput(format!(
"view frame needs an areal lattice of at least 2x2 columns, got {ncol}x{nrow}"
)));
}
return Ok(Self {
origin_x: g.origin_x,
origin_y: g.origin_y,
spacing_x: g.spacing_x,
spacing_y: g.spacing_y,
ncol,
nrow,
});
}
let lat = areal_lattice(grid)?;
Ok(Self {
origin_x: lat.xori,
origin_y: lat.yori,
spacing_x: lat.xinc,
spacing_y: lat.yinc,
ncol: lat.ncol,
nrow: lat.nrow,
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ScalarLayer {
pub name: String,
pub units: String,
pub values: Vec<f64>,
pub range: ValueRange,
}
impl ScalarLayer {
pub(crate) fn new(name: impl Into<String>, units: impl Into<String>, values: Vec<f64>) -> Self {
let range = ValueRange::of(values.iter().copied());
Self {
name: name.into(),
units: units.into(),
values,
range,
}
}
}