mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
The far-zoom heatmap now uses the ½× grid (4096 × 2048, ≈10 km per pixel, one-pixel blur), so a lone fire's spot matches its marker at the 3,000 km hand-off. The spot size slider and its share option are gone. Markers and heatmap are both kept built at every zoom and cross-fade by camera height between 2,400 and 3,600 km on every frame, so a fast zoom never shows a beat with neither. The heatmap fades by imagery alpha: hiding a Cesium imagery layer unmaps it from the globe tiles, and showing it again re-mapped them late, which was the gap at the band edge. Under the markers the heatmap is built 150 ms after the data or filter changes.
89 lines
2.5 KiB
JavaScript
89 lines
2.5 KiB
JavaScript
/**
|
|
* Drapes the Active Fires heat raster over the globe as one full-extent
|
|
* imagery tile on the active imagery host (globe or 3D-tiles), like the wind
|
|
* layer's scalar field. Owns the canvas and the imagery layer; nothing else
|
|
* in the fires layer touches imagery.
|
|
*
|
|
* `show` is only for the layer being on or off. The zoom hand-off fades
|
|
* `alpha` instead: Cesium unmaps a hidden imagery layer from the globe
|
|
* tiles and re-maps it when shown, so a show toggle at the band edge left
|
|
* a beat with neither markers nor heat. At alpha 0 the layer stays mapped
|
|
* and costs no draw.
|
|
*/
|
|
|
|
const defaultCanvas = (width, height) => {
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
return canvas;
|
|
};
|
|
|
|
export function createHeatImagery({
|
|
cesium,
|
|
getHost,
|
|
createCanvas = defaultCanvas,
|
|
}) {
|
|
let raster = null;
|
|
let layer = null;
|
|
let collection = null;
|
|
let show = true;
|
|
let opacity = 1;
|
|
|
|
function detach() {
|
|
if (layer && collection) collection.remove(layer, true);
|
|
layer = null;
|
|
collection = null;
|
|
}
|
|
|
|
function attach() {
|
|
detach();
|
|
if (!raster) return;
|
|
const host = getHost?.() ?? { collection: null, kind: 'none' };
|
|
if (!host.collection) return;
|
|
const canvas = createCanvas(raster.width, raster.height);
|
|
const ctx = canvas.getContext('2d');
|
|
const image = ctx.createImageData(raster.width, raster.height);
|
|
image.data.set(raster.rgba);
|
|
ctx.putImageData(image, 0, 0);
|
|
const provider = new cesium.SingleTileImageryProvider({
|
|
url: canvas.toDataURL('image/png'),
|
|
tileWidth: raster.width,
|
|
tileHeight: raster.height,
|
|
rectangle: cesium.Rectangle.MAX_VALUE,
|
|
});
|
|
collection = host.collection;
|
|
layer = collection.addImageryProvider(provider);
|
|
layer.show = show;
|
|
layer.alpha = opacity;
|
|
collection.raiseToTop?.(layer);
|
|
}
|
|
|
|
return {
|
|
setRaster(next) {
|
|
raster = next;
|
|
attach();
|
|
},
|
|
/** True once a raster was handed over (painted or waiting for a host). */
|
|
hasRaster() {
|
|
return raster !== null;
|
|
},
|
|
setShow(value) {
|
|
show = Boolean(value);
|
|
if (layer) layer.show = show;
|
|
},
|
|
/** Hand-off fade, 0..1; the layer stays mapped at 0. */
|
|
setOpacity(value) {
|
|
opacity = Math.min(1, Math.max(0, Number(value) || 0));
|
|
if (layer) layer.alpha = opacity;
|
|
},
|
|
rehome() {
|
|
const host = getHost?.();
|
|
if ((host?.collection ?? null) !== collection) attach();
|
|
},
|
|
destroy() {
|
|
detach();
|
|
raster = null;
|
|
},
|
|
};
|
|
}
|