fix(map): Pfeilspitze zeigt exakt entlang der Linie (v1.8.10)

Die Pfeilspitze wurde mit dem geografischen Winkel (atan2 auf lng/lat) gedreht, der wegen
der Mercator-Verzerrung nicht mit der auf dem Bildschirm gezeichneten Linie übereinstimmt
(v.a. bei nördlichen Breiten). Jetzt aus den projizierten Pixeln berechnet (viewport-
ausgerichtet) → Spitze zeigt korrekt in Linienrichtung.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-23 22:30:22 +02:00
parent 405639db7d
commit e5811f03fb
2 changed files with 15 additions and 11 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "lageplan",
"version": "1.8.9",
"version": "1.8.10",
"description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation",
"private": true,
"scripts": {

View File

@@ -1489,27 +1489,31 @@ export function MapView({
const lineCoords = f.geometry.coordinates as number[][]
if (lineCoords.length < 2) return
// Geographic bearing from p1 to p2 (works for short distances)
const p1 = lineCoords[lineCoords.length - 2]
const p2 = lineCoords[lineCoords.length - 1]
const dLng = p2[0] - p1[0]
const dLat = p2[1] - p1[1]
// atan2(dLng, dLat) gives angle from north (up), clockwise — matches CSS triangle ▲ default
const geoBearing = Math.atan2(dLng, dLat) * (180 / Math.PI)
// Bildschirm-Winkel aus projizierten Pixeln berechnen — der geografische Winkel
// (atan2 auf lng/lat) stimmt wegen der Mercator-Verzerrung NICHT mit der auf dem
// Bildschirm gezeichneten Linie überein (v.a. bei nördlichen Breiten). So zeigt die
// Spitze exakt entlang der Linie.
const a = map.current.project(p1 as [number, number])
const b = map.current.project(p2 as [number, number])
// CSS-Dreieck zeigt nach oben (y). Winkel im Uhrzeigersinn von „oben".
const screenAngle = Math.atan2(b.x - a.x, -(b.y - a.y)) * (180 / Math.PI)
const color = (f.properties.color as string) || '#000000'
const arrowEl = document.createElement('div')
arrowEl.style.cssText = `
width: 0; height: 0;
border-left: 12px solid transparent;
border-right: 12px solid transparent;
border-bottom: 24px solid ${color};
transform: rotate(${geoBearing}deg);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 20px solid ${color};
transform: rotate(${screenAngle}deg);
transform-origin: center center;
pointer-events: none;
`
const marker = new maplibregl.Marker({ element: arrowEl, anchor: 'center', rotationAlignment: 'map' })
// Viewport-ausgerichtet (Bildschirm), passend zum bildschirm-basierten Winkel.
const marker = new maplibregl.Marker({ element: arrowEl, anchor: 'center' })
.setLngLat(p2 as [number, number])
.addTo(map.current)
markersRef.current.push(marker)