From 615f7f927789c77fc772bbd2c61df973bdc3ad55 Mon Sep 17 00:00:00 2001 From: Nan Kyung Lee Date: Mon, 16 Mar 2026 09:51:30 +0900 Subject: [PATCH] =?UTF-8?q?feat(aerial):=20=EB=93=9C=EB=A1=A0=20=EC=A7=80?= =?UTF-8?q?=EB=8F=84=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=E2=80=94=20=ED=95=A8=EC=A0=95=20=EC=82=BC=EA=B0=81=ED=98=95=20?= =?UTF-8?q?+=20=EC=97=B0=EA=B2=B0=EC=84=A0=20+=20=EB=93=9C=EB=A1=A0=20?= =?UTF-8?q?=EC=9B=90=ED=98=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 함정: 삼각형 아이콘 + 함정명 라벨 (좌하단) - 드론: 원형 아이콘 (십자 프로펠러 + 본체 + 카메라 렌즈) (우상단) - 함정↔드론 점선 연결선으로 소속 관계 표시 - 상태별 색상: 송출중(초록), 연결중(시안), 오류(빨강), 대기(회색) - 송출중 드론 빨간 LED 깜빡임 유지 - 드론 모델명 라벨 (M300/M30T/Mavic3E) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../tabs/aerial/components/RealtimeDrone.tsx | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/frontend/src/tabs/aerial/components/RealtimeDrone.tsx b/frontend/src/tabs/aerial/components/RealtimeDrone.tsx index 3c01228..308b0e2 100644 --- a/frontend/src/tabs/aerial/components/RealtimeDrone.tsx +++ b/frontend/src/tabs/aerial/components/RealtimeDrone.tsx @@ -7,11 +7,11 @@ import type { DroneStreamItem } from '../services/aerialApi' import { CCTVPlayer } from './CCTVPlayer' import type { CCTVPlayerHandle } from './CCTVPlayer' -/** 드론 위치 좌표 (함정 모항 기준) */ -const DRONE_COORDS: Record = { - 'busan-1501': { lat: 35.0796, lon: 129.0756 }, - 'incheon-3008': { lat: 37.4541, lon: 126.5986 }, - 'mokpo-3015': { lat: 34.7780, lon: 126.3780 }, +/** 함정 위치 + 드론 비행 위치 */ +const DRONE_POSITIONS: Record = { + 'busan-1501': { ship: { lat: 35.0796, lon: 129.0756 }, drone: { lat: 35.1100, lon: 129.1100 } }, + 'incheon-3008': { ship: { lat: 37.4541, lon: 126.5986 }, drone: { lat: 37.4800, lon: 126.5600 } }, + 'mokpo-3015': { ship: { lat: 34.7780, lon: 126.3780 }, drone: { lat: 34.8050, lon: 126.4100 } }, } const DRONE_MAP_STYLE: StyleSpecification = { @@ -266,55 +266,61 @@ export function RealtimeDrone() { attributionControl={false} > {streams.map(stream => { - const coord = DRONE_COORDS[stream.id] - if (!coord) return null - const si = statusInfo(stream.status) + const pos = DRONE_POSITIONS[stream.id] + if (!pos) return null + const statusColor = stream.status === 'streaming' ? '#22c55e' : stream.status === 'starting' ? '#06b6d4' : stream.status === 'error' ? '#ef4444' : '#94a3b8' return ( { e.originalEvent.stopPropagation(); setMapPopup(stream) }} > -
- {/* 드론 SVG 아이콘 */} - +
+ + {/* 연결선 (점선) */} + + + {/* 함정 삼각형 (좌하단) */} + + + {/* 함정명 */} + + {stream.shipName.replace(/서 /, ' ')} + + {/* 드론 원형 아이콘 (우상단) */} + + {/* 드론 내부 — 십자 프로펠러 */} + + + {/* 프로펠러 4개 */} + + + + {/* 본체 */} - - {/* 팔 4개 */} - - - - - {/* 프로펠러 */} - - - - - {/* 카메라 */} - - {/* 송출중 표시 */} + + + {/* 송출중 LED */} {stream.status === 'streaming' && ( - - + + )} + {/* 드론 이름 */} + + {stream.droneModel.split(' ').slice(-1)[0]} - {/* 라벨 */} -
- {stream.shipName} -
) })} {/* 드론 클릭 팝업 */} - {mapPopup && DRONE_COORDS[mapPopup.id] && ( + {mapPopup && DRONE_POSITIONS[mapPopup.id] && ( setMapPopup(null)} closeOnClick={false}