aaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

This commit is contained in:
core 2024-10-19 22:12:46 -04:00
parent 2dd910e919
commit 3633c01142
Signed by: core
GPG Key ID: FDBF740DADDCEECF
9 changed files with 415 additions and 36 deletions

View File

@ -4,6 +4,8 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/wxbox-pal/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/wxbox-pal/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/wxbox-tiler/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/wxbox-tiler/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/wxbox-eccodes-sys/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/wxbox-web/wxbox-eccodes-sys/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" /> <excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" /> <excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" /> <excludeFolder url="file://$MODULE_DIR$/tmp" />

242
Cargo.lock generated
View File

@ -285,6 +285,29 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bindgen"
version = "0.69.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
dependencies = [
"bitflags 2.6.0",
"cexpr",
"clang-sys",
"itertools",
"lazy_static",
"lazycell",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
"which",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -365,6 +388,15 @@ dependencies = [
"shlex", "shlex",
] ]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -385,6 +417,17 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]] [[package]]
name = "convert_case" name = "convert_case"
version = "0.4.0" version = "0.4.0"
@ -499,6 +542,40 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "eccodes"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7851e2dfed8264270ebee69af9ab16ffc6d0981f7829d4c8379359b97f5abeed"
dependencies = [
"eccodes-sys",
"errno",
"fallible-iterator",
"fallible-streaming-iterator",
"libc",
"log",
"ndarray",
"num-derive",
"num-traits",
"thiserror",
]
[[package]]
name = "eccodes-sys"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2bdc129e03da6393abb9fea441903fb6a5e88f1fd15da111c17be03017fd59"
dependencies = [
"bindgen",
"pkg-config",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.34" version = "0.8.34"
@ -524,6 +601,18 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.1.1" version = "2.1.1"
@ -645,6 +734,12 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]] [[package]]
name = "grib" name = "grib"
version = "0.10.2" version = "0.10.2"
@ -717,6 +812,15 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.12" version = "0.2.12"
@ -901,6 +1005,15 @@ version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.11"
@ -931,12 +1044,34 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.159" version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "libloading"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.14" version = "0.4.14"
@ -976,6 +1111,16 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matrixmultiply"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
@ -988,6 +1133,12 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.0" version = "0.8.0"
@ -1028,6 +1179,31 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "ndarray"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"portable-atomic",
"portable-atomic-util",
"rawpointer",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]] [[package]]
name = "num" name = "num"
version = "0.4.3" version = "0.4.3"
@ -1067,6 +1243,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.46" version = "0.1.46"
@ -1272,6 +1459,21 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "portable-atomic"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
[[package]]
name = "portable-atomic-util"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90a7d5beecc52a491b54d6dd05c7a45ba1801666a5baad9fdbfc6fef8d2d206c"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@ -1287,6 +1489,16 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "prettyplease"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904afd36257cdb6ce0bee88b7981847bd7b955e5e216bb32f466b302923ad446"
dependencies = [
"proc-macro2",
"syn",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.2.0" version = "3.2.0"
@ -1344,6 +1556,12 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.7" version = "0.5.7"
@ -1452,6 +1670,12 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.1"
@ -1678,9 +1902,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.79" version = "2.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" checksum = "e6e185e337f816bc8da115b8afcb3324006ccc82eeaddf35113888d3bd8e44ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2064,6 +2288,18 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.52.0" version = "0.52.0"
@ -2207,9 +2443,11 @@ name = "wxbox-tiler"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"actix-web", "actix-web",
"eccodes",
"flate2", "flate2",
"grib", "grib",
"mime", "mime",
"ndarray",
"ordered-float", "ordered-float",
"png", "png",
"reqwest", "reqwest",

View File

@ -1,10 +1,19 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
pub struct Palette { #[macro_export]
pub colors: BTreeMap<OrderedFloat<f64>, Color> 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 }};
} }
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Palette {
pub colors: BTreeMap<OrderedFloat<f64>, Color>,
pub clear_value: f64
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Color { pub struct Color {
pub red: u8, pub red: u8,
pub green: u8, pub green: u8,
@ -25,7 +34,9 @@ impl Color {
impl Palette { impl Palette {
pub fn color_for(&self, val: f64) -> Color { pub fn color_for(&self, val: f64) -> Color {
if self.colors.is_empty() { 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"); panic!("color_for() on empty palette");
} else { } else {
let mut best_v: Option<Color> = None; let mut best_v: Option<Color> = None;
@ -38,3 +49,40 @@ impl Palette {
} }
} }
} }
#[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));
}
}

View File

@ -17,3 +17,5 @@ tokio = "1"
wxbox-pal = { version = "0.1", path = "../wxbox-pal" } wxbox-pal = { version = "0.1", path = "../wxbox-pal" }
png = "0.17" png = "0.17"
mime = "0.3.17" mime = "0.3.17"
eccodes = { version = "0.12", features = ["message_ndarray"] }
ndarray = "0.16"

View File

@ -70,9 +70,9 @@ pub enum GribMapError {
IoError(#[from] std::io::Error) IoError(#[from] std::io::Error)
} }
pub type GriddedLookupTable = BTreeMap<OrderedFloat<f64>, BTreeMap<OrderedFloat<f64>, f64>>; pub type LookupTable2D = BTreeMap<OrderedFloat<f64>, BTreeMap<OrderedFloat<f64>, f64>>;
pub async fn map_grib2(i: Response) -> Result<GriddedLookupTable, GribMapError> { pub async fn map_grib2(i: Response) -> Result<LookupTable2D, GribMapError> {
let b = Cursor::new(i.bytes().await?.to_vec()); let b = Cursor::new(i.bytes().await?.to_vec());
let mut decoder = GzDecoder::new(b); let mut decoder = GzDecoder::new(b);

View File

@ -6,16 +6,22 @@ mod pixmap;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::env::args; use std::env::args;
use std::ffi::c_void;
use std::fmt::Debug;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::{BufReader, Cursor, Read};
use std::ptr::{null, null_mut};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use std::time::SystemTime; use std::time::SystemTime;
use actix_web::{App, get, HttpServer}; use actix_web::{App, get, HttpServer};
use actix_web::web::Data; use actix_web::web::Data;
use eccodes::{CodesHandle, FallibleStreamingIterator, KeyedMessage, KeyRead, ProductKind};
use eccodes::codes_handle::CodesFile;
use flate2::read::GzDecoder;
use grib::codetables::{CodeTable1_4, CodeTable4_2, CodeTable4_3, Lookup}; use grib::codetables::{CodeTable1_4, CodeTable4_2, CodeTable4_3, Lookup};
use grib::{GribError, SectionBody}; use grib::{GribError, SectionBody};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use crate::grib2::GriddedLookupTable; use crate::grib2::{LookupTable2D, lookup};
use crate::sources::noaa::mrms_cref; use crate::sources::noaa::mrms_cref;
#[global_allocator] #[global_allocator]
@ -28,12 +34,16 @@ pub enum LutKey {
} }
pub struct AppState { pub struct AppState {
lut_cache: RwLock<BTreeMap<LutKey, GriddedLookupTable>>, lut_cache: RwLock<BTreeMap<LutKey, LookupTable2D>>,
lut_cache_timestamps: RwLock<BTreeMap<LutKey, SystemTime>> lut_cache_timestamps: RwLock<BTreeMap<LutKey, SystemTime>>
} }
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
let f = reqwest::get("https://mrms.ncep.noaa.gov/data/2D/ReflectivityAtLowestAltitude/MRMS_ReflectivityAtLowestAltitude.latest.grib2.gz").await.unwrap();
let data = Data::new(AppState { let data = Data::new(AppState {
lut_cache: RwLock::new(BTreeMap::new()), lut_cache: RwLock::new(BTreeMap::new()),
lut_cache_timestamps: RwLock::new(BTreeMap::new()) lut_cache_timestamps: RwLock::new(BTreeMap::new())
@ -43,8 +53,9 @@ async fn main() -> std::io::Result<()> {
.service(mrms_cref) .service(mrms_cref)
.app_data(data.clone()) .app_data(data.clone())
}) })
.bind(("127.0.0.1", 8080))? .bind(("::", 8080))?
.run() .run()
.await .await
} }

View File

@ -1,26 +1,25 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::f64::consts::PI;
use std::io::{BufReader, BufWriter, Cursor, Read}; use std::io::{BufReader, BufWriter, Cursor, Read};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use actix_web::{get, HttpResponse, web}; use actix_web::{get, HttpResponse, web};
use actix_web::http::header::{CONTENT_TYPE, HeaderValue}; use actix_web::http::header::{CONTENT_TYPE, HeaderValue};
use actix_web::http::{header, StatusCode}; use actix_web::http::{header, StatusCode};
use actix_web::web::Data; use actix_web::web::Data;
use eccodes::{CodesHandle, FallibleStreamingIterator, ProductKind};
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use grib::{GribError, SectionBody}; use grib::{GribError, SectionBody};
use grib::codetables::{CodeTable4_2, CodeTable4_3, Lookup}; use grib::codetables::{CodeTable4_2, CodeTable4_3, Lookup};
use ndarray::{Order, Zip};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use png::{BitDepth, ColorType, Encoder}; use png::{BitDepth, ColorType, Encoder};
use wxbox_pal::{Color, Palette}; use wxbox_pal::{Color, Palette};
use crate::{AppState, LutKey}; use crate::{AppState, LutKey};
use crate::coords::bounds; use crate::coords::{bounds, ul};
use crate::grib2::{closest_key, lookup, map_grib2}; use crate::grib2::{closest_key, LookupTable2D, lookup, map_grib2};
use crate::pixmap::Pixmap; use crate::pixmap::Pixmap;
use wxbox_pal::color;
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 }) };
}
#[get("/mrms_cref/{z}/{x}/{y}.png")] #[get("/mrms_cref/{z}/{x}/{y}.png")]
async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data<AppState>) -> HttpResponse { async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data<AppState>) -> HttpResponse {
@ -51,20 +50,53 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data<AppState>) -> Ht
let mut lct_writer = data.lut_cache_timestamps.write().await; let mut lct_writer = data.lut_cache_timestamps.write().await;
let mut lc_writer = data.lut_cache.write().await; let mut lc_writer = data.lut_cache.write().await;
let f = reqwest::get("https://mrms.ncep.noaa.gov/data/2D/ReflectivityAtLowestAltitude/MRMS_ReflectivityAtLowestAltitude.latest.grib2.gz").await.unwrap(); let f = reqwest::get("https://mrms.ncep.noaa.gov/data/2D/ReflectivityAtLowestAltitude/MRMS_ReflectivityAtLowestAltitude.latest.grib2.gz").await.unwrap();
let map = map_grib2(f).await.unwrap(); let data = f.bytes().await.unwrap().to_vec();
let b = Cursor::new(data);
let mut decoder = GzDecoder::new(b);
let mut data = vec![];
decoder.read_to_end(&mut data).expect("decoding failure");
let product_kind = ProductKind::GRIB;
let mut handle = CodesHandle::new_from_memory(data.clone(), product_kind).unwrap();
let msg = handle.next().expect("empty grib2 file").expect("empty grib2 file").try_clone().unwrap();
let mut map = LookupTable2D::new();
let mut arr = msg.to_lons_lats_values().unwrap();
Zip::from(&arr.latitudes)
.and(&arr.longitudes)
.and(&arr.values)
.for_each(|&lat, &long, &value| {
let lat = OrderedFloat(lat);
let mut long = long;
if long > 180.0 {
long -= 360.0;
}
let long = OrderedFloat(long);
if value != -99.0 && value != -999.0 {
println!("{}, {} => {}", lat, long, value);
}
if let Some(row) = map.get_mut(&lat) {
row.insert(long, value);
} else {
map.insert(lat, BTreeMap::from([
(long, value)
]));
}
});
lc_writer.insert(LutKey::NoaaMrmsCref, map); lc_writer.insert(LutKey::NoaaMrmsCref, map);
lct_writer.insert(LutKey::NoaaMrmsCref, SystemTime::now()); lct_writer.insert(LutKey::NoaaMrmsCref, SystemTime::now());
} }
if let Some(map) = data.lut_cache.read().await.get(&LutKey::NoaaMrmsCref) { if let Some(map) = data.lut_cache.read().await.get(&LutKey::NoaaMrmsCref) {
let closest_south = match closest_key(&map, bbox.south) {
Some(c) => c,
None => { eprintln!("gridded LUT is empty?"); return HttpResponse::new(StatusCode::NOT_FOUND); }
};
let closest_north = match closest_key(&map, bbox.north) {
Some(c) => c,
None => { eprintln!("gridded LUT is empty?"); return HttpResponse::new(StatusCode::NOT_FOUND); }
};
let pal = Palette { let pal = Palette {
colors: BTreeMap::from([ colors: BTreeMap::from([
@ -78,10 +110,48 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data<AppState>) -> Ht
color!(85, 184, 184, 184), color!(85, 184, 184, 184),
color!(95, 184, 184, 184) color!(95, 184, 184, 184)
]), ]),
clear_value: -99.0
}; };
let mut image: Pixmap = Pixmap::new(); let mut image: Pixmap = Pixmap::new();
let tile_size = 360.0 / 2.0_f64.powi(z);
let degrees_per_px = tile_size / 256.0;
let upper_left = ul(tile);
for x in 0..256 {
for y in 0..256 {
let lon = upper_left.lng + degrees_per_px * x as f64;
let lat = upper_left.lat + degrees_per_px * y as f64;
let closest_lat_in_map = &closest_key(&map, lat).unwrap();
let row = map.get(closest_lat_in_map).unwrap();
let closest_long_in_row = closest_key(&row, lon).unwrap();
let value = row.get(&closest_long_in_row).unwrap();
let color = match value {
-999.0 | -99.0 => Color { red: 0, green: 0, blue: 0, alpha: 5 },
value_at_pos => {
match *value_at_pos {
..=-30.0 => color!(rgb: 165, 165, 165),
-30.0..=10.0 => color!(rgb: 0, 165, 255),
10.0..=20.0 => color!(rgb: 16, 255, 8),
20.0..=35.0 => color!(rgb: 251, 238, 0),
35.0..=50.0 => color!(rgb: 255, 0, 0),
50.0..=65.0 => color!(rgb: 247, 1, 249),
65.0..=75.0 => color!(rgb: 255, 255, 255),
75.0.. => color!(rgb: 184, 184, 184),
_ => unreachable!()
}
}
};
image.set(x, y, color);
}
}
/*
for (lat, row) in map.range(closest_south..=closest_north) { for (lat, row) in map.range(closest_south..=closest_north) {
let closest_west = match closest_key(&row, bbox.west) { let closest_west = match closest_key(&row, bbox.west) {
Some(c) => c, Some(c) => c,
@ -94,32 +164,40 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data<AppState>) -> Ht
for (long, gridpoint) in row.range(closest_west..=closest_east) { for (long, gridpoint) in row.range(closest_west..=closest_east) {
let epsg_lon = **long; let epsg_lon = **long;
let epsg_lat = **lat; let epsg_lat = **lat;
let wm_x = epsg_lon; let wm_x = epsg_lon;
let wm_y = epsg_lat.tan().asinh(); let wm_y = epsg_lat.tan().asinh();
let x = 0.5 + wm_x / 360.0; let x = 0.5 + wm_x / 360.0;
let y = 0.5 - wm_y / (2.0 * std::f64::consts::PI); let y = 0.5 - wm_y / (2.0 * std::f64::consts::PI);
let zoom = z; let zoom = z;
let n = 2.0_f64.powi(zoom); let n = 2.0_f64.powi(zoom);
let x_tile = n * x; let x_tile = n * x;
let y_tile = n * y; let y_tile = n * y;
let x_pixel = x_tile.fract() * 256.0; let x_pixel = x_tile.fract() * 256.0;
let y_pixel = y_tile.fract() * 256.0; let y_pixel = y_tile.fract() * 256.0;
let x = x_pixel.trunc() as usize; let xf = x_pixel.trunc() as usize;
let y = y_pixel.trunc() as usize; let yf = y_pixel.trunc() as usize;
println!("@{} -> {}: {} -> {} -> {} -> {} -> {} => {}\n{} -> {} -> {} -> {} -> {} => {}", z, n, epsg_lon, wm_x, x, x_tile, x_pixel, xf, epsg_lat, wm_y, y, y_tile, y_pixel, yf);
let mut color = pal.color_for(*gridpoint); let mut color = pal.color_for(*gridpoint);
let existing = image.get(x, y); let existing = image.get(xf, yf);
if existing != (Color { red: 0, green: 0, blue: 0, alpha: 0 }) { if existing != (Color { red: 0, green: 0, blue: 0, alpha: 0 }) {
color = color.average(existing); color = color.average(existing);
} }
image.set(x, y, color); image.set(xf, yf, color);
} }
} }
*/
let mut buf: Vec<u8> = vec![]; let mut buf: Vec<u8> = vec![];
// borrow checker insanity // borrow checker insanity
{ {

View File

@ -15,7 +15,7 @@
} }
).addTo(m); ).addTo(m);
L.tileLayer( L.tileLayer(
'http://localhost:8080/mrms_cref/{z}/{x}/{y}.png', 'http://100.64.0.1:8080/mrms_cref/{z}/{x}/{y}.png',
{ {
attribution: '&copy; NOAA', attribution: '&copy; NOAA',
maxZoom: 19 maxZoom: 19