- frontend/ 폴더로 프론트엔드 전체 이관 - signal-batch API 연동 (한국 선박 위치 데이터) - Tailwind CSS 4 + CSS 변수 테마 토큰 (dark/light) - i18next 다국어 (ko/en) 인프라 + 28개 컴포넌트 적용 - 레이어 패널 트리 구조 재설계 (카테고리별 온/오프, 범례) - Google OAuth 로그인 화면 + DEV LOGIN 우회 - 외부 API CORS 프록시 전환 (Airplanes.live, OpenSky, CelesTrak) - ShipLayer 이미지 탭 전환 (signal-batch / MarineTraffic) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
452 lines
16 KiB
TypeScript
452 lines
16 KiB
TypeScript
import type { Aircraft, Ship } from '../types';
|
|
|
|
// T0 = main strike moment
|
|
const T0 = new Date('2026-03-01T12:01:00Z').getTime();
|
|
const HOUR = 3600_000;
|
|
const DEG2RAD = Math.PI / 180;
|
|
|
|
// ── Waypoint system ──────────────────────────────────
|
|
// Each waypoint: [hoursFromT0, lat, lng]
|
|
type WP = [number, number, number];
|
|
|
|
// Interpolate position along waypoints at a given time
|
|
function interpWaypoints(wps: WP[], hoursFromT0: number): { lat: number; lng: number; heading: number } {
|
|
// Clamp to waypoint range
|
|
if (hoursFromT0 <= wps[0][0]) {
|
|
const next = wps.length > 1 ? wps[1] : wps[0];
|
|
return { lat: wps[0][1], lng: wps[0][2], heading: calcHeading(wps[0][1], wps[0][2], next[1], next[2]) };
|
|
}
|
|
if (hoursFromT0 >= wps[wps.length - 1][0]) {
|
|
const prev = wps.length > 1 ? wps[wps.length - 2] : wps[wps.length - 1];
|
|
const last = wps[wps.length - 1];
|
|
return { lat: last[1], lng: last[2], heading: calcHeading(prev[1], prev[2], last[1], last[2]) };
|
|
}
|
|
|
|
// Find segment
|
|
for (let i = 0; i < wps.length - 1; i++) {
|
|
const [t0, lat0, lng0] = wps[i];
|
|
const [t1, lat1, lng1] = wps[i + 1];
|
|
if (hoursFromT0 >= t0 && hoursFromT0 <= t1) {
|
|
const frac = (hoursFromT0 - t0) / (t1 - t0);
|
|
const lat = lat0 + (lat1 - lat0) * frac;
|
|
const lng = lng0 + (lng1 - lng0) * frac;
|
|
const heading = calcHeading(lat0, lng0, lat1, lng1);
|
|
return { lat, lng, heading };
|
|
}
|
|
}
|
|
|
|
// Fallback
|
|
return { lat: wps[0][1], lng: wps[0][2], heading: 0 };
|
|
}
|
|
|
|
function calcHeading(lat1: number, lng1: number, lat2: number, lng2: number): number {
|
|
const dLng = (lng2 - lng1) * DEG2RAD;
|
|
const y = Math.sin(dLng) * Math.cos(lat2 * DEG2RAD);
|
|
const x = Math.cos(lat1 * DEG2RAD) * Math.sin(lat2 * DEG2RAD) -
|
|
Math.sin(lat1 * DEG2RAD) * Math.cos(lat2 * DEG2RAD) * Math.cos(dLng);
|
|
return ((Math.atan2(y, x) / DEG2RAD) + 360) % 360;
|
|
}
|
|
|
|
// Generate trail by sampling recent positions
|
|
function generateTrail(wps: WP[], hoursFromT0: number, count: number, intervalMin: number): [number, number][] {
|
|
const trail: [number, number][] = [];
|
|
for (let i = count; i >= 0; i--) {
|
|
const t = hoursFromT0 - (i * intervalMin) / 60;
|
|
const pos = interpWaypoints(wps, t);
|
|
trail.push([pos.lat, pos.lng]);
|
|
}
|
|
return trail;
|
|
}
|
|
|
|
// ── Aircraft Flight Plans ────────────────────────────
|
|
// Waypoints: [hoursFromT0, lat, lng]
|
|
|
|
const FLIGHT_PLANS: Record<string, WP[]> = {
|
|
// ── SURVEILLANCE / ISR ──
|
|
|
|
// FORTE12 (RQ-4B Global Hawk) - 24h ISR racetrack over Iraq/Iran border
|
|
'ae1461': [
|
|
[-12, 33.0, 42.0], [-10, 34.0, 44.0], [-8, 33.0, 46.0], [-6, 34.0, 44.0],
|
|
[-4, 33.0, 42.0], [-2, 34.0, 44.0], [0, 33.5, 45.0], [2, 33.0, 42.0],
|
|
[4, 34.0, 44.0], [6, 33.0, 46.0], [8, 34.0, 44.0], [10, 33.0, 42.0],
|
|
[12, 34.0, 44.0],
|
|
],
|
|
// FORTE13 (MQ-4C Triton) - Maritime ISR racetrack over Persian Gulf
|
|
'ae1462': [
|
|
[-10, 27.0, 54.0], [-8, 26.5, 56.0], [-6, 27.5, 55.0], [-4, 26.5, 56.5],
|
|
[-2, 27.0, 54.0], [0, 26.5, 56.0], [2, 27.5, 55.0], [4, 26.5, 56.5],
|
|
[6, 27.0, 54.0], [8, 26.5, 56.0], [10, 27.5, 55.0],
|
|
],
|
|
// TOXIN31 (RC-135V) - SIGINT orbit from Crete over Eastern Med
|
|
'ae5420': [
|
|
[-10, 35.5, 26.0], [-8, 35.0, 30.0], [-6, 35.5, 34.0], [-4, 35.0, 30.0],
|
|
[-2, 35.5, 26.0], [0, 35.0, 30.0], [2, 35.5, 34.0], [4, 35.0, 30.0],
|
|
[6, 35.5, 26.0], [8, 35.0, 30.0],
|
|
],
|
|
// SNTRY60 (E-3G AWACS) - AEW orbit over northern Iraq
|
|
'ae0005': [
|
|
[-6, 35.0, 43.0], [-4, 34.5, 44.5], [-2, 35.0, 43.0], [-1, 34.5, 44.5],
|
|
[0, 35.0, 43.5], [1, 34.5, 44.5], [2, 35.0, 43.0], [4, 34.5, 44.5],
|
|
[6, 35.0, 43.0], [8, 34.5, 44.5], [10, 35.0, 43.0],
|
|
],
|
|
// DRAGON01 (U-2S) - Ultra-high altitude recon over Iran
|
|
'ae0006': [
|
|
[-8, 24.2, 54.7], [-6, 28.0, 52.0], [-4, 31.0, 53.0], [-2, 33.0, 52.0],
|
|
[0, 30.5, 52.0], [2, 28.0, 53.0], [4, 25.0, 54.0],
|
|
],
|
|
// REAPER41 (MQ-9A) - Armed ISR racetrack over western Iraq
|
|
'ae0007': [
|
|
[-6, 33.0, 42.0], [-4, 32.0, 41.0], [-2, 33.0, 42.5], [0, 32.0, 41.5],
|
|
[2, 33.0, 42.0], [4, 32.0, 41.0], [6, 33.0, 42.5], [8, 32.0, 41.5],
|
|
],
|
|
// REAPER42 (MQ-9A) - Armed ISR over Strait of Hormuz
|
|
'ae0008': [
|
|
[-4, 26.5, 56.5], [-2, 27.0, 55.5], [0, 26.5, 56.0], [2, 27.0, 55.5],
|
|
[4, 26.5, 56.5], [6, 27.0, 55.5], [8, 26.5, 56.0], [10, 27.0, 55.5],
|
|
],
|
|
|
|
// ── TANKERS ──
|
|
|
|
// ETHYL71 (KC-135R) - Refueling orbit over western Iraq
|
|
'ae0001': [
|
|
[-8, 32.5, 40.0], [-6, 31.5, 41.5], [-4, 32.5, 40.0], [-2, 31.5, 41.5],
|
|
[-1, 32.0, 40.5], [0, 31.5, 41.5], [1, 32.5, 40.0], [3, 31.5, 41.5],
|
|
[5, 32.5, 40.0], [6, 32.0, 40.5],
|
|
],
|
|
// STEEL55 (KC-46A) - B-2 tanker support orbit over northern Gulf
|
|
'ae0009': [
|
|
[-10, 28.0, 49.0], [-8, 29.0, 50.5], [-6, 28.0, 49.0], [-4, 29.0, 50.5],
|
|
[-2, 28.0, 49.0], [0, 29.0, 50.5], [2, 28.0, 49.0], [4, 28.5, 50.0],
|
|
],
|
|
// PACK22 (KC-135R) - Refueling over eastern Jordan (Israeli strikes)
|
|
'ae0003': [
|
|
[-3, 32.0, 37.5], [-1.5, 33.0, 39.0], [0, 32.0, 37.5], [1.5, 33.0, 39.0],
|
|
[3, 32.0, 37.5], [4, 32.5, 38.0],
|
|
],
|
|
// NCHO45 (KC-10A) - Refueling orbit over Kuwait/northern Gulf
|
|
'ae0002': [
|
|
[-4, 29.5, 47.0], [-2, 30.2, 48.5], [0, 29.5, 47.0], [1, 30.2, 48.5],
|
|
[3, 29.5, 47.0], [5, 30.2, 48.5],
|
|
],
|
|
|
|
// ── BOMBERS ──
|
|
|
|
// DEATH11 (B-2A Spirit) - Ingress from south, strike Tehran/Isfahan, egress
|
|
'ae2001': [
|
|
[-6, 22.0, 58.0], [-4, 25.0, 55.0], [-2, 28.0, 53.0], [-1, 30.0, 52.0],
|
|
[0, 32.5, 51.5], [1, 30.0, 53.0], [2, 26.0, 56.0],
|
|
],
|
|
// DEATH12 (B-2A Spirit) - Second bomber, offset route
|
|
'ae2002': [
|
|
[-6, 21.5, 59.0], [-4, 24.5, 56.0], [-2, 27.5, 53.5], [-1, 29.5, 52.5],
|
|
[0, 32.0, 52.0], [1, 29.5, 53.5], [2, 25.5, 57.0],
|
|
],
|
|
|
|
// ── US FIGHTERS ──
|
|
|
|
// RAGE01 (F-22A) - Al Udeid scramble, air superiority sweep
|
|
'ae3001': [
|
|
[-2, 25.2, 51.4], [-1.5, 27.0, 50.0], [-1, 29.0, 48.5], [0, 31.0, 47.0],
|
|
[1, 32.5, 48.0], [2, 31.0, 47.0], [3, 28.0, 49.0], [4, 25.5, 51.0],
|
|
],
|
|
// RAGE02 (F-22A) - Wingman
|
|
'ae3002': [
|
|
[-2, 25.0, 51.2], [-1.5, 26.8, 49.8], [-1, 28.8, 48.3], [0, 30.8, 46.8],
|
|
[1, 32.3, 47.8], [2, 30.8, 46.8], [3, 27.8, 48.8], [4, 25.3, 50.8],
|
|
],
|
|
// VIPER11 (F-35A) - Deep strike into Iran from Al Udeid
|
|
'ae3003': [
|
|
[-3, 25.2, 51.4], [-2, 28.0, 50.0], [-1, 31.0, 49.0], [0, 33.0, 50.0],
|
|
[1, 31.0, 49.0], [2, 28.0, 50.0], [3, 25.2, 51.4],
|
|
],
|
|
// VIPER12 (F-35A) - Wingman
|
|
'ae3004': [
|
|
[-3, 25.0, 51.2], [-2, 27.8, 49.8], [-1, 30.8, 48.8], [0, 32.8, 49.8],
|
|
[1, 30.8, 48.8], [2, 27.8, 49.8], [3, 25.0, 51.2],
|
|
],
|
|
// IRON41 (F-15E) - Strike Eagles, deep strike
|
|
'ae3005': [
|
|
[-4, 24.2, 54.7], [-3, 27.0, 53.0], [-2, 29.5, 51.0], [-1, 31.5, 50.0],
|
|
[0, 33.5, 51.5], [1, 31.0, 50.5], [2, 27.0, 53.0],
|
|
],
|
|
// IRON42 (F-15E) - Wingman
|
|
'ae3006': [
|
|
[-4, 24.0, 54.5], [-3, 26.8, 52.8], [-2, 29.3, 50.8], [-1, 31.3, 49.8],
|
|
[0, 33.3, 51.3], [1, 30.8, 50.3], [2, 26.8, 52.8],
|
|
],
|
|
// NAVY51 (F/A-18F) - Launch from USS Abraham Lincoln, strike & return
|
|
'ae3007': [
|
|
[-1, 23.5, 62.0], [-0.5, 24.5, 59.0], [0, 26.0, 57.0], [1, 27.5, 55.5],
|
|
[2, 25.0, 58.0], [3, 23.5, 62.0],
|
|
],
|
|
// NAVY52 (F/A-18E) - Lincoln CAP
|
|
'ae3008': [
|
|
[-1, 23.0, 62.5], [-0.5, 24.0, 60.0], [0, 24.5, 59.0], [1, 24.0, 60.5],
|
|
[2, 23.5, 61.5], [3, 23.0, 62.5],
|
|
],
|
|
|
|
// ── ISRAELI FIGHTERS ──
|
|
|
|
// IRON33 (F-35I Adir) - Nevatim → Iran strike → return
|
|
'ae0012': [
|
|
[-2, 31.2, 34.9], [-1.5, 31.8, 36.0], [-1, 32.5, 38.0], [-0.5, 33.0, 42.0],
|
|
[0, 33.5, 48.0], [0.5, 33.0, 45.0], [1, 32.5, 40.0], [1.5, 32.0, 37.0],
|
|
[2, 31.5, 35.5], [3, 31.2, 34.9],
|
|
],
|
|
// IRON34 (F-35I Adir) - Wingman
|
|
'ae4002': [
|
|
[-2, 31.0, 35.2], [-1.5, 31.6, 36.2], [-1, 32.3, 38.2], [-0.5, 32.8, 42.2],
|
|
[0, 33.3, 47.8], [0.5, 32.8, 44.8], [1, 32.3, 39.8], [1.5, 31.8, 37.2],
|
|
[2, 31.3, 35.7], [3, 31.0, 35.2],
|
|
],
|
|
// RAGE22 (F-15I Ra'am) - Ramon → eastern Iraq standoff launch → return
|
|
'ae0011': [
|
|
[-1.5, 30.0, 34.8], [-1, 30.5, 36.0], [-0.5, 31.0, 38.5], [0, 31.5, 40.0],
|
|
[0.5, 31.0, 38.5], [1, 30.5, 36.0], [1.5, 30.2, 35.0], [2, 30.0, 34.8],
|
|
],
|
|
// RAGE23 (F-15I Ra'am) - Wingman
|
|
'ae4004': [
|
|
[-1.5, 29.8, 35.0], [-1, 30.3, 36.2], [-0.5, 30.8, 38.7], [0, 31.3, 40.2],
|
|
[0.5, 30.8, 38.7], [1, 30.3, 36.2], [1.5, 30.0, 35.2], [2, 29.8, 35.0],
|
|
],
|
|
// VIPER01 (F-16I Sufa) - CAP orbit over Negev/Golan
|
|
'ae0010': [
|
|
[-2, 30.8, 34.7], [-1.5, 31.5, 35.5], [-1, 32.5, 35.8], [-0.5, 33.0, 35.5],
|
|
[0, 32.5, 35.8], [0.5, 31.5, 35.5], [1, 30.8, 34.7], [1.5, 31.5, 35.5],
|
|
[2, 32.5, 35.8], [3, 31.5, 35.0],
|
|
],
|
|
|
|
// ── CARGO ──
|
|
|
|
// RCH882 (C-17A) - Al Udeid → Al Asad
|
|
'ae0004': [
|
|
[-6, 25.1, 51.3], [-4.5, 27.0, 49.0], [-3, 29.0, 47.0], [-1.5, 31.5, 44.5],
|
|
[-1, 33.8, 42.4],
|
|
],
|
|
// RCH445 (C-17A) - Ramstein → Gulf post-strike resupply
|
|
'ae0013': [
|
|
[2, 36.0, 40.0], [4, 33.0, 43.0], [6, 30.0, 46.0], [8, 27.0, 49.0],
|
|
[9, 25.1, 51.3],
|
|
],
|
|
// RCH901 (C-5M) - Ramstein → Al Udeid heavy lift
|
|
'ae5001': [
|
|
[-3, 37.0, 30.0], [-1.5, 35.0, 35.0], [0, 33.0, 40.0], [2, 30.0, 45.0],
|
|
[4, 27.0, 49.0], [5, 25.1, 51.3],
|
|
],
|
|
|
|
// ── CIVILIAN ──
|
|
|
|
// QTR8101 - Doha → Amman
|
|
'738012': [
|
|
[-11, 25.3, 51.6], [-9, 27.5, 48.0], [-7, 30.0, 43.0], [-5, 31.9, 36.0],
|
|
],
|
|
// EK412 - Dubai → Istanbul
|
|
'710104': [
|
|
[-8, 25.3, 55.3], [-6, 28.0, 50.0], [-4, 32.0, 44.0], [-2, 37.0, 38.0],
|
|
],
|
|
// QTR306 - Doha → London (post-strike, southern routing to avoid Iranian airspace)
|
|
'738020': [
|
|
[3, 25.3, 51.6], [5, 26.0, 45.0], [7, 30.0, 38.0], [9, 34.0, 32.0],
|
|
[10, 38.0, 28.0],
|
|
],
|
|
// ETD55 - Abu Dhabi → Cairo (restricted routing post-strike)
|
|
'710110': [
|
|
[3, 24.4, 54.7], [5, 26.0, 48.0], [7, 28.5, 40.0], [9, 30.1, 31.4],
|
|
],
|
|
};
|
|
|
|
// ── Aircraft Propagation ─────────────────────────────
|
|
|
|
export function propagateAircraft(
|
|
baseAircraft: Aircraft[],
|
|
currentTime: number,
|
|
): Aircraft[] {
|
|
const hoursFromT0 = (currentTime - T0) / HOUR;
|
|
|
|
// Filter to only aircraft active at this time
|
|
const active = baseAircraft.filter(ac => {
|
|
if (ac.activeStart != null && currentTime < ac.activeStart) return false;
|
|
if (ac.activeEnd != null && currentTime > ac.activeEnd) return false;
|
|
return true;
|
|
});
|
|
|
|
return active.map(ac => {
|
|
const flightPlan = FLIGHT_PLANS[ac.icao24];
|
|
|
|
if (flightPlan) {
|
|
// Waypoint-based interpolation
|
|
const pos = interpWaypoints(flightPlan, hoursFromT0);
|
|
const trail = generateTrail(flightPlan, hoursFromT0, 5, 10); // 5 points, 10min intervals
|
|
|
|
return {
|
|
...ac,
|
|
lat: pos.lat,
|
|
lng: pos.lng,
|
|
heading: pos.heading,
|
|
trail,
|
|
};
|
|
}
|
|
|
|
// No flight plan - keep static position (for API-sourced aircraft)
|
|
return ac;
|
|
});
|
|
}
|
|
|
|
// ── Ship Waypoints ───────────────────────────────────
|
|
|
|
const SHIP_PLANS: Record<string, WP[]> = {
|
|
// USS Abraham Lincoln CSG - Arabian Sea patrol
|
|
'369970072': [
|
|
[-12, 23.0, 62.0], [-8, 23.5, 61.0], [-4, 23.0, 62.0], [0, 23.5, 61.5],
|
|
[4, 23.0, 62.0], [8, 23.5, 61.0], [12, 23.0, 62.0],
|
|
],
|
|
// USS Frank E. Petersen Jr DDG-121 - escort ahead
|
|
'369970121': [
|
|
[-12, 23.3, 61.8], [-8, 23.8, 60.8], [-4, 23.3, 61.8], [0, 23.8, 61.3],
|
|
[4, 23.3, 61.8], [8, 23.8, 60.8], [12, 23.3, 61.8],
|
|
],
|
|
// USS Spruance DDG-111 - screen
|
|
'369970111': [
|
|
[-12, 22.7, 62.3], [-8, 23.2, 61.3], [-4, 22.7, 62.3], [0, 23.2, 61.8],
|
|
[4, 22.7, 62.3], [8, 23.2, 61.3], [12, 22.7, 62.3],
|
|
],
|
|
// USS Michael Murphy DDG-112 - screen
|
|
'369970112': [
|
|
[-12, 23.5, 62.5], [-8, 24.0, 61.5], [-4, 23.5, 62.5], [0, 24.0, 62.0],
|
|
[4, 23.5, 62.5], [8, 24.0, 61.5], [12, 23.5, 62.5],
|
|
],
|
|
// USS Gerald R. Ford CVN-78 - Red Sea
|
|
'369970078': [
|
|
[-12, 18.5, 39.0], [-8, 19.0, 38.5], [-4, 18.5, 39.0], [0, 19.0, 38.5],
|
|
[4, 18.5, 39.0], [8, 19.0, 38.5], [12, 18.5, 39.0],
|
|
],
|
|
// USS McFaul DDG-74 - Arabian Sea
|
|
'369970074': [
|
|
[-12, 22.0, 60.0], [-8, 22.5, 59.5], [-4, 22.0, 60.0], [0, 22.5, 59.5],
|
|
[4, 22.0, 60.0], [8, 22.5, 59.5], [12, 22.0, 60.0],
|
|
],
|
|
// USS John Finn DDG-113 - Arabian Sea
|
|
'369970113': [
|
|
[-12, 24.0, 59.0], [-8, 24.5, 58.5], [-4, 24.0, 59.0], [0, 24.5, 58.5],
|
|
[4, 24.0, 59.0], [8, 24.5, 58.5], [12, 24.0, 59.0],
|
|
],
|
|
// USS Milius DDG-69 - Northern Arabian Sea
|
|
'369970069': [
|
|
[-12, 25.0, 58.0], [-8, 25.5, 57.5], [-4, 25.0, 58.0], [0, 25.5, 57.5],
|
|
[4, 25.0, 58.0], [8, 25.5, 57.5], [12, 25.0, 58.0],
|
|
],
|
|
// USS Delbert D. Black DDG-119 - Aegis BMD station
|
|
'369970119': [
|
|
[-12, 26.0, 57.0], [-8, 26.5, 56.5], [-4, 26.0, 57.0], [0, 26.5, 56.5],
|
|
[4, 26.0, 57.0], [8, 26.5, 56.5], [12, 26.0, 57.0],
|
|
],
|
|
// USS Pinckney DDG-91 - Arabian Sea patrol
|
|
'369970091': [
|
|
[-12, 21.0, 61.0], [-8, 21.5, 60.5], [-4, 21.0, 61.0], [0, 21.5, 60.5],
|
|
[4, 21.0, 61.0], [8, 21.5, 60.5], [12, 21.0, 61.0],
|
|
],
|
|
// USS Mitscher DDG-57 - Aegis BMD station
|
|
'369970057': [
|
|
[-12, 25.5, 59.0], [-8, 26.0, 58.5], [-4, 25.5, 59.0], [0, 26.0, 58.5],
|
|
[4, 25.5, 59.0], [8, 26.0, 58.5], [12, 25.5, 59.0],
|
|
],
|
|
// USS Canberra LCS-30 - Persian Gulf patrol (central Gulf, away from Qatar coast)
|
|
'369970030': [
|
|
[-12, 27.2, 51.5], [-8, 27.5, 51.0], [-4, 27.2, 51.5], [0, 27.5, 51.0],
|
|
[4, 27.2, 51.5], [8, 27.5, 51.0], [12, 27.2, 51.5],
|
|
],
|
|
// USS Tulsa LCS-16 - Persian Gulf patrol (east of Qatar, open water)
|
|
'369970016': [
|
|
[-12, 26.2, 53.0], [-8, 26.5, 52.5], [-4, 26.2, 53.0], [0, 26.5, 52.5],
|
|
[4, 26.2, 53.0], [8, 26.5, 52.5], [12, 26.2, 53.0],
|
|
],
|
|
// HMS Diamond D34 - re-deployed to Gulf
|
|
'232001034': [
|
|
[-6, 25.0, 57.0], [-3, 25.5, 56.5], [0, 26.0, 56.5], [3, 26.2, 56.0],
|
|
[6, 25.8, 56.5], [9, 26.2, 56.0], [12, 25.8, 56.5],
|
|
],
|
|
// HMS Middleton M34 - minesweeper, leaving early (Gulf of Oman → Arabian Sea)
|
|
'232001041': [
|
|
[-12, 26.0, 56.8], [-8, 25.5, 57.0], [-4, 25.0, 57.5], [0, 24.5, 58.0],
|
|
],
|
|
// FS Languedoc D653 - off Cyprus
|
|
'227001653': [
|
|
[-8, 34.8, 33.0], [-4, 34.5, 33.5], [0, 34.2, 33.0], [4, 34.5, 33.5],
|
|
[8, 34.2, 33.0], [12, 34.5, 33.5],
|
|
],
|
|
// FS Charles de Gaulle R91 - 크레타 → 동부 지중해 진입 (T0+4h~)
|
|
'227001091': [
|
|
[4, 35.0, 28.0], [6, 34.8, 29.5], [8, 34.5, 31.0], [10, 34.2, 32.5],
|
|
[12, 34.0, 33.5],
|
|
],
|
|
// IRGCN Fast Attack 1 - Strait of Hormuz
|
|
'422001001': [
|
|
[-10, 26.2, 54.5], [-6, 26.5, 54.0], [-3, 26.8, 53.5], [0, 26.5, 54.0],
|
|
[2, 26.8, 54.5], [4, 27.0, 55.0],
|
|
],
|
|
// IRGCN Fast Attack 2
|
|
'422001002': [
|
|
[-10, 27.0, 54.0], [-6, 26.8, 53.5], [-3, 26.5, 53.0], [0, 26.8, 53.5],
|
|
[2, 27.0, 53.8],
|
|
],
|
|
// IRGCN Fast Attack 3
|
|
'422001003': [
|
|
[-10, 26.0, 55.0], [-6, 26.3, 54.5], [-3, 26.6, 54.0], [0, 26.3, 54.5],
|
|
[2, 26.0, 55.0],
|
|
],
|
|
// IRIS Dena F75 - 호르무즈 해협 인근 이란 해군 초계
|
|
'422010001': [
|
|
[-12, 25.8, 57.2], [-8, 25.5, 57.5], [-4, 25.2, 57.8], [0, 25.5, 58.2],
|
|
[4, 25.8, 57.8], [8, 26.0, 57.5], [12, 25.5, 57.2],
|
|
],
|
|
// Eagle Vellore - Korean VLCC escaping Hormuz before blockade
|
|
// Fixed: start from Al Basra oil terminal (sea), route through Persian Gulf → Hormuz → Arabian Sea
|
|
'440107001': [
|
|
[-6, 29.8, 48.8], [-5, 28.5, 49.5], [-4, 27.2, 51.5], [-3, 26.8, 53.0],
|
|
[-2, 26.5, 55.0], [-1, 26.0, 56.3], [0, 25.5, 57.5],
|
|
[2, 25.0, 58.5], [4, 24.0, 60.0], [8, 22.0, 62.0], [12, 20.0, 64.0],
|
|
],
|
|
// ROKS Choi Young DDH-981 - Cheonghae Unit, Hormuz area
|
|
'440001981': [
|
|
[-12, 25.5, 57.0], [-8, 26.0, 56.5], [-4, 25.5, 57.0], [0, 26.0, 56.5],
|
|
[4, 25.5, 57.0], [8, 26.0, 56.5], [12, 25.5, 57.0],
|
|
],
|
|
};
|
|
|
|
// ── Ship Propagation ─────────────────────────────────
|
|
|
|
export function propagateShips(
|
|
baseShips: Ship[],
|
|
currentTime: number,
|
|
isLive = false,
|
|
): Ship[] {
|
|
const hoursFromT0 = (currentTime - T0) / HOUR;
|
|
|
|
const active = baseShips.filter(ship => {
|
|
// In live mode, skip time-window filtering — show all ships from API
|
|
if (isLive) return true;
|
|
if (ship.activeStart != null && currentTime < ship.activeStart) return false;
|
|
if (ship.activeEnd != null && currentTime > ship.activeEnd) return false;
|
|
return true;
|
|
});
|
|
|
|
return active.map(ship => {
|
|
const plan = SHIP_PLANS[ship.mmsi];
|
|
|
|
if (plan) {
|
|
const pos = interpWaypoints(plan, hoursFromT0);
|
|
const trail = generateTrail(plan, hoursFromT0, 4, 30); // 4 points, 30min intervals
|
|
|
|
return {
|
|
...ship,
|
|
lat: pos.lat,
|
|
lng: pos.lng,
|
|
heading: pos.heading,
|
|
trail,
|
|
};
|
|
}
|
|
|
|
return ship;
|
|
});
|
|
}
|