diff --git a/static/css/globe.css b/static/css/globe.css
index a79d767..98d49b4 100644
--- a/static/css/globe.css
+++ b/static/css/globe.css
@@ -33,7 +33,7 @@ html, body { height: 100%; overflow: hidden; background: var(--bg-primary); colo
}
.header-brand { display: flex; align-items: center; gap: 8px; }
.header-title { font-size: 13px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase; color: var(--accent); }
-.header-stats { flex: 1; text-align: center; font-size: 11px; color: var(--text-dim); letter-spacing: 1px; }
+.header-stats { text-align: center; font-size: 11px; color: var(--text-dim); letter-spacing: 1px; }
.header-actions { display: flex; gap: 8px; }
.header-btn {
background: none; border: 1px solid var(--border); border-radius: 4px;
@@ -415,7 +415,7 @@ html, body { height: 100%; overflow: hidden; background: var(--bg-primary); colo
/* === Lage-Selector === */
-.header-lage { display: flex; align-items: center; }
+.header-lage { display: flex; align-items: center; flex: 1; justify-content: center; }
.lage-select {
background: rgba(255,255,255,0.05);
border: 1px solid var(--border);
diff --git a/static/js/layers/disasters.js b/static/js/layers/disasters.js
index 84ae17f..1a7651c 100644
--- a/static/js/layers/disasters.js
+++ b/static/js/layers/disasters.js
@@ -35,75 +35,36 @@ const DisastersLayer = {
.catch(function() {});
},
- _findMonitorInfo(lat, lon) {
- if (!this._monitorData) return null;
- var selectedId = (typeof Globe !== 'undefined') ? Globe._currentLageId : null;
- if (!selectedId) return null;
-
- // Naechsten Monitor-Standort zur Klickposition finden
+ _buildMonitorHtml(lat, lon) {
+ if (!this._monitorData) return '';
var features = this._monitorData.features || [];
var best = null, bestDist = 999;
for (var i = 0; i < features.length; i++) {
- var f = features[i];
- var c = f.geometry.coordinates;
+ var c = features[i].geometry.coordinates;
var d = Math.abs(c[1] - lat) + Math.abs(c[0] - lon);
- if (d < bestDist) { bestDist = d; best = f; }
+ if (d < bestDist) { bestDist = d; best = features[i]; }
}
+ if (!best || bestDist > 3) return '';
- // Lage-Infos
- var incidents = this._monitorData.incidents || [];
- var incident = null;
- for (var j = 0; j < incidents.length; j++) {
- if (incidents[j].id === selectedId) { incident = incidents[j]; break; }
- }
-
- if (!best || bestDist > 5) return { incident: incident, feature: null };
- return { incident: incident, feature: best };
- },
-
- _buildMonitorHtml(lat, lon) {
- var info = this._findMonitorInfo(lat, lon);
- if (!info || !info.incident) return '';
+ var p = best.properties;
+ var articles = p.articles || [];
+ if (!articles.length) return '';
var html = '
';
- html += '
MONITOR: ' +
- (info.incident.title || 'LAGE').toUpperCase() + '
';
-
- // Ortsspezifische Headlines wenn passender Standort gefunden
- if (info.feature) {
- var p = info.feature.properties;
- var headlines = p.headlines || [];
- if (headlines.length && headlines[0]) {
- html += '
' + (p.name || '') +
- ' (' + (p.article_count || 0) + ' Artikel)
';
- html += '
';
- for (var h = 0; h < Math.min(headlines.length, 3); h++) {
- if (headlines[h] && headlines[h].trim()) {
- html += '
• ' +
- headlines[h].trim().substring(0, 120) + '
';
- }
- }
- html += '
';
- }
- } else {
- // Kein passender Standort in der Naehe — globale Summary
- if (info.incident.summary) {
- var summary = info.incident.summary
- .replace(/##?\s*/g, '').replace(/\*\*/g, '').replace(/\[(\d+)\]/g, '')
- .substring(0, 400);
- html += '
' + summary + '
';
- }
- }
-
- if (info.incident.updated_at) {
- html += '
Stand: ' +
- new Date(info.incident.updated_at).toLocaleString('de-DE') + '
';
- }
+ html += '
MONITOR: ' +
+ (p.name || '').toUpperCase() + ' (' + (p.article_count || 0) + ' Artikel)
';
+ articles.forEach(function(a) {
+ html += '
';
+ html += '
' + (a.headline || '') + '
';
+ if (a.summary) html += '
' + a.summary.substring(0, 200) + '
';
+ if (a.source) html += '
' + a.source + '
';
+ html += '
';
+ });
html += '
';
return html;
},
- _fetch() {
+ _fetch() {
var self = this;
var loadEl = document.getElementById('loading-disasters');
var statusEl = document.getElementById('status-disasters');
diff --git a/static/js/layers/monitor.js b/static/js/layers/monitor.js
index 1a75c7f..de43317 100644
--- a/static/js/layers/monitor.js
+++ b/static/js/layers/monitor.js
@@ -1,6 +1,5 @@
/**
- * Monitor-Layer: OSINT-Daten aus dem AegisSight Monitor.
- * Zeigt geoparsete Standorte mit Zusammenfassungen auf dem Globus.
+ * Monitor-Layer: OSINT-Daten mit ortsspezifischen Artikeln.
*/
const MonitorLayer = {
_viewer: null,
@@ -17,15 +16,9 @@ const MonitorLayer = {
this._viewer = viewer;
this._points = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection());
this._labels = viewer.scene.primitives.add(new Cesium.LabelCollection());
- this._fetch();
var self = this;
- this._interval = setInterval(function() { self._fetch(); }, 120000);
-
- // Klick-Handler
this._handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
- this._handler.setInputAction(function(click) {
- self._onClick(click.position);
- }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
+ this._handler.setInputAction(function(click) { self._onClick(click.position); }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
},
stop() {
@@ -36,90 +29,6 @@ const MonitorLayer = {
this._count = 0; this._data = []; this._incidents = [];
},
- _onClick(position) {
- if (!this._viewer || !this._data.length) return;
- var cartesian = this._viewer.scene.pickPosition(position);
- if (!cartesian) {
- var ray = this._viewer.scene.camera.getPickRay(position);
- cartesian = this._viewer.scene.globe.pick(ray, this._viewer.scene);
- }
- if (!cartesian) return;
- var carto = Cesium.Cartographic.fromCartesian(cartesian);
- var clickLat = Cesium.Math.toDegrees(carto.latitude);
- var clickLon = Cesium.Math.toDegrees(carto.longitude);
-
- // Naechsten Monitor-Punkt finden
- var best = null, bestDist = 999;
- for (var i = 0; i < this._data.length; i++) {
- var f = this._data[i];
- var c = f.geometry.coordinates;
- var d = Math.abs(c[1] - clickLat) + Math.abs(c[0] - clickLon);
- if (d < bestDist) { bestDist = d; best = f; }
- }
- if (!best || bestDist > 2) return;
-
- var p = best.properties;
- var incident = null;
- for (var j = 0; j < this._incidents.length; j++) {
- if (this._incidents[j].id === p.incident_id) { incident = this._incidents[j]; break; }
- }
-
- // Info-Panel bauen
- var catColors = { primary: '#ff2222', secondary: '#ff8800', tertiary: '#4499ff', mentioned: '#888888' };
- var catLabels = { primary: 'Hauptgeschehen', secondary: 'Reaktionen', tertiary: 'Beteiligte', mentioned: 'Erwaehnt' };
- var catColor = catColors[p.category] || '#888';
- var catLabel = catLabels[p.category] || p.category;
-
- var html = '';
-
- // Ort + Kategorie
- html += '
';
- html += '' + (p.name || '?') + '';
- if (p.country) html += ' (' + p.country + ')';
- html += '
' + catLabel.toUpperCase() + '';
- html += ' · ' + (p.article_count || 0) + ' Artikel';
- html += '
';
-
- // Headlines
- var headlines = p.headlines || [];
- if (headlines.length && headlines[0]) {
- html += '
';
- for (var h = 0; h < Math.min(headlines.length, 3); h++) {
- if (headlines[h] && headlines[h].trim()) {
- html += '
• ' + headlines[h].trim().substring(0, 120) + '
';
- }
- }
- html += '
';
- }
-
- // Lage-Zusammenfassung
- if (incident && incident.summary) {
- html += '
';
- html += '
' + (incident.title || 'LAGE').toUpperCase() + '
';
- // Summary kuerzen und Markdown-Formatierung entfernen
- var summary = incident.summary
- .replace(/##?\s*/g, '')
- .replace(/\*\*/g, '')
- .replace(/\[(\d+)\]/g, '')
- .substring(0, 500);
- html += '
' + summary;
- if (incident.summary.length > 500) html += '...';
- html += '
';
- if (incident.updated_at) {
- html += '
Stand: ' + new Date(incident.updated_at).toLocaleString('de-DE') + '
';
- }
- html += '
';
- }
-
- html += '
';
-
- this._viewer.trackedEntity = undefined;
- this._viewer.selectedEntity = new Cesium.Entity({
- name: (p.name || '?') + ' — ' + (incident ? incident.title : 'Monitor'),
- description: html,
- });
- },
-
_fetchForLage(lageId) {
var self = this;
var loadEl = document.getElementById('loading-monitor');
@@ -141,68 +50,96 @@ const MonitorLayer = {
},
_fetch() {
- var self = this;
- var loadEl = document.getElementById('loading-monitor');
- var statusEl = document.getElementById('status-monitor');
- if (loadEl) loadEl.classList.add('active');
- if (statusEl) { statusEl.textContent = 'Lade Monitor-Daten...'; statusEl.classList.add('active'); }
var lageId = (typeof Globe !== 'undefined' && Globe._currentLageId) ? Globe._currentLageId : '';
- fetch('/api/monitor-feed' + (lageId ? '?incident_id=' + lageId : ''))
- .then(function(r) { return r.json(); })
- .then(function(data) {
- self._data = data.features || [];
- self._incidents = data.incidents || [];
- self._count = self._data.length;
- self._render();
- if (statusEl) {
- var incCount = self._incidents.length;
- statusEl.textContent = self._count + ' Orte aus ' + incCount + ' Lagen';
- }
- })
- .catch(function(e) { console.warn('Monitor error:', e); if (statusEl) statusEl.textContent = 'Fehler'; })
- .finally(function() { if (loadEl) loadEl.classList.remove('active'); setTimeout(function() { if (statusEl) statusEl.classList.remove('active'); }, 8000); });
+ if (lageId) this._fetchForLage(lageId);
+ },
+
+ _onClick(position) {
+ if (!this._viewer || !this._data.length) return;
+ var cartesian = this._viewer.scene.pickPosition(position);
+ if (!cartesian) {
+ var ray = this._viewer.scene.camera.getPickRay(position);
+ cartesian = this._viewer.scene.globe.pick(ray, this._viewer.scene);
+ }
+ if (!cartesian) return;
+ var carto = Cesium.Cartographic.fromCartesian(cartesian);
+ var clickLat = Cesium.Math.toDegrees(carto.latitude);
+ var clickLon = Cesium.Math.toDegrees(carto.longitude);
+
+ var best = null, bestDist = 999;
+ for (var i = 0; i < this._data.length; i++) {
+ var f = this._data[i];
+ var c = f.geometry.coordinates;
+ var d = Math.abs(c[1] - clickLat) + Math.abs(c[0] - clickLon);
+ if (d < bestDist) { bestDist = d; best = f; }
+ }
+ if (!best || bestDist > 2) return;
+
+ var p = best.properties;
+ var catColors = { primary: '#ff2222', secondary: '#ff8800', tertiary: '#4499ff', mentioned: '#888888' };
+ var color = catColors[p.category] || '#888';
+
+ var html = '';
+ html += '
' + (p.name || '?') + '';
+ if (p.country) html += '
(' + p.country + ')';
+ html += '
' + (p.article_count || 0) + ' Artikel';
+
+ // Ortsspezifische Artikel
+ var articles = p.articles || [];
+ if (articles.length) {
+ html += '
';
+ articles.forEach(function(a) {
+ html += '
';
+ html += '
' + (a.headline || '?') + '
';
+ if (a.summary) {
+ html += '
' + a.summary + '
';
+ }
+ html += '
';
+ if (a.source) html += a.source;
+ if (a.date) html += ' | ' + new Date(a.date).toLocaleDateString('de-DE');
+ html += '
';
+ });
+ html += '
';
+ }
+ html += '
';
+
+ this._viewer.trackedEntity = undefined;
+ this._viewer.selectedEntity = new Cesium.Entity({
+ name: (p.name || '?') + ' — ' + (p.incident_title || ''),
+ description: html,
+ });
},
_render() {
if (!this._points) return;
this._points.removeAll();
this._labels.removeAll();
-
var colors = {
'primary': Cesium.Color.fromCssColorString('#ff2222'),
'secondary': Cesium.Color.fromCssColorString('#ff8800'),
'tertiary': Cesium.Color.fromCssColorString('#4499ff'),
'mentioned': Cesium.Color.fromCssColorString('#888888'),
};
-
for (var i = 0; i < this._data.length; i++) {
var f = this._data[i];
var coords = f.geometry.coordinates;
var p = f.properties;
- var cat = p.category || 'mentioned';
- var color = colors[cat] || colors.mentioned;
+ var color = colors[p.category] || colors.mentioned;
var count = p.article_count || 1;
var size = Math.min(Math.max(Math.sqrt(count) * 2.5, 4), 14);
-
this._points.add({
position: Cesium.Cartesian3.fromDegrees(coords[0], coords[1], 1000),
- pixelSize: size,
- color: color,
- outlineColor: Cesium.Color.WHITE.withAlpha(0.6),
- outlineWidth: 1,
+ pixelSize: size, color: color,
+ outlineColor: Cesium.Color.WHITE.withAlpha(0.6), outlineWidth: 1,
});
-
if (count >= 2) {
this._labels.add({
position: Cesium.Cartesian3.fromDegrees(coords[0], coords[1], 1000),
text: (p.name || '?') + ' (' + count + ')',
- font: '11px sans-serif',
- fillColor: color,
- outlineColor: Cesium.Color.BLACK,
- outlineWidth: 3,
+ font: '11px sans-serif', fillColor: color,
+ outlineColor: Cesium.Color.BLACK, outlineWidth: 3,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
- pixelOffset: new Cesium.Cartesian2(0, -size - 6),
- scale: 0.8,
+ pixelOffset: new Cesium.Cartesian2(0, -size - 6), scale: 0.8,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 3000000),
});