diff --git a/Cargo.lock b/Cargo.lock index 8deaf0f..9616960 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,21 +237,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -403,20 +388,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets", -] - [[package]] name = "clang-sys" version = "1.8.1" @@ -489,27 +460,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - [[package]] name = "deranged" version = "0.3.11" @@ -740,28 +690,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "grib" -version = "0.10.2" -source = "git+https://github.com/noritada/grib-rs#b3b427c4abdaf07af13d39f96ded9384603bcb01" -dependencies = [ - "chrono", - "grib-build", - "num", - "num_enum", - "openjpeg-sys", - "png", -] - -[[package]] -name = "grib-build" -version = "0.4.3" -source = "git+https://github.com/noritada/grib-rs#b3b427c4abdaf07af13d39f96ded9384603bcb01" -dependencies = [ - "csv", - "serde", -] - [[package]] name = "h2" version = "0.3.26" @@ -950,29 +878,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "idna" version = "0.5.0" @@ -1204,30 +1109,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - [[package]] name = "num-complex" version = "0.4.6" @@ -1263,28 +1144,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1294,27 +1153,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "object" version = "0.36.5" @@ -1330,16 +1168,6 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" -[[package]] -name = "openjpeg-sys" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2aeb0ac97a0a7726b973a217b6002f5157fd65c5d5033cf907227dbb7e114a" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "openssl" version = "0.10.66" @@ -1499,15 +1327,6 @@ dependencies = [ "syn", ] -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit", -] - [[package]] name = "proc-macro2" version = "1.0.87" @@ -2091,23 +1910,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - [[package]] name = "tower-service" version = "0.3.3" @@ -2300,15 +2102,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-registry" version = "0.2.0" @@ -2421,15 +2214,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - [[package]] name = "wxbox-pal" version = "0.1.0" @@ -2445,7 +2229,6 @@ dependencies = [ "actix-web", "eccodes", "flate2", - "grib", "mime", "ndarray", "ordered-float", diff --git a/wxbox-tiler/Cargo.toml b/wxbox-tiler/Cargo.toml index 028f10f..e1406f0 100644 --- a/wxbox-tiler/Cargo.toml +++ b/wxbox-tiler/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] actix-web = "4" thiserror = "1" -grib = { git = "https://github.com/noritada/grib-rs" } ordered-float = "4" tikv-jemallocator = "0.6" reqwest = "0.12" diff --git a/wxbox-tiler/src/coords.rs b/wxbox-tiler/src/coords.rs deleted file mode 100644 index 18a245c..0000000 --- a/wxbox-tiler/src/coords.rs +++ /dev/null @@ -1,213 +0,0 @@ -/// Constant representing the value of PI -const PI: f64 = std::f64::consts::PI; - -/// Constant representing the conversion factor from radians to degrees -const R2D: f64 = 180.0f64 / PI; - -/// Earth radius in meters -const RE: f64 = 6378137.0; - -/// Circumference of the Earth -const CE: f64 = 2.0f64 * PI * RE; - -/// Represents a tile on a map -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct Tile { - pub x: i32, - pub y: i32, - pub z: i32, -} - -impl Tile { - /// Creates a new Tile object with the specified x, y, and zoom level - pub fn new(x: i32, y: i32, z: i32) -> Self { - let (lo, hi) = minmax(z); - if !(lo <= x && x <= hi) || !(lo <= y && y <= hi) { - panic!("require tile x and y to be within the range (0, 2 ** zoom)"); - } - Tile { x, y, z } - } -} - -/// Returns the minimum and maximum values for a tile at the given zoom level -fn minmax(z: i32) -> (i32, i32) { - let max_value = 2_i32.pow(z as u32); - (0, max_value - 1) -} - -/// Represents a geographical coordinate in longitude and latitude -#[derive(Debug, PartialEq)] -pub struct LngLat { - pub lng: f64, - pub lat: f64, -} - -/// Represents a point in web mercator projected coordinates -#[derive(Debug, PartialEq)] -pub struct XY { - pub x: f64, - pub y: f64, -} - -/// Represents a bounding box in geographical coordinates -#[derive(Debug, PartialEq)] -pub struct LngLatBbox { - /// Westernmost longitude of the bounding box - pub west: f64, - /// Southernmost latitude of the bounding box - pub south: f64, - /// Easternmost longitude of the bounding box - pub east: f64, - /// Northernmost latitude of the bounding box - pub north: f64, -} - -/// Represents a bounding box in web mercator projected coordinates -#[derive(Debug, PartialEq)] -pub struct Bbox { - /// Left coordinate of the bounding box - pub left: f64, - /// Bottom coordinate of the bounding box - pub bottom: f64, - /// Right coordinate of the bounding box - pub right: f64, - /// Top coordinate of the bounding box - pub top: f64, -} - -/// Calculates the upper-left geographical coordinates of a given tile -pub fn ul(tile: Tile) -> LngLat { - let z2 = 2.0_f64.powf(tile.z as f64); - let lon_deg = tile.x as f64 / z2 * 360.0 - 180.0; - let lat_rad = (PI * (1.0 - 2.0 * tile.y as f64 / z2)).sinh().atan(); - let lat_deg = lat_rad.to_degrees(); - - LngLat { lng: lon_deg, lat: lat_deg } -} - -/// Calculates the bounding box of a given tile in geographical coordinates -pub fn bounds(tile: Tile) -> LngLatBbox { - let z2 = 2.0_f64.powf(tile.z as f64); - - let west = tile.x as f64 / z2 * 360.0 - 180.0; - let north_rad = (PI * (1.0 - 2.0 * tile.y as f64 / z2)).sinh().atan(); - let north = north_rad.to_degrees(); - - let east = (tile.x + 1) as f64 / z2 * 360.0 - 180.0; - let south_rad = (PI * (1.0 - 2.0 * (tile.y + 1) as f64 / z2)).sinh().atan(); - let south = south_rad.to_degrees(); - - LngLatBbox { west, south, east, north } -} - -/// Calculates the bounding box of a given tile in web mercator projected coordinates -pub fn xy_bounds(tile: Tile) -> Bbox { - let tile_size = CE / 2.0_f64.powf(tile.z as f64); - let left = tile.x as f64 * tile_size - CE / 2.0; - let right = left + tile_size; - let top = CE / 2.0 - tile.y as f64 * tile_size; - let bottom = top - tile_size; - - Bbox { left, bottom, right, top } -} - -/// Converts geographical coordinates (LngLat) to web mercator projected coordinates (XY) -pub fn convert_xy(lng_lat: LngLat) -> XY { - let x = RE * lng_lat.lng.to_radians(); - - let y: f64; - if lng_lat.lat <= -90.0 { - y = f64::NEG_INFINITY; - } else if lng_lat.lat >= 90.0 { - y = f64::INFINITY; - } else { - y = RE * ((PI * 0.25) + (0.5 * lng_lat.lat.to_radians())).tan().ln(); - } - - XY { x, y } -} - -/// Converts web mercator projected coordinates (XY) to geographical coordinates (LngLat) -pub fn convert_lng_lat(xy: XY) -> LngLat { - let lng = xy.x * R2D / RE; - let lat = ((PI * 0.5) - 2.0 * (-xy.y / RE).exp().atan()) * R2D; - - LngLat { lng, lat } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ul() { - let tile: Tile = Tile::new(486, 332, 10); - let result = ul(tile); - let expected_result = LngLat { lng: -9.140625, lat: 53.33087298301705 }; - - assert_eq!(result, expected_result) - } - - #[test] - fn test_bounds() { - let tile: Tile = Tile::new(486, 332, 10); - let result = bounds(tile); - let expected_result = LngLatBbox { - west: -9.140625, - south: 53.120405283106564, - east: -8.7890625, - north: 53.33087298301705, - }; - - assert_eq!(result, expected_result) - } - - #[test] - fn test_xy_bounds() { - let tile: Tile = Tile::new(486, 332, 10); - let result = xy_bounds(tile); - let expected_result = Bbox { - left: -1017529.7205322646, - bottom: 7005300.768279833, - right: -978393.9620502543, - top: 7044436.526761843, - }; - - assert_eq!(result, expected_result) - } - - #[test] - fn test_xy_positive() { - let lng_lat = LngLat { lng: -9.140625, lat: 53.33087298301705 }; - let result = convert_xy(lng_lat); - let expected = XY { x: -1017529.7205322663, y: 7044436.526761846 }; - assert_eq!(result, expected); - } - - #[test] - fn test_xy_negative() { - let lng_lat = LngLat { lng: -122.4194, lat: -100.0 }; // Latitude less than -90 - let result = convert_xy(lng_lat); - let expected = XY { x: -13627665.271218073, y: f64::NEG_INFINITY }; - assert_eq!(result, expected); - } - - #[test] - fn test_xy_positive_infinity() { - let lng_lat = LngLat { lng: -122.4194, lat: 100.0 }; // Latitude greater than 90 - let result = convert_xy(lng_lat); - let expected = XY { - x: -13627665.271218073, - y: f64::INFINITY, - }; - assert_eq!(result, expected); - } - - #[test] - fn test_lng_lat() { - let xy = XY { x: -1017529.7205322663, y: 7044436.526761846 }; - let result = convert_lng_lat(xy); - let expected = LngLat { lng: -9.140625000000002, lat: 53.33087298301706 }; - assert_eq!(result, expected); - } -} diff --git a/wxbox-tiler/src/grib2.rs b/wxbox-tiler/src/grib2.rs index 2262fd2..dab6b7a 100644 --- a/wxbox-tiler/src/grib2.rs +++ b/wxbox-tiler/src/grib2.rs @@ -1,16 +1,9 @@ -use std::borrow::Cow; use std::collections::BTreeMap; -use std::io::{BufReader, Cursor, Read}; -use flate2::read::GzDecoder; -use grib::codetables::{CodeTable4_2, CodeTable4_3, Lookup}; -use grib::{GribError, SectionBody}; use ordered_float::OrderedFloat; -use reqwest::Response; -use thiserror::Error; -pub fn closest_key(map: &BTreeMap, V>, val: f64) -> Option> { +pub fn closest_key(map: &BTreeMap, V>, val: f64) -> Option<(OrderedFloat, f64)> { let mut r1 = map.range(OrderedFloat(val)..); - let mut r2 = map.range(..=OrderedFloat(val)); + let r2 = map.range(..=OrderedFloat(val)); let o1 = r1.next(); let o2 = r2.last(); match (o1, o2) { @@ -18,19 +11,19 @@ pub fn closest_key(map: &BTreeMap, V>, val: f64) -> Option< None }, (Some(i), None) => { - Some(*i.0) + Some((*i.0, (*i.0 - val).abs())) }, (None, Some(i)) => { - Some(*i.0) + Some((*i.0, (*i.0 - val).abs())) }, (Some(i1), Some(i2)) => { // abs(f - i) let i1_dist = (i1.0 - val).abs(); let i2_dist = (i2.0 - val).abs(); - return Some(if i1_dist < i2_dist { - *i1.0 + Some(if i1_dist < i2_dist { + (*i1.0, i1_dist) } else { - *i2.0 + (*i2.0, i2_dist) }) } } @@ -49,7 +42,7 @@ pub fn lookup(map: &BTreeMap, V>, val: f64) -> Option<&V> { // abs(f - i) let i1_dist = (i1.0 - val).abs(); let i2_dist = (i2.0 - val).abs(); - return Some(if i1_dist < i2_dist { + Some(if i1_dist < i2_dist { i1.1 } else { i2.1 @@ -58,178 +51,5 @@ pub fn lookup(map: &BTreeMap, V>, val: f64) -> Option<&V> { } } -#[derive(Error, Debug)] -pub enum GribMapError { - #[error("grib2 parse error: {0}")] - ParseError(#[from] GribError), - #[error("reqwest error: {0}")] - ReqwestError(#[from] reqwest::Error), - #[error("empty data")] - EmptyData, - #[error("i/o error")] - IoError(#[from] std::io::Error) -} pub type LookupTable2D = BTreeMap, BTreeMap, f64>>; - -pub async fn map_grib2(i: Response) -> Result { - let b = Cursor::new(i.bytes().await?.to_vec()); - let mut decoder = GzDecoder::new(b); - - let mut data = vec![]; - decoder.read_to_end(&mut data)?; - - let r = BufReader::new(Cursor::new(data)); - let grib = grib::from_reader(r)?; - if grib.is_empty() { - return Err(GribMapError::EmptyData); - } - let mi = grib - .iter() - .filter(|((_, submessage_part), _)| *submessage_part == 0); - - for (message_index, submessage) in mi { - if let (Some(SectionBody::Section0(sect0_body)), Some(SectionBody::Section1(sect1_body))) = - (&submessage.0.body.body, &submessage.1.body.body) - { - println!( - "{:?} D{} RT {} D/T {} C {} SC {} P/S {}", - message_index, - sect0_body.discipline, - sect1_body.ref_time()?, - sect1_body.data_type(), - sect1_body.centre_id(), - sect1_body.subcentre_id(), - sect1_body.prod_status(), - ); - } - } - - let mut map: BTreeMap, BTreeMap, f64>> = BTreeMap::new(); - - for (i, submessage) in grib.iter() { - let id = format!("{}.{}", i.0, i.1); - let prod_def = submessage.prod_def(); - let category = prod_def - .parameter_category() - .zip(prod_def.parameter_number()) - .map(|(c, n)| { - CodeTable4_2::new(submessage.indicator().discipline, c) - .lookup(usize::from(n)) - .to_string() - }) - .unwrap_or_default(); - let generating_process = prod_def - .generating_process() - .map(|v| CodeTable4_3.lookup(usize::from(v)).to_string()) - .unwrap_or_default(); - let forecast_time = prod_def - .forecast_time() - .map(|ft| ft.to_string()) - .unwrap_or_default(); - let surfaces = prod_def - .fixed_surfaces() - .map(|(first, second)| (format_surface(&first), format_surface(&second))) - .unwrap_or((String::new(), String::new())); - let grid_def = submessage.grid_def(); - let num_grid_points = grid_def.num_points(); - let num_points_represented = submessage.repr_def().num_points(); - let grid_type = grib::GridDefinitionTemplateValues::try_from(grid_def) - .map(|def| Cow::from(def.short_name())) - .unwrap_or_else(|_| { - Cow::from(format!("unknown (template {})", grid_def.grid_tmpl_num())) - }); - println!( - "{} │ {}, {}, {}, {}, {}, │ {}/{} {}", - id, - category, - generating_process, - forecast_time, - surfaces.0, - surfaces.1, - num_grid_points - num_points_represented, - num_grid_points, - grid_type, - ); - - println!("ll"); - - let latlons = submessage.latlons(); - - println!("decoding"); - - let decoder = grib::Grib2SubmessageDecoder::from(submessage)?; - let values = decoder.dispatch()?; - - println!("preparing"); - - let values = values.collect::>().into_iter(); // workaround for mutability - let latlons = match latlons { - Ok(iter) => LatLonIteratorWrapper::LatLon(iter), - Err(GribError::NotSupported(_)) => { - let nan_iter = vec![(f32::NAN, f32::NAN); values.len()].into_iter(); - LatLonIteratorWrapper::NaN(nan_iter) - } - Err(e) => panic!("{}", e), - }; - let values = latlons.zip(values); - - println!("mapping"); - - map = BTreeMap::new(); - - // prepare the map - for ((lat, long), value) in values { - let lat = OrderedFloat(lat as f64); - let mut long = long as f64; - if long > 180.0 { - long -= 360.0 - } - let long = OrderedFloat(long); - let value = value as f64; - if !map.contains_key(&lat) { - map.insert(lat, BTreeMap::new()); - } - map.get_mut(&lat).expect("unreachable").insert(long, value); - } - } - - Ok(map) -} - -fn format_surface(surface: &grib::FixedSurface) -> String { - let value = surface.value(); - let unit = surface - .unit() - .map(|s| format!(" [{s}]")) - .unwrap_or_default(); - format!("{value}{unit}") -} - -#[derive(Clone)] -enum LatLonIteratorWrapper { - LatLon(L), - NaN(N), -} - -impl Iterator for LatLonIteratorWrapper -where - L: Iterator, - N: Iterator, -{ - type Item = (f32, f32); - - fn next(&mut self) -> Option { - match self { - Self::LatLon(value) => value.next(), - Self::NaN(value) => value.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - match self { - Self::LatLon(value) => value.size_hint(), - Self::NaN(value) => value.size_hint(), - } - } -} \ No newline at end of file diff --git a/wxbox-tiler/src/main.rs b/wxbox-tiler/src/main.rs index ec50ff9..30a04b6 100644 --- a/wxbox-tiler/src/main.rs +++ b/wxbox-tiler/src/main.rs @@ -3,25 +3,13 @@ pub(crate) mod coords; mod grib2; mod pixmap; -use std::borrow::Cow; -use std::collections::{BTreeMap, HashMap}; -use std::env::args; -use std::ffi::c_void; +use std::collections::{BTreeMap}; use std::fmt::Debug; -use std::fs::File; -use std::io::{BufReader, Cursor, Read}; -use std::ptr::{null, null_mut}; use tokio::sync::RwLock; use std::time::SystemTime; -use actix_web::{App, get, HttpServer}; +use actix_web::{App, HttpServer}; 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::{GribError, SectionBody}; -use ordered_float::OrderedFloat; -use crate::grib2::{LookupTable2D, lookup}; +use crate::grib2::{LookupTable2D}; use crate::sources::noaa::mrms_cref; #[global_allocator] diff --git a/wxbox-tiler/src/sources/noaa/mod.rs b/wxbox-tiler/src/sources/noaa/mod.rs index 9855eb8..2f6d854 100644 --- a/wxbox-tiler/src/sources/noaa/mod.rs +++ b/wxbox-tiler/src/sources/noaa/mod.rs @@ -1,23 +1,18 @@ -use std::borrow::Cow; use std::collections::BTreeMap; use std::f64::consts::PI; -use std::io::{BufReader, BufWriter, Cursor, Read}; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::io::{BufWriter, Cursor, Read}; +use std::time::{SystemTime}; use actix_web::{get, HttpResponse, web}; -use actix_web::http::header::{CONTENT_TYPE, HeaderValue}; use actix_web::http::{header, StatusCode}; use actix_web::web::Data; use eccodes::{CodesHandle, FallibleStreamingIterator, ProductKind}; use flate2::read::GzDecoder; -use grib::{GribError, SectionBody}; -use grib::codetables::{CodeTable4_2, CodeTable4_3, Lookup}; -use ndarray::{Order, Zip}; +use ndarray::{Zip}; use ordered_float::OrderedFloat; use png::{BitDepth, ColorType, Encoder}; use wxbox_pal::{Color, Palette}; use crate::{AppState, LutKey}; -use crate::coords::{bounds, ul}; -use crate::grib2::{closest_key, LookupTable2D, lookup, map_grib2}; +use crate::grib2::{closest_key, LookupTable2D}; use crate::pixmap::Pixmap; use wxbox_pal::color; @@ -27,9 +22,9 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data) -> Ht let x = path.1; let y = path.2; - let tile = crate::coords::Tile::new(x, y, z); + let lon = (x as f64 / 2.0_f64.powi(z)) * 360.0 - 180.0; + let lat = (PI - (y as f64 / 2.0_f64.powi(z)) * 2.0 * PI).sinh().atan() * (180.0 / PI); - let bbox = bounds(tile); let mut needs_reload = false; @@ -64,7 +59,7 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data) -> Ht let mut map = LookupTable2D::new(); - let mut arr = msg.to_lons_lats_values().unwrap(); + let arr = msg.to_lons_lats_values().unwrap(); Zip::from(&arr.latitudes) .and(&arr.longitudes) @@ -79,10 +74,6 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data) -> Ht 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 { @@ -118,18 +109,27 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data) -> Ht 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 lon = lon + degrees_per_px * x as f64; + let lat = 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 closest_lat_in_map = &closest_key(map, lat).unwrap(); - let value = row.get(&closest_long_in_row).unwrap(); + if closest_lat_in_map.1 > 0.01 { + // too far + continue; + } + + let row = map.get(&closest_lat_in_map.0).unwrap(); + let closest_long_in_row = closest_key(row, lon).unwrap(); + + if closest_long_in_row.1 > 0.01 { + // too far + continue; + } + + let value = row.get(&closest_long_in_row.0).unwrap(); let color = match value { -999.0 | -99.0 => Color { red: 0, green: 0, blue: 0, alpha: 5 }, @@ -147,7 +147,7 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data) -> Ht } } }; - image.set(x, y, color); + image.set(y, x, color); } } @@ -202,7 +202,7 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data) -> Ht // borrow checker insanity { let mut cur: Cursor<_> = Cursor::new(&mut buf); - let ref mut w = BufWriter::new(&mut cur); + let w = &mut BufWriter::new(&mut cur); let mut encoder = Encoder::new(w, 256, 256); encoder.set_color(ColorType::Rgba); encoder.set_depth(BitDepth::Eight); @@ -225,7 +225,7 @@ async fn mrms_cref(path: web::Path<(i32, i32, i32)>, data: Data) -> Ht .body(buf) } else { println!("gridded LUT not available for LutKey::NoaaMrmsCref while handling /mrms_cref/{z}/{x}/{y} tile request"); - return HttpResponse::new(StatusCode::NOT_FOUND); + HttpResponse::new(StatusCode::NOT_FOUND) } }