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", "name": "lageplan",
"version": "1.8.9", "version": "1.8.10",
"description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation",
"private": true, "private": true,
"scripts": { "scripts": {

View File

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