diff --git a/backend/pom.xml b/backend/pom.xml index e7a3698..18354c4 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -19,7 +19,7 @@ KCG Monitoring Dashboard Backend - 21 + 17 0.12.6 diff --git a/backend/src/main/java/gc/mda/kcg/collector/aircraft/AirplanesLiveCollector.java b/backend/src/main/java/gc/mda/kcg/collector/aircraft/AirplanesLiveCollector.java index bf3c4c2..1e36ac9 100644 --- a/backend/src/main/java/gc/mda/kcg/collector/aircraft/AirplanesLiveCollector.java +++ b/backend/src/main/java/gc/mda/kcg/collector/aircraft/AirplanesLiveCollector.java @@ -86,7 +86,7 @@ public class AirplanesLiveCollector { @PostConstruct public void init() { - Thread.ofVirtual().name("aircraft-init").start(() -> { + new Thread(() -> { doInitialLoad("iran", IRAN_QUERIES, iranRegionBuffers); iranInitDone = true; mergePointResults("iran", iranRegionBuffers); @@ -96,7 +96,7 @@ public class AirplanesLiveCollector { koreaInitDone = true; mergePointResults("korea", koreaRegionBuffers); log.info("Airplanes.live 한국 초기 로드 완료"); - }); + }, "aircraft-init").start(); } private void doInitialLoad(String region, List queries, Map> buffers) { diff --git a/backend/src/main/java/gc/mda/kcg/collector/osint/OsintCollector.java b/backend/src/main/java/gc/mda/kcg/collector/osint/OsintCollector.java index e58b2b9..c61adad 100644 --- a/backend/src/main/java/gc/mda/kcg/collector/osint/OsintCollector.java +++ b/backend/src/main/java/gc/mda/kcg/collector/osint/OsintCollector.java @@ -58,12 +58,12 @@ public class OsintCollector { @PostConstruct public void init() { - Thread.ofVirtual().name("osint-init").start(() -> { + new Thread(() -> { log.info("OSINT 초기 캐시 로드 시작"); refreshCache("iran"); refreshCache("korea"); log.info("OSINT 초기 캐시 로드 완료"); - }); + }, "osint-init").start(); } @Scheduled(initialDelay = 30_000, fixedDelay = 10_000) diff --git a/backend/src/main/java/gc/mda/kcg/collector/satellite/SatelliteCollector.java b/backend/src/main/java/gc/mda/kcg/collector/satellite/SatelliteCollector.java index 9acdcac..1ce1bdd 100644 --- a/backend/src/main/java/gc/mda/kcg/collector/satellite/SatelliteCollector.java +++ b/backend/src/main/java/gc/mda/kcg/collector/satellite/SatelliteCollector.java @@ -49,11 +49,11 @@ public class SatelliteCollector { @PostConstruct public void init() { - Thread.ofVirtual().name("satellite-init").start(() -> { + new Thread(() -> { log.info("위성 TLE 초기 캐시 로드 시작"); loadCacheFromDb(); log.info("위성 TLE 초기 캐시 로드 완료"); - }); + }, "satellite-init").start(); } @Scheduled(initialDelay = 60_000, fixedDelay = 600_000) diff --git a/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java b/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java index a95e121..9da360c 100644 --- a/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java +++ b/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java @@ -38,10 +38,10 @@ public class PressureCollector { @PostConstruct public void init() { - Thread.ofVirtual().name("pressure-init").start(() -> { + new Thread(() -> { log.info("Open-Meteo 기압 데이터 초기 로드"); collect(); - }); + }, "pressure-init").start(); } @Scheduled(initialDelay = 45_000, fixedDelay = 600_000) diff --git a/backend/src/main/java/gc/mda/kcg/collector/sensor/SeismicCollector.java b/backend/src/main/java/gc/mda/kcg/collector/sensor/SeismicCollector.java index f734cf0..7d3d1e8 100644 --- a/backend/src/main/java/gc/mda/kcg/collector/sensor/SeismicCollector.java +++ b/backend/src/main/java/gc/mda/kcg/collector/sensor/SeismicCollector.java @@ -31,10 +31,10 @@ public class SeismicCollector { @PostConstruct public void init() { - Thread.ofVirtual().name("seismic-init").start(() -> { + new Thread(() -> { log.info("USGS 지진 데이터 초기 로드"); collect(); - }); + }, "seismic-init").start(); } @Scheduled(initialDelay = 60_000, fixedDelay = 300_000) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9239ad7..c589f9d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -22,6 +22,7 @@ import { useTranslation } from 'react-i18next'; import LoginPage from './components/auth/LoginPage'; import CollectorMonitor from './components/common/CollectorMonitor'; import { FieldAnalysisModal } from './components/korea/FieldAnalysisModal'; +import { filterFacilities } from './data/meEnergyHazardFacilities'; import './App.css'; function App() { @@ -66,7 +67,7 @@ function AuthenticatedApp({ user, onLogout }: AuthenticatedAppProps) { meFacilities: true, militaryOnly: false, overseasUS: false, - overseasUK: false, + overseasIsrael: false, overseasIran: false, overseasUAE: false, overseasSaudi: false, @@ -220,8 +221,8 @@ function AuthenticatedApp({ user, onLogout }: AuthenticatedAppProps) { currentTime, ); - const toggleLayer = useCallback((key: keyof LayerVisibility) => { - setLayers(prev => ({ ...prev, [key]: !prev[key] })); + const toggleLayer = useCallback((key: string) => { + setLayers(prev => ({ ...prev, [key]: !prev[key as keyof typeof prev] })); }, []); // Handle event card click from timeline: fly to location on map @@ -481,18 +482,45 @@ function AuthenticatedApp({ user, onLogout }: AuthenticatedAppProps) { { key: 'meFacilities', label: '주요시설/군사', color: '#ef4444', count: 35 }, { key: 'sensorCharts', label: t('layers.sensorCharts'), color: '#22c55e' }, ]} - overseasItems={[ - { key: 'overseasUS', label: '🇺🇸 미국', color: '#3b82f6' }, - { key: 'overseasUK', label: '🇬🇧 영국', color: '#dc2626' }, - { key: 'overseasIran', label: '🇮🇷 이란', color: '#22c55e' }, - { key: 'overseasUAE', label: '🇦🇪 UAE', color: '#f59e0b' }, - { key: 'overseasSaudi', label: '🇸🇦 사우디아라비아', color: '#84cc16' }, - { key: 'overseasOman', label: '🇴🇲 오만', color: '#e11d48' }, - { key: 'overseasQatar', label: '🇶🇦 카타르', color: '#8b5cf6' }, - { key: 'overseasKuwait', label: '🇰🇼 쿠웨이트', color: '#f97316' }, - { key: 'overseasIraq', label: '🇮🇶 이라크', color: '#65a30d' }, - { key: 'overseasBahrain', label: '🇧🇭 바레인', color: '#e11d48' }, - ]} + overseasItems={(() => { + const fc = (ck: string, st?: string) => filterFacilities(ck, st as never).length; + const energyChildren = (ck: string) => [ + { key: `${ck}Power`, label: '발전소', color: '#a855f7', count: fc(ck, 'power') }, + { key: `${ck}Wind`, label: '풍력단지', color: '#22d3ee', count: fc(ck, 'wind') }, + { key: `${ck}Nuclear`, label: '원자력발전소', color: '#f59e0b', count: fc(ck, 'nuclear') }, + { key: `${ck}Thermal`, label: '화력발전소', color: '#64748b', count: fc(ck, 'thermal') }, + ]; + const hazardChildren = (ck: string) => [ + { key: `${ck}Petrochem`, label: '석유화학단지', color: '#f97316', count: fc(ck, 'petrochem') }, + { key: `${ck}Lng`, label: 'LNG저장기지', color: '#0ea5e9', count: fc(ck, 'lng') }, + { key: `${ck}OilTank`, label: '유류저장탱크', color: '#eab308', count: fc(ck, 'oil_tank') }, + { key: `${ck}HazPort`, label: '위험물항만하역시설', color: '#dc2626', count: fc(ck, 'haz_port') }, + ]; + const fullCountry = (key: string, label: string, color: string, ck: string) => ({ + key, label, color, children: [ + { key: `${ck}Energy`, label: '에너지/발전시설', color: '#a855f7', children: energyChildren(ck) }, + { key: `${ck}Hazard`, label: '위험시설', color: '#ef4444', children: hazardChildren(ck) }, + ], + }); + const compactCountry = (key: string, label: string, color: string, ck: string) => ({ + key, label, color, children: [ + { key: `${ck}Energy`, label: '에너지/발전시설', color: '#a855f7', count: filterFacilities(ck).filter(f => f.category === 'energy').length }, + { key: `${ck}Hazard`, label: '위험시설', color: '#ef4444', count: filterFacilities(ck).filter(f => f.category === 'hazard').length }, + ], + }); + return [ + fullCountry('overseasUS', '🇺🇸 미국', '#3b82f6', 'us'), + fullCountry('overseasIsrael', '🇮🇱 이스라엘', '#0ea5e9', 'il'), + fullCountry('overseasIran', '🇮🇷 이란', '#22c55e', 'ir'), + fullCountry('overseasUAE', '🇦🇪 UAE', '#f59e0b', 'ae'), + fullCountry('overseasSaudi', '🇸🇦 사우디아라비아', '#84cc16', 'sa'), + compactCountry('overseasOman', '🇴🇲 오만', '#e11d48', 'om'), + compactCountry('overseasQatar', '🇶🇦 카타르', '#8b5cf6', 'qa'), + compactCountry('overseasKuwait', '🇰🇼 쿠웨이트', '#f97316', 'kw'), + compactCountry('overseasIraq', '🇮🇶 이라크', '#65a30d', 'iq'), + compactCountry('overseasBahrain', '🇧🇭 바레인', '#e11d48', 'bh'), + ]; + })()} hiddenAcCategories={hiddenAcCategories} hiddenShipCategories={hiddenShipCategories} onAcCategoryToggle={toggleAcCategory} diff --git a/frontend/src/components/common/LayerPanel.tsx b/frontend/src/components/common/LayerPanel.tsx index ee0c1da..e458401 100644 --- a/frontend/src/components/common/LayerPanel.tsx +++ b/frontend/src/components/common/LayerPanel.tsx @@ -124,9 +124,16 @@ interface OverseasItem { key: string; label: string; color: string; + count?: number; children?: OverseasItem[]; } +/** Recursively count leaf nodes (items without children) */ +function countOverseasTree(item: OverseasItem): number { + if (!item.children?.length) return item.count ?? 0; + return item.children.reduce((sum, c) => sum + countOverseasTree(c), 0); +} + interface LayerPanelProps { layers: Record; onToggle: (key: string) => void; @@ -194,6 +201,10 @@ export function LayerPanel({ .filter(([cat]) => cat !== 'civilian' && cat !== 'unknown') .reduce((sum, [, c]) => sum + c, 0); + const overseasTotalCount = overseasItems + ? overseasItems.reduce((sum, item) => sum + countOverseasTree(item), 0) + : 0; + return (

LAYERS

@@ -542,7 +553,7 @@ export function LayerPanel({ {item.children.map(child => ( - onToggle(child.key)} - /> +
+ onToggle(child.key)} + onExpand={child.children?.length ? () => toggleExpand(`overseas-${child.key}`) : undefined} + /> + {child.children?.length && expanded.has(`overseas-${child.key}`) && ( +
+ {child.children.map(gc => ( + onToggle(gc.key)} + /> + ))} +
+ )} +
))}
)} diff --git a/frontend/src/components/iran/MEEnergyHazardLayer.tsx b/frontend/src/components/iran/MEEnergyHazardLayer.tsx new file mode 100644 index 0000000..bf4ca2b --- /dev/null +++ b/frontend/src/components/iran/MEEnergyHazardLayer.tsx @@ -0,0 +1,183 @@ +import { Marker, Popup } from 'react-map-gl/maplibre'; +import { useState, useMemo } from 'react'; +import { + ME_ENERGY_HAZARD_FACILITIES, + SUB_TYPE_META, + layerKeyToSubType, + layerKeyToCountry, + type EnergyHazardFacility, +} from '../../data/meEnergyHazardFacilities'; +import { WindTurbineIcon } from '../korea/WindFarmLayer'; +import type { FacilitySubType } from '../../data/meEnergyHazardFacilities'; + +function FacilityIcon({ subType, color, size = 18 }: { subType: FacilitySubType; color: string; size?: number }) { + const s = size; + switch (subType) { + case 'power': + return ; + case 'nuclear': + return ☢️; + case 'thermal': + return 🏭; + case 'petrochem': // Petrochemical - oil drum with pipe + return ( + + + + + + + + + + ); + case 'lng': // LNG - snowflake/cold tank + return ( + + + + + + + + + ); + case 'oil_tank': // Oil tank - cylinder + return ( + + + + + + + + ); + case 'haz_port': // Hazardous port - warning triangle + return ( + + + + + + ); + default: + return 📍; + } +} + +interface Props { + layers: Record; +} + +export function MEEnergyHazardLayer({ layers }: Props) { + const [selected, setSelected] = useState(null); + + // Collect active country+subType combos from layer keys + const visibleFacilities = useMemo(() => { + const active = new Set(); // "countryKey:subType" + + // Also check parent energy/hazard keys (e.g. omEnergy -> show all om energy) + const energySubTypes = ['power', 'wind', 'nuclear', 'thermal'] as const; + const hazardSubTypes = ['petrochem', 'lng', 'oil_tank', 'haz_port'] as const; + + for (const [key, on] of Object.entries(layers)) { + if (!on) continue; + const ck = layerKeyToCountry(key); + const st = layerKeyToSubType(key); + if (ck && st) { + active.add(`${ck}:${st}`); + } + // Parent energy key (e.g. irEnergy) -> activate all energy subtypes for that country + if (ck && key.endsWith('Energy')) { + for (const s of energySubTypes) active.add(`${ck}:${s}`); + } + if (ck && key.endsWith('Hazard')) { + for (const s of hazardSubTypes) active.add(`${ck}:${s}`); + } + } + + if (active.size === 0) return []; + return ME_ENERGY_HAZARD_FACILITIES.filter(f => + active.has(`${f.countryKey}:${f.subType}`) + ); + }, [layers]); + + if (visibleFacilities.length === 0) return null; + + return ( + <> + {visibleFacilities.map(f => { + const meta = SUB_TYPE_META[f.subType]; + return ( + { e.originalEvent.stopPropagation(); setSelected(f); }} + > +
+ {f.subType === 'wind' ? ( + + ) : ( + + )} +
+
+ ); + })} + + {selected && ( + setSelected(null)} + maxWidth="260px" + className="facility-popup" + > +
+
+ {SUB_TYPE_META[selected.subType].icon} {selected.nameKo} +
+
{selected.name}
+
+ + {SUB_TYPE_META[selected.subType].label} + + {selected.capacityMW && ( + + {selected.capacityMW.toLocaleString()} MW + + )} +
+
{selected.description}
+
+ {selected.lat.toFixed(4)}N, {selected.lng.toFixed(4)}E +
+
+
+ )} + + ); +} diff --git a/frontend/src/components/iran/SatelliteMap.tsx b/frontend/src/components/iran/SatelliteMap.tsx index eb2bc9a..2d570fc 100644 --- a/frontend/src/components/iran/SatelliteMap.tsx +++ b/frontend/src/components/iran/SatelliteMap.tsx @@ -10,6 +10,7 @@ import { SeismicMarker } from '../layers/SeismicMarker'; import { OilFacilityLayer } from './OilFacilityLayer'; import { AirportLayer } from './AirportLayer'; import { MEFacilityLayer } from './MEFacilityLayer'; +import { MEEnergyHazardLayer } from './MEEnergyHazardLayer'; import { iranOilFacilities } from '../../data/oilFacilities'; import { middleEastAirports } from '../../data/airports'; import type { GeoEvent, Aircraft, SatellitePosition, Ship, LayerVisibility } from '../../types'; @@ -273,6 +274,7 @@ export function SatelliteMap({ events, currentTime, aircraft, satellites, ships, {layers.oilFacilities && } {layers.airports && } {layers.meFacilities && } + ); } diff --git a/frontend/src/components/korea/WindFarmLayer.tsx b/frontend/src/components/korea/WindFarmLayer.tsx index d2a0648..b40bbef 100644 --- a/frontend/src/components/korea/WindFarmLayer.tsx +++ b/frontend/src/components/korea/WindFarmLayer.tsx @@ -3,18 +3,22 @@ import { Marker, Popup } from 'react-map-gl/maplibre'; import { KOREA_WIND_FARMS } from '../../data/windFarms'; import type { WindFarm } from '../../data/windFarms'; -const COLOR = '#00bcd4'; +const COLOR = '#0891b2'; -function WindTurbineIcon({ size = 18 }: { size?: number }) { +export function WindTurbineIcon({ size = 20, color }: { size?: number; color?: string }) { + const c = color || COLOR; return ( - - - - - - - - + + + + + + + + + + + ); } @@ -37,7 +41,7 @@ export function WindFarmLayer() { className="flex flex-col items-center cursor-pointer" style={{ filter: `drop-shadow(0 0 3px ${COLOR}88)` }} > - +
setSelected(null)} closeOnClick={false} anchor="bottom" maxWidth="280px" className="gl-popup"> -
-
- 🌀 - {selected.name} +
+ {/* Header - full width */} +
+ + {selected.name}
-
- - {selected.status} - - - 해상풍력 - - - {selected.region} - -
-
-
용량 : {selected.capacityMW} MW
-
터빈 : {selected.turbines}기
- {selected.year &&
준공 : {selected.year}년
} -
지역 : {selected.region}
-
-
- {selected.lat.toFixed(4)}°N, {selected.lng.toFixed(4)}°E + + {/* Body */} +
+ {/* Tags */} +
+ + {selected.status} + + + 해상풍력 + + + {selected.region} + +
+ + {/* Info grid */} +
+
용량 {selected.capacityMW} MW
+
터빈 {selected.turbines}기
+ {selected.year &&
준공 {selected.year}년
} +
지역 {selected.region}
+
+ + {/* Coordinates */} +
+ {selected.lat.toFixed(4)}°N, {selected.lng.toFixed(4)}°E +
diff --git a/frontend/src/data/meEnergyHazardFacilities.ts b/frontend/src/data/meEnergyHazardFacilities.ts new file mode 100644 index 0000000..cb89eee --- /dev/null +++ b/frontend/src/data/meEnergyHazardFacilities.ts @@ -0,0 +1,180 @@ +// Middle East Energy & Hazard Facilities (OSINT + OpenStreetMap) + +export type FacilitySubType = + | 'power' | 'wind' | 'nuclear' | 'thermal' // energy + | 'petrochem' | 'lng' | 'oil_tank' | 'haz_port'; // hazard + +export interface EnergyHazardFacility { + id: string; + name: string; + nameKo: string; + lat: number; + lng: number; + country: string; // ISO-2 + countryKey: string; // overseas layer key prefix (us, il, ir, ae, sa, om, qa, kw, iq, bh) + category: 'energy' | 'hazard'; + subType: FacilitySubType; + capacityMW?: number; + description: string; +} + +export const SUB_TYPE_META: Record = { + power: { label: '발전소', color: '#a855f7', icon: '⚡' }, + wind: { label: '풍력단지', color: '#22d3ee', icon: '🌬' }, + nuclear: { label: '원자력발전소', color: '#f59e0b', icon: '☢' }, + thermal: { label: '화력발전소', color: '#64748b', icon: '🏭' }, + petrochem: { label: '석유화학단지', color: '#f97316', icon: '🛢' }, + lng: { label: 'LNG저장기지', color: '#0ea5e9', icon: '❄' }, + oil_tank: { label: '유류저장탱크', color: '#eab308', icon: '🛢' }, + haz_port: { label: '위험물항만하역시설', color: '#dc2626', icon: '⚠' }, +}; + +// layer key -> subType mapping +export function layerKeyToSubType(key: string): FacilitySubType | null { + if (key.endsWith('Power')) return 'power'; + if (key.endsWith('Wind')) return 'wind'; + if (key.endsWith('Nuclear')) return 'nuclear'; + if (key.endsWith('Thermal')) return 'thermal'; + if (key.endsWith('Petrochem')) return 'petrochem'; + if (key.endsWith('Lng')) return 'lng'; + if (key.endsWith('OilTank')) return 'oil_tank'; + if (key.endsWith('HazPort')) return 'haz_port'; + return null; +} + +export function layerKeyToCountry(key: string): string | null { + const m = key.match(/^(us|il|ir|ae|sa|om|qa|kw|iq|bh)/); + return m ? m[1] : null; +} + +export const ME_ENERGY_HAZARD_FACILITIES: EnergyHazardFacility[] = [ + // ════════════════════════════════════════════ + // 🇺🇸 미국 (중동 주둔 시설 + 에너지 인프라) + // ════════════════════════════════════════════ + { id: 'US-E01', name: 'Al Udeid Power Plant', nameKo: '알우데이드 발전소', lat: 25.1175, lng: 51.3150, country: 'US', countryKey: 'us', category: 'energy', subType: 'power', capacityMW: 200, description: '미군 알우데이드 기지 전용 발전시설' }, + { id: 'US-H01', name: 'Bahrain NAVSUP Fuel Depot', nameKo: '바레인 미해군 유류저장소', lat: 26.2361, lng: 50.6036, country: 'US', countryKey: 'us', category: 'hazard', subType: 'oil_tank', description: 'NSA Bahrain 유류 보급 시설' }, + { id: 'US-H02', name: 'Jebel Ali US Navy Fuel Terminal', nameKo: '제벨알리 미해군 연료터미널', lat: 25.0100, lng: 55.0600, country: 'US', countryKey: 'us', category: 'hazard', subType: 'haz_port', description: '미 제5함대 연료 보급 항만' }, + + // ════════════════════════════════════════════ + // 🇮🇱 이스라엘 + // ════════════════════════════════════════════ + // Energy + { id: 'IL-E01', name: 'Orot Rabin Power Station', nameKo: '오롯 라빈 화력발전소', lat: 32.3915, lng: 34.8610, country: 'IL', countryKey: 'il', category: 'energy', subType: 'thermal', capacityMW: 2590, description: '이스라엘 최대 석탄/가스 복합 발전소 (하데라)' }, + { id: 'IL-E02', name: 'Rutenberg Power Station', nameKo: '루텐베르그 화력발전소', lat: 31.6200, lng: 34.5300, country: 'IL', countryKey: 'il', category: 'energy', subType: 'thermal', capacityMW: 2250, description: '아슈켈론 석탄 화력발전소' }, + { id: 'IL-E03', name: 'Eshkol Power Station', nameKo: '에쉬콜 발전소', lat: 31.7940, lng: 34.6350, country: 'IL', countryKey: 'il', category: 'energy', subType: 'thermal', capacityMW: 1096, description: '아슈도드 해안 천연가스 복합화력 (IEC 운영)' }, + { id: 'IL-E04', name: 'Hagit Power Station', nameKo: '하깃 발전소', lat: 32.5600, lng: 35.0800, country: 'IL', countryKey: 'il', category: 'energy', subType: 'power', capacityMW: 600, description: '북부 가스터빈 발전소' }, + { id: 'IL-E05', name: 'Dimona Nuclear Research Center', nameKo: '디모나 원자력연구센터', lat: 31.0014, lng: 35.1467, country: 'IL', countryKey: 'il', category: 'energy', subType: 'nuclear', description: '네게브 원자력연구시설 (IRR-2)' }, + { id: 'IL-E06', name: 'Ashalim Solar Power Station', nameKo: '아샬림 태양광발전소', lat: 31.1300, lng: 34.6600, country: 'IL', countryKey: 'il', category: 'energy', subType: 'power', capacityMW: 310, description: '네게브 사막 CSP+PV 복합 발전' }, + // Hazard + { id: 'IL-H01', name: 'Haifa Bay Petrochemical Complex', nameKo: '하이파만 석유화학단지', lat: 32.8100, lng: 35.0500, country: 'IL', countryKey: 'il', category: 'hazard', subType: 'petrochem', description: 'Oil Refineries Ltd. + Bazan Group 정유/석유화학 단지' }, + { id: 'IL-H02', name: 'Ashdod Oil Terminal', nameKo: '아시도드 유류터미널', lat: 31.8200, lng: 34.6350, country: 'IL', countryKey: 'il', category: 'hazard', subType: 'oil_tank', description: 'EAPC 원유 수입 터미널 + 저장탱크' }, + { id: 'IL-H03', name: 'Ashkelon Desalination & Energy Hub', nameKo: '아슈켈론 에너지허브', lat: 31.6100, lng: 34.5400, country: 'IL', countryKey: 'il', category: 'hazard', subType: 'haz_port', description: '해수담수화 + LNG 수입 터미널' }, + { id: 'IL-H04', name: 'Eilat-Ashkelon Pipeline Terminal', nameKo: 'EAPC 에일라트 터미널', lat: 29.5500, lng: 34.9500, country: 'IL', countryKey: 'il', category: 'hazard', subType: 'oil_tank', description: '홍해 원유 수입 파이프라인 터미널' }, + + // ════════════════════════════════════════════ + // 🇮🇷 이란 + // ════════════════════════════════════════════ + // Energy + { id: 'IR-E01', name: 'Bushehr Nuclear Power Plant', nameKo: '부셰르 원자력발전소', lat: 28.8267, lng: 50.8867, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'nuclear', capacityMW: 1000, description: '이란 유일 상업 원전 (VVER-1000)' }, + { id: 'IR-E02', name: 'Ramin Thermal Power Plant', nameKo: '라민 화력발전소', lat: 31.3100, lng: 48.7400, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 1890, description: '아바즈 인근 증기 화력발전소' }, + { id: 'IR-E03', name: 'Neka Thermal Power Plant', nameKo: '네카 화력발전소', lat: 36.6500, lng: 53.3300, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 2074, description: '카스피해 연안 최대 화력발전소' }, + { id: 'IR-E04', name: 'Shahid Rajaee Power Plant', nameKo: '샤히드 라자이 발전소', lat: 36.3700, lng: 52.9900, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 1780, description: '가즈빈 복합화력' }, + { id: 'IR-E05', name: 'Isfahan Nuclear Facility (UCF)', nameKo: '이스파한 핵시설 (UCF)', lat: 32.7200, lng: 51.7200, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'nuclear', description: '우라늄전환시설' }, + { id: 'IR-E06', name: 'Natanz Enrichment Facility', nameKo: '나탄즈 우라늄농축시설', lat: 33.7250, lng: 51.7267, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'nuclear', description: '주요 원심분리기 농축시설 (지하)' }, + { id: 'IR-E07', name: 'Manjil-Rudbar Wind Farm', nameKo: '만질-루드바르 풍력단지', lat: 36.7400, lng: 49.4000, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'wind', capacityMW: 91, description: '이란 최대 풍력발전단지' }, + { id: 'IR-E08', name: 'Bandar Abbas Power Plant', nameKo: '반다르아바스 발전소', lat: 27.2000, lng: 56.2500, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 1057, description: '호르무즈해협 연안 발전소' }, + { id: 'IR-E09', name: 'Shahid Montazeri Power Plant', nameKo: '샤히드 몬타제리 발전소', lat: 32.6500, lng: 51.6800, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 1600, description: '이스파한 복합화력 발전소' }, + { id: 'IR-E10', name: 'Shahid Salimi (Neka) Power Plant', nameKo: '샤히드 살리미 발전소', lat: 36.6300, lng: 53.3000, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 2400, description: '마잔다란주 가스복합 발전소' }, + { id: 'IR-E11', name: 'Besat Power Plant', nameKo: '베사트 발전소', lat: 35.8300, lng: 50.9800, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 1000, description: '테헤란 남부 가스터빈 발전소' }, + { id: 'IR-E12', name: 'Parand Power Plant', nameKo: '파란드 복합화력발전소', lat: 35.4700, lng: 51.0100, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 1536, description: '테헤란 서남부 복합화력' }, + { id: 'IR-E13', name: 'Shahid Rajaei Dam & Hydro', nameKo: '샤히드 라자이 수력발전소', lat: 36.1500, lng: 53.2800, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'power', capacityMW: 225, description: '사리 인근 수력발전 댐' }, + { id: 'IR-E14', name: 'Karun-3 Hydropower Plant', nameKo: '카룬-3 수력발전소', lat: 31.9200, lng: 49.8500, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'power', capacityMW: 2000, description: '이란 최대 수력발전소 (후제스탄주)' }, + { id: 'IR-E15', name: 'Dez Dam Hydropower', nameKo: '데즈댐 수력발전소', lat: 32.6100, lng: 48.7700, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'power', capacityMW: 520, description: '후제스탄주 데즈강 수력발전' }, + { id: 'IR-E16', name: 'Tabriz Thermal Power Plant', nameKo: '타브리즈 화력발전소', lat: 38.0600, lng: 46.3200, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'thermal', capacityMW: 1386, description: '동아제르바이잔주 복합화력' }, + { id: 'IR-E17', name: 'Zarand Solar Power Plant', nameKo: '자란드 태양광발전소', lat: 30.8100, lng: 56.5600, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'power', capacityMW: 10, description: '케르만주 태양광 시범단지' }, + { id: 'IR-E18', name: 'Fordow Enrichment Facility', nameKo: '포르도 우라늄농축시설', lat: 34.8800, lng: 51.5800, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'nuclear', description: '지하 우라늄 농축시설 (FFEP, 쿰 인근)' }, + { id: 'IR-E19', name: 'Arak Heavy Water Reactor', nameKo: '아라크 중수로', lat: 34.0400, lng: 49.2400, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'nuclear', description: 'IR-40 중수 연구용 원자로 (마르카지주)' }, + { id: 'IR-E20', name: 'Binaloud Wind Farm', nameKo: '비날루드 풍력단지', lat: 36.2300, lng: 58.6900, country: 'IR', countryKey: 'ir', category: 'energy', subType: 'wind', capacityMW: 28, description: '라자비호라산주 풍력발전' }, + // Hazard + { id: 'IR-H01', name: 'South Pars Gas Complex (Assaluyeh)', nameKo: '사우스파르스 가스단지 (아살루예)', lat: 27.4800, lng: 52.6100, country: 'IR', countryKey: 'ir', category: 'hazard', subType: 'petrochem', description: '세계 최대 가스전 육상 처리시설 (20+ 페이즈)' }, + { id: 'IR-H02', name: 'Kharg Island Oil Terminal', nameKo: '하르그섬 원유터미널', lat: 29.2300, lng: 50.3100, country: 'IR', countryKey: 'ir', category: 'hazard', subType: 'oil_tank', description: '이란 원유 수출의 90% 처리 (저장 2,800만 배럴)' }, + { id: 'IR-H03', name: 'Bandar Imam Khomeini Petrochemical', nameKo: '반다르 이맘호메이니 석유화학', lat: 30.4300, lng: 49.0800, country: 'IR', countryKey: 'ir', category: 'hazard', subType: 'petrochem', description: 'Mahshahr 특별경제구역 석유화학단지' }, + { id: 'IR-H04', name: 'Tombak LNG Terminal', nameKo: '톰박 LNG터미널', lat: 27.5200, lng: 52.5500, country: 'IR', countryKey: 'ir', category: 'hazard', subType: 'lng', description: 'Iran LNG 수출 터미널 (건설중)' }, + { id: 'IR-H05', name: 'Bandar Abbas Oil Refinery', nameKo: '반다르아바스 정유소', lat: 27.2100, lng: 56.2800, country: 'IR', countryKey: 'ir', category: 'hazard', subType: 'petrochem', description: '일 320,000배럴 정유시설' }, + { id: 'IR-H06', name: 'Lavan Island Oil Terminal', nameKo: '라반섬 원유터미널', lat: 26.8100, lng: 53.3600, country: 'IR', countryKey: 'ir', category: 'hazard', subType: 'oil_tank', description: '페르시아만 원유 저장/선적 시설' }, + + // ════════════════════════════════════════════ + // 🇦🇪 UAE + // ════════════════════════════════════════════ + // Energy + { id: 'AE-E01', name: 'Barakah Nuclear Power Plant', nameKo: '바라카 원자력발전소', lat: 23.9592, lng: 52.2567, country: 'AE', countryKey: 'ae', category: 'energy', subType: 'nuclear', capacityMW: 5600, description: '아랍 최초 상업 원전 (APR-1400 x4)' }, + { id: 'AE-E02', name: 'Jebel Ali Power & Desalination', nameKo: '제벨알리 발전/담수', lat: 25.0200, lng: 55.1100, country: 'AE', countryKey: 'ae', category: 'energy', subType: 'thermal', capacityMW: 8695, description: '세계 최대 복합 발전/담수 단지' }, + { id: 'AE-E03', name: 'Shams Solar Power Station', nameKo: '샴스 태양광발전소', lat: 23.5800, lng: 53.7100, country: 'AE', countryKey: 'ae', category: 'energy', subType: 'power', capacityMW: 100, description: '아부다비 CSP 태양열 발전' }, + { id: 'AE-E04', name: 'Hassyan Clean Coal Power Plant', nameKo: '하시안 청정석탄발전소', lat: 24.9600, lng: 55.0300, country: 'AE', countryKey: 'ae', category: 'energy', subType: 'thermal', capacityMW: 2400, description: '두바이 석탄→가스 전환 중' }, + // Hazard + { id: 'AE-H01', name: 'Ruwais Industrial Complex (ADNOC)', nameKo: '루와이스 산업단지 (ADNOC)', lat: 24.1100, lng: 52.7300, country: 'AE', countryKey: 'ae', category: 'hazard', subType: 'petrochem', description: 'ADNOC 정유/석유화학 통합단지 (세계 최대급)' }, + { id: 'AE-H02', name: 'Das Island LNG Terminal', nameKo: '다스섬 LNG터미널', lat: 25.1600, lng: 52.8700, country: 'AE', countryKey: 'ae', category: 'hazard', subType: 'lng', description: 'ADGAS LNG 수출 터미널 (연 570만톤)' }, + { id: 'AE-H03', name: 'Fujairah Oil Terminal (FOSC)', nameKo: '푸자이라 유류터미널', lat: 25.1200, lng: 56.3400, country: 'AE', countryKey: 'ae', category: 'hazard', subType: 'oil_tank', description: '세계 3대 벙커링 허브 (저장 1,400만m3)' }, + { id: 'AE-H04', name: 'Jebel Ali Free Zone Port', nameKo: '제벨알리 자유무역항', lat: 25.0000, lng: 55.0700, country: 'AE', countryKey: 'ae', category: 'hazard', subType: 'haz_port', description: '중동 최대 항만 (위험물 취급)' }, + + // ════════════════════════════════════════════ + // 🇸🇦 사우디아라비아 + // ════════════════════════════════════════════ + // Energy + { id: 'SA-E01', name: 'Shoaiba Power & Desalination', nameKo: '쇼아이바 발전/담수', lat: 20.7000, lng: 39.5100, country: 'SA', countryKey: 'sa', category: 'energy', subType: 'thermal', capacityMW: 5600, description: '홍해 연안 세계 최대급 복합 발전/담수' }, + { id: 'SA-E02', name: 'Rabigh Power Plant', nameKo: '라비그 발전소', lat: 22.8000, lng: 39.0200, country: 'SA', countryKey: 'sa', category: 'energy', subType: 'thermal', capacityMW: 2100, description: '홍해 연안 가스복합 발전소' }, + { id: 'SA-E03', name: 'Dumat Al Jandal Wind Farm', nameKo: '두마트알잔달 풍력단지', lat: 29.8100, lng: 39.8700, country: 'SA', countryKey: 'sa', category: 'energy', subType: 'wind', capacityMW: 400, description: '중동 최대 풍력단지' }, + { id: 'SA-E04', name: 'Jubail IWPP', nameKo: '주바일 발전소', lat: 27.0200, lng: 49.6200, country: 'SA', countryKey: 'sa', category: 'energy', subType: 'thermal', capacityMW: 2745, description: '동부 산업도시 복합 발전' }, + // Hazard + { id: 'SA-H01', name: 'Ras Tanura Oil Terminal', nameKo: '라스타누라 원유터미널', lat: 26.6400, lng: 50.1600, country: 'SA', countryKey: 'sa', category: 'hazard', subType: 'oil_tank', description: '세계 최대 해상 원유 선적 시설 (일 600만 배럴)' }, + { id: 'SA-H02', name: 'Jubail Industrial City (SABIC)', nameKo: '주바일 산업단지 (SABIC)', lat: 27.0000, lng: 49.6500, country: 'SA', countryKey: 'sa', category: 'hazard', subType: 'petrochem', description: '세계 최대 석유화학 산업단지' }, + { id: 'SA-H03', name: 'Yanbu Industrial City', nameKo: '얀부 산업단지', lat: 23.9600, lng: 38.2400, country: 'SA', countryKey: 'sa', category: 'hazard', subType: 'petrochem', description: '홍해 연안 정유/석유화학 단지' }, + { id: 'SA-H04', name: 'Ras Al-Khair LNG Import', nameKo: '라스알카이르 LNG', lat: 27.4800, lng: 49.2600, country: 'SA', countryKey: 'sa', category: 'hazard', subType: 'lng', description: 'LNG 수입/가스화 터미널' }, + { id: 'SA-H05', name: 'Abqaiq Oil Processing', nameKo: '아브카이크 원유처리시설', lat: 25.9400, lng: 49.6800, country: 'SA', countryKey: 'sa', category: 'hazard', subType: 'oil_tank', description: '세계 최대 원유 안정화 시설 (2019 공격 대상)' }, + + // ════════════════════════════════════════════ + // 🇴🇲 오만 + // ════════════════════════════════════════════ + { id: 'OM-E01', name: 'Barka Power & Desalination', nameKo: '바르카 발전/담수', lat: 23.6800, lng: 57.8700, country: 'OM', countryKey: 'om', category: 'energy', subType: 'thermal', capacityMW: 2007, description: 'GDF Suez 운영 복합발전' }, + { id: 'OM-E02', name: 'Dhofar Wind Farm', nameKo: '도파르 풍력단지', lat: 17.0200, lng: 54.1000, country: 'OM', countryKey: 'om', category: 'energy', subType: 'wind', capacityMW: 50, description: 'GCC 최초 대형 풍력단지' }, + { id: 'OM-H01', name: 'Sohar Industrial Port', nameKo: '소하르 산업항', lat: 24.3600, lng: 56.7400, country: 'OM', countryKey: 'om', category: 'hazard', subType: 'petrochem', description: '정유소+석유화학+알루미늄 제련단지' }, + { id: 'OM-H02', name: 'Qalhat LNG Terminal', nameKo: '칼하트 LNG터미널', lat: 22.9200, lng: 59.3700, country: 'OM', countryKey: 'om', category: 'hazard', subType: 'lng', description: 'Oman LNG 수출 (연 1,060만톤)' }, + + // ════════════════════════════════════════════ + // 🇶🇦 카타르 + // ════════════════════════════════════════════ + { id: 'QA-E01', name: 'Ras Laffan Power Plant', nameKo: '라스라판 발전소', lat: 25.9100, lng: 51.5500, country: 'QA', countryKey: 'qa', category: 'energy', subType: 'thermal', capacityMW: 2730, description: '카타르 최대 발전소' }, + { id: 'QA-H01', name: 'Ras Laffan Industrial City', nameKo: '라스라판 산업단지', lat: 25.9200, lng: 51.5300, country: 'QA', countryKey: 'qa', category: 'hazard', subType: 'lng', description: '세계 최대 LNG 수출기지 (QatarEnergy, 연 7,700만톤)' }, + { id: 'QA-H02', name: 'Mesaieed Industrial City', nameKo: '메사이드 산업단지', lat: 24.9900, lng: 51.5600, country: 'QA', countryKey: 'qa', category: 'hazard', subType: 'petrochem', description: 'QatarEnergy 정유/석유화학/비료 단지' }, + { id: 'QA-H03', name: 'Dukhan Oil Field Terminal', nameKo: '두칸 유전터미널', lat: 25.4300, lng: 50.7700, country: 'QA', countryKey: 'qa', category: 'hazard', subType: 'oil_tank', description: '서부 해안 육상 유전 터미널' }, + + // ════════════════════════════════════════════ + // 🇰🇼 쿠웨이트 + // ════════════════════════════════════════════ + { id: 'KW-E01', name: 'Az-Zour Power Plant', nameKo: '아즈주르 발전소', lat: 28.7200, lng: 48.3800, country: 'KW', countryKey: 'kw', category: 'energy', subType: 'thermal', capacityMW: 4800, description: '쿠웨이트 최대 발전/담수' }, + { id: 'KW-H01', name: 'Mina Al Ahmadi Refinery', nameKo: '미나알아흐마디 정유소', lat: 29.0600, lng: 48.1500, country: 'KW', countryKey: 'kw', category: 'hazard', subType: 'petrochem', description: 'KNPC 운영 (일 466,000배럴)' }, + { id: 'KW-H02', name: 'Az-Zour LNG Import Terminal', nameKo: '아즈주르 LNG터미널', lat: 28.7100, lng: 48.3500, country: 'KW', countryKey: 'kw', category: 'hazard', subType: 'lng', description: '쿠웨이트 LNG 수입 터미널' }, + { id: 'KW-H03', name: 'Mina Abdullah Oil Tank Farm', nameKo: '미나압둘라 유류저장기지', lat: 29.0000, lng: 48.1700, country: 'KW', countryKey: 'kw', category: 'hazard', subType: 'oil_tank', description: '남부 원유 저장/선적' }, + + // ════════════════════════════════════════════ + // 🇮🇶 이라크 + // ════════════════════════════════════════════ + { id: 'IQ-E01', name: 'Basra Gas Power Plant', nameKo: '바스라 가스발전소', lat: 30.5100, lng: 47.7800, country: 'IQ', countryKey: 'iq', category: 'energy', subType: 'thermal', capacityMW: 1500, description: '남부 이라크 최대 발전소' }, + { id: 'IQ-H01', name: 'Basra Oil Terminal (ABOT)', nameKo: '알바스라 원유터미널', lat: 29.6800, lng: 48.8000, country: 'IQ', countryKey: 'iq', category: 'hazard', subType: 'oil_tank', description: '이라크 원유 수출의 85% (페르시아만)' }, + { id: 'IQ-H02', name: 'Khor Al-Zubair Port', nameKo: '코르알주바이르 항', lat: 30.1700, lng: 47.8700, country: 'IQ', countryKey: 'iq', category: 'hazard', subType: 'haz_port', description: '이라크 주요 위험물 하역항' }, + { id: 'IQ-H03', name: 'Rumaila Oil Field', nameKo: '루마일라 유전', lat: 30.6300, lng: 47.4300, country: 'IQ', countryKey: 'iq', category: 'hazard', subType: 'oil_tank', description: '이라크 최대 유전 (일 150만 배럴)' }, + + // ════════════════════════════════════════════ + // 🇧🇭 바레인 + // ════════════════════════════════════════════ + { id: 'BH-E01', name: 'Al Dur Power & Water Plant', nameKo: '알두르 발전/담수', lat: 25.9400, lng: 50.6200, country: 'BH', countryKey: 'bh', category: 'energy', subType: 'thermal', capacityMW: 1234, description: '바레인 최대 발전소' }, + { id: 'BH-H01', name: 'Sitra Oil Refinery (BAPCO)', nameKo: '시트라 정유소 (BAPCO)', lat: 26.1500, lng: 50.6100, country: 'BH', countryKey: 'bh', category: 'hazard', subType: 'petrochem', description: '바레인 유일 정유시설 (일 267,000배럴)' }, + { id: 'BH-H02', name: 'Khalifa Bin Salman Port', nameKo: '칼리파빈살만항', lat: 26.0200, lng: 50.5500, country: 'BH', countryKey: 'bh', category: 'hazard', subType: 'haz_port', description: '바레인 주요 무역항 (위험물 하역)' }, +]; + +// Helper: filter by country key and subType +export function filterFacilities(countryKey: string, subType?: FacilitySubType): EnergyHazardFacility[] { + return ME_ENERGY_HAZARD_FACILITIES.filter(f => + f.countryKey === countryKey && (subType ? f.subType === subType : true) + ); +} diff --git a/frontend/src/data/sampleData.ts b/frontend/src/data/sampleData.ts index 44bc8df..1dbbb20 100644 --- a/frontend/src/data/sampleData.ts +++ b/frontend/src/data/sampleData.ts @@ -1429,6 +1429,48 @@ export const sampleEvents: GeoEvent[] = [ label: 'UN 안보리 — 호르무즈 해협 긴급회의 소집', description: 'UN 안보리, 호르무즈 해협 상선 피격 관련 긴급회의 소집. 중국·러시아 즉각 휴전 촉구, 미국 항행자유 강조.', }, + + // ═══ D+20 (2026-03-21) 나탄즈-디모나 핵시설 교차공격 ═══ + { + id: 'd20-il1', timestamp: T0 + 20 * DAY + 4 * HOUR, + lat: 33.7250, lng: 51.7267, type: 'strike', + label: '나탄즈 — 이스라엘 핵시설 공습', + description: 'IAF, 이란 나탄즈 우라늄 농축시설 정밀 타격. 이란 원자력청 "나탄즈 농축시설이 공격 표적이 됐다" 확인. IAEA 방사능 유출 미확인.', + imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Natanz_nuclear.jpg/320px-Natanz_nuclear.jpg', + imageCaption: '나탄즈 핵시설 위성사진 (Wikimedia Commons)', + }, + { + id: 'd20-ir-assess', timestamp: T0 + 20 * DAY + 6 * HOUR, + lat: 33.7250, lng: 51.7267, type: 'alert', + label: '나탄즈 — 이란 방사능 조사 착수', + description: '이란 원자력안전센터, 나탄즈 시설 인근 방사성 오염물질 배출 가능성 정밀 기술 조사. "현재까지 방사성 물질 누출 보고 없음, 인근 주민 위협 없음" 발표.', + }, + { + id: 'd20-ir1', timestamp: T0 + 20 * DAY + 10 * HOUR, + lat: 31.0014, lng: 35.1467, type: 'strike', + label: '디모나 — 이란 보복 미사일 공격', + description: 'IRGC, 나탄즈 피격 보복으로 이스라엘 디모나 핵연구센터 겨냥 탄도미사일 발사. 이스라엘 방공 요격 실패, 최소 30명 이상 사상자 발생. 핵연구센터 직접 피해는 미확인.', + imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Negev_Nuclear_Research_Center.jpg/320px-Negev_Nuclear_Research_Center.jpg', + imageCaption: '디모나 네게브 핵연구센터 (Wikimedia Commons)', + }, + { + id: 'd20-il-def', timestamp: T0 + 20 * DAY + 10.5 * HOUR, + lat: 31.0014, lng: 35.1467, type: 'alert', + label: '디모나 — 요격 실패 조사 착수', + description: '이스라엘군, 이란발 탄도미사일 요격 실패 경위 조사 착수. 요격 미사일이 목표물 격추에 실패, 미사일이 마을에 충돌. 막대한 재산 피해.', + }, + { + id: 'd20-iaea', timestamp: T0 + 20 * DAY + 12 * HOUR, + lat: 48.2082, lng: 16.3738, type: 'alert', + label: 'IAEA — 양측 핵시설 상황 파악 중', + description: 'IAEA, 나탄즈 및 디모나 핵시설 상황 파악 중. 그로시 사무총장 "핵사고 위험 회피 위해 군사행동 자제 거듭 촉구". 양측 시설 모두 비정상 방사능 수치 미감지.', + }, + { + id: 'd20-p1', timestamp: T0 + 20 * DAY + 14 * HOUR, + lat: 38.8977, lng: -77.0365, type: 'alert', + label: '워싱턴 — 미국 핵시설 공격 우려 성명', + description: '미 국무부, 이란의 디모나 공격에 강력 규탄. "핵시설 겨냥 군사행동은 국제법 중대 위반" 경고. 이스라엘 방공체계 지원 강화 발표.', + }, ]; // 24시간 동안 10분 간격 센서 데이터 생성 diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 186af89..fe906ed 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -145,7 +145,7 @@ export interface LayerVisibility { meFacilities: boolean; militaryOnly: boolean; overseasUS: boolean; - overseasUK: boolean; + overseasIsrael: boolean; overseasIran: boolean; overseasUAE: boolean; overseasSaudi: boolean; @@ -154,6 +154,8 @@ export interface LayerVisibility { overseasKuwait: boolean; overseasIraq: boolean; overseasBahrain: boolean; + // Dynamic keys for energy/hazard sub-layers + [key: string]: boolean; } export type AppMode = 'replay' | 'live';