import { useState, useEffect, useRef } from 'react'; import { convertToGridCoords, getUltraShortForecast, getCurrentBaseDateTime, } from '@tabs/weather/services/weatherApi'; import type { StabilityClass, WeatherFetchResult } from '../utils/dispersionTypes'; /** * Turner 간이법으로 Pasquill-Gifford 안정도 산출 * 풍속 + 시간대(주간/야간) 기반 */ function deriveStabilityClass(windSpeed: number, hour: number): StabilityClass { const isNight = hour < 6 || hour >= 18; if (isNight) { if (windSpeed < 2) return 'F'; if (windSpeed < 3) return 'E'; return 'D'; } // 주간 (중간 수준 일사량 가정) if (windSpeed < 2) return 'A'; if (windSpeed < 3) return 'B'; if (windSpeed < 5) return 'C'; return 'D'; } /** 풍향(도) → 16방위 문자열 */ export function windDirToCompass(deg: number): string { const dirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']; const idx = Math.round(((deg % 360) + 360) % 360 / 22.5) % 16; return dirs[idx]; } const DEFAULT_WEATHER: WeatherFetchResult = { windSpeed: 5.0, windDirection: 270, temperature: 15, humidity: 60, stability: 'D', isLoading: false, error: null, lastUpdate: null, }; /** * 위치 기반 기상정보 자동조회 훅 * KMA 초단기실황 API 활용, 500ms 디바운스 * baseDate: 'YYYY-MM-DD' (선택), baseTime: 'HH:mm' (선택) * 미제공 시 getCurrentBaseDateTime()으로 현재 시각 사용 */ export function useWeatherFetch(lat: number, lon: number, baseDate?: string, baseTime?: string): WeatherFetchResult { const [weather, setWeather] = useState({ ...DEFAULT_WEATHER, isLoading: true, }); const timerRef = useRef | null>(null); useEffect(() => { if (timerRef.current) clearTimeout(timerRef.current); timerRef.current = setTimeout(async () => { setWeather(prev => ({ ...prev, isLoading: true, error: null })); try { const { nx, ny } = convertToGridCoords(lat, lon); let apiBaseDate: string; let apiBaseTime: string; let stabilityHour: number; if (baseDate && baseTime) { // 'YYYY-MM-DD' → 'YYYYMMDD' apiBaseDate = baseDate.replace(/-/g, ''); // 'HH:mm' → 'HH00' apiBaseTime = baseTime.slice(0, 2) + '00'; stabilityHour = parseInt(baseTime.slice(0, 2), 10); } else { const current = getCurrentBaseDateTime(); apiBaseDate = current.baseDate; apiBaseTime = current.baseTime; stabilityHour = new Date().getHours(); } const forecasts = await getUltraShortForecast(nx, ny, apiBaseDate, apiBaseTime); if (forecasts.length > 0) { const f = forecasts[0]; const stability = deriveStabilityClass(f.windSpeed, stabilityHour); setWeather({ windSpeed: f.windSpeed, windDirection: f.windDirection, temperature: f.temperature, humidity: f.humidity, stability, isLoading: false, error: null, lastUpdate: new Date(), }); } else { setWeather({ ...DEFAULT_WEATHER, isLoading: false, error: 'API 응답 데이터 없음', lastUpdate: new Date(), }); } } catch { setWeather({ ...DEFAULT_WEATHER, isLoading: false, error: 'KMA API 조회 실패 (기본값 사용)', lastUpdate: new Date(), }); } }, 500); return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, [lat, lon, baseDate, baseTime]); return weather; }