From c7e3246b448a8d30c4a197f93506191960324c08 Mon Sep 17 00:00:00 2001 From: Hermann Date: Fri, 24 Jul 2026 12:52:09 +0200 Subject: [PATCH] Fix: Nutzerstandort auf der Karte anzeigen Behebt Gitea Issue #2 "Standort auf Karte anzeigen": der per Browser-Geolocation ermittelte oder manuell eingegebene Standort (state.userLocation) wurde bisher nur zum Zentrieren/Entfernungsfilter verwendet, aber nie selbst auf der Karte dargestellt. Neuer, nicht interaktiver "Hier bist du"-Marker (blauer Punkt mit Puls-Ring, js/map/markers.js: createUserLocationMarker()) wird direkt auf die Karte gelegt (ausserhalb der clusterGroup, damit er nie mit Ausflugsziel-Markern geclustert wird) und reagiert reaktiv auf Aenderungen von state.userLocation in js/map/mapController.js. --- css/components.css | 45 +++++++++++++++++++++++++++++++++++++++++ js/map/mapController.js | 42 ++++++++++++++++++++++++++++++++++++-- js/map/markers.js | 29 ++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/css/components.css b/css/components.css index b6f713e..ede237e 100644 --- a/css/components.css +++ b/css/components.css @@ -148,6 +148,51 @@ border: 2px solid #fff; } +/* "Hier bist du"-Marker für den übermittelten/manuell eingegebenen + Nutzerstandort (Gitea Issue #2 "Standort auf Karte anzeigen"). */ + +.eventmap-user-marker { + background: transparent; + border: none; + position: relative; +} + +.eventmap-user-marker__dot { + position: absolute; + top: 50%; + left: 50%; + width: 14px; + height: 14px; + margin: -7px 0 0 -7px; + background: #1a73e8; + border: 2px solid #fff; + border-radius: 50%; + box-shadow: var(--shadow-card); +} + +.eventmap-user-marker__pulse { + position: absolute; + top: 50%; + left: 50%; + width: 22px; + height: 22px; + margin: -11px 0 0 -11px; + background: rgba(26, 115, 232, 0.35); + border-radius: 50%; + animation: eventmap-user-marker-pulse 2s ease-out infinite; +} + +@keyframes eventmap-user-marker-pulse { + 0% { + transform: scale(0.6); + opacity: 0.8; + } + 100% { + transform: scale(1.8); + opacity: 0; + } +} + /* Karten-Popup */ .eventmap-popup { diff --git a/js/map/mapController.js b/js/map/mapController.js index 049ecff..82a82ab 100644 --- a/js/map/mapController.js +++ b/js/map/mapController.js @@ -4,16 +4,18 @@ (function () { const { DEFAULT_MAP_CENTER } = EventMap.config; - const { subscribe, setState, getFilteredPlaces } = EventMap.store; + const { subscribe, getState, setState, getFilteredPlaces } = EventMap.store; const { getPlaceLatLng } = EventMap.models; - const { createMarkerForPlace } = EventMap.map; + const { createMarkerForPlace, createUserLocationMarker } = EventMap.map; let map = null; let clusterGroup = null; + let userLocationMarker = null; const markersByPlaceId = new Map(); let lastFilteredKey = null; let lastSelectedPlaceId = null; + let lastUserLocationKey = null; /** Günstiger Vergleichsschlüssel für eine gefilterte Trefferliste (Reihenfolge + IDs). */ function computeFilteredKey(places) { @@ -72,6 +74,30 @@ map.setView([lat, lng], zoom); } + /** + * Zeigt den übermittelten/manuell eingegebenen Nutzerstandort als eigenen + * Marker auf der Karte an (Gitea Issue #2). Läuft außerhalb der + * clusterGroup, damit er nie mit Ausflugsziel-Markern geclustert wird und + * immer sichtbar bleibt. + */ + function renderUserLocation(userLocation) { + if (!userLocation) { + if (userLocationMarker) { + map.removeLayer(userLocationMarker); + userLocationMarker = null; + } + return; + } + + const { lat, lng } = userLocation; + if (userLocationMarker) { + userLocationMarker.setLatLng([lat, lng]); + } else { + userLocationMarker = createUserLocationMarker(lat, lng); + userLocationMarker.addTo(map); + } + } + function getMapInstance() { return map; } @@ -111,6 +137,10 @@ fitToPlaces(initialFiltered); lastFilteredKey = computeFilteredKey(initialFiltered); + const initialUserLocation = getState().userLocation; + renderUserLocation(initialUserLocation); + lastUserLocationKey = initialUserLocation ? `${initialUserLocation.lat},${initialUserLocation.lng}` : null; + subscribe((state) => { // Filter/Standort können sich ändern, ohne dass sich state.places // ändert - daher bei jeder Store-Änderung neu filtern und nur bei @@ -128,6 +158,14 @@ centerOnPlace(state.selectedPlaceId); } } + + const userLocationKey = state.userLocation + ? `${state.userLocation.lat},${state.userLocation.lng}` + : null; + if (userLocationKey !== lastUserLocationKey) { + lastUserLocationKey = userLocationKey; + renderUserLocation(state.userLocation); + } }); return map; diff --git a/js/map/markers.js b/js/map/markers.js index 13a8397..1ebf2d5 100644 --- a/js/map/markers.js +++ b/js/map/markers.js @@ -60,5 +60,34 @@ return marker; } + let userLocationIcon = null; + + function getUserLocationIcon() { + if (!userLocationIcon) { + userLocationIcon = L.divIcon({ + className: 'eventmap-user-marker', + html: '', + iconSize: [22, 22], + iconAnchor: [11, 11], + }); + } + return userLocationIcon; + } + + /** + * Erzeugt einen (nicht klickbaren) Marker für den übermittelten/manuell + * eingegebenen Nutzerstandort (Gitea Issue #2 "Standort auf Karte + * anzeigen"). Bewusst kein Popup/Interaktion, da es kein Ausflugsziel ist. + */ + function createUserLocationMarker(lat, lng) { + return L.marker([lat, lng], { + icon: getUserLocationIcon(), + interactive: false, + keyboard: false, + zIndexOffset: 1000, + }); + } + EventMap.map.createMarkerForPlace = createMarkerForPlace; + EventMap.map.createUserLocationMarker = createUserLocationMarker; })();