Neue Features: Satelliten-Orbits, Naturkatastrophen, Visual Modes
SATELLITEN (CelesTrak TLE): - Raumstationen (ISS), GPS, Galileo, Wetter, Erdbeobachtung, Starlink - Echtzeit-Positionsberechnung aus Kepler-Elementen (2s Update) - Orbitbahnen als leuchtende Linien (Stationen, GPS, Galileo) - Farbkodiert nach Gruppe (rot=Station, orange=GPS, blau=Galileo) NATURKATASTROPHEN (NASA EONET): - Waldbraende, Vulkane, Stuerme, Fluten, Erdrutsche, Eis - Farbige Punkte mit Emoji-Labels - Klick zeigt Details und Quellen VISUAL MODES: - STD: Standard-Ansicht - NVG: Nachtsicht (gruener Monochrom-Filter) - FLIR: Thermal-Ansicht (invertiert, Infrarot-Look) - CRT: Retro-Monitor (Scanlines, Vignette) 4 neue Dateien: satellites.js, disasters.js, visualmodes.js, data_satellites.py, data_disasters.py
Dieser Commit ist enthalten in:
142
static/js/layers/satellites.js
Normale Datei
142
static/js/layers/satellites.js
Normale Datei
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Satelliten-Layer: Orbitale Positionen berechnet aus TLE-Daten.
|
||||
* Vereinfachte Kreisbahn-Berechnung (kein SGP4, reicht fuer Visualisierung).
|
||||
*/
|
||||
const SatellitesLayer = {
|
||||
_viewer: null,
|
||||
_points: null,
|
||||
_orbits: null,
|
||||
_interval: null,
|
||||
_count: 0,
|
||||
_data: [],
|
||||
_animFrame: null,
|
||||
|
||||
start(viewer) {
|
||||
if (this._points) return;
|
||||
this._viewer = viewer;
|
||||
this._points = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection());
|
||||
this._orbits = viewer.scene.primitives.add(new Cesium.PolylineCollection());
|
||||
this._fetch();
|
||||
var self = this;
|
||||
// Positionen alle 2s aktualisieren (Satelliten bewegen sich schnell)
|
||||
this._interval = setInterval(function() { self._updatePositions(); }, 2000);
|
||||
},
|
||||
|
||||
stop() {
|
||||
if (this._interval) { clearInterval(this._interval); this._interval = null; }
|
||||
if (this._points && this._viewer) { this._viewer.scene.primitives.remove(this._points); this._points = null; }
|
||||
if (this._orbits && this._viewer) { this._viewer.scene.primitives.remove(this._orbits); this._orbits = null; }
|
||||
this._count = 0; this._data = [];
|
||||
},
|
||||
|
||||
_fetch() {
|
||||
var self = this;
|
||||
var loadEl = document.getElementById('loading-satellites');
|
||||
var statusEl = document.getElementById('status-satellites');
|
||||
if (loadEl) loadEl.classList.add('active');
|
||||
if (statusEl) { statusEl.textContent = 'Lade Orbitdaten...'; statusEl.classList.add('active'); }
|
||||
fetch('/api/satellites')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
self._data = data.satellites || [];
|
||||
self._count = self._data.length;
|
||||
self._updatePositions();
|
||||
self._drawOrbits();
|
||||
if (statusEl) statusEl.textContent = self._count + ' Satelliten';
|
||||
})
|
||||
.catch(function(e) { console.warn('Satellites error:', e); if (statusEl) statusEl.textContent = 'Fehler'; })
|
||||
.finally(function() { if (loadEl) loadEl.classList.remove('active'); setTimeout(function() { if (statusEl) statusEl.classList.remove('active'); }, 5000); });
|
||||
},
|
||||
|
||||
_calcPosition(sat, time) {
|
||||
// Vereinfachte Kreisbahn aus Kepler-Elementen
|
||||
var n = sat.meanMotion; // Umlaeufe pro Tag
|
||||
if (!n || n <= 0) return null;
|
||||
var period = 86400 / n; // Umlaufzeit in Sekunden
|
||||
var a = Math.pow(398600.4418 * Math.pow(period / (2 * Math.PI), 2), 1/3); // Semi-major axis in km
|
||||
var altitude = a - 6371; // Hoehe ueber Erdoberflaeche in km
|
||||
if (altitude < 100 || altitude > 50000) return null;
|
||||
|
||||
var epochMs = new Date(sat.epoch).getTime();
|
||||
var elapsed = (time - epochMs) / 1000; // Sekunden seit Epoch
|
||||
var M = (sat.meanAnomaly || 0) + (360 * n / 86400) * elapsed; // Aktuelle Mean Anomaly
|
||||
M = ((M % 360) + 360) % 360; // Normalisieren
|
||||
|
||||
var inc = (sat.inclination || 0) * Math.PI / 180;
|
||||
var raan = (sat.raOfAscNode || 0) * Math.PI / 180;
|
||||
var argP = (sat.argOfPericenter || 0) * Math.PI / 180;
|
||||
var nu = M * Math.PI / 180; // True anomaly ≈ Mean anomaly fuer kleine Exzentrizitaet
|
||||
|
||||
// Position im Orbital-Frame
|
||||
var u = argP + nu;
|
||||
var r = a; // Kreisbahn-Naeherung
|
||||
|
||||
// ECI -> ECEF (vereinfacht: Erdrotation ignoriert fuer Visualisierung)
|
||||
var x = r * (Math.cos(raan) * Math.cos(u) - Math.sin(raan) * Math.sin(u) * Math.cos(inc));
|
||||
var y = r * (Math.sin(raan) * Math.cos(u) + Math.cos(raan) * Math.sin(u) * Math.cos(inc));
|
||||
var z = r * Math.sin(u) * Math.sin(inc);
|
||||
|
||||
// Erdrotation beruecksichtigen (grob)
|
||||
var gmst = elapsed * 7.2921159e-5; // Sternzeit-Winkel
|
||||
var xr = x * Math.cos(gmst) + y * Math.sin(gmst);
|
||||
var yr = -x * Math.sin(gmst) + y * Math.cos(gmst);
|
||||
|
||||
var lon = Math.atan2(yr, xr) * 180 / Math.PI;
|
||||
var lat = Math.asin(z / r) * 180 / Math.PI;
|
||||
|
||||
return { lat: lat, lon: lon, alt: altitude * 1000 }; // alt in Metern
|
||||
},
|
||||
|
||||
_updatePositions() {
|
||||
if (!this._points || !this._data.length) return;
|
||||
this._points.removeAll();
|
||||
var now = Date.now();
|
||||
var colors = {
|
||||
'stations': Cesium.Color.fromCssColorString('#ff4444'),
|
||||
'gps-ops': Cesium.Color.fromCssColorString('#ffaa00'),
|
||||
'galileo': Cesium.Color.fromCssColorString('#44aaff'),
|
||||
'weather': Cesium.Color.fromCssColorString('#aa44ff'),
|
||||
'resource': Cesium.Color.fromCssColorString('#44ffaa'),
|
||||
'starlink': Cesium.Color.fromCssColorString('#888888'),
|
||||
};
|
||||
for (var i = 0; i < this._data.length; i++) {
|
||||
var pos = this._calcPosition(this._data[i], now);
|
||||
if (!pos) continue;
|
||||
var color = colors[this._data[i].group] || Cesium.Color.WHITE;
|
||||
this._points.add({
|
||||
position: Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, pos.alt),
|
||||
pixelSize: this._data[i].group === 'starlink' ? 1.5 : 3,
|
||||
color: color,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_drawOrbits() {
|
||||
if (!this._orbits || !this._data.length) return;
|
||||
this._orbits.removeAll();
|
||||
var now = Date.now();
|
||||
var colors = {
|
||||
'stations': Cesium.Color.fromCssColorString('#ff4444').withAlpha(0.4),
|
||||
'gps-ops': Cesium.Color.fromCssColorString('#ffaa00').withAlpha(0.15),
|
||||
'galileo': Cesium.Color.fromCssColorString('#44aaff').withAlpha(0.15),
|
||||
};
|
||||
// Nur Orbits fuer Stationen, GPS, Galileo zeichnen (nicht Starlink — zu viele)
|
||||
for (var i = 0; i < this._data.length; i++) {
|
||||
var sat = this._data[i];
|
||||
if (!colors[sat.group]) continue;
|
||||
var positions = [];
|
||||
var period = 86400000 / (sat.meanMotion || 14); // Umlaufzeit in ms
|
||||
for (var t = 0; t <= period; t += period / 60) {
|
||||
var pos = this._calcPosition(sat, now + t);
|
||||
if (pos) positions.push(Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, pos.alt));
|
||||
}
|
||||
if (positions.length > 10) {
|
||||
this._orbits.add({
|
||||
positions: positions,
|
||||
width: sat.group === 'stations' ? 1.5 : 0.5,
|
||||
material: Cesium.Material.fromType('Color', { color: colors[sat.group] }),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren