diff --git a/wxbox-pal/src/lib.rs b/wxbox-pal/src/lib.rs index a57f589..e350f0f 100644 --- a/wxbox-pal/src/lib.rs +++ b/wxbox-pal/src/lib.rs @@ -1,19 +1,7 @@ -use std::collections::BTreeMap; + use ordered_float::OrderedFloat; -#[macro_export] -macro_rules! color { - ($v:expr,$r:expr,$g:expr,$b:expr) => { (OrderedFloat(f64::from($v)), Color { red: $r, green: $g, blue: $b, alpha: 255 }) }; - ($v:expr,$r:expr,$g:expr,$b:expr,$a:expr) => { (OrderedFloat(f64::from($v)), Color { red: $r, green: $g, blue: $b, alpha: $a }) }; - (rgb: $r:expr,$g:expr,$b:expr) => { Color { red: $r, green: $g, blue: $b, alpha: 255 }}; - (rgba: $r:expr,$g:expr,$b:expr,$a:expr) => { Color { red: $r, green: $g, blue: $b, alpha: $a }}; -} - -pub struct Palette { - pub colors: BTreeMap, Color>, - pub clear_value: f64 -} -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone)] pub struct Color { pub red: u8, pub green: u8, @@ -21,70 +9,74 @@ pub struct Color { pub alpha: u8 } -impl Color { - pub fn average(self, other: Color) -> Color { - Color { - red: (self.red + other.red) / 2, - green: (self.green + other.green) / 2, - blue: (self.blue + other.blue) / 2, - alpha: (self.alpha + other.alpha) / 2 - } - } +pub type Palette = Vec<((OrderedFloat, OrderedFloat), (Color, Color))>; + +macro_rules! c { + ($r:expr,$g:expr,$b:expr) => { + Color { red: $r, green: $g, blue: $b, alpha: 255 } + }; + ($r:expr,$g:expr,$b:expr,$a:expr) => { + Color { red: $r, green: $g, blue: $b, alpha: $a } + }; +} +macro_rules! r { + ($f:expr,$t:expr) => { + (OrderedFloat($f), OrderedFloat($t)) + }; } -impl Palette { - pub fn color_for(&self, val: f64) -> Color { - if val <= self.clear_value { - Color { red: 0, green: 0, blue: 0, alpha: 0 } - } else if self.colors.is_empty() { - panic!("color_for() on empty palette"); - } else { - let mut best_v: Option = None; - for (k, v) in &self.colors { - if val < **k { - best_v = Some(*v); - } +pub fn create_test_palette() -> Palette { + let mut out = vec![]; + + out.push((r!(f64::NEG_INFINITY, 10.0), (c!(164, 164, 255), c!(164, 164, 255)))); + out.push((r!(10.0, 20.0), (c!(64, 128, 255), c!(32, 64, 128)))); + out.push((r!(20.0, 30.0), (c!(0, 255, 0), c!(0, 128, 0)))); + out.push((r!(30.0, 40.0), (c!(255, 255, 0), c!(255, 128, 0)))); + out.push((r!(40.0, 50.0), (c!(255, 255, 0), c!(255, 128, 0)))); + out.push((r!(50.0, 60.0), (c!(255, 0, 0), c!(160, 0, 0)))); + out.push((r!(60.0, 70.0), (c!(255, 0, 255), c!(128, 0, 128)))); + out.push((r!(70.0, 80.0), (c!(255, 255, 255), c!(128, 128, 128)))); + out.push((r!(80.0, f64::INFINITY), (c!(128, 128, 128), c!(128, 128, 128)))); + + out +} + +pub trait ColorPalette { + fn colorize(&self, at: f64) -> Color; +} +impl ColorPalette for Palette { + fn colorize(&self, at: f64) -> Color { + for (raw_range, color_range) in self { + let range = *raw_range.0 .. *raw_range.1; + if range.contains(&at) { + + let mapped_end = *raw_range.1 - *raw_range.0; + let mapped_point = at - *raw_range.0; + let t = mapped_point / mapped_end; + + let color_start = color_range.0; + let color_end = color_range.1; + + return lerp_color(color_start, color_end, t); } - best_v.expect("color_for() on empty palette?") + } + Color { + red: 0, + green: 0, + blue: 0, + alpha: 0 } } } -/* -#[cfg(test)] -mod tests { - use std::collections::BTreeMap; - use crate::Palette; - use crate::Color; - use ordered_float::OrderedFloat; - #[test] - fn basic_palette_test() { - let pal = Palette { - colors: BTreeMap::from([ - color!(-30, 165, 165, 165), - color!(10, 0, 165, 255), - color!(20, 16, 255, 8), - color!(35, 251, 238, 0), - color!(50, 255, 0, 0), - color!(65, 247, 1, 249), - color!(75, 255, 255, 255), - color!(85, 184, 184, 184), - color!(95, 184, 184, 184) - ]), - clear_value: -99.0 - }; - - - - assert_eq!(pal.color_for(-999.0), color!(rgba: 0, 0, 0, 0)); - assert_eq!(pal.color_for(-99.0), color!(rgba: 0, 0, 0, 0)); - - assert_eq!(pal.color_for(-30.0), color!(rgb: 165, 165, 165)); - assert_eq!(pal.color_for(-25.0), color!(rgb: 165, 165, 165)); - - assert_eq!(pal.color_for(10.0), color!(rgb: 0, 165, 255)); - assert_eq!(pal.color_for(15.0), color!(rgb: 0, 165, 255)); - } +fn lerp(p0: f64, p1: f64, t: f64) -> f64 { + p0 + t * (p1 - p0) } - - */ \ No newline at end of file +fn lerp_color(c0: Color, c1: Color, t: f64) -> Color { + Color { + red: lerp(c0.red as f64, c1.red as f64, t) as u8, + green: lerp(c0.green as f64, c1.green as f64, t) as u8, + blue: lerp(c0.blue as f64, c1.blue as f64, t) as u8, + alpha: lerp(c0.alpha as f64, c1.alpha as f64, t) as u8 + } +} \ No newline at end of file diff --git a/wxbox-tiler/src/sources/noaa/mod.rs b/wxbox-tiler/src/sources/noaa/mod.rs index f83678f..7cfc548 100644 --- a/wxbox-tiler/src/sources/noaa/mod.rs +++ b/wxbox-tiler/src/sources/noaa/mod.rs @@ -10,11 +10,10 @@ use flate2::read::GzDecoder; use ndarray::{Zip}; use ordered_float::{FloatCore, OrderedFloat}; use png::{BitDepth, ColorType, Encoder}; -use wxbox_pal::{Color, Palette}; +use wxbox_pal::{Color, ColorPalette, create_test_palette, Palette}; use crate::{AppState, LutKey}; use crate::grib2::{closest_key, LookupTable2D}; use crate::pixmap::Pixmap; -use wxbox_pal::color; use crate::tile::xyz_to_bbox; #[get("/mrms_cref/{z}/{x}/{y}.png")] @@ -90,7 +89,7 @@ async fn mrms_cref(path: web::Path<(u8, u32, u32)>, data: Data) -> Htt } if let Some(map) = data.lut_cache.read().await.get(&LutKey::NoaaMrmsCref) { - +/* let pal = Palette { colors: BTreeMap::from([ color!(-30, 165, 165, 165, 0), @@ -106,6 +105,8 @@ async fn mrms_cref(path: web::Path<(u8, u32, u32)>, data: Data) -> Htt clear_value: -99.0 }; + + */ let mut image: Pixmap = Pixmap::new(); let degrees_lat_per_px = (max_lat - min_lat) / 256.0; @@ -145,10 +146,14 @@ async fn mrms_cref(path: web::Path<(u8, u32, u32)>, data: Data) -> Htt continue; } + let pal = create_test_palette(); + let color = match value { -999.0 => Color { red: 0, green: 0, blue: 0, alpha: 30 }, -99.0 => Color { red: 0, green: 0, blue: 0, alpha: 0 }, value_at_pos => { + pal.colorize(*value_at_pos) + /* match *value_at_pos { ..=-30.0 => color!(rgb: 165, 165, 165), -30.0..=10.0 => color!(rgb: 0, 165, 255), @@ -160,6 +165,8 @@ async fn mrms_cref(path: web::Path<(u8, u32, u32)>, data: Data) -> Htt 75.0.. => color!(rgb: 184, 184, 184), _ => unreachable!() } + + */ } }; image.set(y, x, color);