aaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

This commit is contained in:
core 2024-10-20 10:39:37 -04:00
parent 3633c01142
commit 046b581855
Signed by: core
GPG Key ID: FDBF740DADDCEECF
6 changed files with 38 additions and 661 deletions

217
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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);
}
}

View File

@ -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<V>(map: &BTreeMap<OrderedFloat<f64>, V>, val: f64) -> Option<OrderedFloat<f64>> {
pub fn closest_key<V>(map: &BTreeMap<OrderedFloat<f64>, V>, val: f64) -> Option<(OrderedFloat<f64>, 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<V>(map: &BTreeMap<OrderedFloat<f64>, 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<V>(map: &BTreeMap<OrderedFloat<f64>, 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<V>(map: &BTreeMap<OrderedFloat<f64>, 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<OrderedFloat<f64>, BTreeMap<OrderedFloat<f64>, f64>>;
pub async fn map_grib2(i: Response) -> Result<LookupTable2D, GribMapError> {
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<OrderedFloat<f64>, BTreeMap<OrderedFloat<f64>, 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::<Vec<_>>().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<L, N> {
LatLon(L),
NaN(N),
}
impl<L, N> Iterator for LatLonIteratorWrapper<L, N>
where
L: Iterator<Item = (f32, f32)>,
N: Iterator<Item = (f32, f32)>,
{
type Item = (f32, f32);
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::LatLon(value) => value.next(),
Self::NaN(value) => value.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Self::LatLon(value) => value.size_hint(),
Self::NaN(value) => value.size_hint(),
}
}
}

View File

@ -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]

View File

@ -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<AppState>) -> 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<AppState>) -> 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<AppState>) -> 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<AppState>) -> 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<AppState>) -> 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<AppState>) -> 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<AppState>) -> 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)
}
}