kcg-monitoring/frontend/src/utils/marineTraffic.ts
Nan Kyung Lee 7174dfd629 feat: 중국어선 조업분석, 어구/어망 분류, 이란 시설, 레이어 재구성
- 어선 분류 개선: AIS Ship Type 30 + category fallback + 선박명 패턴
- 어구/어망 카테고리 신설: 선박명_숫자_ / 선박명% 패턴으로 분류
- 중국어선 조업분석: GC-KCG-2026-001 + CSSA 보고서 기반 (안강망 추가)
- 중국어선 선단 탐지: 본선-부속선 쌍, 운반선 환적, 선망 선단
- 어구/어망 → 모선 연결선 시각화
- 어구 SVG 아이콘 5종 (트롤/자망/안강망/선망/기본)
- 이란 주변국 시설 레이어 (MEFacilityLayer 35개소)
- 사우스파르스 가스전 피격 + 카타르 라스라판 보복 공격 반영
- 한국 해군부대 10개소 추가
- 레이어 재구성: 선박(최상위) → 항공망(항공기+위성) → 해양안전 → 국가기관망
- 어선 국적별 하위 분류 (선박 분류 내 어선 펼치기)
- 오른쪽 패널 접기/펼치기 (한국현황, 중국현황, 조업분석, OSINT)
- 항공망 기본 접힘 처리
- 센서차트 기본 숨김

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:46:27 +09:00

74 lines
3.7 KiB
TypeScript

// MarineTraffic-style ship classification
// Maps S&P STAT5CODE prefixes, VesselType strings, and custom typecodes to MT categories
// AIS Ship Type Number → category (ITU-R M.1371-5 Table 50)
// 20: Wing in ground, 30: Fishing, 31-32: Towing, 33: Dredging, 34: Diving ops
// 35: Military, 36: Sailing, 37: Pleasure, 40-49: High speed, 50: Pilot
// 60-69: Passenger, 70-79: Cargo, 80-89: Tanker, 90-99: Other
function classifyAisShipType(code: string): string | null {
const num = parseInt(code, 10);
if (isNaN(num)) return null;
if (num === 30) return 'fishing';
if (num >= 31 && num <= 34) return 'tug_special';
if (num === 35) return 'military';
if (num === 36) return 'pleasure';
if (num === 37) return 'pleasure';
if (num >= 40 && num <= 49) return 'high_speed';
if (num === 50 || num === 51 || num === 52 || num === 53 || num === 54 || num === 55) return 'tug_special';
if (num >= 60 && num <= 69) return 'passenger';
if (num >= 70 && num <= 79) return 'cargo';
if (num >= 80 && num <= 89) return 'tanker';
return null;
}
export function getMarineTrafficCategory(typecode?: string, category?: string): string {
if (!typecode) {
// Fallback to our internal category
if (category === 'tanker') return 'tanker';
if (category === 'cargo') return 'cargo';
if (category === 'fishing') return 'fishing';
if (category === 'destroyer' || category === 'warship' || category === 'carrier' || category === 'patrol') return 'military';
if (category === 'civilian') return 'unspecified';
return 'unspecified';
}
const code = typecode.toUpperCase();
// AIS Ship Type number (e.g. "30" = fishing)
const aisResult = classifyAisShipType(code);
if (aisResult) return aisResult;
// Our custom typecodes (exact match)
if (code === 'VLCC' || code === 'LNG' || code === 'LPG') return 'tanker';
if (code === 'CONT' || code === 'BULK') return 'cargo';
if (code === 'DDH' || code === 'DDG' || code === 'CVN' || code === 'FFG' || code === 'LCS' || code === 'MCM' || code === 'PC' || code === 'LPH') return 'military';
if (code === 'PASS') return 'passenger';
if (code === 'FISH' || code === 'TRAWL') return 'fishing';
if (code === 'FISHINGGEAR') return 'fishing_gear';
// VesselType strings (e.g. "Cargo", "Tanker", "Passenger") — match BEFORE STAT5CODE
// to avoid "Cargo" matching STAT5CODE prefix "C" → fishing
const lower = code.toLowerCase();
if (lower.includes('tanker')) return 'tanker';
if (lower.includes('cargo') || lower.includes('container') || lower.includes('bulk')) return 'cargo';
if (lower.includes('passenger') || lower.includes('cruise') || lower.includes('ferry')) return 'passenger';
if (lower.includes('fishing') || lower.includes('trawl') || lower.includes('trawler')) return 'fishing';
if (lower.includes('tug') || lower.includes('supply') || lower.includes('offshore')) return 'tug_special';
if (lower.includes('high speed')) return 'high_speed';
if (lower.includes('pleasure') || lower.includes('yacht') || lower.includes('sailing')) return 'pleasure';
if (lower.includes('pilot') || lower.includes('search') || lower.includes('law enforcement')) return 'tug_special';
if (lower.includes('naval') || lower.includes('military')) return 'military';
// S&P STAT5CODE (IHS StatCode5) — 2nd char is digit (e.g. "A1xxxx", "B2xxxx")
if (code.length >= 2 && /\d/.test(code[1])) {
if (code.startsWith('A1')) return 'tanker';
if (code.startsWith('A2')) return 'cargo';
if (code.startsWith('A3')) return 'cargo';
if (code.startsWith('B')) return 'passenger';
if (code.startsWith('C')) return 'fishing';
if (code.startsWith('D')) return 'tug_special';
if (code.startsWith('E')) return 'tug_special';
if (code.startsWith('X')) return 'unspecified';
}
return 'unspecified';
}