Satellitenbilder-Switcher: 9 Imagery-Quellen

Dropdown im Layer-Panel unter SATELLITENBILDER:
- Cesium Ion (Standard, photorealistisch mit Terrain)
- Esri World Imagery (hochaufloesend, 0.3-1m)
- Sentinel-2 2024/2023/2022/2020/2018 (ESA Copernicus, historisch)
- OpenTopoMap (topographische Karte)
- OpenStreetMap (Referenz)

Sentinel-2 historisch ermoeglicht Vorher/Nachher-Vergleiche:
z.B. Waldbraende, Ueberschwemmungen, Infrastruktur-Veraenderungen.
Alle Quellen kostenlos, kein API-Key, volle CORS-Unterstuetzung.
Dieser Commit ist enthalten in:
Claude Dev
2026-03-24 21:53:48 +01:00
Ursprung e8a9e49dba
Commit 324f42e4a7
4 geänderte Dateien mit 121 neuen und 0 gelöschten Zeilen

Datei anzeigen

@@ -552,3 +552,20 @@ html, body { height: 100%; overflow: hidden; background: var(--bg-primary); colo
.ch-ring-1 { width: 200px; height: 200px; transform: translate(-50%, -50%); } .ch-ring-1 { width: 200px; height: 200px; transform: translate(-50%, -50%); }
.ch-ring-2 { width: 400px; height: 400px; transform: translate(-50%, -50%); } .ch-ring-2 { width: 400px; height: 400px; transform: translate(-50%, -50%); }
.ch-ring-3 { width: 600px; height: 600px; transform: translate(-50%, -50%); } .ch-ring-3 { width: 600px; height: 600px; transform: translate(-50%, -50%); }
/* === Imagery Selector === */
.imagery-select {
width: 100%;
padding: 4px 6px;
background: rgba(255,255,255,0.05);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text);
font-family: var(--font-mono);
font-size: 10px;
cursor: pointer;
outline: none;
}
.imagery-select:hover { border-color: var(--accent); }
.imagery-select option { background: var(--bg-primary); color: var(--text); }

Datei anzeigen

@@ -129,6 +129,23 @@
</div> </div>
</div> </div>
<div class="panel-divider"></div> <div class="panel-divider"></div>
<div class="panel-divider"></div>
<div class="panel-section">
<div style="font-size:9px;letter-spacing:1.5px;color:var(--accent);margin-bottom:6px;">SATELLITENBILDER</div>
<select id="imagery-select" class="imagery-select" onchange="ImagerySwitch.switchTo(this.value)">
<option value="default">Cesium Ion (Standard)</option>
<option value="esri">Esri World Imagery</option>
<option value="s2-2024">Sentinel-2 (2024)</option>
<option value="s2-2023">Sentinel-2 (2023)</option>
<option value="s2-2022">Sentinel-2 (2022)</option>
<option value="s2-2020">Sentinel-2 (2020)</option>
<option value="s2-2018">Sentinel-2 (2018)</option>
<option value="topo">OpenTopoMap</option>
<option value="osm">OpenStreetMap</option>
</select>
<div id="imagery-label" style="font-size:9px;color:var(--text-dim);margin-top:2px;">Cesium Ion (Standard)</div>
</div>
<div class="panel-divider"></div>
<div class="panel-coords" id="coords-display"> <div class="panel-coords" id="coords-display">
LAT: --&nbsp;&nbsp;LON: -- LAT: --&nbsp;&nbsp;LON: --
</div> </div>
@@ -182,6 +199,7 @@
<script src="/static/js/ui/sidebar.js"></script> <script src="/static/js/ui/sidebar.js"></script>
<script src="/static/js/layers/monitor.js"></script> <script src="/static/js/layers/monitor.js"></script>
<script src="/static/js/layers/weather.js"></script> <script src="/static/js/layers/weather.js"></script>
<script src="/static/js/ui/imagery.js"></script>
<script src="/static/js/ui/search.js"></script> <script src="/static/js/ui/search.js"></script>
<script src="/static/js/ui/crosshairs.js"></script> <script src="/static/js/ui/crosshairs.js"></script>
<script src="/static/js/layers/visualmodes.js"></script> <script src="/static/js/layers/visualmodes.js"></script>

Datei anzeigen

@@ -84,6 +84,7 @@ const Globe = {
this._toggleLabels(true); this._toggleLabels(true);
if (typeof VisualModes !== 'undefined') VisualModes.init(); if (typeof VisualModes !== 'undefined') VisualModes.init();
if (typeof Sidebar !== 'undefined') Sidebar.init(); if (typeof Sidebar !== 'undefined') Sidebar.init();
if (typeof ImagerySwitch !== 'undefined') ImagerySwitch.init(this.viewer);
if (typeof SearchUI !== 'undefined') SearchUI.init(); if (typeof SearchUI !== 'undefined') SearchUI.init();
this._loadLagen(); this._loadLagen();
document.getElementById('bottom-stats').textContent = 'Globe initialisiert — Lade Daten...'; document.getElementById('bottom-stats').textContent = 'Globe initialisiert — Lade Daten...';

85
static/js/ui/imagery.js Normale Datei
Datei anzeigen

@@ -0,0 +1,85 @@
/**
* Imagery Switcher: Verschiedene Satellitenbilder + historische Aufnahmen.
*/
const ImagerySwitch = {
_viewer: null,
_currentLayer: null,
_currentId: 'default',
_sources: {
'default': { name: 'Cesium Ion (Standard)', url: null },
'esri': {
name: 'Esri World Imagery',
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
},
's2-2024': {
name: 'Sentinel-2 (2024)',
url: 'https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2024_3857/default/GoogleMapsCompatible/{z}/{x}/{y}.jpg',
},
's2-2023': {
name: 'Sentinel-2 (2023)',
url: 'https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2023_3857/default/GoogleMapsCompatible/{z}/{x}/{y}.jpg',
},
's2-2022': {
name: 'Sentinel-2 (2022)',
url: 'https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2022_3857/default/GoogleMapsCompatible/{z}/{x}/{y}.jpg',
},
's2-2020': {
name: 'Sentinel-2 (2020)',
url: 'https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2020_3857/default/GoogleMapsCompatible/{z}/{x}/{y}.jpg',
},
's2-2018': {
name: 'Sentinel-2 (2018)',
url: 'https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2018_3857/default/GoogleMapsCompatible/{z}/{x}/{y}.jpg',
},
'topo': {
name: 'OpenTopoMap',
url: 'https://tile.opentopomap.org/{z}/{x}/{y}.png',
},
'osm': {
name: 'OpenStreetMap',
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
},
},
init(viewer) {
this._viewer = viewer;
},
switchTo(id) {
if (!this._viewer || this._currentId === id) return;
var src = this._sources[id];
if (!src) return;
// Alle eigenen Imagery-Layer entfernen (nicht den Cesium-Default und nicht Labels)
if (this._currentLayer) {
this._viewer.imageryLayers.remove(this._currentLayer);
this._currentLayer = null;
}
if (id === 'default') {
// Cesium Ion Default wiederherstellen — nichts tun, der ist immer da
this._currentId = id;
} else {
// Neuen Layer als untersten hinzufuegen (ueber dem Default)
this._currentLayer = this._viewer.imageryLayers.addImageryProvider(
new Cesium.UrlTemplateImageryProvider({
url: src.url,
maximumLevel: 18,
credit: src.name,
})
);
// Unter Labels aber ueber Default
this._viewer.imageryLayers.lower(this._currentLayer);
this._currentId = id;
}
// Dropdown aktualisieren
var sel = document.getElementById('imagery-select');
if (sel) sel.value = id;
// Status
var label = document.getElementById('imagery-label');
if (label) label.textContent = src.name;
},
};