From fe7d998f671076f4daa1fb2f550972949eaea44a Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 30 Apr 2026 12:34:24 -0500 Subject: [PATCH] =?UTF-8?q?fix(weather-ca):=20paint=20HRDPS=20cells=20at?= =?UTF-8?q?=200.5=C2=B0=20size,=20not=20HRRR's=200.125=C2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The canvas overlay hard-coded a 0.0625° half-step when projecting each cell to canvas pixels — correct for HRRR (0.125° native grid) but wrong for HRDPS, which we downsample to 0.5° (0.25° half-step). At HRRR's half-step every HRDPS cell got painted at 1/16th its actual area, so the overlay over Canada looked like a sparse pinstripe of vertical streaks instead of contiguous coverage. Plumb halfStep through the WeatherCanvasOverlay constructor and pick 0.25 vs 0.0625 in the hook based on data-source. --- assets/js/weather_map_hook.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/assets/js/weather_map_hook.ts b/assets/js/weather_map_hook.ts index e8f46e97..ad238b08 100644 --- a/assets/js/weather_map_hook.ts +++ b/assets/js/weather_map_hook.ts @@ -475,7 +475,7 @@ function buildPaintScale(layerId: LayerId): PaintScale | BoolPaintScale | null { // SVG overlay — at low zoom (whole-CONUS, ~80k cells) SVG paint cost // dominated; canvas is one fillRect per cell with a precomputed LUT. const WeatherCanvasOverlayProto: L.LayerOptions & { - initialize: (this: WeatherCanvasOverlay, pack: CellPack | null, layerId: LayerId) => void + initialize: (this: WeatherCanvasOverlay, pack: CellPack | null, layerId: LayerId, halfStep: number) => void onAdd: (this: WeatherCanvasOverlay, map: L.Map) => WeatherCanvasOverlay onRemove: (this: WeatherCanvasOverlay, map: L.Map) => WeatherCanvasOverlay setPack: (this: WeatherCanvasOverlay, pack: CellPack | null) => void @@ -483,9 +483,10 @@ const WeatherCanvasOverlayProto: L.LayerOptions & { _reset: (this: WeatherCanvasOverlay) => void _draw: (this: WeatherCanvasOverlay) => void } = { - initialize(pack: CellPack | null, layerId: LayerId): void { + initialize(pack: CellPack | null, layerId: LayerId, halfStep: number): void { this.pack = pack this.layerId = layerId + this.halfStep = halfStep }, onAdd(map: L.Map): WeatherCanvasOverlay { this._map = map @@ -537,7 +538,7 @@ const WeatherCanvasOverlayProto: L.LayerOptions & { if (!paintScale) return const map = this._map - const halfStep = 0.0625 + const halfStep = this.halfStep const lats = this.pack.lats const lons = this.pack.lons const n = this.pack.cellCount @@ -578,6 +579,7 @@ const WeatherCanvasOverlayProto: L.LayerOptions & { interface WeatherCanvasOverlay extends L.Layer { pack: CellPack | null layerId: LayerId + halfStep: number _map: L.Map _canvas: HTMLCanvasElement setPack(pack: CellPack | null): void @@ -585,7 +587,7 @@ interface WeatherCanvasOverlay extends L.Layer { } // eslint-disable-next-line @typescript-eslint/no-explicit-any -const WeatherCanvasOverlayCtor: new (pack: CellPack | null, layerId: LayerId) => WeatherCanvasOverlay = (L.Layer as any).extend(WeatherCanvasOverlayProto) +const WeatherCanvasOverlayCtor: new (pack: CellPack | null, layerId: LayerId, halfStep: number) => WeatherCanvasOverlay = (L.Layer as any).extend(WeatherCanvasOverlayProto) function buildDetailHTML(point: WeatherPoint): string { if (!point) return "" @@ -1050,7 +1052,12 @@ export const WeatherMap: WeatherMapHook = { } if (!this.weatherOverlay) { - this.weatherOverlay = new WeatherCanvasOverlayCtor(this.pack, this.selectedLayer) + // HRRR is on a 0.125° native grid → 0.0625 half-step. + // HRDPS in this app is downsampled to 0.5° → 0.25 half-step. + // Without this, HRDPS cells were painted 1/16th their actual area + // and looked like a sparse pinstripe instead of an overlay. + const halfStep = this.source === "hrdps" ? 0.25 : 0.0625 + this.weatherOverlay = new WeatherCanvasOverlayCtor(this.pack, this.selectedLayer, halfStep) this.weatherOverlay.addTo(this.map) } else { this.weatherOverlay.pack = this.pack