import { useState, useEffect } from 'react' import { MapContainer, TileLayer, useMapEvents } from 'react-leaflet' import type { LatLngExpression } from 'leaflet' import 'leaflet/dist/leaflet.css' import { WeatherRightPanel } from './WeatherRightPanel' import { WeatherMapOverlay } from './WeatherMapOverlay' import { OceanForecastOverlay } from './OceanForecastOverlay' import { OceanCurrentLayer } from './OceanCurrentLayer' import { WaterTemperatureLayer } from './WaterTemperatureLayer' import { WindParticleLayer } from './WindParticleLayer' import { useWeatherData } from '../hooks/useWeatherData' import { useOceanForecast } from '../hooks/useOceanForecast' type TimeOffset = '0' | '3' | '6' | '9' interface WeatherStation { id: string name: string location: { lat: number; lon: number } wind: { speed: number direction: number speed_1k: number speed_3k: number } wave: { height: number period: number } temperature: { current: number feelsLike: number } pressure: number visibility: number } interface WeatherForecast { time: string hour: string icon: string temperature: number windSpeed: number } // Base weather station locations (weather data will be fetched from API) const baseStations = [ { id: 'incheon', name: '인천', location: { lat: 37.45, lon: 126.43 } }, { id: 'ulsan', name: '울산', location: { lat: 35.52, lon: 129.38 } }, { id: 'yeosu', name: '여수', location: { lat: 34.74, lon: 127.75 } }, { id: 'jeju', name: '제주', location: { lat: 33.51, lon: 126.53 } }, { id: 'pohang', name: '포항', location: { lat: 36.03, lon: 129.38 } }, { id: 'mokpo', name: '목포', location: { lat: 34.78, lon: 126.38 } }, { id: 'gunsan', name: '군산', location: { lat: 35.97, lon: 126.7 } }, { id: 'sokcho', name: '속초', location: { lat: 38.21, lon: 128.59 } }, { id: 'tongyeong', name: '통영', location: { lat: 34.83, lon: 128.43 } }, { id: 'donghae', name: '동해', location: { lat: 37.52, lon: 129.14 } } ] // Generate forecast data based on time offset const generateForecast = (timeOffset: TimeOffset): WeatherForecast[] => { const baseHour = parseInt(timeOffset) const forecasts: WeatherForecast[] = [] for (let i = 0; i < 5; i++) { const hour = baseHour + i * 3 const icons = ['☀️', '⛅', '☁️', '🌦️', '🌧️'] forecasts.push({ time: `+${hour}시`, hour: `${hour}시`, icon: icons[i % icons.length], temperature: Math.floor(Math.random() * 5) + 5, windSpeed: Math.floor(Math.random() * 5) + 6 }) } return forecasts } // Map click handler component function MapClickHandler({ onMapClick }: { onMapClick: (lat: number, lon: number) => void }) { useMapEvents({ click(e) { onMapClick(e.latlng.lat, e.latlng.lng) } }) return null } export function WeatherView() { // Fetch real-time weather data from API const { weatherStations, loading, error, lastUpdate } = useWeatherData(baseStations) // Fetch ocean forecast data from KHOA API const { selectedForecast, availableTimes, loading: oceanLoading, error: oceanError, selectForecast } = useOceanForecast('KOREA') const [timeOffset, setTimeOffset] = useState('0') const [selectedStation, setSelectedStation] = useState(null) const [selectedLocation, setSelectedLocation] = useState<{ lat: number; lon: number } | null>( null ) const [enabledLayers, setEnabledLayers] = useState>( new Set(['wind', 'labels']) ) const [oceanForecastOpacity, setOceanForecastOpacity] = useState(0.6) // Set initial selected station when data loads useEffect(() => { if (weatherStations.length > 0 && !selectedStation) { // eslint-disable-next-line react-hooks/set-state-in-effect setSelectedStation(weatherStations[0]) } }, [weatherStations, selectedStation]) const mapCenter: LatLngExpression = [36.5, 127.8] const handleStationClick = (station: WeatherStation) => { setSelectedStation(station) setSelectedLocation(null) } const handleMapClick = (lat: number, lon: number) => { // Find nearest station const nearestStation = weatherStations.reduce((nearest, station) => { const distance = Math.sqrt( Math.pow(station.location.lat - lat, 2) + Math.pow(station.location.lon - lon, 2) ) const nearestDistance = Math.sqrt( Math.pow(nearest.location.lat - lat, 2) + Math.pow(nearest.location.lon - lon, 2) ) return distance < nearestDistance ? station : nearest }, weatherStations[0]) setSelectedStation(nearestStation) setSelectedLocation({ lat, lon }) } const toggleLayer = (layer: string) => { const newLayers = new Set(enabledLayers) if (newLayers.has(layer)) { newLayers.delete(layer) } else { newLayers.add(layer) } setEnabledLayers(newLayers) } const weatherData = selectedStation ? { stationName: selectedLocation ? `해양 지점 (${selectedLocation.lat.toFixed(2)}°N, ${selectedLocation.lon.toFixed(2)}°E)` : selectedStation.name, location: selectedLocation || selectedStation.location, currentTime: new Date().toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' }), wind: selectedStation.wind, wave: selectedStation.wave, temperature: selectedStation.temperature, pressure: selectedStation.pressure, visibility: selectedStation.visibility, forecast: generateForecast(timeOffset) } : null return (
{/* Main Map Area */}
{/* Tab Navigation */}
{lastUpdate ? `마지막 업데이트: ${lastUpdate.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' })}` : '데이터 로딩 중...'} {loading && (
)} {error && ( ⚠️ {error} )}
{/* Map */}
{/* Weather Overlay */} {/* Ocean Forecast Overlay */} {/* Ocean Current Arrows */} {/* Water Temperature Heatmap */} {/* Windy-style Wind Particle Animation */} {/* Layer Controls */}
기상 레이어
{/* 해황예보도 레이어 */}
{/* 투명도 조절 슬라이더 */} {enabledLayers.has('oceanForecast') && (
투명도: setOceanForecastOpacity(Number(e.target.value) / 100)} className="flex-1 h-1 bg-bg-3 rounded-lg appearance-none cursor-pointer" /> {Math.round(oceanForecastOpacity * 100)}%
{/* 예보 시간 선택 */} {availableTimes.length > 0 && (
예보 시간:
{availableTimes.map((time) => ( ))}
)} {oceanLoading && (
로딩 중...
)} {oceanError && (
오류 발생
)} {selectedForecast && (
현재: {selectedForecast.name} • {selectedForecast.day.slice(4, 6)}/{selectedForecast.day.slice(6, 8)} {selectedForecast.hour}:00
)}
)}
{/* Legend */}
기상 범례
{/* Wind Speed - Windy style */}
바람 (m/s)
35710131620+
{/* Wave Height */}
파고 (m)
< 1.5: 낮음
1.5-2.5: 보통
> 2.5: 높음
💡 지도를 클릭하여 해당 지점의 기상 예보를 확인하세요
{/* Right Panel - Weather Details */}
) }