interhack26 / travel_time.py
danvancea's picture
Upload 34 files
4e22ad8 verified
Raw
History Blame Contribute Delete
2.54 kB
"""All-pairs travel-time + distance matrix between snapped OSM road nodes.
Snaps each (lat, lng) to its nearest road node, then runs one Dijkstra per
unique source on the *directed* graph (one-ways respected). The matrix is
asymmetric and dense — every fitness evaluation downstream is now an O(1)
numpy lookup.
"""
from __future__ import annotations
import csv
import json
import time
from dataclasses import dataclass
from pathlib import Path
from dataclasses import dataclass
import networkx as nx
import numpy as np
import osmnx as ox
import pyproj
from graph_manager import DEFAULT_COORDS_PATH, get_or_build_graph
DEFAULT_MATRIX_PATH = Path(__file__).with_name("travel_time.npz")
@dataclass
class TravelMatrix:
point_ids: list[str]
node_ids: list[int]
time_s: np.ndarray
dist_m: np.ndarray
def index_of(self, point_id: str) -> int:
return self.point_ids.index(point_id)
def _snap(graph: nx.MultiDiGraph, points: list[tuple[float, float]]) -> list[int]:
proj = ox.projection.project_graph(graph)
transformer = pyproj.Transformer.from_crs(
graph.graph["crs"], proj.graph["crs"], always_xy=True
)
lats = [p[0] for p in points]
lngs = [p[1] for p in points]
proj_x, proj_y = transformer.transform(lngs, lats)
return list(ox.distance.nearest_nodes(proj, X=list(proj_x), Y=list(proj_y)))
def build_matrix(
graph: nx.MultiDiGraph,
points: list[tuple[str, float, float]],
) -> TravelMatrix:
point_ids = [p[0] for p in points]
node_ids = _snap(graph, [(p[1], p[2]) for p in points])
n = len(points)
time_s = np.full((n, n), np.inf, dtype=np.float32)
dist_m = np.full((n, n), np.inf, dtype=np.float32)
by_node: dict[int, list[int]] = {}
for i, nd in enumerate(node_ids):
by_node.setdefault(nd, []).append(i)
targets = set(node_ids)
for src_node, src_rows in by_node.items():
t_to = nx.single_source_dijkstra_path_length(graph, src_node, weight="travel_time")
d_to = nx.single_source_dijkstra_path_length(graph, src_node, weight="length")
for tgt in targets:
t = t_to.get(tgt, np.inf)
d = d_to.get(tgt, np.inf)
for j in by_node[tgt]:
for i in src_rows:
time_s[i, j] = t
dist_m[i, j] = d
np.fill_diagonal(time_s, 0.0)
np.fill_diagonal(dist_m, 0.0)
return TravelMatrix(point_ids, node_ids, time_s, dist_m)