"""Kreuzungsflächen und die Fahrbahn als Ganzes.""" from __future__ import annotations from shapely.geometry import Point from leonida.roads import Edge, Graph, Node from leonida.section import FILLET, carriageway_area, junction_area def kreuzung(klass: str = "hauptstrasse", arm: float = 60.0) -> Graph: return Graph( nodes=[Node((0.0, 0.0)), Node((arm, 0.0)), Node((-arm, 0.0)), Node((0.0, arm)), Node((0.0, -arm))], edges=[Edge(0, i, [(0.0, 0.0), p], 18.0, klass) for i, p in ((1, (arm, 0.0)), (2, (-arm, 0.0)), (3, (0.0, arm)), (4, (0.0, -arm)))]) def test_kreuzung_deckt_den_knoten_ab(): area = junction_area(kreuzung(), 0) assert area.contains(Point(0, 0)) def test_einspringende_ecke_ist_gerundet(): area = junction_area(kreuzung(), 0) # Ohne Rundung endete die Fläche an der Ecke (6, 6) scharf - halbe Fahrbahn in beide # Richtungen. Mit 6 m Radius ist die einspringende Ecke ausgefüllt. assert area.contains(Point(6.5, 6.5)) assert FILLET == 6.0 def test_zwei_parallele_strassen_verschmelzen_nicht(): # Zwei Wohnstraßen mit 10 m Abstand der Mittellinien: Fahrbahn je 7 m, dazwischen 3 m Lücke. # Je ein Querarm macht aus jeder Straße eine echte Kreuzung (Grad 3) - ohne ihn hätte keiner # der vier Knoten Grad >= 3, junction_area() liefe nie, und der Test bewachte nichts (siehe # Fixbericht). Ein auf die ganze Karte statt je Knoten angewandtes Schließen würde die beiden # Fahrbahnen trotzdem verkleben, weil ihr Abstand (3 m) unter 2 * FILLET (12 m) liegt - # Gegenprobe dazu ebenfalls im Fixbericht. graph = Graph( nodes=[Node((0.0, 0.0)), Node((100.0, 0.0)), Node((50.0, 0.0)), Node((50.0, -50.0)), Node((0.0, 10.0)), Node((100.0, 10.0)), Node((50.0, 10.0)), Node((50.0, 60.0))], edges=[Edge(0, 2, [(0.0, 0.0), (50.0, 0.0)], 12.0, "wohnstrasse"), Edge(2, 1, [(50.0, 0.0), (100.0, 0.0)], 12.0, "wohnstrasse"), Edge(2, 3, [(50.0, 0.0), (50.0, -50.0)], 12.0, "wohnstrasse"), Edge(4, 6, [(0.0, 10.0), (50.0, 10.0)], 12.0, "wohnstrasse"), Edge(6, 5, [(50.0, 10.0), (100.0, 10.0)], 12.0, "wohnstrasse"), Edge(6, 7, [(50.0, 10.0), (50.0, 60.0)], 12.0, "wohnstrasse")]) assert graph.degree(2) == 3 and graph.degree(6) == 3, "beide Straßen brauchen eine echte Kreuzung" area = carriageway_area(graph) assert len(area.geoms) == 2, "getrennte Straßen bleiben getrennt" def test_fahrbahn_enthaelt_baender_und_kreuzung(): area = carriageway_area(kreuzung()) # Schnittstellenzusage: immer MultiPolygon, auch wenn - wie hier - eine einzelne # zusammenhängende Kreuzung unary_union() zu einem einfachen Polygon vereint. Task 7 # iteriert über .geoms; das bricht sonst genau an kleinen, zusammenhängenden Prüfkörpern. assert area.geom_type == "MultiPolygon" assert area.contains(Point(0, 0)), "die Kreuzung selbst" assert area.contains(Point(40, 0)), "ein Band" assert not area.contains(Point(40, 40)), "die Ecke zwischen zwei Armen bleibt frei" def test_schleife_bekommt_zwei_stummel_kein_loch_im_belag(): # Eine Schleife hängt mit beiden Enden am selben Knoten. incident() liefert sie nur einmal, # und edge.a == node ist bei ihr immer wahr - ohne einen zweiten Stummel für die zweite # Abgangsrichtung bliebe zwischen dem zurückgeschnittenen Schleifenband und der # Kreuzungsfläche ein Loch. Zwei gerade Arme sorgen dafür, dass der Knoten überhaupt als # Kreuzung (Grad >= 3) zählt. graph = Graph( nodes=[Node((0.0, 0.0)), Node((-100.0, 0.0)), Node((0.0, -100.0))], edges=[Edge(0, 0, [(0.0, 0.0), (0.0, 20.0), (20.0, 20.0), (20.0, 0.0), (0.0, 0.0)], 18.0, "hauptstrasse"), Edge(0, 1, [(0.0, 0.0), (-100.0, 0.0)], 18.0, "hauptstrasse"), Edge(0, 2, [(0.0, 0.0), (0.0, -100.0)], 18.0, "hauptstrasse")]) area = carriageway_area(graph) # Zwischen der Kreuzungsfläche (endet bei halber Fahrbahn = 6 m) und dem zurückgeschnittenen # Schleifenband (beginnt beim Setback = 8 m) liegt (7, 0) - ohne den zweiten Stummel außerhalb. assert area.contains(Point(7.0, 0.0))