diff --git a/Cargo.lock b/Cargo.lock index 7ba0b3c..0a636e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2743,9 +2743,11 @@ dependencies = [ "ordered-float", "png", "reqwest", + "serde", "thiserror 1.0.64", "tikv-jemallocator", "tokio", + "toml", "tracing", "tracing-subscriber", "wxbox-grib2", diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..6941b58 --- /dev/null +++ b/config.toml @@ -0,0 +1,16 @@ +[sources.grib2.noaa_mrms_merged_composite_reflectivity_qc] +from = "https://mrms.ncep.noaa.gov/data/2D/HAWAII/MergedReflectivityQCComposite/MRMS_MergedReflectivityQCComposite.latest.grib2.gz" +needs_gzip = true +valid_for = 120 +palette = """ +Color: 10 164 164 255 100 100 192 +Color: 20 64 128 255 32 64 128 +Color: 30 0 255 0 0 128 0 +Color: 40 255 255 0 255 128 0 +Color: 50 255 0 0 160 0 0 +Color: 60 255 0 255 128 0 128 +Color: 70 255 255 255 128 128 128 +Color: 80 128 128 128 +""" +missing = -99.0 +no_coverage = -999.0 \ No newline at end of file diff --git a/wxbox-tiler/Cargo.toml b/wxbox-tiler/Cargo.toml index cf3b6b3..eb71b27 100644 --- a/wxbox-tiler/Cargo.toml +++ b/wxbox-tiler/Cargo.toml @@ -19,6 +19,8 @@ mime = "0.3.17" wxbox-grib2 = { version = "0.1", path = "../wxbox-grib2" } tracing = "0.1" tracing-subscriber = "0.3" +toml = "0.8" +serde = { version = "1", features = ["derive"] } [dev-dependencies] approx = "0.5" diff --git a/wxbox-tiler/src/config.rs b/wxbox-tiler/src/config.rs new file mode 100644 index 0000000..978a7bc --- /dev/null +++ b/wxbox-tiler/src/config.rs @@ -0,0 +1,23 @@ +use std::collections::HashMap; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Config { + pub sources: Sources +} + +#[derive(Deserialize)] +pub struct Sources { + pub grib2: HashMap +} + +#[derive(Deserialize)] +pub struct Grib2Source { + pub from: String, + pub needs_gzip: bool, + pub valid_for: u64, + pub palette: String, + pub missing: Option, + pub range_folded: Option, + pub no_coverage: Option +} \ No newline at end of file diff --git a/wxbox-tiler/src/main.rs b/wxbox-tiler/src/main.rs index 7caa532..4a2daf0 100644 --- a/wxbox-tiler/src/main.rs +++ b/wxbox-tiler/src/main.rs @@ -1,32 +1,42 @@ pub(crate) mod sources; mod pixmap; +mod config; use std::collections::{BTreeMap, HashMap}; +use std::env::args; use std::fmt::Debug; +use std::fs; use std::sync::Arc; use tokio::sync::RwLock; use std::time::SystemTime; use actix_web::{App, HttpServer}; use actix_web::web::Data; use wxbox_grib2::GribMessage; +use crate::config::Config; pub struct AppState { grib2_cache: RwLock>>>, - grib2_cache_timestamps: RwLock> + grib2_cache_timestamps: RwLock>, + config: Config } #[actix_web::main] async fn main() -> std::io::Result<()> { tracing_subscriber::fmt::init(); + let config_path = args().nth(1).unwrap(); + let config_str = fs::read_to_string(config_path).unwrap(); + let config: Config = toml::from_str(&config_str).unwrap(); + let data = Data::new(AppState { grib2_cache: RwLock::new(HashMap::new()), - grib2_cache_timestamps: RwLock::new(HashMap::new()) + grib2_cache_timestamps: RwLock::new(HashMap::new()), + config }); HttpServer::new(move || { App::new() - .service(sources::noaa::noaa_mrms_merged_composite_reflectivity_qc) + .service(sources::grib2::grib2_source) .app_data(data.clone()) }) .bind(("::", 8080))? diff --git a/wxbox-tiler/src/sources/grib2.rs b/wxbox-tiler/src/sources/grib2.rs index b9bf6c4..dab71a9 100644 --- a/wxbox-tiler/src/sources/grib2.rs +++ b/wxbox-tiler/src/sources/grib2.rs @@ -3,12 +3,18 @@ use std::f64::consts::PI; use std::io::{BufWriter, Cursor, Read}; use std::sync::Arc; use std::time::SystemTime; +use actix_web::error::UrlencodedError::ContentType; +use actix_web::http::StatusCode; +use actix_web::HttpResponse; +use actix_web::web::Data; use flate2::read::GzDecoder; use png::{BitDepth, ColorType, Encoder}; use tokio::sync::RwLock; use wxbox_grib2::GribMessage; use wxbox_grib2::wgs84::LatLong; use wxbox_pal::{Color, ColorPalette, Palette}; +use crate::AppState; +use crate::config::Grib2Source; use crate::pixmap::Pixmap; pub async fn needs_reload(lct: &RwLock>, lutkey: &String, valid_for: u64) -> bool { @@ -125,6 +131,35 @@ pub async fn render(xtile: f64, ytile: f64, z: i32, tilesize: usize, pal: Palett buf } +#[actix_web::get("/grib2/{id}/{z}/{x}/{y}.png")] +pub async fn grib2_source(path: actix_web::web::Path<(String, i32, u32, u32)>, data: Data) -> HttpResponse { + if let Some(known_source) = data.config.sources.grib2.get(&path.0) { + reload_if_required( + &known_source.from, + known_source.needs_gzip, + known_source.valid_for.into(), + &path.0, + &data.grib2_cache_timestamps, + &data.grib2_cache + ).await; + let lct_reader = data.grib2_cache_timestamps.read().await; + if let Some(grib2) = data.grib2_cache.read().await.get(&path.0) { + HttpResponse::Ok() + .insert_header(actix_web::http::header::ContentType(mime::IMAGE_PNG)) + // TODO: use the timestamp in the grib2 ID section + .insert_header(("x-wxbox-tiler-data-valid-time", lct_reader.get(&path.0).expect("impossible").duration_since(::std::time::UNIX_EPOCH).expect("time went backwards").as_secs().to_string())) + .insert_header(("Access-Control-Allow-Origin", "*")) + .insert_header(("Access-Control-Expose-Headers", "*")) + .insert_header(("Access-Control-Allow-Headers", "*")) + .body(crate::sources::grib2::render(path.2 as f64, path.3 as f64, path.1, 256, wxbox_pal::parser::parse(&known_source.palette).unwrap(), grib2, known_source.missing, known_source.range_folded, known_source.no_coverage).await) + } else { + HttpResponse::new(StatusCode::NOT_FOUND) + } + } else { + HttpResponse::new(StatusCode::NOT_FOUND) + } +} +/* #[macro_export] macro_rules! grib2_handler { (mount $f:ident, at: $path:expr, from: $from:expr, needs_gzip: $needs_gzip:expr, valid_for: $valid_for:expr, palette: $pal:expr, missing: $missing:expr, range_folded: $rf:expr, no_coverage: $nc:expr) => { @@ -152,4 +187,4 @@ macro_rules! grib2_handler { } } }; -} \ No newline at end of file +}*/ \ 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 4f2be63..31b1c45 100644 --- a/wxbox-tiler/src/sources/noaa/mod.rs +++ b/wxbox-tiler/src/sources/noaa/mod.rs @@ -1,13 +1,13 @@ -use crate::grib2_handler; - +//use crate::grib2_handler; +/* grib2_handler! { mount noaa_mrms_merged_composite_reflectivity_qc, at: "/noaa_mrms_merged_composite_reflectivity_qc/{z}/{x}/{y}.png", - from: "https://mrms.ncep.noaa.gov/data/2D/MergedReflectivityQCComposite/MRMS_MergedReflectivityQCComposite.latest.grib2.gz", + from: "https://mrms.ncep.noaa.gov/data/2D/HAWAII/MergedReflectivityQCComposite/MRMS_MergedReflectivityQCComposite.latest.grib2.gz", needs_gzip: true, valid_for: 120, palette: wxbox_pal::parser::parse(wxbox_pal::default_palettes::DEFAULT_REFLECTIVITY_PALETTE).unwrap(), missing: Some(-99.0), range_folded: None, no_coverage: Some(-999.0) -} \ No newline at end of file +}*/ \ No newline at end of file