feat(layers): Recent Imagery — open satellite imagery for a selected box (#716)

Select a box on the world and browse the last 30 days of open satellite imagery over it, keyless: NASA GIBS Harmonized Landsat Sentinel-2 (30 m) and the VIIRS daily overview (250 m), with a day strip, IMAGE / VS BASEMAP / A / B modes with a swipe divider, PNG export and share links. The before/after swipe from the Nepal scene moves into shared modules so both use it.

Co-authored-by: manjunath22466 <[email protected]>
This commit is contained in:
Bilawal Sidhuandmanjunath22466 authored and GitHub committed 2026-09-22 20:34:11 -05:00
1 parent 25491f7e33
commit 082074a006
50 files changed
+13169 -440

No files matched your search

+28 -1
View File
@@ -31,11 +31,12 @@ const PANEL_GROUPS = [
},
{
label: 'Cameras',
ids: ['cctv', 'alpr-cameras'],
ids: ['cctv', 'recent-imagery'],
},
{
label: 'Infrastructure',
ids: [
'alpr-cameras',
'military-installations',
'local-datacenters',
'telegeography-submarine-cables',
@@ -126,6 +127,8 @@ export class LayerPanel {
this._removers = [];
this._destroyed = false;
this._cancelRowControlsRefresh = null;
this._recentImageryFactory = null;
this._recentImageryPanel = null;
}
mount(container) {
if (this._destroyed) return;
@@ -138,8 +141,29 @@ export class LayerPanel {
container?.ownerDocument?.getElementById?.('weather-panel-body'),
setLayerParams: this.setLayerParams,
});
this._mountRecentImagery();
this._renderToggles();
}
/**
* Host the Recent Imagery readout in its rail body, like the weather
* readout. The application supplies the factory once the layer, viewer and
* box tool exist; a remount rebuilds the readout in place.
* @param {((container: HTMLElement) => { destroy: () => void } | null) | null} factory
*/
attachRecentImagery(factory) {
if (this._destroyed) return;
this._recentImageryFactory = typeof factory === 'function' ? factory : null;
this._mountRecentImagery();
}
_mountRecentImagery() {
this._recentImageryPanel?.destroy();
this._recentImageryPanel = null;
const container = this._toggleContainer?.ownerDocument?.getElementById?.(
'recent-imagery-panel-body',
);
if (container && this._recentImageryFactory)
this._recentImageryPanel = this._recentImageryFactory(container) || null;
}
_bind(element, type, listener) {
element.addEventListener(type, listener);
this._removers.push(() => element.removeEventListener(type, listener));
@@ -156,6 +180,9 @@ export class LayerPanel {
this._releaseBindings();
this._weatherPanel?.destroy();
this._weatherPanel = null;
this._recentImageryPanel?.destroy();
this._recentImageryPanel = null;
this._recentImageryFactory = null;
this._toggleContainer = null;
}
_renderToggles() {