import { useMemo } from 'react'; import { ScatterplotLayer } from '@deck.gl/layers'; import type { Layer } from '@deck.gl/core'; import { hexToRgba } from '@common/components/map/mapUtils'; interface OceanCurrentData { lat: number; lon: number; direction: number; // 0-360도 speed: number; // m/s } interface OceanCurrentLayerProps { visible: boolean; opacity?: number; } // 한반도 육지 영역 판별 (간략화된 폴리곤) const isOnLand = (lat: number, lon: number): boolean => { const peninsula: [number, number][] = [ [38.5, 124.5], [38.5, 128.3], [37.8, 128.8], [37.0, 129.2], [36.0, 129.5], [35.1, 129.2], [34.8, 128.6], [34.5, 127.8], [34.3, 126.5], [34.8, 126.1], [35.5, 126.0], [36.0, 126.3], [36.8, 126.0], [37.5, 126.2], [38.5, 124.5], ]; // 제주도 영역 if (lat >= 33.1 && lat <= 33.7 && lon >= 126.1 && lon <= 127.0) return true; // Ray casting algorithm let inside = false; for (let i = 0, j = peninsula.length - 1; i < peninsula.length; j = i++) { const [yi, xi] = peninsula[i]; const [yj, xj] = peninsula[j]; if (yi > lat !== yj > lat && lon < ((xj - xi) * (lat - yi)) / (yj - yi) + xi) { inside = !inside; } } return inside; }; // 한국 해역의 대략적인 해류 데이터 생성 const generateOceanCurrentData = (): OceanCurrentData[] => { const data: OceanCurrentData[] = []; for (let lat = 33.5; lat <= 38.0; lat += 0.8) { for (let lon = 125.0; lon <= 130.5; lon += 0.8) { if (isOnLand(lat, lon)) continue; let direction = 0; let speed = 0.3; if (lon > 128.5) { // 동해 — 북동진하는 동한난류 direction = 30 + Math.random() * 20; speed = 0.4 + Math.random() * 0.3; } else if (lon < 126.5) { // 서해 — 북진 direction = 350 + Math.random() * 20; speed = 0.2 + Math.random() * 0.2; } else { // 남해 — 동진 direction = 80 + Math.random() * 20; speed = 0.3 + Math.random() * 0.3; } data.push({ lat, lon, direction, speed }); } } return data; }; // 속도에 따른 hex 색상 function getCurrentHexColor(speed: number): string { if (speed > 0.5) return '#ef4444'; if (speed > 0.3) return '#f59e0b'; return '#3b82f6'; } // 해류 데이터는 컴포넌트 외부에서 한 번만 생성 (랜덤이므로 안정화) const OCEAN_CURRENT_DATA = generateOceanCurrentData(); // eslint-disable-next-line react-refresh/only-export-components /** * OceanCurrentLayer — deck.gl ScatterplotLayer 배열 반환 훅 * * 기존: Leaflet Marker + SVG DivIcon + CSS rotate * 전환: ScatterplotLayer (크기=속도, 색상=방향) * * 방향 표현 한계: deck.gl ScatterplotLayer는 원형이므로 방향을 색상으로 구분 * - 북동(0~90°): 파랑 * - 남동(90~180°): 초록 * - 남서(180~270°): 주황 * - 북서(270~360°): 빨강 * 속도는 반경 크기(5~20km)로 표현 */ // eslint-disable-next-line react-refresh/only-export-components export function useOceanCurrentLayers(props: OceanCurrentLayerProps): Layer[] { const { visible, opacity = 0.7 } = props; return useMemo(() => { if (!visible) return []; const data = OCEAN_CURRENT_DATA.map((c) => ({ position: [c.lon, c.lat] as [number, number], // 반경: 속도 비례 (5~20km) radius: 5000 + c.speed * 25000, fillColor: hexToRgba(getCurrentHexColor(c.speed), Math.round(opacity * 180)), lineColor: hexToRgba(getCurrentHexColor(c.speed), Math.round(opacity * 230)), })); return [ new ScatterplotLayer({ id: 'ocean-current-layer', data, getPosition: (d) => d.position, getRadius: (d) => d.radius, getFillColor: (d) => d.fillColor, getLineColor: (d) => d.lineColor, getLineWidth: 1, stroked: true, radiusUnits: 'meters', pickable: false, updateTriggers: { getFillColor: [opacity], getLineColor: [opacity], }, }) as unknown as Layer, ]; }, [visible, opacity]); } /** * OceanCurrentLayer — React 컴포넌트 (null 반환, layers는 useOceanCurrentLayers로 분리) * * WeatherView에서 deck.gl layers 배열에 useOceanCurrentLayers() 결과를 주입하여 사용한다. * 이 컴포넌트는 이전 Leaflet 방식과의 호환성을 위해 유지하되 실제 렌더링은 하지 않는다. */ export function OceanCurrentLayer(props: OceanCurrentLayerProps) { // visible 상태 변화에 따른 side-effect 없음 — 렌더링은 useOceanCurrentLayers 훅이 담당 void props; return null; }