causalis-backend / backend /src /world_model.py
volfiros
Initial deploy
3daa112
Raw History Blame Contribute Delete
3.73 kB
import json
from pathlib import Path
import geopandas as gpd
import networkx as nx
import pandas as pd
from shapely.geometry import Point, mapping
from src.data_loader import load_ports, load_chokepoints, load_routes, load_vessels
class MaritimeWorldModel:
def __init__(self):
self._ports = load_ports()
self._chokepoints = load_chokepoints()
self._routes = load_routes()
self._vessels = load_vessels()
self._graph = self._build_graph()
def _build_graph(self) -> nx.Graph:
graph = nx.Graph()
for _, port in self._ports.iterrows():
graph.add_node(port["id"], name=port["name"], latitude=port["latitude"], longitude=port["longitude"])
for _, route in self._routes.iterrows():
graph.add_edge(
route["origin_port_id"],
route["destination_port_id"],
weight=route["distance_nm"],
route_id=route["id"],
route_name=route["name"],
transit_days=route["transit_days"],
chokepoints_transited=route["chokepoints_transited"],
)
return graph
def get_port_graph(self) -> nx.Graph:
return self._graph
def get_chokepoints(self) -> gpd.GeoDataFrame:
return self._chokepoints
def get_routes(self) -> pd.DataFrame:
return self._routes
def get_connectivity(self, port_id: str) -> list:
if port_id not in self._graph:
return []
return list(self._graph.neighbors(port_id))
def get_chokepoints_in_region(self, min_lat: float, max_lat: float, min_lon: float, max_lon: float) -> gpd.GeoDataFrame:
centroids = self._chokepoints.geometry.centroid
mask = (
(centroids.y >= min_lat) & (centroids.y <= max_lat) &
(centroids.x >= min_lon) & (centroids.x <= max_lon)
)
return self._chokepoints[mask].copy()
def get_shortest_path(self, origin_id: str, destination_id: str) -> list:
if origin_id == destination_id:
return [origin_id]
try:
return nx.shortest_path(self._graph, origin_id, destination_id, weight="weight")
except nx.NetworkXNoPath:
return []
def get_path_distance(self, origin_id: str, destination_id: str) -> float:
path = self.get_shortest_path(origin_id, destination_id)
if len(path) < 2:
return 0.0
total = 0.0
for i in range(len(path) - 1):
edge_data = self._graph.edges[path[i], path[i + 1]]
total += edge_data["weight"]
return total
def to_geojson(self, entity_type: str) -> dict:
if entity_type == "ports":
features = []
for _, port in self._ports.iterrows():
feature = {
"type": "Feature",
"properties": {
"id": port["id"],
"name": port["name"],
"country": port["country"],
"type": port["type"],
"annual_teu": int(port["annual_teu"]),
"max_draft_meters": float(port["max_draft_meters"]),
"typical_dwell_hours": int(port["typical_dwell_hours"]),
},
"geometry": mapping(Point(port["longitude"], port["latitude"])),
}
features.append(feature)
return {"type": "FeatureCollection", "features": features}
elif entity_type == "chokepoints":
return json.loads(self._chokepoints.to_json())
else:
raise ValueError(f"Unknown entity type: {entity_type}")