- 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>
78 lines
3.0 KiB
TypeScript
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>
|
|
)
|
|
}
|