import { useState } from 'react';
interface ReconItem {
id: string
name: string
type: 'vessel' | 'pollution'
status: 'complete' | 'processing'
points: string
polygons: string
coverage: string
}
const reconItems: ReconItem[] = [
{ id: 'V-001', name: '불명선박-A', type: 'vessel', status: 'complete', points: '980K', polygons: '38K', coverage: '97.1%' },
{ id: 'V-002', name: '불명선박-B', type: 'vessel', status: 'complete', points: '1.2M', polygons: '48K', coverage: '98.4%' },
{ id: 'V-003', name: '어선 #37', type: 'vessel', status: 'processing', points: '420K', polygons: '16K', coverage: '64.2%' },
{ id: 'P-001', name: '유류오염-A', type: 'pollution', status: 'complete', points: '560K', polygons: '22K', coverage: '95.8%' },
{ id: 'P-002', name: '유류오염-B', type: 'pollution', status: 'processing', points: '310K', polygons: '12K', coverage: '52.1%' },
]
function Vessel3DModel({ viewMode, status }: { viewMode: string; status: string }) {
const isProcessing = status === 'processing'
const isWire = viewMode === 'wire'
const isPoint = viewMode === 'point'
const [vesselPoints] = useState(() =>
Array.from({ length: 300 }, (_, i) => {
const x = 35 + Math.random() * 355
const y = 15 + Math.random() * 160
const inHull = y > 60 && y < 175 && x > 35 && x < 390
const inBridge = x > 260 && x < 330 && y > 25 && y < 60
if (!inHull && !inBridge && Math.random() > 0.15) return null
const alpha = 0.15 + Math.random() * 0.55
const r = 0.8 + Math.random() * 0.8
return { i, x, y, r, alpha }
})
)
// 선박 SVG 와이어프레임/솔리드 3D 투시
return (
{/* 처리중 오버레이 */}
{isProcessing && (
)}
)
}
function Pollution3DModel({ viewMode, status }: { viewMode: string; status: string }) {
const isProcessing = status === 'processing'
const isWire = viewMode === 'wire'
const isPoint = viewMode === 'point'
const [pollutionPoints] = useState(() =>
Array.from({ length: 400 }, (_, i) => {
const cx = 190, cy = 145, rx = 130, ry = 75
const angle = Math.random() * Math.PI * 2
const r = Math.sqrt(Math.random())
const x = cx + r * rx * Math.cos(angle)
const y = cy + r * ry * Math.sin(angle)
if (x < 40 || x > 340 || y < 50 || y > 230) return null
const dist = Math.sqrt(((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2)
const intensity = Math.max(0.1, 1 - dist)
const color = dist < 0.4 ? `rgba(239,68,68,${intensity * 0.7})` : dist < 0.7 ? `rgba(249,115,22,${intensity * 0.5})` : `rgba(234,179,8,${intensity * 0.3})`
const circleR = 0.6 + Math.random() * 1.2
return { i, x, y, r: circleR, color }
})
)
return (
{/* 두께 색상 범례 바 */}
{viewMode === '3d' && !isProcessing && (
)}
{isProcessing && (
)}
)
}
export function SensorAnalysis() {
const [subTab, setSubTab] = useState<'vessel' | 'pollution'>('vessel')
const [viewMode, setViewMode] = useState('3d')
const [selectedItem, setSelectedItem] = useState(reconItems[1])
const filteredItems = reconItems.filter(r => r.type === (subTab === 'vessel' ? 'vessel' : 'pollution'))
return (
{/* Left Panel */}
{/* 3D Reconstruction List */}
📋 3D 재구성 완료 목록
{filteredItems.map(item => (
setSelectedItem(item)}
className={`flex items-center gap-2 px-2 py-2 rounded-sm cursor-pointer transition-colors border ${
selectedItem.id === item.id
? 'bg-[rgba(6,182,212,0.08)] border-primary-cyan/20'
: 'border-transparent hover:bg-white/[0.02]'
}`}
>
{item.name}
{item.id} · {item.points} pts
{item.status === 'complete' ? '✅ 완료' : '⏳ 처리중'}
))}
{/* Source Images */}
📹 촬영 원본
{[
{ label: 'D-01 정면', sensor: '광학', color: 'text-primary-blue' },
{ label: 'D-02 좌현', sensor: 'IR', color: 'text-status-red' },
{ label: 'D-03 우현', sensor: '광학', color: 'text-primary-purple' },
{ label: 'D-02 상부', sensor: 'IR', color: 'text-status-red' },
].map((src, i) => (
{src.label.split(' ')[0]}
{src.label}
{src.sensor}
))}
{/* Center Panel - 3D Canvas */}
{/* Simulated 3D viewport */}
{/* Grid floor */}
{/* 3D Model Visualization */}
{selectedItem.type === 'vessel' ? (
) : (
)}
{/* Axis indicator */}
{/* Title */}
3D Vessel Analysis
{selectedItem.name} 정밀분석
34.58°N, 129.30°E · {selectedItem.status === 'complete' ? '재구성 완료' : '처리중'}
{/* View Mode Buttons */}
{[
{ id: '3d', label: '3D모델' },
{ id: 'point', label: '포인트클라우드' },
{ id: 'wire', label: '와이어프레임' },
].map(m => (
))}
{/* Bottom Stats */}
{[
{ value: selectedItem.points, label: '포인트' },
{ value: selectedItem.polygons, label: '폴리곤' },
{ value: '3', label: '시점' },
{ value: selectedItem.coverage, label: '커버리지' },
{ value: '0.023m', label: 'RMS오차' },
].map((s, i) => (
))}
{/* Right Panel - Analysis Details */}
{/* Ship/Pollution Info */}
📊 분석 정보
{(selectedItem.type === 'vessel' ? [
['대상', selectedItem.name],
['선종 추정', '일반화물선 (추정)'],
['길이', '약 85m'],
['폭', '약 14m'],
['AIS 상태', 'OFF (미식별)'],
['최초 탐지', '2026-01-18 14:20'],
['촬영 시점', '3 시점 (정면/좌현/우현)'],
['센서', '광학 4K + IR 열화상'],
] : [
['대상', selectedItem.name],
['유형', '유류 오염'],
['추정 면적', '0.42 km²'],
['추정 유출량', '12.6 kL'],
['유종', 'B-C유 (추정)'],
['최초 탐지', '2026-01-18 13:50'],
['확산 속도', '0.3 km/h (ESE 방향)'],
]).map(([k, v], i) => (
{k}
{v}
))}
{/* AI Detection Results */}
🤖 AI 탐지 결과
{(selectedItem.type === 'vessel' ? [
{ label: '선박 식별', confidence: 94, color: 'bg-status-green' },
{ label: '선종 분류', confidence: 78, color: 'bg-status-yellow' },
{ label: '손상 감지', confidence: 45, color: 'bg-status-orange' },
{ label: '화물 분석', confidence: 62, color: 'bg-status-yellow' },
] : [
{ label: '유막 탐지', confidence: 97, color: 'bg-status-green' },
{ label: '유종 분류', confidence: 85, color: 'bg-status-green' },
{ label: '두께 추정', confidence: 72, color: 'bg-status-yellow' },
{ label: '확산 예측', confidence: 68, color: 'bg-status-orange' },
]).map((r, i) => (
{r.label}
{r.confidence}%
))}
{/* Comparison / Measurements */}
📐 3D 측정값
{(selectedItem.type === 'vessel' ? [
['전장 (LOA)', '84.7 m'],
['형폭 (Breadth)', '14.2 m'],
['건현 (Freeboard)', '3.8 m'],
['흘수 (Draft)', '5.6 m (추정)'],
['마스트 높이', '22.3 m'],
] : [
['유막 면적', '0.42 km²'],
['최대 길이', '1.24 km'],
['최대 폭', '0.68 km'],
['평균 두께', '0.8 mm'],
['최대 두께', '3.2 mm'],
]).map(([k, v], i) => (
{k}
{v}
))}
{/* Action Buttons */}
)
}