mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Move the Active Fires hand-off up to 4,500 km
The heatmap now takes over above 4,500 km instead of 3,000 km, with the cross-fade between 3,600 and 5,400 km. The markers band caps at 40,000 detections, which covers the ~40,000 in the padded view over Africa at the low end of the fade, so weak fires still stay as markers. To keep that rebuild near its old cost, markers outside the close band read the anchor already built for each fire instead of the ground floor cache: only the close band warms floors, so the lookup there always missed. A 40,000-marker rebuild now costs ~55 ms, down from ~120 ms.
This commit is contained in:
1 parent
9a1bc34042
commit
f608ae81e7
6 files changed
+40
-20
No files matched your search
@@ -5,7 +5,7 @@
|
||||
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,
|
||||
one distinct spot per fire, empty where nothing burns, and cross-fades with
|
||||
the markers around 3,000 km with no gap, keeping weak fires as markers. The filter applies once before
|
||||
the markers around 4,500 km with no gap, keeping weak fires as markers. 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
|
||||
|
||||
@@ -2205,7 +2205,7 @@ none is a cold hue, so the brightest marks are the strongest fires; ~90% of
|
||||
detections are under 20 MW and stay dim red. Glow sprites and fire-card
|
||||
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
|
||||
At far zoom (camera above ~4,500 km) the layer drapes a Snap Map–style
|
||||
heatmap instead of individual markers: the shown detections are linearly binned
|
||||
(each detection's weight shared between the four nearest pixel centres, so spots
|
||||
sit at the true position and stay round) on a
|
||||
@@ -2221,14 +2221,15 @@ nothing burns (`src/data/firmsHeatRaster.js`). It is one full-globe imagery
|
||||
tile on the active imagery host (`src/layers/firms/heatImagery.js`), rebuilt
|
||||
on each data refresh or filter change (the first far-band paint at once,
|
||||
later ones 150 ms after the last change). Heatmap and markers are both kept
|
||||
built at every zoom and cross-fade by camera height between 2,400 and
|
||||
3,600 km, every frame, so a fast zoom never shows a gap: the heatmap fades by
|
||||
built at every zoom and cross-fade by camera height between 3,600 and
|
||||
5,400 km, every frame, so a fast zoom never shows a gap: the heatmap fades by
|
||||
imagery `alpha` (a hidden Cesium imagery layer is unmapped from the globe
|
||||
tiles and re-mapped late when shown), the markers by sprite alpha, and the
|
||||
marker collection is hidden once the heatmap is whole. The 750–3,000 km
|
||||
markers band draws up to 20,000 detections (the strongest in the padded
|
||||
marker collection is hidden once the heatmap is whole. The 750–4,500 km
|
||||
markers band draws up to 40,000 detections (the strongest in the padded
|
||||
view), so the weak fires the heatmap shows stay on screen as markers when it
|
||||
fades out. "N FIRES" cards from a 2° / 1° aggregation still label the hottest
|
||||
fades out; it reads each marker's already-built anchor instead of the ground
|
||||
floor cache, which only the close band warms. "N FIRES" cards from a 2° / 1° aggregation still label the hottest
|
||||
areas; their accent is the strongest fire's FRP color.
|
||||
|
||||
The Active Fires row carries a "Fire intensity (FRP)" slider on a log scale
|
||||
|
||||
@@ -521,7 +521,7 @@ test('far zoom drapes one heatmap and the hand-off cross-fades it with the marke
|
||||
assert.equal(markers.get(0).color.alpha, 1);
|
||||
|
||||
// Mid hand-off both are drawn, half each, in either band.
|
||||
setHeight(3_000_000);
|
||||
setHeight(4_500_000);
|
||||
h.layer._applyHandoffFadeForTest();
|
||||
assert.equal(added[0].alpha, 0.5);
|
||||
assert.equal(markers.show, true);
|
||||
|
||||
@@ -231,6 +231,19 @@ export function createModel({
|
||||
return fire.position;
|
||||
}
|
||||
|
||||
/**
|
||||
* The anchor already built for a detection, without the floor lookup;
|
||||
* for the far marker band, where floors are never warmed and the height
|
||||
* difference is invisible, so tens of thousands of markers skip a cache
|
||||
* read each per rebuild. Builds one through {@link firePosition} once.
|
||||
* @param {Object} fire - Detection record.
|
||||
* @returns {Cesium.Cartesian3}
|
||||
*/
|
||||
|
||||
function fireBuiltPosition(fire) {
|
||||
return fire.position ?? firePosition(fire);
|
||||
}
|
||||
|
||||
/**
|
||||
* Occlusion-test anchor for one detection. Returns the render position
|
||||
* outright for anchors comfortably above the ellipsoid (the common case) and
|
||||
@@ -238,11 +251,11 @@ export function createModel({
|
||||
* NEVER feeds rendering: the datum-correct anchor from {@link firePosition}
|
||||
* is what the sprite and the card are drawn at.
|
||||
* @param {Object} fire - Detection record.
|
||||
* @param {Cesium.Cartesian3} [position] Its render anchor, if at hand.
|
||||
* @returns {Cesium.Cartesian3}
|
||||
*/
|
||||
|
||||
function fireCullPosition(fire) {
|
||||
const position = firePosition(fire);
|
||||
function fireCullPosition(fire, position = firePosition(fire)) {
|
||||
if (fire.positionHeight >= CULL_LIFT_THRESHOLD_M) return position;
|
||||
if (!fire.cullPosition) {
|
||||
fire.cullPosition = Cesium.Cartesian3.fromDegrees(
|
||||
@@ -587,6 +600,7 @@ export function createModel({
|
||||
sizeBucket,
|
||||
glowSprite,
|
||||
firePosition,
|
||||
fireBuiltPosition,
|
||||
fireCullPosition,
|
||||
cellCullPosition,
|
||||
applyHorizonCull,
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as Cesium from 'cesium';
|
||||
export const REFRESH_INTERVAL_MS = 600_000;
|
||||
|
||||
/**
|
||||
* LOD bands keyed by camera height. `heat` bands (above ~3,000 km) drape a
|
||||
* LOD bands keyed by camera height. `heat` bands (above ~4,500 km) drape a
|
||||
* Snap Map–style heatmap over the globe (firmsHeatRaster.js); their
|
||||
* `gridDegrees` aggregation only feeds the "N FIRES" cards. `detections`
|
||||
* bands render individual fire detections as glow-sprite billboards.
|
||||
@@ -26,21 +26,21 @@ export const LOD_LEVELS = [
|
||||
},
|
||||
{
|
||||
id: 'regional',
|
||||
minHeight: 3000000,
|
||||
minHeight: 4500000,
|
||||
mode: 'heat',
|
||||
gridDegrees: 1.0,
|
||||
maxCells: 3600,
|
||||
labelDistance: 8500000,
|
||||
},
|
||||
{
|
||||
// Up to 20,000 markers: at the 2,400 km end of the hand-off the padded
|
||||
// view over Africa, the densest region, holds ~21,000 detections, and a
|
||||
// Up to 40,000 markers: at the 3,600 km end of the hand-off the padded
|
||||
// view over Africa, the densest region, holds ~40,000 detections, and a
|
||||
// tighter cap left the heatmap's weak spots dissolving into nothing.
|
||||
// A rebuild of that many costs ~60 ms, only there.
|
||||
id: 'local',
|
||||
minHeight: 750000,
|
||||
mode: 'detections',
|
||||
maxDetections: 20000,
|
||||
maxDetections: 40000,
|
||||
labelDistance: 4500000,
|
||||
},
|
||||
{
|
||||
@@ -55,7 +55,7 @@ export const LOD_LEVELS = [
|
||||
/**
|
||||
* 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 the size of its
|
||||
* marker at the 3,000 km hand-off and the two cross-fade without a jump;
|
||||
* marker near the hand-off and the two cross-fade without a jump;
|
||||
* neighbours merge only when they nearly touch (σ 3 on a 2048 grid smeared
|
||||
* each fire over ~200 km). Built once per data refresh or filter change,
|
||||
* never per camera move.
|
||||
@@ -69,12 +69,12 @@ export const HEAT_RASTER = Object.freeze({
|
||||
|
||||
/**
|
||||
* Camera heights, in metres, over which the heatmap and the markers
|
||||
* cross-fade around the 3,000 km band edge: markers alone below `from`,
|
||||
* cross-fade around the 4,500 km band edge: markers alone below `from`,
|
||||
* heatmap alone above `to`. Wider than the ±10 % band hysteresis, so
|
||||
* whichever band is active inside it, both layers are already built.
|
||||
*/
|
||||
|
||||
export const HANDOFF_FADE = Object.freeze({ from: 2_400_000, to: 3_600_000 });
|
||||
export const HANDOFF_FADE = Object.freeze({ from: 3_600_000, to: 5_400_000 });
|
||||
|
||||
/**
|
||||
* Trailing delay before a stale heatmap is rebuilt while one is already on
|
||||
|
||||
@@ -354,10 +354,15 @@ export function createRendering({
|
||||
}
|
||||
|
||||
const markers = [];
|
||||
// Only the close band warms terrain floors (renderDetections).
|
||||
const anchor =
|
||||
lod.id === 'close'
|
||||
? components.model.firePosition
|
||||
: components.model.fireBuiltPosition;
|
||||
for (const fire of candidates) {
|
||||
const coreSize = components.model.frpPixelSize(fire.frp);
|
||||
const position = components.model.firePosition(fire);
|
||||
const cullPosition = components.model.fireCullPosition(fire);
|
||||
const position = anchor(fire);
|
||||
const cullPosition = components.model.fireCullPosition(fire, position);
|
||||
const pickId = `firms-${fire.index}`;
|
||||
layerState._pickIndexById.set(pickId, fire);
|
||||
layerState._cullPositions.push(cullPosition);
|
||||
|
||||
Reference in new issue
Block a user