feat: ssr
Some checks failed
Verify Latest Dependencies / Verify Latest Dependencies (push) Has been cancelled
build and test / wxbox - latest (push) Has been cancelled

This commit is contained in:
core 2025-05-15 07:14:41 -04:00
parent 9a714a9323
commit 74d9647689
Signed by: core
GPG key ID: FDBF740DADDCEECF
7 changed files with 60 additions and 13 deletions

15
Cargo.lock generated
View file

@ -4972,6 +4972,20 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-http"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e"
dependencies = [
"bitflags 2.9.0",
"bytes",
"http",
"pin-project-lite",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
@ -6330,6 +6344,7 @@ dependencies = [
"strum_macros 0.27.1",
"tokio",
"toml",
"tower-http",
"tracing",
"tracing-subscriber",
"wxbox-ar2",

View file

@ -31,6 +31,18 @@
img.onload = () => {
map?.addImage('radar-rect-gray', img);
};
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

@ -87,6 +87,7 @@ pub fn parse(input: Vec<u8>) -> nexrad_data::result::Result<Scan> {
let mut vcp = None;
let mut radials = vec![];
println!("{:?}", file.header());
for mut record in file.records() {
if record.compressed() {
record = record.decompress()?;

View file

@ -7,6 +7,7 @@ edition = "2024"
# web server
tokio = { version = "1", features = ["full"] }
axum = "0.8"
tower-http = { version = "0.6", features = ["cors"]}
# caching
moka = { version = "0.12", features = ["future"] }

View file

@ -15,8 +15,8 @@ Color: 80 128 128 128
missing = -99.0
no_coverage = -999.0
[data.nexrad.l2_reflectivity]
from = "aaaa"
[data.nexrad.base_reflectivity_halfdegree]
from = "DS.p94r0"
palette = """
Color: 5 0x40 0xe8 0xe3
Color: 10 0x26 0xa4 0xfa

View file

@ -14,6 +14,8 @@ use moka::future::Cache;
use std::env::args;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use axum::http::Method;
use tower_http::cors::{Any, CorsLayer};
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::util::SubscriberInitExt;
use wxbox_grib2::GribMessage;
@ -58,11 +60,16 @@ async fn main() -> anyhow::Result<()> {
config: Arc::new(config),
};
let cors = CorsLayer::new()
.allow_methods([Method::GET])
.allow_origin(Any);
let app = Router::new()
.route("/grib2/{source}/{z}/{x}/{y}", get(grib2_handler))
.route("/grib2/{source}/metadata", get(grib2_metadata))
.route("/nexrad/{source}/{z}/{x}/{y}", get(nexrad_handler))
.with_state(state);
.route("/nexrad/{source}/{site}/{z}/{x}/{y}", get(nexrad_handler))
.with_state(state)
.layer(cors);
let listener = tokio::net::TcpListener::bind("[::]:3000").await?;
axum::serve(listener, app).await?;

View file

@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::f64::consts::PI;
use std::fmt::Debug;
use std::io;
use std::{fs, io};
use std::io::{Cursor, ErrorKind};
use std::num::TryFromIntError;
use std::sync::Arc;
@ -46,7 +46,7 @@ impl Debug for NexradDataSource {
#[tracing::instrument(level = "info")]
pub async fn nexrad_handler(
Path((source, z, x, y)): Path<(String, usize, usize, String)>,
Path((source, site, z, x, y)): Path<(String, String, usize, usize, String)>,
State(state): State<AppState>,
) -> Result<impl IntoResponse, AppError> {
let mut y = y
@ -59,7 +59,7 @@ pub async fn nexrad_handler(
}
let y: usize = y.parse()?;
let tile_id = TileId {
source,
source: format!("{source}/{site}"),
z,
x,
y,
@ -73,7 +73,7 @@ pub async fn nexrad_handler(
// is this even a valid data source?
let data_id = tile_id.data_id();
let Some(ds) = state.config.data.nexrad.get(&data_id.source) else {
let Some(ds) = state.config.data.nexrad.get(&source) else {
return Err(anyhow!("invalid/unknown nexrad state").into());
};
@ -82,7 +82,7 @@ pub async fn nexrad_handler(
// lets check if we have the raw data
let data = if !state.nexrad_data_cache.contains_key(&data_id) {
// we don't, so let's start by starting a task for that
load_nexrad_data(state.nexrad_data_cache, data_id, ds.clone()).await?
load_nexrad_data(state.nexrad_data_cache, data_id, ds.clone(), &site).await?
} else {
state.nexrad_data_cache.get(&data_id).await.unwrap()
};
@ -90,7 +90,7 @@ pub async fn nexrad_handler(
// we know we need to build the tile, so let's do that now
// it also returns it, so we can conveniently return it right now
let pixel_data =
render_to_png(state.nexrad_tile_cache.clone(), data, tile_id, ds.clone()).await?;
render_to_png(state.nexrad_tile_cache.clone(), data, tile_id, &site, ds.clone()).await?;
Ok((
[(header::CONTENT_TYPE, "image/png")],
@ -102,10 +102,21 @@ async fn load_nexrad_data(
cache: NexradDataCache,
data_id: DataId,
data_source: NexradDataSource,
site: &str,
) -> anyhow::Result<Arc<Scan>> {
let _load_span = info_span!("load_nexrad_data");
let bytes = DATA_BYTES.to_vec();
// todo: find the correct
let url = format!("https://noaa-nexrad-level2.s3.amazonaws.com/2025/05/15/KCYS/KCYS20250515_003010_V06");
let client = reqwest::Client::new();
let r = client.get(url).send().await?;
if !r.status().is_success() {
bail!("nexrad data failed to load: {}", r.status());
}
let bytes = r.bytes().await?.to_vec();
let data = Arc::new(parse(bytes)?);
@ -175,7 +186,7 @@ async fn render_to_png(
cache: NexradTileCache,
data: Arc<Scan>,
tile_id: TileId,
site: &str,
data_source: NexradDataSource,
) -> anyhow::Result<Arc<Vec<u8>>> {
let span = info_span!("render_to_png");
@ -192,7 +203,7 @@ async fn render_to_png(
let first_sweep = data.sweeps.get(0).unwrap();
let g = Geodesic::wgs84();
let radar = SITES.sites.get("KCRP").unwrap();
let radar = SITES.sites.get(site).unwrap();
drop(prep_span);