Spaces:
Sleeping
Sleeping
| """Trace each van's route along actual OSM roads, returning a polyline. | |
| Used to render routes on the frontend map as curves following the streets, | |
| not as straight chord lines between stops. | |
| """ | |
| from __future__ import annotations | |
| import networkx as nx | |
| from travel_time import TravelMatrix | |
| def _edge_coords(graph: nx.MultiDiGraph, u: int, v: int) -> list[tuple[float, float]]: | |
| """Coords along the (u, v) edge. Uses curve geometry when present.""" | |
| data = graph.get_edge_data(u, v) | |
| if data: | |
| first = next(iter(data.values())) | |
| geom = first.get("geometry") | |
| if geom is not None: | |
| xs, ys = geom.xy | |
| return [(float(y), float(x)) for x, y in zip(xs, ys)] | |
| return [ | |
| (float(graph.nodes[u]["y"]), float(graph.nodes[u]["x"])), | |
| (float(graph.nodes[v]["y"]), float(graph.nodes[v]["x"])), | |
| ] | |
| def route_polyline( | |
| graph: nx.MultiDiGraph, | |
| matrix: TravelMatrix, | |
| stop_sequence: list[str], | |
| ) -> list[tuple[float, float]]: | |
| """Concatenate OSM-traced legs into one (lat, lng) polyline. | |
| `stop_sequence` typically starts and ends with "DEPOT", e.g. | |
| ["DEPOT", "S005", "S007", "DEPOT"]. | |
| """ | |
| out: list[tuple[float, float]] = [] | |
| for src_id, dst_id in zip(stop_sequence, stop_sequence[1:]): | |
| src_node = matrix.node_ids[matrix.index_of(src_id)] | |
| dst_node = matrix.node_ids[matrix.index_of(dst_id)] | |
| try: | |
| path = nx.shortest_path(graph, src_node, dst_node, weight="travel_time") | |
| except nx.NetworkXNoPath: | |
| continue | |
| for u, v in zip(path, path[1:]): | |
| seg = _edge_coords(graph, u, v) | |
| # Avoid duplicating the joint node at the boundary between legs. | |
| if out and seg and out[-1] == seg[0]: | |
| out.extend(seg[1:]) | |
| else: | |
| out.extend(seg) | |
| return out | |