Draw each far-zoom fire as a marker-sized heat spot

The heat raster was 2048 px wide with a 3 px blur, so every fire spread
over ~200 km and the far-zoom map read as clouds next to the markers.
Double the grid to ~10 km per pixel and blur by one pixel: a lone fire
is now a spot about as wide as its marker at the 3,000 km hand-off.
This commit is contained in:
German Khokhlov committed 2026-11-02 22:43:51 +04:00
1 parent 314292a006
commit ad171f2854
4 files changed
+37 -8

No files matched your search

+1 -1
View File
@@ -4,6 +4,6 @@
log-scale minimum-FRP slider whose track is the ramp, an All / Nominal+ /
High confidence switch and an "N of M shown" counter to the layer row. Far
zoom shows a Snap Map–style heatmap in the same colors instead of squares,
empty where nothing burns. The filter applies once before rendering, so markers, cards, the heatmap, context and the top-fires query agree; share
one marker-sized spot per fire, empty where nothing burns. The filter applies once before rendering, so markers, cards, the heatmap, context and the top-fires query agree; share
links keep it (`w` token: `f`, `c`), and `get_active_fires` takes matching
`min_frp_mw` and `confidence` arguments. Default 0 MW / All (gekh, #1426).
+3 -2
View File
@@ -2185,9 +2185,10 @@ accents read that ramp; confidence and brightness do not affect the color.
At far zoom (camera above ~3,000 km) the layer drapes a Snap Map–style
heatmap instead of individual markers: the shown detections are binned on a
global 2048 × 1024 grid, each weighted by its FRP in megawatts (so heat is
global 4096 × 2048 grid (≈10 km per pixel), each weighted by its FRP in megawatts (so heat is
radiated power, and raising the FRP slider does not dim areas where strong fires
burn), Gaussian-blurred,
burn), Gaussian-blurred by one pixel so each fire stays a spot about the size
of its marker,
normalized on a log scale against the 99.5th percentile and mapped through the
same hues, from a bright red edge through orange and gold to a pale
white-hot core. The edge starts more than half opaque, so regions far below
+25
View File
@@ -1,6 +1,8 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { createHeatImagery } from './heatImagery.js';
import { buildHeatRaster } from '../../data/firmsHeatRaster.js';
import { HEAT_RASTER } from './policy.js';
function harness() {
const layers = [];
@@ -85,3 +87,26 @@ test('destroy removes the layer', () => {
h.imagery.destroy();
assert.equal(h.layers.length, 0);
});
test('the shipped raster paints a lone weak fire as a marker-sized spot', () => {
// A hot cluster elsewhere sets the scale, as Africa does on real data.
const fires = [{ lat: 0, lon: 0, frp: 5 }];
for (let i = 0; i < 40; i++) {
fires.push({ lat: -10 + (i % 5) * 0.1, lon: 25 + i * 0.05, frp: 300 });
}
const r = buildHeatRaster(fires, HEAT_RASTER);
const y = Math.floor((90 / 180) * r.height);
const painted = [];
for (let x = 0; x < r.width; x++) {
const lon = ((x + 0.5) / r.width) * 360 - 180;
if (Math.abs(lon) < 5 && r.rgba[(y * r.width + x) * 4 + 3] > 0) {
painted.push(x);
}
}
const kmPerPx = 40075 / r.width;
assert.ok(painted.length > 0, 'the weak fire still shows');
assert.ok(
painted.length * kmPerPx <= 80,
`spot ${Math.round(painted.length * kmPerPx)} km wide`,
);
});
+8 -5
View File
@@ -49,14 +49,17 @@ export const LOD_LEVELS = [
];
/**
* Global heat raster for the `heat` bands: ≈0.18° per pixel, Gaussian σ in
* pixels. Built once per data refresh or filter change, never per camera move.
* Global heat raster for the `heat` bands: ≈0.09° (≈10 km) per pixel and a
* one-pixel Gaussian σ, so a lone fire is a spot about as wide as its marker
* at the 3,000 km hand-off and neighbours merge only when they really touch
* (2048 px with σ 3 smeared each fire over ~200 km). Built once per data
* refresh or filter change, never per camera move.
*/
export const HEAT_RASTER = Object.freeze({
width: 2048,
height: 1024,
sigmaPx: 3,
width: 4096,
height: 2048,
sigmaPx: 1,
});
/**