aaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
This commit is contained in:
parent
3633c01142
commit
046b581855
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue