- AIS 타겟에 shipImagePath/shipImageCount 필드 추가 - 선박 이미지 API 연동 (fetchShipImagesByImo) - 지도 위 사진 인디케이터 (ScatterplotLayer) - 호버 툴팁에 썸네일 표시 - 정보 패널 카드 갤러리 (스크롤+화살표) - 고화질 이미지 모달 (initialIndex 지원) - Vite 프록시 /shipimg 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
217 lines
9.2 KiB
TypeScript
217 lines
9.2 KiB
TypeScript
/**
|
|
* 오버레이/경고 시각 검증용 더미 데이터 — 김개발(DEV) 모드 전용.
|
|
*
|
|
* 서해 해역에 12척의 가상 선박을 배치하여 다음 시나리오를 재현:
|
|
* - 정상 쌍끌이 (pair link, ~1 NM)
|
|
* - 이격 쌍끌이 (pair_separation alarm, ~8 NM)
|
|
* - 선단 원 (fleet circle, 5척 동일 선주)
|
|
* - 환적 의심 (transshipment alarm, FC ↔ PS < 0.5 NM)
|
|
* - AIS 지연 (ais_stale alarm, 2시간 전 타임스탬프)
|
|
* - 수역 이탈 (zone_violation alarm, PT가 zone 4에 위치)
|
|
* - closed_season은 계절(월)에 따라 자동 판정 (2월에는 미발생)
|
|
*
|
|
* 사용법:
|
|
* DashboardPage에서 isDevMode일 때 targetsInScope + legacyHits에 병합.
|
|
* 기존 computePairLinks / computeFcLinks / computeFleetCircles /
|
|
* computeLegacyAlarms 파이프라인이 자동으로 오버레이/경고를 생성.
|
|
*/
|
|
|
|
import type { AisTarget } from '../../../entities/aisTarget/model/types';
|
|
import type { LegacyVesselInfo } from '../../../entities/legacyVessel/model/types';
|
|
|
|
// ── 타임스탬프 ──────────────────────────────────────────────
|
|
|
|
const FRESH_TS = new Date().toISOString();
|
|
const STALE_TS = new Date(Date.now() - 120 * 60_000).toISOString(); // 2시간 전 → ais_stale
|
|
|
|
// ── 팩토리 ──────────────────────────────────────────────────
|
|
|
|
function makeAis(o: Partial<AisTarget> & Pick<AisTarget, 'mmsi' | 'lat' | 'lon'>): AisTarget {
|
|
return {
|
|
imo: 0,
|
|
name: '',
|
|
callsign: '',
|
|
vesselType: '',
|
|
heading: o.cog ?? 0,
|
|
sog: 0,
|
|
cog: 0,
|
|
rot: 0,
|
|
length: 30,
|
|
width: 8,
|
|
draught: 4,
|
|
destination: '',
|
|
eta: '',
|
|
status: 'underway',
|
|
messageTimestamp: FRESH_TS,
|
|
receivedDate: FRESH_TS,
|
|
source: 'mock',
|
|
classType: 'A',
|
|
...o,
|
|
};
|
|
}
|
|
|
|
function makeLegacy(
|
|
o: Partial<LegacyVesselInfo> & Pick<LegacyVesselInfo, 'permitNo' | 'shipCode' | 'mmsiList'>,
|
|
): LegacyVesselInfo {
|
|
return {
|
|
shipNameRoman: '',
|
|
shipNameCn: null,
|
|
ton: 100,
|
|
callSign: '',
|
|
workSeaArea: '서해',
|
|
workTerm1: '2025-01-01',
|
|
workTerm2: '2025-12-31',
|
|
quota: '',
|
|
ownerCn: null,
|
|
ownerRoman: null,
|
|
pairPermitNo: null,
|
|
pairShipNameCn: null,
|
|
checklistSheet: null,
|
|
sources: { permittedList: true, checklist: false, fleet906: false },
|
|
...o,
|
|
};
|
|
}
|
|
|
|
// ── 선박 정의 (12척) ────────────────────────────────────────
|
|
|
|
/*
|
|
* Group 1 — 정상 쌍끌이 (간격 ~1 NM, 경고 없음)
|
|
* 위치: 서해남부(zone 3) 125.3°E 34.0°N 부근
|
|
*/
|
|
const PT_01_AIS = makeAis({ mmsi: 990001, lat: 34.00, lon: 125.30, sog: 3.3, cog: 45, name: 'MOCK VESSEL 1', shipImagePath: 'https://picsum.photos/seed/ship990001v1/800/600', shipImageCount: 3 });
|
|
const PT_02_AIS = makeAis({ mmsi: 990002, lat: 34.01, lon: 125.32, sog: 3.3, cog: 45, name: 'MOCK VESSEL 2' });
|
|
|
|
const PT_01_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P001', shipCode: 'PT', mmsiList: [990001],
|
|
shipNameRoman: 'MOCK VESSEL 1', shipNameCn: '模拟渔船一号',
|
|
pairPermitNo: 'MOCK-P002', pairShipNameCn: '模拟渔船二号',
|
|
ownerRoman: 'MOCK Owner A', ownerCn: '模拟A渔业',
|
|
});
|
|
const PT_02_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P002', shipCode: 'PT-S', mmsiList: [990002],
|
|
shipNameRoman: 'MOCK VESSEL 2', shipNameCn: '模拟渔船二号',
|
|
pairPermitNo: 'MOCK-P001', pairShipNameCn: '模拟渔船一号',
|
|
ownerRoman: 'MOCK Owner A', ownerCn: '模拟A渔业',
|
|
});
|
|
|
|
/*
|
|
* Group 2 — 이격 쌍끌이 (간격 ~8 NM → pair_separation alarm)
|
|
* 위치: 서해남부(zone 3) 125.0°E 34.5°N 부근
|
|
*/
|
|
const PT_03_AIS = makeAis({ mmsi: 990003, lat: 34.50, lon: 125.00, sog: 3.5, cog: 90, name: 'MOCK VESSEL 3', shipImagePath: 'https://picsum.photos/seed/ship990003v1/800/600', shipImageCount: 2 });
|
|
const PT_04_AIS = makeAis({ mmsi: 990004, lat: 34.60, lon: 125.12, sog: 3.5, cog: 90, name: 'MOCK VESSEL 4' });
|
|
|
|
const PT_03_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P003', shipCode: 'PT', mmsiList: [990003],
|
|
shipNameRoman: 'MOCK VESSEL 3', shipNameCn: '模拟渔船三号',
|
|
pairPermitNo: 'MOCK-P004', pairShipNameCn: '模拟渔船四号',
|
|
ownerRoman: 'MOCK Owner B', ownerCn: '模拟B渔业',
|
|
});
|
|
const PT_04_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P004', shipCode: 'PT-S', mmsiList: [990004],
|
|
shipNameRoman: 'MOCK VESSEL 4', shipNameCn: '模拟渔船四号',
|
|
pairPermitNo: 'MOCK-P003', pairShipNameCn: '模拟渔船三号',
|
|
ownerRoman: 'MOCK Owner B', ownerCn: '模拟B渔业',
|
|
});
|
|
|
|
/*
|
|
* Group 3 — 선단 (동일 선주 5척 → fleet circle)
|
|
* 위치: 서해중간(zone 4) 124.8°E 35.2°N 부근
|
|
* #11(GN)은 AIS 지연 2시간 → ais_stale alarm 동시 발생
|
|
*/
|
|
const GN_01_AIS = makeAis({ mmsi: 990005, lat: 35.20, lon: 124.80, sog: 1.0, cog: 180, name: 'MOCK VESSEL 5', shipImagePath: 'https://picsum.photos/seed/ship990005v1/800/600', shipImageCount: 1 });
|
|
const GN_02_AIS = makeAis({ mmsi: 990006, lat: 35.22, lon: 124.85, sog: 1.2, cog: 170, name: 'MOCK VESSEL 6' });
|
|
const GN_03_AIS = makeAis({ mmsi: 990007, lat: 35.18, lon: 124.82, sog: 0.8, cog: 200, name: 'MOCK VESSEL 7' });
|
|
const OT_01_AIS = makeAis({ mmsi: 990008, lat: 35.25, lon: 124.78, sog: 3.5, cog: 160, name: 'MOCK VESSEL 8', shipImagePath: 'https://picsum.photos/seed/ship990008v1/800/600', shipImageCount: 4 });
|
|
const GN_04_AIS = makeAis({
|
|
mmsi: 990011, lat: 35.00, lon: 125.20, sog: 1.5, cog: 190, name: 'MOCK VESSEL 10',
|
|
messageTimestamp: STALE_TS, receivedDate: STALE_TS,
|
|
});
|
|
|
|
const GN_01_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P005', shipCode: 'GN', mmsiList: [990005],
|
|
shipNameRoman: 'MOCK VESSEL 5', shipNameCn: '模拟渔船五号',
|
|
ownerRoman: 'MOCK Owner C', ownerCn: '模拟C渔业',
|
|
});
|
|
const GN_02_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P006', shipCode: 'GN', mmsiList: [990006],
|
|
shipNameRoman: 'MOCK VESSEL 6', shipNameCn: '模拟渔船六号',
|
|
ownerRoman: 'MOCK Owner C', ownerCn: '模拟C渔业',
|
|
});
|
|
const GN_03_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P007', shipCode: 'GN', mmsiList: [990007],
|
|
shipNameRoman: 'MOCK VESSEL 7', shipNameCn: '模拟渔船七号',
|
|
ownerRoman: 'MOCK Owner C', ownerCn: '模拟C渔业',
|
|
});
|
|
const OT_01_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P008', shipCode: 'OT', mmsiList: [990008],
|
|
shipNameRoman: 'MOCK VESSEL 8', shipNameCn: '模拟渔船八号',
|
|
ownerRoman: 'MOCK Owner C', ownerCn: '模拟C渔业',
|
|
});
|
|
const GN_04_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P011', shipCode: 'GN', mmsiList: [990011],
|
|
shipNameRoman: 'MOCK VESSEL 10', shipNameCn: '模拟渔船十号',
|
|
ownerRoman: 'MOCK Owner C', ownerCn: '模拟C渔业',
|
|
});
|
|
|
|
/*
|
|
* Group 4 — 환적 의심 (FC ↔ PS 거리 ~0.15 NM → transshipment alarm)
|
|
* 위치: 서해남부(zone 3) 125.5°E 34.3°N 부근
|
|
*/
|
|
const FC_01_AIS = makeAis({ mmsi: 990009, lat: 34.30, lon: 125.50, sog: 1.0, cog: 0, name: 'MOCK CARRIER 1', shipImagePath: 'https://picsum.photos/seed/ship990009v1/800/600', shipImageCount: 2 });
|
|
const PS_01_AIS = makeAis({ mmsi: 990010, lat: 34.302, lon: 125.502, sog: 0.5, cog: 10, name: 'MOCK VESSEL 9' });
|
|
|
|
const FC_01_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P009', shipCode: 'FC', mmsiList: [990009],
|
|
shipNameRoman: 'MOCK CARRIER 1', shipNameCn: '模拟运船一号',
|
|
ownerRoman: 'MOCK Owner D', ownerCn: '模拟D渔业',
|
|
});
|
|
const PS_01_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P010', shipCode: 'PS', mmsiList: [990010],
|
|
shipNameRoman: 'MOCK VESSEL 9', shipNameCn: '模拟渔船九号',
|
|
ownerRoman: 'MOCK Owner E', ownerCn: '模拟E渔业',
|
|
});
|
|
|
|
/*
|
|
* Group 5 — 수역 이탈 (PT가 zone 4 에 위치 → zone_violation alarm)
|
|
* PT는 zone 2,3만 허가. zone 4(서해중간)에 위치하면 이탈 판정.
|
|
* 위치: 서해중간(zone 4) 125.0°E 36.5°N 부근
|
|
*/
|
|
const PT_05_AIS = makeAis({ mmsi: 990012, lat: 36.50, lon: 125.00, sog: 3.3, cog: 270, name: 'MOCK VESSEL 11', shipImagePath: 'https://picsum.photos/seed/ship990012v1/800/600', shipImageCount: 1 });
|
|
|
|
const PT_05_LEG = makeLegacy({
|
|
permitNo: 'MOCK-P012', shipCode: 'PT', mmsiList: [990012],
|
|
shipNameRoman: 'MOCK VESSEL 11', shipNameCn: '模拟渔船十一号',
|
|
ownerRoman: 'MOCK Owner F', ownerCn: '模拟F渔业',
|
|
});
|
|
|
|
// ── 공개 API ────────────────────────────────────────────────
|
|
|
|
/** 더미 AIS 타겟 (12척) */
|
|
export const MOCK_AIS_TARGETS: AisTarget[] = [
|
|
PT_01_AIS, PT_02_AIS, // Group 1: 정상 쌍끌이
|
|
PT_03_AIS, PT_04_AIS, // Group 2: 이격 쌍끌이
|
|
GN_01_AIS, GN_02_AIS, GN_03_AIS, OT_01_AIS, GN_04_AIS, // Group 3: 선단 + AIS 지연
|
|
FC_01_AIS, PS_01_AIS, // Group 4: 환적 의심
|
|
PT_05_AIS, // Group 5: 수역 이탈
|
|
];
|
|
|
|
/** 더미 legacy 매칭 엔트리 (MMSI → LegacyVesselInfo) */
|
|
export const MOCK_LEGACY_ENTRIES: [number, LegacyVesselInfo][] = [
|
|
[990001, PT_01_LEG],
|
|
[990002, PT_02_LEG],
|
|
[990003, PT_03_LEG],
|
|
[990004, PT_04_LEG],
|
|
[990005, GN_01_LEG],
|
|
[990006, GN_02_LEG],
|
|
[990007, GN_03_LEG],
|
|
[990008, OT_01_LEG],
|
|
[990009, FC_01_LEG],
|
|
[990010, PS_01_LEG],
|
|
[990011, GN_04_LEG],
|
|
[990012, PT_05_LEG],
|
|
];
|
|
|
|
/** 더미 MMSI 집합 — 필터링/하이라이팅에 활용 */
|
|
export const MOCK_MMSI_SET = new Set(MOCK_AIS_TARGETS.map((t) => t.mmsi));
|