diff --git a/lagen/iran-konflikt/index.html b/lagen/iran-konflikt/index.html index ad3f525..bf0bcda 100644 --- a/lagen/iran-konflikt/index.html +++ b/lagen/iran-konflikt/index.html @@ -11,6 +11,8 @@ + + @@ -186,6 +188,7 @@ + diff --git a/lagen/iran-konflikt/lagebild.css b/lagen/iran-konflikt/lagebild.css index d481a16..11b75bd 100644 --- a/lagen/iran-konflikt/lagebild.css +++ b/lagen/iran-konflikt/lagebild.css @@ -1553,3 +1553,17 @@ a.source-detail-article-title:hover { margin-top: 1rem; } } + +/* Marker-Cluster Dark Theme */ +.marker-cluster-small, +.marker-cluster-medium, +.marker-cluster-large { + background: rgba(21, 29, 46, 0.8); +} +.marker-cluster-small div, +.marker-cluster-medium div, +.marker-cluster-large div { + background: rgba(200, 168, 81, 0.9); + color: #0A1832; + font-weight: 600; +} diff --git a/lagen/iran-konflikt/lagebild.js b/lagen/iran-konflikt/lagebild.js index 9cec32a..70e71bb 100644 --- a/lagen/iran-konflikt/lagebild.js +++ b/lagen/iran-konflikt/lagebild.js @@ -655,7 +655,7 @@ var Lagebild = { renderArticlesTab: function() {}, - /* ===== TAB: KARTE (Pulse Markers) ===== */ + /* ===== TAB: KARTE (Clustered Pulse Markers) ===== */ renderMap: function() { if (this.map) { this.map.remove(); this.map = null; } this.map = L.map('map-container', { @@ -664,7 +664,6 @@ var Lagebild = { maxBoundsViscosity: 1.0 }).setView([33.0, 48.0], 5); - // Deutsche OSM-Kacheln L.tileLayer('https://tile.openstreetmap.de/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19, @@ -685,7 +684,6 @@ var Lagebild = { }); } - // Kategorie-Farben var categoryColors = { primary: '#ef4444', secondary: '#f59e0b', @@ -698,7 +696,6 @@ var Lagebild = { tertiary: 'Beteiligte', mentioned: 'Erwaehnt' }; - // Dynamische Labels aus API verwenden (falls vorhanden) var categoryLabels = {}; if (this.data && this.data.category_labels) { var apiLabels = this.data.category_labels; @@ -710,7 +707,6 @@ var Lagebild = { categoryLabels = defaultCategoryLabels; } - // Locations aus API-Daten laden var locs = (this.data && this.data.locations) ? this.data.locations : []; if (locs.length === 0) { @@ -720,22 +716,60 @@ var Lagebild = { document.getElementById('map-container').appendChild(emptyDiv); } + var clusterGroup = L.markerClusterGroup({ + maxClusterRadius: 50, + spiderfyOnMaxZoom: true, + showCoverageOnHover: false, + zoomToBoundsOnClick: true, + disableClusteringAtZoom: 10 + }); + var usedCategories = {}; + var bounds = []; for (var i = 0; i < locs.length; i++) { var l = locs[i]; if (!l.lat || !l.lon) continue; var cat = l.category || 'mentioned'; var color = categoryColors[cat] || '#7b7b7b'; usedCategories[cat] = true; + + // Popup mit Artikel-Links var popupText = '' + (l.name || '') + ''; if (l.country_code) popupText += ' (' + l.country_code + ')'; popupText += '
' + (l.article_count || 0) + ' Artikel'; - L.marker([l.lat, l.lon], { icon: pulseIcon(color) }) - .addTo(this.map) - .bindPopup(popupText); + if (l.top_articles && l.top_articles.length > 0) { + popupText += '
'; + for (var j = 0; j < l.top_articles.length; j++) { + var a = l.top_articles[j]; + var hl = (a.headline || '').replace(/\*\*/g, ''); + if (hl.length > 60) hl = hl.substring(0, 60) + '\u2026'; + if (a.url) { + popupText += '' + hl + ''; + } else { + popupText += '' + hl + ''; + } + popupText += '' + (a.source || '') + ''; + } + popupText += '
'; + } + + var marker; + if (cat === 'primary' || cat === 'secondary') { + marker = L.marker([l.lat, l.lon], { icon: pulseIcon(color) }); + } else { + marker = L.circleMarker([l.lat, l.lon], { + radius: 5, fillColor: color, fillOpacity: 0.7, + color: color, weight: 1, opacity: 0.9 + }); + } + marker.bindPopup(popupText, { maxWidth: 300 }); + clusterGroup.addLayer(marker); + bounds.push([l.lat, l.lon]); } - // Dark legend (dynamisch) + this.map.addLayer(clusterGroup); + + // Dark legend var legend = L.control({ position: 'bottomright' }); legend.onAdd = function() { var div = L.DomUtil.create('div', 'map-legend'); @@ -751,6 +785,10 @@ var Lagebild = { }; legend.addTo(this.map); + if (bounds.length > 0) { + this.map.fitBounds(bounds, { padding: [30, 30], maxZoom: 7 }); + } + // Dark popup styling if (!document.getElementById('leaflet-dark-style')) { var style = document.createElement('style'); diff --git a/vorschau/css/style.css b/vorschau/css/style.css index de51c69..5a63c4d 100644 --- a/vorschau/css/style.css +++ b/vorschau/css/style.css @@ -372,3 +372,17 @@ a { color:inherit; text-decoration:none; } .container { padding:0 16px; } } + +/* Marker-Cluster Dark Theme */ +.marker-cluster-small, +.marker-cluster-medium, +.marker-cluster-large { + background: rgba(21, 29, 46, 0.8); +} +.marker-cluster-small div, +.marker-cluster-medium div, +.marker-cluster-large div { + background: rgba(200, 168, 81, 0.9); + color: #0A1832; + font-weight: 600; +} diff --git a/vorschau/index.html b/vorschau/index.html index 623b506..c043a69 100644 --- a/vorschau/index.html +++ b/vorschau/index.html @@ -8,6 +8,8 @@ + + @@ -95,7 +97,7 @@

Der AegisSight Monitor überwacht hunderte Quellen rund um die Uhr und erstellt strukturierte Lagebilder, ohne manuellen Aufwand. Neue Entwicklungen werden in Minuten erfasst, analysiert und eingeordnet. Sie entscheiden, was überwacht wird, der Monitor liefert das Lagebild.

Live-Beispiel: Der Iran-Konflikt wird mit über 14.900 Artikeln aus 375 Quellen kontinuierlich überwacht.

- Live-Beispiel ansehen + Live-Beispiel ansehen
@@ -125,7 +127,7 @@

Definieren Sie ein Thema, der Monitor recherchiert, strukturiert und belegt vollautomatisch. Quellenbasierte Analyse mit Faktenprüfung, kein Copy-Paste aus Suchmaschinen. Das Ergebnis: ein vollständiges Dossier mit Quellenbelegen und Faktenchecks.

Beispiel: Ein Dossier zur rechtlichen Lage von Deepfakes in Deutschland, 121 Artikel aus 90 Quellen, automatisch erstellt.

- Recherche-Beispiel ansehen + Recherche-Beispiel ansehen
@@ -141,7 +143,7 @@

Arabisch, Persisch, Hebräisch, Russisch, Chinesisch: Der Monitor verarbeitet Quellen, die den meisten Analystenteams sprachlich verschlossen bleiben. Automatische Übersetzung, Kontextanalyse und Einordnung. Globale Abdeckung ohne globale Personalkosten.

Im Iran-Konflikt werden Primärquellen in Farsi, Arabisch und Hebräisch direkt ausgewertet.

- Live-Beispiel ansehen + Live-Beispiel ansehen
@@ -157,7 +159,7 @@

Geopolitische Konflikte, Unternehmensrisiken, regionale Krisen oder Einzelpersonen: Der AegisSight Monitor skaliert mit Ihrem Bedarf. Definieren Sie Ihr Thema, wählen Sie Ihre Quellen, der Rest läuft automatisch. Jede Lage erhält ihr eigenes Lagebild mit Quellenbelegen, Karte und Faktencheck.

Beispiel: Cyberangriffe auf deutsche Infrastruktur, 93 Artikel aus 41 Quellen, automatisch überwacht.

- Live-Beispiel ansehen + Live-Beispiel ansehen
@@ -478,6 +480,7 @@ + diff --git a/vorschau/js/app.js b/vorschau/js/app.js index f2cba37..473eb85 100644 --- a/vorschau/js/app.js +++ b/vorschau/js/app.js @@ -292,7 +292,7 @@ function mdToHtml(md) { /* ==================== LEAFLET MAP ==================== */ function clearMarkers() { - if (markerLayer) markerLayer.clearLayers(); + if (markerLayer) { mapInstance.removeLayer(markerLayer); markerLayer = null; } if (legendControl && mapInstance) { mapInstance.removeControl(legendControl); legendControl = null; } } @@ -311,7 +311,6 @@ function mdToHtml(md) { maxZoom: 19, noWrap: true }).addTo(mapInstance); - markerLayer = L.layerGroup().addTo(mapInstance); setTimeout(function () { mapInstance.invalidateSize(); }, 500); } @@ -327,11 +326,31 @@ function mdToHtml(md) { }); } + function buildPopup(loc) { + var html = '' + (loc.name || '') + ''; + if (loc.country_code) html += ' (' + loc.country_code + ')'; + html += '
' + (loc.article_count || 0) + ' Artikel'; + if (loc.top_articles && loc.top_articles.length > 0) { + html += '
'; + loc.top_articles.forEach(function (a) { + var hl = (a.headline || '').replace(/\*\*/g, ''); + if (hl.length > 60) hl = hl.substring(0, 60) + '\u2026'; + if (a.url) { + html += '' + hl + ''; + } else { + html += '' + hl + ''; + } + html += '' + (a.source || '') + ''; + }); + html += '
'; + } + return html; + } + function showMarkers(locations, apiLabels) { if (!mapInstance) createMap(); clearMarkers(); - var categoryColors = { primary: '#ef4444', secondary: '#f59e0b', @@ -343,7 +362,7 @@ function mdToHtml(md) { primary: 'Hauptgeschehen', secondary: 'Reaktionen', tertiary: 'Beteiligte', - mentioned: 'Erwähnt' + mentioned: 'Erw\u00e4hnt' }; var categoryLabels = {}; @@ -351,6 +370,14 @@ function mdToHtml(md) { categoryLabels[k] = (apiLabels && apiLabels[k]) || defaultLabels[k]; }); + var clusterGroup = L.markerClusterGroup({ + maxClusterRadius: 50, + spiderfyOnMaxZoom: true, + showCoverageOnHover: false, + zoomToBoundsOnClick: true, + disableClusteringAtZoom: 10 + }); + var usedCategories = {}; var bounds = []; @@ -360,17 +387,23 @@ function mdToHtml(md) { var color = categoryColors[cat] || '#7b7b7b'; usedCategories[cat] = true; - var popup = '' + (loc.name || '') + ''; - if (loc.country_code) popup += ' (' + loc.country_code + ')'; - popup += '
' + (loc.article_count || 0) + ' Artikel'; - - L.marker([loc.lat, loc.lon], { icon: pulseIcon(color) }) - .addTo(markerLayer) - .bindPopup(popup); + var marker; + if (cat === 'primary' || cat === 'secondary') { + marker = L.marker([loc.lat, loc.lon], { icon: pulseIcon(color) }); + } else { + marker = L.circleMarker([loc.lat, loc.lon], { + radius: 5, fillColor: color, fillOpacity: 0.7, + color: color, weight: 1, opacity: 0.9 + }); + } + marker.bindPopup(buildPopup(loc), { maxWidth: 300 }); + clusterGroup.addLayer(marker); bounds.push([loc.lat, loc.lon]); }); - // Dark legend (exact lagebild style) + markerLayer = clusterGroup; + mapInstance.addLayer(markerLayer); + var legend = L.control({ position: 'bottomright' }); legend.onAdd = function () { var div = L.DomUtil.create('div');