지도 엔진을 Leaflet 1.9에서 MapLibre GL JS 5.x + deck.gl 9.x로 전환. 15개 파일 수정, Leaflet 완전 제거. WebGL 단일 canvas로 z-index 충돌 해결, 유류 입자 ScatterplotLayer GPU 렌더링으로 10~100배 성능 향상. - MapView.tsx: MapLibre Map + DeckGLOverlay(MapboxOverlay interleaved) - 유류 입자/오일펜스/HNS: deck.gl ScatterplotLayer/PathLayer - 역추적 리플레이: createBacktrackLayers() 함수 패턴 - 기상 오버레이: WeatherMapOverlay/OceanCurrent/WindParticle deck.gl 전환 - 수온 히트맵: WaterTemperatureLayer deck.gl ScatterplotLayer - 해황예보도: MapLibre image source + raster layer - SCAT/Assets/Incidents: MapLibre Map + deck.gl 레이어 - WMS 밝기: raster-brightness-min/max 네이티브 속성 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
70 lines
1.9 KiB
TypeScript
Executable File
70 lines
1.9 KiB
TypeScript
Executable File
import { useEffect, useState } from 'react'
|
|
import { Source, Layer } from '@vis.gl/react-maplibre'
|
|
import type { OceanForecastData } from '../services/khoaApi'
|
|
|
|
interface OceanForecastOverlayProps {
|
|
forecast: OceanForecastData | null
|
|
opacity?: number
|
|
visible?: boolean
|
|
}
|
|
|
|
// 한국 해역 범위 (MapLibre image source용 좌표 배열)
|
|
// [left, bottom, right, top] → MapLibre coordinates 순서: [sw, nw, ne, se]
|
|
// [lon, lat] 순서
|
|
const KOREA_IMAGE_COORDINATES: [[number, number], [number, number], [number, number], [number, number]] = [
|
|
[124.5, 33.0], // 남서 (제주 남쪽)
|
|
[124.5, 38.5], // 북서
|
|
[132.0, 38.5], // 북동 (동해 북쪽)
|
|
[132.0, 33.0], // 남동
|
|
]
|
|
|
|
/**
|
|
* OceanForecastOverlay
|
|
*
|
|
* 기존: react-leaflet ImageOverlay + LatLngBounds
|
|
* 전환: @vis.gl/react-maplibre Source(type=image) + Layer(type=raster)
|
|
*
|
|
* MapLibre image source는 Map 컴포넌트 자식으로 직접 렌더링 가능
|
|
*/
|
|
export function OceanForecastOverlay({
|
|
forecast,
|
|
opacity = 0.6,
|
|
visible = true,
|
|
}: OceanForecastOverlayProps) {
|
|
const [loadedUrl, setLoadedUrl] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (!forecast?.filePath) return
|
|
let cancelled = false
|
|
const img = new Image()
|
|
img.onload = () => { if (!cancelled) setLoadedUrl(forecast.filePath) }
|
|
img.onerror = () => { if (!cancelled) setLoadedUrl(null) }
|
|
img.src = forecast.filePath
|
|
return () => { cancelled = true }
|
|
}, [forecast?.filePath])
|
|
|
|
const imageLoaded = !!loadedUrl && loadedUrl === forecast?.filePath
|
|
|
|
if (!visible || !forecast || !imageLoaded) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<Source
|
|
id="ocean-forecast-image"
|
|
type="image"
|
|
url={forecast.filePath}
|
|
coordinates={KOREA_IMAGE_COORDINATES}
|
|
>
|
|
<Layer
|
|
id="ocean-forecast-raster"
|
|
type="raster"
|
|
paint={{
|
|
'raster-opacity': opacity,
|
|
'raster-resampling': 'linear',
|
|
}}
|
|
/>
|
|
</Source>
|
|
)
|
|
}
|