From 997e200d6855c3f63349682d3719fa647c0c1481 Mon Sep 17 00:00:00 2001 From: Pepe Ziberi Date: Wed, 22 Jul 2026 21:01:24 +0200 Subject: [PATCH] feat(wind): Live-Windrichtung vom Standort (Open-Meteo) (v1.6.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neuer „Live vom Standort"-Knopf in der Windrose holt die aktuelle Windrichtung + Geschwindigkeit von Open-Meteo (kostenlos, ohne Key) - Beim Öffnen eines Einsatzes ohne gesetzten Wind wird die Windrichtung automatisch vom Einsatzort vorgeschlagen (einmal pro Projekt, online) - Toast zeigt Richtung, Grad, km/h und Böen - Neue lib/wind.ts (fetchLiveWind) Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 2 +- src/app/app/page.tsx | 36 ++++++++++++++++++++++++++++++ src/components/map/map-compass.tsx | 25 +++++++++++++++++++-- src/components/map/map-view.tsx | 4 ++++ src/lib/wind.ts | 32 ++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 src/lib/wind.ts diff --git a/package.json b/package.json index 928061f..d8a987d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lageplan", - "version": "1.6.0", + "version": "1.6.1", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "private": true, "scripts": { diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx index 11e5927..b2cf0ce 100644 --- a/src/app/app/page.tsx +++ b/src/app/app/page.tsx @@ -27,6 +27,8 @@ import { useMapExport } from '@/hooks/use-map-export' import { useAutoSave } from '@/hooks/use-auto-save' import { useOfflineSync } from '@/hooks/use-offline-sync' import { preloadTilesForArea, shouldPreloadForProject, markPreloadedForProject } from '@/lib/offline-tiles' +import { fetchLiveWind } from '@/lib/wind' +import { degToCompass } from '@/components/map/map-compass' import { useRealtimeSync } from '@/hooks/use-realtime-sync' import type { Project, ProjectMode, DrawFeature, Feature, JournalEntry, DrawMode } from '@/types' import { useToolStore } from '@/stores/tool-store' @@ -484,6 +486,39 @@ export default function AppPage() { } catch { /* Komfort — Fehler still ignorieren */ } }, [currentProject]) + // Live-Wind von Open-Meteo für den aktuellen Kartenausschnitt (Einsatzort) holen + const handleFetchLiveWind = useCallback(async () => { + if (!currentProject) return + const c = mapRef.current?.getCenter?.() || currentProject.mapCenter + if (!c) return + const wind = await fetchLiveWind(c.lat, c.lng) + if (!wind) { + toast({ title: 'Wind nicht verfügbar', description: 'Keine Live-Daten für diesen Standort.', variant: 'destructive' }) + return + } + await handleWindChange(wind.direction) + toast({ + title: 'Live-Wind übernommen', + description: `aus ${degToCompass(wind.direction)} (${wind.direction}°) · ${Math.round(wind.speed)} km/h${wind.gusts ? `, Böen ${Math.round(wind.gusts)} km/h` : ''}`, + }) + }, [currentProject, handleWindChange, toast]) + + // Beim Öffnen eines Einsatzes ohne gesetzten Wind automatisch die aktuelle + // Windrichtung vorschlagen (nur einmal pro Projekt, online). + const autoWindRef = useRef(null) + useEffect(() => { + const proj = currentProject + if (!proj?.id || typeof navigator === 'undefined' || !navigator.onLine) return + if (typeof proj.windDirection === 'number') return + if (autoWindRef.current === proj.id) return + autoWindRef.current = proj.id + const c = proj.mapCenter + if (!c?.lat || !c?.lng) return + fetchLiveWind(c.lat, c.lng).then((wind) => { + if (wind) handleWindChange(wind.direction) + }) + }, [currentProject?.id, currentProject?.windDirection, handleWindChange]) + // Fullscreen toggle const toggleFullscreen = useCallback(() => { if (!document.fullscreenElement) { @@ -1063,6 +1098,7 @@ export default function AppPage() { onToggleFullscreen={toggleFullscreen} windDirection={currentProject?.windDirection} onWindChange={handleWindChange} + onFetchLiveWind={handleFetchLiveWind} /> diff --git a/src/components/map/map-compass.tsx b/src/components/map/map-compass.tsx index f819b1b..0e24519 100644 --- a/src/components/map/map-compass.tsx +++ b/src/components/map/map-compass.tsx @@ -1,7 +1,7 @@ 'use client' import { useState } from 'react' -import { Wind, X } from 'lucide-react' +import { Wind, X, Loader2, Satellite } from 'lucide-react' interface MapCompassProps { /** Karten-Bearing in Grad (0 = Norden oben) */ @@ -10,6 +10,8 @@ interface MapCompassProps { windDirection: number | null | undefined canEdit: boolean onWindChange: (dir: number | null) => void + /** Live-Wind (Open-Meteo) für den aktuellen Standort holen */ + onFetchLiveWind?: () => Promise | void } const DIRS = ['N', 'NO', 'O', 'SO', 'S', 'SW', 'W', 'NW'] @@ -24,10 +26,17 @@ const WIND_OPTIONS: { label: string; deg: number }[] = [ { label: 'S', deg: 180 }, { label: 'SW', deg: 225 }, { label: 'W', deg: 270 }, { label: 'NW', deg: 315 }, ] -export function MapCompass({ bearing, windDirection, canEdit, onWindChange }: MapCompassProps) { +export function MapCompass({ bearing, windDirection, canEdit, onWindChange, onFetchLiveWind }: MapCompassProps) { const [open, setOpen] = useState(false) + const [loadingLive, setLoadingLive] = useState(false) const hasWind = typeof windDirection === 'number' + const handleLive = async () => { + if (!onFetchLiveWind) return + setLoadingLive(true) + try { await onFetchLiveWind() } finally { setLoadingLive(false) } + } + return (
{/* Kompass mit Nordpfeil (dreht mit dem Karten-Bearing) */} @@ -75,6 +84,18 @@ export function MapCompass({ bearing, windDirection, canEdit, onWindChange }: Ma Wind aus
+ {onFetchLiveWind && ( + + )} +
oder Richtung wählen:
{WIND_OPTIONS.map((w) => (