79ac9dad84
- Logistik: Musik-Player Playlist-Modus (5 Gruppen, 20 Tracks) - Heli: End-Screen auf .ggs-endscreen, Mission-Bilder, Voice-Lines, Briefing-Audio - Logistik: Layout-Tausch Auftraege+Fahrzeuge links, Karte rechts - Logistik: Tier-1-Staedte ausgebaut, Auto-Timescale-Badge - Logistik: Roadnet/Railnet Dijkstra-Routing, Balance-Updates - Atlas: 5 Atlas-Inbox-Nachrichten (Cards-Pattern, DALL-E-Key, Musik-Playlists) - Status-Updates Heli + Logistik Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
114 lines
4.1 KiB
Python
114 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Erweitert lg-roadnet.json um innsbruck und graz als Knoten. Neue Kanten:
|
|
- muenchen-innsbruck (A8/A93, ~170 km)
|
|
- innsbruck-salzburg (A12/A10 ueber Loferer Hochtal, ~190 km)
|
|
- innsbruck-mailand (A13+A22 Brenner, ~390 km)
|
|
- wien-graz (A2 Sued, ~200 km)
|
|
- salzburg-graz (A10+A9 via Tauerntunnel+Liezen, ~280 km)
|
|
|
|
Polylines per OSRM holen + Douglas-Peucker mit ~110 m Toleranz.
|
|
"""
|
|
import json
|
|
import math
|
|
import ssl
|
|
import time
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
ROADNET = Path(__file__).resolve().parents[3] / "assets" / "data" / "lg-roadnet.json"
|
|
LOCATIONS = Path(__file__).resolve().parents[3] / "assets" / "data" / "lg-locations.json"
|
|
TOLERANCE_DEG = 0.001
|
|
|
|
SSL_CTX = ssl.create_default_context()
|
|
SSL_CTX.check_hostname = False
|
|
SSL_CTX.verify_mode = ssl.CERT_NONE
|
|
OSRM = "https://router.project-osrm.org/route/v1/driving/{coords}?overview=full&geometries=geojson"
|
|
|
|
|
|
def perp_dist(pt, a, b):
|
|
dx, dy = b[0] - a[0], b[1] - a[1]
|
|
if dx == 0 and dy == 0:
|
|
return math.hypot(pt[0] - a[0], pt[1] - a[1])
|
|
t = max(0.0, min(1.0, ((pt[0] - a[0]) * dx + (pt[1] - a[1]) * dy) / (dx*dx + dy*dy)))
|
|
return math.hypot(pt[0] - (a[0] + t*dx), pt[1] - (a[1] + t*dy))
|
|
|
|
|
|
def rdp(points, eps):
|
|
if len(points) < 3:
|
|
return list(points)
|
|
max_d, idx = 0.0, 0
|
|
for i in range(1, len(points) - 1):
|
|
d = perp_dist(points[i], points[0], points[-1])
|
|
if d > max_d:
|
|
max_d, idx = d, i
|
|
if max_d <= eps:
|
|
return [points[0], points[-1]]
|
|
return rdp(points[:idx+1], eps)[:-1] + rdp(points[idx:], eps)
|
|
|
|
|
|
def fetch(waypoints):
|
|
coords = ";".join(f"{w[1]},{w[0]}" for w in waypoints)
|
|
with urllib.request.urlopen(OSRM.format(coords=coords), timeout=30, context=SSL_CTX) as r:
|
|
data = json.load(r)
|
|
route = data["routes"][0]
|
|
poly = [[round(c[1], 5), round(c[0], 5)] for c in route["geometry"]["coordinates"]]
|
|
return poly, route["distance"] / 1000.0
|
|
|
|
|
|
def main():
|
|
net = json.loads(ROADNET.read_text(encoding="utf-8"))
|
|
locs = {l["id"]: l for l in json.loads(LOCATIONS.read_text(encoding="utf-8"))}
|
|
|
|
# Neue Knoten
|
|
existing_ids = {n["id"] for n in net["nodes"]}
|
|
for nid in ("innsbruck", "graz"):
|
|
if nid in existing_ids:
|
|
continue
|
|
loc = locs[nid]
|
|
net["nodes"].append({"id": nid, "locationId": nid, "name": loc["name"]})
|
|
|
|
# Neue Kanten
|
|
NEW_EDGES = [
|
|
("muenchen-innsbruck", "muenchen", "innsbruck", 170, "A8/A93"),
|
|
("innsbruck-salzburg", "innsbruck", "salzburg", 190, "A12/A93/A10"),
|
|
("innsbruck-mailand", "innsbruck", "mailand", 390, "A13/A22 Brenner"),
|
|
("wien-graz", "wien", "graz", 200, "A2 Sued"),
|
|
("salzburg-graz", "salzburg", "graz", 280, "A10/A9 Tauern+Liezen"),
|
|
]
|
|
existing_edge_ids = {e["id"] for e in net["edges"]}
|
|
|
|
for eid, fnode, tnode, dist, code in NEW_EDGES:
|
|
if eid in existing_edge_ids:
|
|
print(f"skip {eid} (schon da)")
|
|
continue
|
|
fl = locs[net["nodes"][[n["id"] for n in net["nodes"]].index(fnode)]["locationId"]]
|
|
tl = locs[net["nodes"][[n["id"] for n in net["nodes"]].index(tnode)]["locationId"]]
|
|
start = [fl["lat"], fl["lon"]]
|
|
end = [tl["lat"], tl["lon"]]
|
|
print(f"-> {eid}: OSRM [{start}] -> [{end}] ...", flush=True)
|
|
try:
|
|
poly, osrm_km = fetch([start, end])
|
|
poly = rdp(poly, TOLERANCE_DEG)
|
|
net["edges"].append({
|
|
"id": eid, "from": fnode, "to": tnode,
|
|
"distanceKm": dist, "routeCode": code,
|
|
"polyline": poly,
|
|
"distanceKmOsrm": round(osrm_km, 1),
|
|
})
|
|
print(f" {osrm_km:.0f} km real, {len(poly)} Punkte")
|
|
except Exception as e:
|
|
print(f" FEHLER: {e}")
|
|
time.sleep(1.1)
|
|
|
|
ROADNET.write_text(
|
|
json.dumps(net, ensure_ascii=False, separators=(",", ":")),
|
|
encoding="utf-8",
|
|
)
|
|
sz = ROADNET.stat().st_size / 1024
|
|
print(f"\nFertig: {len(net['edges'])} Kanten, {sz:.1f} KB")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|