|
26 | 26 | } from "./isobarLayer"; |
27 | 27 | import TileLayer from "ol/layer/Tile"; |
28 | 28 | import XYZ from "ol/source/XYZ"; |
| 29 | + import TileWMS from "ol/source/TileWMS.js"; |
29 | 30 |
|
30 | 31 | // Minimal view of the parent's layerOptions entries the weather code touches. |
31 | 32 | // (The parent's full LayerOption has more fields; structural typing lets us |
|
422 | 423 | } |
423 | 424 | }); |
424 | 425 |
|
| 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 | +
|
425 | 443 | // Pushes the weather layerOptions placeholders + fires the async layer setup. |
426 | 444 | // Gated on mapGlobal.map existing (so tileBase is resolved and api() points at |
427 | 445 | // the right origin) and on the NOAA cache being reachable (matches the parent's |
|
478 | 496 | maxZoom: weatherMaxZoom, |
479 | 497 | }); |
480 | 498 | } |
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. |
485 | 501 | if (!mapGlobal.layerOptions.find((l) => l.name === "lightning")) { |
486 | 502 | mapGlobal.layerOptions.push({ |
487 | 503 | name: "lightning", |
|
511 | 527 | // via /noaa-weather/data/{model}/latest.json. No CDN/tile path. |
512 | 528 | setupWindLayer(); |
513 | 529 | 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 | + } |
514 | 556 |
|
515 | 557 | // GOES-East GeoColor (visible/IR cloud composite) satellite imagery from |
516 | 558 | // NASA GIBS — free, no API key. TIME=default serves the latest near-real- |
|
0 commit comments