Kartenfeature: Geoparsing + Leaflet-Karte im Dashboard

- Neues Geoparsing-Modul (spaCy NER + geonamescache/Nominatim)
- article_locations-Tabelle mit Migration
- Pipeline-Integration nach Artikel-Speicherung
- API-Endpunkt GET /incidents/{id}/locations
- Leaflet.js + MarkerCluster im Dashboard-Grid
- Theme-aware Kartenkacheln (CartoDB dark / OSM light)
- Gold-Akzent MarkerCluster, Popup mit Artikelliste

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-03-04 22:04:07 +01:00
Ursprung 23ac6d6fd7
Commit 4bfc626067
11 geänderte Dateien mit 746 neuen und 10 gelöschten Zeilen

Datei anzeigen

@@ -19,6 +19,7 @@ const ThemeManager = {
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem(this._key, next);
this._updateIcon(next);
UI.updateMapTheme();
},
_updateIcon(theme) {
const btn = document.getElementById('theme-toggle');
@@ -589,20 +590,21 @@ const App = {
async loadIncidentDetail(id) {
try {
const [incident, articles, factchecks, snapshots] = await Promise.all([
const [incident, articles, factchecks, snapshots, locations] = await Promise.all([
API.getIncident(id),
API.getArticles(id),
API.getFactChecks(id),
API.getSnapshots(id),
API.getLocations(id).catch(() => []),
]);
this.renderIncidentDetail(incident, articles, factchecks, snapshots);
this.renderIncidentDetail(incident, articles, factchecks, snapshots, locations);
} catch (err) {
UI.showToast('Fehler beim Laden: ' + err.message, 'error');
}
},
renderIncidentDetail(incident, articles, factchecks, snapshots) {
renderIncidentDetail(incident, articles, factchecks, snapshots, locations) {
// Header Strip
document.getElementById('incident-title').textContent = incident.title;
document.getElementById('incident-description').textContent = incident.description || '';
@@ -726,6 +728,9 @@ const App = {
});
this.rerenderTimeline();
this._resizeTimelineTile();
// Karte rendern
UI.renderMap(locations || []);
},
_collectEntries(filterType, searchTerm, range) {