signal-batch/frontend/src/features/area-search/components/StsAnalysisTab.tsx
htlee 1cc25f9f3b feat: 다중구역이동 항적 분석 + STS 접촉 분석 프론트엔드 이관
- terra-draw 기반 지도 폴리곤/사각형/원 그리기 + 편집 (OL Draw 대체)
- 구역 항적 분석: ANY/ALL/SEQUENTIAL 검색모드, 다중구역 시각화
- STS 선박쌍 접촉 분석: 접촉쌍 그룹핑, 위험도 indicator, ScatterplotLayer
- Deck.gl 레이어: PathLayer + TripsLayer + IconLayer (커서 기반 O(1) 보간)
- 공유 타임라인 컨트롤 (재생/배속/프로그레스바)
- CSV 내보내기 (다중 방문 동적 컬럼, BOM+UTF-8)
- ApiExplorer 5모드 통합 (positions/vessel/replay/area-search/sts)

신규 17파일 (features/area-search/), 수정 5파일

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 17:07:14 +09:00

78 lines
3.0 KiB
TypeScript

import { useStsStore } from '../stores/stsStore'
import { STS_LIMITS } from '../types/sts.types'
import StsContactList from './StsContactList'
export default function StsAnalysisTab() {
const minDuration = useStsStore((s) => s.minContactDurationMinutes)
const maxDistance = useStsStore((s) => s.maxContactDistanceMeters)
const setMinDuration = useStsStore((s) => s.setMinContactDuration)
const setMaxDistance = useStsStore((s) => s.setMaxContactDistance)
const summary = useStsStore((s) => s.summary)
const queryCompleted = useStsStore((s) => s.queryCompleted)
return (
<div className="space-y-3">
{/* 파라미터 슬라이더 */}
<div className="space-y-2">
<label className="block text-xs font-medium text-muted">STS </label>
{/* 최소 접촉 시간 */}
<div>
<div className="flex items-center justify-between text-[11px]">
<span className="text-foreground/70"> </span>
<span className="font-mono font-medium text-primary">{minDuration}</span>
</div>
<input
type="range"
min={STS_LIMITS.DURATION_MIN}
max={STS_LIMITS.DURATION_MAX}
step={10}
value={minDuration}
onChange={(e) => setMinDuration(Number(e.target.value))}
className="w-full accent-primary"
/>
<div className="flex justify-between text-[9px] text-muted">
<span>{STS_LIMITS.DURATION_MIN}</span>
<span>{STS_LIMITS.DURATION_MAX}</span>
</div>
</div>
{/* 최대 접촉 거리 */}
<div>
<div className="flex items-center justify-between text-[11px]">
<span className="text-foreground/70"> </span>
<span className="font-mono font-medium text-primary">{maxDistance}m</span>
</div>
<input
type="range"
min={STS_LIMITS.DISTANCE_MIN}
max={STS_LIMITS.DISTANCE_MAX}
step={50}
value={maxDistance}
onChange={(e) => setMaxDistance(Number(e.target.value))}
className="w-full accent-primary"
/>
<div className="flex justify-between text-[9px] text-muted">
<span>{STS_LIMITS.DISTANCE_MIN}m</span>
<span>{STS_LIMITS.DISTANCE_MAX}m</span>
</div>
</div>
</div>
{/* 결과 요약 */}
{summary && (
<div className="flex items-center gap-3 rounded-md bg-primary/5 px-2 py-1.5 text-xs">
<span className="font-medium text-primary">{summary.totalContactPairs} </span>
<span className="text-muted">{summary.totalVesselsInvolved} </span>
{summary.processingTimeMs != null && (
<span className="ml-auto text-muted">{summary.processingTimeMs}ms</span>
)}
</div>
)}
{/* 접촉 쌍 리스트 */}
{queryCompleted && <StsContactList />}
</div>
)
}