#!/usr/bin/env python3 """ Vereinfacht die OSRM-Polylines in lg-roadnet.json via Douglas-Peucker (~100 m Toleranz). Reduziert die Dateigroesse von ~9 MB auf ~1 MB, ohne dass die Kurvenfuehrung auf Europa-Zoomstufen sichtbar leidet. Dazu: Berlin-Kopenhagen manuell reparieren — OSRM Auto-Profil kennt die Fähre Rostock-Gedser nicht und umrundet die Ostsee (1027 km). Wir setzen die Route auf Berlin->Rostock (Land, OSRM) + Rostock-Gedser (Fähre, direkt) + Gedser->Kopenhagen (Land, OSRM), Distanz = 450 km analog Realfahrt. """ 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" TOLERANCE_DEG = 0.001 # ~110 m — glatt genug auf Europa-Zoom 4-8 OSRM = "https://router.project-osrm.org/route/v1/driving/{coords}?overview=full&geometries=geojson" SSL_CTX = ssl.create_default_context() SSL_CTX.check_hostname = False SSL_CTX.verify_mode = ssl.CERT_NONE def perpendicular_distance(pt, a, b): """Abstand pt zu Gerade a-b (in lat/lon-Einheiten, nicht metrisch, ok fuer RDP).""" dx = b[0] - a[0] dy = b[1] - a[1] if dx == 0 and dy == 0: return math.hypot(pt[0] - a[0], pt[1] - a[1]) t = ((pt[0] - a[0]) * dx + (pt[1] - a[1]) * dy) / (dx * dx + dy * dy) t = max(0.0, min(1.0, t)) proj_x = a[0] + t * dx proj_y = a[1] + t * dy return math.hypot(pt[0] - proj_x, pt[1] - proj_y) def rdp(points, epsilon): """Douglas-Peucker-Vereinfachung.""" if len(points) < 3: return list(points) # Max-deviation Punkt finden max_d = 0.0 idx = 0 for i in range(1, len(points) - 1): d = perpendicular_distance(points[i], points[0], points[-1]) if d > max_d: max_d = d idx = i if max_d <= epsilon: return [points[0], points[-1]] left = rdp(points[:idx + 1], epsilon) right = rdp(points[idx:], epsilon) return left[:-1] + right def fetch_route(waypoints): coords = ";".join(f"{w[1]},{w[0]}" for w in waypoints) url = OSRM.format(coords=coords) with urllib.request.urlopen(url, timeout=30, context=SSL_CTX) as resp: data = json.load(resp) route = data["routes"][0] geo = route["geometry"]["coordinates"] polyline = [[round(c[1], 5), round(c[0], 5)] for c in geo] distance_km = route["distance"] / 1000.0 return polyline, distance_km def main(): net = json.loads(ROADNET.read_text(encoding="utf-8")) # 1) Berlin-Kopenhagen (Faehre Rostock-Gedser) neu bauen print("Berlin-Kopenhagen (Faehre rekonstruieren):") rostock = [54.09, 12.14] gedser = [54.58, 11.93] berlin = [52.52, 13.405] kph = [55.6761, 12.5683] try: poly1, d1 = fetch_route([berlin, rostock]) time.sleep(1.1) poly3, d3 = fetch_route([gedser, kph]) # Faehren-Segment: gerade Linie mit zwei Zwischenpunkten fuer Animation poly2 = [rostock, [54.33, 12.03], gedser] combined = poly1 + poly2[1:] + poly3[1:] # Land-Strecke + Faehre-Distanz (Rostock-Gedser ca. 48 km Luftlinie ueber Ostsee) total_km = d1 + 48 + d3 print(f" Berlin->Rostock {d1:.0f} km, Faehre 48 km, Gedser->KPH {d3:.0f} km = {total_km:.0f} km") for edge in net["edges"]: if edge["id"] == "berlin-kopenhagen": edge["polyline"] = combined edge["distanceKmOsrm"] = round(total_km, 1) break except Exception as e: print(f" FEHLER: {e} — behalte bisherige Polyline.") # 2) Alle Polylines vereinfachen print(f"\nDouglas-Peucker mit Tolerance {TOLERANCE_DEG}° (~{int(TOLERANCE_DEG*111*1000)} m):") for edge in net["edges"]: before = len(edge.get("polyline") or []) if before < 3: continue simplified = rdp(edge["polyline"], TOLERANCE_DEG) edge["polyline"] = simplified after = len(simplified) ratio = after / before * 100 print(f" {edge['id']}: {before} -> {after} Punkte ({ratio:.1f}%)") ROADNET.write_text( json.dumps(net, ensure_ascii=False, separators=(",", ":"), indent=None), encoding="utf-8", ) new_size = ROADNET.stat().st_size / 1024 / 1024 print(f"\nFertig — {new_size:.2f} MB") if __name__ == "__main__": main()