Skip to content

Commit cc30e32

Browse files
committed
fix lightning
1 parent e9af332 commit cc30e32

2 files changed

Lines changed: 49 additions & 22 deletions

File tree

src/lib/WeatherOverlays.svelte

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
} from "./isobarLayer";
2727
import TileLayer from "ol/layer/Tile";
2828
import XYZ from "ol/source/XYZ";
29+
import TileWMS from "ol/source/TileWMS.js";
2930
3031
// Minimal view of the parent's layerOptions entries the weather code touches.
3132
// (The parent's full LayerOption has more fields; structural typing lets us
@@ -422,6 +423,23 @@
422423
}
423424
});
424425
426+
// Keep the lightning raster current while it's on. NLDN publishes ~every
427+
// 15 min; bump a cache-buster param every 5 min so the browser/GeoServer
428+
// serve the latest grid instead of a pinned tile. The teardown clears the
429+
// timer when the layer is toggled off (the effect re-runs on `.on`).
430+
$effect(() => {
431+
const on = !!mapGlobal.layerOptions.find((l) => l.name === "lightning")?.on;
432+
if (!on) return;
433+
const refresh = () => {
434+
const layer = mapGlobal.layerOptions.find((l) => l.name === "lightning")
435+
?.layer as TileLayer<TileWMS> | undefined;
436+
layer?.getSource()?.updateParams({ _ts: Date.now() });
437+
};
438+
refresh();
439+
const id = setInterval(refresh, 5 * 60 * 1000);
440+
return () => clearInterval(id);
441+
});
442+
425443
// Pushes the weather layerOptions placeholders + fires the async layer setup.
426444
// Gated on mapGlobal.map existing (so tileBase is resolved and api() points at
427445
// the right origin) and on the NOAA cache being reachable (matches the parent's
@@ -478,10 +496,8 @@
478496
maxZoom: weatherMaxZoom,
479497
});
480498
}
481-
// Lightning overlay. Backed by the noaa-glm stub model server-
482-
// side — the option appears in the panel but turning it on
483-
// surfaces the "needs NetCDF decoder" reason. Filled in for real
484-
// when the GLM decoder ships.
499+
// Lightning overlay. NOAA nowCOAST NLDN cloud-to-ground strike-density
500+
// raster (free, no key) — setupLightningLayer fills the .layer ref.
485501
if (!mapGlobal.layerOptions.find((l) => l.name === "lightning")) {
486502
mapGlobal.layerOptions.push({
487503
name: "lightning",
@@ -511,6 +527,32 @@
511527
// via /noaa-weather/data/{model}/latest.json. No CDN/tile path.
512528
setupWindLayer();
513529
setupSatelliteLayer();
530+
setupLightningLayer();
531+
532+
// NOAA nowCOAST NLDN lightning strike density (cloud-to-ground flashes,
533+
// 8 km / 15 min, North America) as a WMS raster overlay — free, no API key,
534+
// sends CORS. No TIME param → GeoServer serves the latest grid; a periodic
535+
// cache-buster (see the $effect below) refreshes it while the layer is on.
536+
// Drawn above satellite (zIndex 7) so strikes read over cloud imagery.
537+
function setupLightningLayer() {
538+
const layer = new TileLayer({
539+
opacity: 0.85,
540+
zIndex: 7,
541+
source: new TileWMS({
542+
url: "https://nowcoast.noaa.gov/geoserver/ows",
543+
params: {
544+
LAYERS: "lightning_detection:ldn_lightning_strike_density",
545+
TRANSPARENT: true,
546+
},
547+
crossOrigin: "anonymous",
548+
transition: 300,
549+
}),
550+
});
551+
const existing = mapGlobal.layerOptions.find(
552+
(l) => l.name === "lightning",
553+
);
554+
if (existing) existing.layer = layer;
555+
}
514556
515557
// GOES-East GeoColor (visible/IR cloud composite) satellite imagery from
516558
// NASA GIBS — free, no API key. TIME=default serves the latest near-real-

weather/weather_models.go

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ var allModels = []*WeatherModel{
173173
pacioosWaveModel(),
174174
pacioosHawaiiWaveModel(),
175175
gfsIsobarsModel(),
176-
noaaGLMLightningStub(),
176+
// Lightning is served straight from NOAA nowCOAST's NLDN strike-density
177+
// WMS as a frontend raster overlay (see WeatherOverlays.svelte), so it
178+
// isn't a backend weather model — no registry entry / GLM decoder needed.
177179
// nomads-gfswave is unregistered until we verify NOMADS' actual
178180
// current DODS URL pattern. Every variant I've tried (date dir
179181
// with/without `gfswave` prefix, gfsv16 suffix, etc.) lands on
@@ -940,23 +942,6 @@ func iconGlobalWindStub() *WeatherModel {
940942
}
941943
}
942944

943-
// noaaGLMLightningStub registers the GOES-East GLM lightning option in
944-
// the picker so it surfaces in the layer panel under the weather
945-
// group, but flips Disabled so toggling it on shows the Reason
946-
// (matching the NAM/ECMWF/ICON pattern). The actual GLM L2 LCFA feed
947-
// is NetCDF4/HDF5 over the NESDIS S3 mirror — decoding that is a
948-
// separate work item.
949-
func noaaGLMLightningStub() *WeatherModel {
950-
return &WeatherModel{
951-
Name: "noaa-glm",
952-
DisplayName: "Lightning (GOES-East GLM, near-real-time)",
953-
Kind: "lightning",
954-
Domain: "americas",
955-
Disabled: true,
956-
Reason: "needs GOES-16/18 GLM NetCDF4 decoder (L2 LCFA strokes)",
957-
}
958-
}
959-
960945
// ----------------------------------------------------------------------
961946
// JSON shape served by /noaa-weather/models
962947
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)