"""Die ausgelieferten Straßen, festgehalten (Entwurf, Abschnitt Tests: Integrationslauf). Der Test liest die committeten Chunks, nicht den Lauf — die Kacheln liegen nicht im Repository. Die Zahlen sind beim Neuerzeugen gemessen. Ändert sich die Extraktion absichtlich, gehören die neuen Zahlen hierher, mit einem Satz im Commit, warum. Die Kreuzungszahl fehlt, weil die Chunks keine Knoten tragen; sie steht in den Messwerten des Entwurfs. """ from __future__ import annotations import json from collections import Counter from pathlib import Path from shapely.geometry import LineString, Point, Polygon from shapely.ops import unary_union ROOT = Path(__file__).resolve().parents[3] / "public" / "data" / "vice-a" def _chunks() -> list[dict]: return [json.loads(p.read_text(encoding="utf-8")) for p in sorted(ROOT.glob("chunk_*.json"))] def _flaeche(chunks, key) -> float: return sum(Polygon(i["poly"][0], i["poly"][1:]).area for c in chunks for i in c.get(key, [])) def test_kantenstuecke_je_klasse(): assert dict(Counter(e["klasse"] for c in _chunks() for e in c["edges"])) == { "boulevard": 270, "wohnstrasse": 537, "gasse": 1010, "hauptstrasse": 401, } def test_markierungsstuecke_je_art(): assert dict(Counter(m["art"] for c in _chunks() for m in c["markings"])) == { "gelb_doppelt": 1216, "gelb_gestrichelt": 2370, "haltelinie": 1555, "weiss_gestrichelt": 2066, "weiss_rand": 1219, "zebra": 14846, } def test_restflaechen_und_bordsteinstuecke(): chunks = _chunks() assert sum(len(c["lots"]) for c in chunks) == 279 # „lots" aus „Stücke, je Chunk gezählt" assert sum(len(c["curb"]) for c in chunks) == 1415 # „curb" aus „Stücke, je Chunk gezählt" def test_flaechen_und_bordsteinlaenge(): chunks = _chunks() assert round(_flaeche(chunks, "carriageway") / 1e6, 4) == 0.9076 # aus „Flächen km²" assert round(_flaeche(chunks, "sidewalk") / 1e6, 4) == 0.2987 assert round(_flaeche(chunks, "lots") / 1e6, 4) == 0.0967 laenge = sum(LineString(line).length for c in chunks for line in c["curb"]) assert round(laenge / 1e3, 2) == 120.28 # aus „Bordstein km" def test_keine_palme_auf_der_fahrbahn(): for c in _chunks(): fahrbahn = [Polygon(i["poly"][0], i["poly"][1:]) for i in c["carriageway"]] auf = [p for p in c["palms"] if any(f.contains(Point(p)) for f in fahrbahn)] assert auf == [], f"Chunk {c['chunk']}" def test_markierung_liegt_nicht_wesentlich_ausserhalb_der_fahrbahn(): """Befund 10 der Fixwelle: Vor Befund 3 lagen 452,65 m² Markierungsfläche (1,03 % von 43.906 m²) außerhalb der Fahrbahn, weil "along" in junction_marks() nur aus dem ersten Segment einer Zufahrt kam. Rechnerisch nachgewiesen gegen die damals ausgelieferten Daten (Commit 0ad6eee, vor dieser Fixwelle) - mit dem hier verwendeten Maß (Fläche von markings minus der Vereinigung von carriageway, je Chunk summiert) maß dieser Befund dort 452,65 m² und wäre an jeder Schranke unter diesem Wert rot gewesen. Befund 3 behebt den Hauptteil davon (auf 110,06 m² = 0,25 %, siehe Fixwelle-Bericht), aber nicht den ganzen Rest: Ein Bogen kann zwischen Streckenlänge reach und reach + STOP_SETBACK + STOP_DEPTH noch einmal abknicken, was ein einzelner Richtungsstrahl nicht mehr auffängt - das verlangte die vom Befund selbst genannte, größere Alternative ("die Markierung entlang der tatsächlichen Kante aufbauen"), die diese Fixwelle nicht umsetzt. Die Schranke hier ist deshalb *kein* reiner Rundungswert, sondern hält den mit Befund 3 erreichten, im Fixwelle-Bericht offengelegten Stand fest (mit etwas Luft nach oben) und schlägt bei einer echten Verschlechterung an. """ gesamt_ausserhalb = 0.0 for c in _chunks(): fahrbahn = unary_union([Polygon(i["poly"][0], i["poly"][1:]) for i in c.get("carriageway", [])]) for m in c.get("markings", []): poly = Polygon(m["poly"][0], m["poly"][1:]) gesamt_ausserhalb += poly.difference(fahrbahn).area if not fahrbahn.is_empty else poly.area assert gesamt_ausserhalb < 130.0, ( f"{gesamt_ausserhalb:.2f} m² Markierungsfläche liegen außerhalb der Fahrbahn " "(Fixwelle-Stand: 110,06 m²) - das ist mehr als der dokumentierte Rest, nicht nur Rundung" )