feat: Karte reagiert auf Karussell-Wechsel
- Map-Instanz wird einmalig erstellt, Marker dynamisch gewechselt - data-lage Attribute auf Carousel-Cards fuer Lagen-Zuordnung - Bei Lage mit Daten: Marker + Legende angezeigt - Bei Platzhalter: Karte ausgeblendet, 'Kartendaten folgen' - Zukunftssicher: Neue Lagen brauchen nur data-lage + summary.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -179,6 +179,10 @@ a { color:inherit; text-decoration:none; }
|
||||
/* ==================== MAP ==================== */
|
||||
.map-section { margin-top:48px; }
|
||||
.map-title { font-size:1.1rem; font-weight:600; color:var(--navy); margin-bottom:16px; text-align:center; }
|
||||
.map-section { transition:opacity 0.3s; }
|
||||
.map-section.map-hidden #map-container { display:none; }
|
||||
.map-section.map-hidden .map-empty { display:flex!important; }
|
||||
.map-empty { display:none; align-items:center; justify-content:center; height:300px; border:2px dashed var(--gray-200); border-radius:var(--radius-lg); color:var(--gray-400); font-size:1rem; background:var(--white); }
|
||||
#map-container { height:420px; border-radius:var(--radius-lg); overflow:hidden; box-shadow:var(--shadow); border:1px solid var(--gray-100); }
|
||||
|
||||
/* Map pulse markers (exact lagebild style) */
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
<button class="carousel-arrow carousel-next" aria-label="Nächste Lage">›</button>
|
||||
<div class="carousel-track" id="carousel">
|
||||
<!-- Iran Card -->
|
||||
<div class="carousel-card card-live active" data-index="0">
|
||||
<div class="carousel-card card-live active" data-index="0" data-lage="iran-konflikt">
|
||||
<div class="demo-badge">LIVE</div>
|
||||
<h3 class="demo-title">Iran-Konflikt</h3>
|
||||
|
||||
@@ -173,13 +173,13 @@
|
||||
<a href="/lagen/iran-konflikt/" class="btn btn-primary btn-block">Vollständiges Lagebild öffnen</a>
|
||||
</div>
|
||||
<!-- Placeholder 2 -->
|
||||
<div class="carousel-card card-placeholder" data-index="1">
|
||||
<div class="carousel-card card-placeholder" data-index="1" data-lage="">
|
||||
<div class="demo-badge badge-soon">Demnächst</div>
|
||||
<h3 class="demo-title placeholder-title">Weitere Lage</h3>
|
||||
<p class="placeholder-text">In Vorbereitung</p>
|
||||
</div>
|
||||
<!-- Placeholder 3 -->
|
||||
<div class="carousel-card card-placeholder" data-index="2">
|
||||
<div class="carousel-card card-placeholder" data-index="2" data-lage="">
|
||||
<div class="demo-badge badge-soon">Demnächst</div>
|
||||
<h3 class="demo-title placeholder-title">Weitere Lage</h3>
|
||||
<p class="placeholder-text">In Vorbereitung</p>
|
||||
@@ -193,9 +193,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Map -->
|
||||
<div class="map-section">
|
||||
<h3 class="map-title">Geografische Verortung der Meldungen</h3>
|
||||
<div class="map-section" id="map-section">
|
||||
<h3 class="map-title" id="map-title">Geografische Verortung der Meldungen</h3>
|
||||
<div id="map-container"></div>
|
||||
<div class="map-empty" id="map-empty" style="display:none">Kartendaten folgen</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -71,6 +71,12 @@
|
||||
|
||||
if (videos.length > 1) startRotation();
|
||||
|
||||
/* ==================== MAP STATE ==================== */
|
||||
var mapInstance = null;
|
||||
var markerLayer = null;
|
||||
var legendControl = null;
|
||||
var lageData = {};
|
||||
|
||||
/* ==================== 3D CAROUSEL ==================== */
|
||||
var cards = document.querySelectorAll('.carousel-card');
|
||||
var dots = document.querySelectorAll('.carousel-dot');
|
||||
@@ -88,6 +94,16 @@
|
||||
dots.forEach(function (dot, i) {
|
||||
dot.classList.toggle('active', i === idx);
|
||||
});
|
||||
// Update map based on active Lage
|
||||
var lage = cards[idx].getAttribute('data-lage');
|
||||
var mapSection = document.getElementById('map-section');
|
||||
if (lage && lageData[lage]) {
|
||||
mapSection.classList.remove('map-hidden');
|
||||
showMarkers(lageData[lage].locations, lageData[lage].category_labels);
|
||||
} else {
|
||||
if (mapSection) mapSection.classList.add('map-hidden');
|
||||
clearMarkers();
|
||||
}
|
||||
}
|
||||
|
||||
cards.forEach(function (card, i) {
|
||||
@@ -188,37 +204,43 @@ function mdToHtml(md) {
|
||||
excerptEl.innerHTML = mdToHtml(data.zusammenfassung);
|
||||
}
|
||||
|
||||
// Map
|
||||
if (data.locations && data.locations.length > 0) {
|
||||
initMap(data.locations, data.category_labels || {});
|
||||
}
|
||||
// Store data and init map
|
||||
lageData['iran-konflikt'] = {
|
||||
locations: data.locations || [],
|
||||
category_labels: data.category_labels || {}
|
||||
};
|
||||
createMap();
|
||||
showMarkers(data.locations || [], data.category_labels || {});
|
||||
})
|
||||
.catch(function () {
|
||||
});
|
||||
}
|
||||
|
||||
/* ==================== LEAFLET MAP (exact lagebild style) ==================== */
|
||||
function initMap(locations, apiLabels) {
|
||||
/* ==================== LEAFLET MAP ==================== */
|
||||
function clearMarkers() {
|
||||
if (markerLayer) markerLayer.clearLayers();
|
||||
if (legendControl && mapInstance) { mapInstance.removeControl(legendControl); legendControl = null; }
|
||||
}
|
||||
|
||||
function createMap() {
|
||||
if (mapInstance) return;
|
||||
var mapEl = document.getElementById('map-container');
|
||||
if (!mapEl || typeof L === 'undefined') return;
|
||||
|
||||
var map = L.map(mapEl, {
|
||||
center: [33.0, 48.0],
|
||||
zoom: 5,
|
||||
zoomControl: true,
|
||||
scrollWheelZoom: true,
|
||||
minZoom: 2,
|
||||
maxBounds: [[-85, -180], [85, 180]],
|
||||
maxBoundsViscosity: 1.0
|
||||
mapInstance = L.map(mapEl, {
|
||||
center: [33.0, 48.0], zoom: 5, zoomControl: true, scrollWheelZoom: true,
|
||||
minZoom: 2, maxBounds: [[-85, -180], [85, 180]], maxBoundsViscosity: 1.0
|
||||
});
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.de/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
maxZoom: 19,
|
||||
noWrap: true
|
||||
}).addTo(map);
|
||||
maxZoom: 19, noWrap: true
|
||||
}).addTo(mapInstance);
|
||||
|
||||
markerLayer = L.layerGroup().addTo(mapInstance);
|
||||
setTimeout(function () { mapInstance.invalidateSize(); }, 500);
|
||||
}
|
||||
|
||||
// Exact same pulse icon as lagebild
|
||||
function pulseIcon(color) {
|
||||
return L.divIcon({
|
||||
className: '',
|
||||
@@ -227,12 +249,15 @@ function mdToHtml(md) {
|
||||
+ '<div class="pulse-marker-ring" style="border-color:' + color + '"></div>'
|
||||
+ '<div class="pulse-marker-dot" style="background:' + color + ';box-shadow:0 0 10px ' + color + '"></div>'
|
||||
+ '</div>',
|
||||
iconSize: [20, 20],
|
||||
iconAnchor: [10, 10],
|
||||
popupAnchor: [0, -12]
|
||||
iconSize: [20, 20], iconAnchor: [10, 10], popupAnchor: [0, -12]
|
||||
});
|
||||
}
|
||||
|
||||
function showMarkers(locations, apiLabels) {
|
||||
if (!mapInstance) createMap();
|
||||
clearMarkers();
|
||||
|
||||
|
||||
var categoryColors = {
|
||||
primary: '#ef4444',
|
||||
secondary: '#f59e0b',
|
||||
@@ -266,7 +291,7 @@ function mdToHtml(md) {
|
||||
popup += '<br><span style="font-size:0.85rem;color:#8896AB;">' + (loc.article_count || 0) + ' Artikel</span>';
|
||||
|
||||
L.marker([loc.lat, loc.lon], { icon: pulseIcon(color) })
|
||||
.addTo(map)
|
||||
.addTo(markerLayer)
|
||||
.bindPopup(popup);
|
||||
bounds.push([loc.lat, loc.lon]);
|
||||
});
|
||||
@@ -285,13 +310,13 @@ function mdToHtml(md) {
|
||||
div.innerHTML = html;
|
||||
return div;
|
||||
};
|
||||
legend.addTo(map);
|
||||
legend.addTo(mapInstance);
|
||||
|
||||
if (bounds.length > 0) {
|
||||
map.fitBounds(bounds, { padding: [30, 30], maxZoom: 7 });
|
||||
mapInstance.fitBounds(bounds, { padding: [30, 30], maxZoom: 7 });
|
||||
}
|
||||
|
||||
setTimeout(function () { map.invalidateSize(); }, 500);
|
||||
setTimeout(function () { mapInstance.invalidateSize(); }, 300);
|
||||
}
|
||||
|
||||
/* ==================== INIT ==================== */
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren