Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions infra_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class InfrastructureAsset:
operator: Optional[str] = None
capacity: Optional[str] = None # e.g., "1.2 Tbps" or "650 MW"
notes: str = ""
region: Optional[str] = None # e.g., "baltic_sea", "trans_pacific"

def get_nearest_point(self, lat: float, lon: float) -> Tuple[float, float, float]:
"""
Expand Down Expand Up @@ -175,7 +176,8 @@ def load_infrastructure_from_json(filepath: str = None) -> List['InfrastructureA
protection_radius_nm=cable.get('protection_radius_nm', 3.0),
operator=cable.get('operator'),
capacity=cable.get('capacity'),
notes=cable.get('notes', '')
notes=cable.get('notes', ''),
region=cable.get('region', 'unknown')
))

# Load pipelines
Expand All @@ -191,7 +193,8 @@ def load_infrastructure_from_json(filepath: str = None) -> List['InfrastructureA
protection_radius_nm=pipeline.get('protection_radius_nm', 5.0),
operator=pipeline.get('operator'),
capacity=pipeline.get('capacity'),
notes=pipeline.get('notes', '')
notes=pipeline.get('notes', ''),
region=pipeline.get('region', 'unknown')
))

# Load wind farms (as point assets)
Expand All @@ -205,7 +208,8 @@ def load_infrastructure_from_json(filepath: str = None) -> List['InfrastructureA
protection_radius_nm=farm.get('protection_radius_nm', 2.0),
operator=farm.get('operator'),
capacity=f"{farm.get('capacity_mw', 0)} MW ({farm.get('turbines', 0)} turbines)",
notes=farm.get('notes', '')
notes=farm.get('notes', ''),
region=farm.get('region', 'unknown')
))

print(f"Loaded {len(assets)} infrastructure assets from {filepath}")
Expand Down Expand Up @@ -952,7 +956,12 @@ def _parse_timestamp(ts) -> datetime:
# =============================================================================

def get_baltic_infrastructure() -> List[dict]:
"""Get list of all infrastructure for map display."""
"""Get list of all infrastructure for map display (legacy name, returns global)."""
return get_global_infrastructure()


def get_global_infrastructure() -> List[dict]:
"""Get list of all global infrastructure for map display."""
return [
{
"name": asset.name,
Expand All @@ -963,7 +972,8 @@ def get_baltic_infrastructure() -> List[dict]:
"protection_radius_nm": asset.protection_radius_nm,
"operator": asset.operator,
"capacity": asset.capacity,
"notes": asset.notes
"notes": asset.notes,
"region": asset.region
}
for asset in get_all_infrastructure()
]
Expand Down
27 changes: 22 additions & 5 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@
# Import infrastructure threat analysis module
try:
from infra_analysis import (
get_baltic_infrastructure, analyze_vessel_for_incident,
analyze_infrastructure_incident, BALTIC_INFRASTRUCTURE
get_baltic_infrastructure, get_global_infrastructure,
analyze_vessel_for_incident, analyze_infrastructure_incident,
BALTIC_INFRASTRUCTURE
)
INFRA_ANALYSIS_AVAILABLE = True
except ImportError:
Expand Down Expand Up @@ -1838,15 +1839,31 @@ def do_GET(self):

# ========== Infrastructure Analysis Endpoints ==========

elif path == '/api/infrastructure' or path == '/api/infrastructure/all':
# Get all global undersea infrastructure for map overlay
if not INFRA_ANALYSIS_AVAILABLE:
return self.send_json({'error': 'Infrastructure analysis module not available'}, 500)
infra = get_global_infrastructure()
# Group by region for stats
regions = {}
for item in infra:
r = item.get('region', 'unknown')
regions[r] = regions.get(r, 0) + 1
return self.send_json({
'infrastructure': infra,
'count': len(infra),
'regions': regions
}, cache_seconds=3600) # Cache for 1 hour

elif path == '/api/infrastructure/baltic':
# Get Baltic Sea undersea infrastructure for map overlay
# Legacy endpoint - now returns all infrastructure
if not INFRA_ANALYSIS_AVAILABLE:
return self.send_json({'error': 'Infrastructure analysis module not available'}, 500)
infra = get_baltic_infrastructure()
infra = get_global_infrastructure()
return self.send_json({
'infrastructure': infra,
'count': len(infra),
'region': 'Baltic Sea'
'region': 'Global' # Updated from 'Baltic Sea'
}, cache_seconds=3600) # Cache for 1 hour

elif path.startswith('/api/vessels/') and path.endswith('/infra-analysis'):
Expand Down
19 changes: 14 additions & 5 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -750,12 +750,16 @@
<div style="width:20px;height:3px;background:#e67e22;margin-right:8px;border-style:dashed"></div>
<span>Gas Pipeline</span>
</div>
<div style="display:flex;align-items:center;margin-bottom:6px">
<div style="width:20px;height:3px;background:#c0392b;margin-right:8px;border-style:dashed"></div>
<span>Oil Pipeline</span>
</div>
<div style="display:flex;align-items:center;margin-bottom:6px">
<div style="width:12px;height:12px;border-radius:50%;background:#f1c40f33;border:1px dashed #f1c40f;margin-right:8px"></div>
<span>Protection Zone</span>
</div>
<div style="margin-top:8px;padding-top:8px;border-top:1px solid #eee;color:#666;font-size:10px">
<span id="infra-count">0</span> assets loaded<br>
<span id="infra-count">0</span> assets • <span id="infra-regions">0</span> regions<br>
Click cable for details
</div>
</div>
Expand Down Expand Up @@ -3358,19 +3362,22 @@
}

// ========== Infrastructure Analysis ==========
// Baltic Sea undersea cables and pipelines overlay
// Global undersea cables and pipelines overlay

async function loadBalticInfrastructure() {
try {
const result = await api('/infrastructure/baltic');
const result = await api('/infrastructure');
balticInfrastructure = result.infrastructure || [];
renderInfrastructure();
console.log(`Loaded ${balticInfrastructure.length} infrastructure assets`);
console.log(`Loaded ${balticInfrastructure.length} infrastructure assets from ${Object.keys(result.regions || {}).length} regions`);

// Update legend count and show it
// Update legend counts
const countEl = document.getElementById('infra-count');
if (countEl) countEl.textContent = balticInfrastructure.length;

const regionsEl = document.getElementById('infra-regions');
if (regionsEl) regionsEl.textContent = Object.keys(result.regions || {}).length;

const legend = document.getElementById('infra-legend');
if (legend && showInfrastructure && balticInfrastructure.length > 0) {
legend.style.display = 'block';
Expand Down Expand Up @@ -3411,10 +3418,12 @@
opacity: 0.7,
dashArray: asset.type.includes('cable') ? null : '10, 5'
});
const regionName = (asset.region || 'unknown').replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
line.bindPopup(`
<div style="min-width:200px">
<h4 style="margin:0 0 8px 0;color:${color}">${asset.name}</h4>
<div style="font-size:12px;color:#666">${asset.type.replace('_', ' ').toUpperCase()}</div>
<div style="font-size:10px;color:#999;margin-top:2px">Region: ${regionName}</div>
${asset.operator ? `<div style="font-size:11px;margin-top:4px"><b>Operator:</b> ${asset.operator}</div>` : ''}
${asset.capacity ? `<div style="font-size:11px"><b>Capacity:</b> ${asset.capacity}</div>` : ''}
${asset.notes ? `<div style="font-size:10px;margin-top:6px;color:#888">${asset.notes}</div>` : ''}
Expand Down