client work
Some checks are pending
Verify Latest Dependencies / Verify Latest Dependencies (push) Waiting to run
build and test / wxbox - latest (push) Waiting to run

This commit is contained in:
core 2025-05-16 19:25:51 -04:00
parent d862865830
commit dd2f25a5e8
8 changed files with 110 additions and 25 deletions

3
Cargo.lock generated
View file

@ -3663,6 +3663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf763ab1c7a3aa408be466efc86efe35ed1bd3dd74173ed39d6b0d0a6f0ba148"
dependencies = [
"memchr",
"serde",
]
[[package]]
@ -6333,10 +6334,12 @@ version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"chrono",
"flate2",
"geographiclib",
"image",
"moka",
"quick-xml 0.37.3",
"rayon",
"reqwest",
"serde",

View file

@ -38,18 +38,6 @@
}
load_image('radar-rect-green');
load_image('radar-rect-red');
map.addSource('kcys-bref-0.5', {
type: 'raster',
tiles: [
'http://localhost:3000/nexrad/base_reflectivity_halfdegree/KCYS/{z}/{x}/{y}@2x.png'
],
'tileSize': 512
});
map.addLayer({
id: 'kcys',
type: 'raster',
source: 'kcys-bref-0.5'
});
});
});
</script>

View file

@ -24,6 +24,7 @@
}: Props = $props();
let idToNameCache: Record<string, string> = $state({});
let idToTileUrlCache: Record<string, string> = $state({});
let currentlySelectingFor: string | null = $state(null);
let currentlySelectingCategory: PrimaryLayer[] | null = $state(null);
@ -65,6 +66,25 @@
onclick={() => {
selectedPrimaryLayer = data.id;
idToNameCache[data.id] = data.layer;
if (!map.getSource(`${selectedSite}-${data.id}`)) {
idToTileUrlCache[data.id] = data.tileUrl;
map.addSource(`${selectedSite}-${data.id}`, {
type: 'raster',
tiles: [
data.tileUrl.replace('{site}', selectedSite)
],
'tileSize': 512
});
}
if (map.getLayer("data")) {
map.removeLayer('data');
}
map.addLayer({
id: 'data',
type: 'raster',
source: `${selectedSite}-${data.id}`
});
}}>{data.layer}</DropdownMenu.Item
>
{/each}
@ -145,10 +165,29 @@
// did we already have a site set?
let alreadyHadSite = selectedSite != null;
pickingSiteForCategory = false;
selectedSite = e.features[0].properties.id;
selectedSite = e.features[0].properties.icao;
if (!alreadyHadSite) {
selectedPrimaryLayer = null;
open = true;
} else {
if (!map.getSource(`${selectedSite}-${selectedPrimaryLayer}`)) {
map.addSource(`${selectedSite}-${selectedPrimaryLayer}`, {
type: 'raster',
tiles: [
idToTileUrlCache[selectedPrimaryLayer].replace('{site}', selectedSite)
],
'tileSize': 512
});
}
if (map.getLayer("data")) {
map.removeLayer('data');
}
map.addLayer({
id: 'data',
type: 'raster',
source: `${selectedSite}-${selectedPrimaryLayer}`
});
} // preserve the layer the user had if they click while already selected.
}
});

View file

@ -37,15 +37,8 @@ export const load: PageServerLoad = async () => {
layer: 'Super Resolution Base Reflectivity (Tilt 1)',
type: 'raster',
tileUrl:
'https://tiler.weather.ax/grib2/noaa_mrms_composite_reflectivity_qcd/{z}/{x}/{y}.png'
'http://localhost:3000/nexrad/base_reflectivity_halfdegree/{site}/{z}/{x}/{y}@2x.png'
},
{
id: '01JV7SEN0GFB76NR843B1GFW5N',
layer: 'Super Resolution Base Velocity (Tilt 1)',
type: 'raster',
tileUrl:
'https://tiler.weather.ax/grib2/noaa_mrms_composite_reflectivity_qcd/{z}/{x}/{y}.png'
}
]
}
];

View file

@ -14,7 +14,7 @@ moka = { version = "0.12", features = ["future"] }
# logging
tracing = "0.1"
tracing-subscriber = "0.3"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# error handling
anyhow = "1"
@ -42,4 +42,6 @@ strum = "0.27"
strum_macros = "0.27"
# meta
wxbox-common = { path = "../common" }
wxbox-common = { path = "../common" }
chrono = "0.4"
quick-xml = { version = "0.37", features = ["serialize"] }

View file

@ -3,6 +3,7 @@ mod error;
mod grib2;
mod nexrad;
mod tiles;
mod nexrad_list_response;
use crate::config::Config;
use crate::grib2::{Grib2DataCache, Grib2TileCache, grib2_handler, grib2_metadata};
@ -16,6 +17,7 @@ use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use axum::http::Method;
use tower_http::cors::{Any, CorsLayer};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::util::SubscriberInitExt;
use wxbox_grib2::GribMessage;
@ -41,6 +43,7 @@ impl Debug for AppState {
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::fmt()
.with_span_events(FmtSpan::CLOSE)
.with_env_filter(EnvFilter::from_default_env())
.init();
let config_file = args().nth(1).expect("usage: wxbox-tiler config.toml");

View file

@ -20,6 +20,7 @@ use std::{fs, io};
use std::io::{Cursor, ErrorKind};
use std::num::TryFromIntError;
use std::sync::Arc;
use chrono::{Datelike, Utc};
use tokio::io::AsyncReadExt;
use tracing::{debug, info_span};
use wxbox_ar2::sites::wsr88d::{SITES, Wsr88dSite};
@ -27,6 +28,7 @@ use wxbox_ar2::{DATA_BYTES, MomentValue, Radial, Scan, Sweep, parse};
use wxbox_grib2::GribMessage;
use wxbox_grib2::wgs84::LatLong;
use wxbox_pal::{ColorPalette, Palette};
use crate::nexrad_list_response::ListBucketResult;
pub type NexradDataCache = Cache<DataId, Arc<wxbox_ar2::Scan>>;
pub type NexradTileCache = Cache<TileId, Arc<Vec<u8>>>;
@ -49,6 +51,7 @@ pub async fn nexrad_handler(
Path((source, site, z, x, y)): Path<(String, String, usize, usize, String)>,
State(state): State<AppState>,
) -> Result<impl IntoResponse, AppError> {
debug!("here");
let mut y = y
.strip_suffix(".png")
.ok_or(io::Error::new(ErrorKind::InvalidInput, "invalid"))?;
@ -106,10 +109,48 @@ async fn load_nexrad_data(
) -> anyhow::Result<Arc<Scan>> {
let _load_span = info_span!("load_nexrad_data");
// todo: find the correct
let url = format!("https://noaa-nexrad-level2.s3.amazonaws.com/2025/05/15/KCYS/KCYS20250515_003010_V06");
let mut searchdate = Utc::now();
let client = reqwest::Client::new();
let mut url;
let mut days_went_back = 0;
'outer: loop {
let year = searchdate.year();
let month = searchdate.month();
let day = searchdate.day();
let listing_url = format!("https://noaa-nexrad-level2.s3.amazonaws.com/?list-type=2&delimiter=/&prefix={year}/{month:02}/{day:02}/{site}/");
debug!("downloading listing from {}", listing_url);
let r = client.get(listing_url).send().await?;
if !r.status().is_success() {
bail!("nexrad data failed to load: {}", r.status());
}
let r: ListBucketResult = quick_xml::de::from_str(&r.text().await?)?;
if let Some(contents) = r.Contents {
for file in contents.iter().rev() {
if file.Key.ends_with("_MDM") {
// fake. continue
continue;
} else {
debug!("nexrad: found l2 file for {} date {}", site, file.LastModified);
url = format!("https://noaa-nexrad-level2.s3.amazonaws.com/{}", file.Key);
break 'outer;
}
}
}
searchdate -= chrono::Duration::days(1);
days_went_back += 1;
if days_went_back > 7 {
bail!("no recent radar data");
}
}
let client = reqwest::Client::new();
debug!("downloading l2 file {}", url);
let r = client.get(url).send().await?;
if !r.status().is_success() {

View file

@ -0,0 +1,16 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct ListBucketResult {
pub Name: String,
pub Prefix: String,
pub KeyCount: usize,
pub MaxKeys: usize,
pub Contents: Option<Vec<ListBucketResultContents>>,
}
#[derive(Deserialize, Debug)]
pub struct ListBucketResultContents {
pub Key: String,
pub LastModified: String
}