"""Haltelinien und Zebrastreifen.""" from __future__ import annotations from collections import Counter from leonida.roads import Edge, Graph, Node from leonida.marking import CROSSWALK_DEPTH, junction_marks, markings from leonida.section import SECTION, setback def kreuzung(klassen=("hauptstrasse",) * 4, arm: float = 80.0) -> Graph: punkte = [(arm, 0.0), (-arm, 0.0), (0.0, arm), (0.0, -arm)] return Graph(nodes=[Node((0.0, 0.0))] + [Node(p) for p in punkte], edges=[Edge(0, i + 1, [(0.0, 0.0), p], SECTION[k].corridor, k) for i, (p, k) in enumerate(zip(punkte, klassen))]) def test_jeder_arm_bekommt_zebra_und_haltelinie(): zahl = Counter(art for _, art in junction_marks(kreuzung(), 0)) assert zahl["haltelinie"] == 4, "eine je Zufahrt" assert zahl["zebra"] > 4 * 5, "je Arm mehrere Balken" def test_zebra_liegt_am_kreuzungsrand(): rand = setback(kreuzung(), 0) zebra = [p for p, art in junction_marks(kreuzung(), 0) if art == "zebra"] # Der Arm nach Osten: Balken zwischen Kreuzungsrand und Kreuzungsrand plus Tiefe. Die # Querbedingung muss enger sein als die halbe Fahrbahnbreite (6 m): Bei |y| < 20 träfe sie # auch Nord- und Südarm-Balken nahe der Ecke (deren eigene Querachse dort ohnehin liegt), die # aber bei x um 8-11 m liegen wie der Ostarm selbst - falsches Grün durch Verwechslung, keine # echte Prüfung (Fixrunde zu Task 9). halb = SECTION["hauptstrasse"].carriageway / 2 ost = [p for p in zebra if p.centroid.x > 0 and abs(p.centroid.y) < halb] assert ost, "der Ostarm hat Balken" for poly in ost: assert rand - 0.1 <= poly.bounds[0] <= rand + CROSSWALK_DEPTH def test_haltelinie_liegt_hinter_dem_zebrastreifen(): rand = setback(kreuzung(), 0) halt = [p for p, art in junction_marks(kreuzung(), 0) if art == "haltelinie"] ost = next(p for p in halt if p.centroid.x > 0 and abs(p.centroid.y) < 20) assert ost.bounds[0] > rand + CROSSWALK_DEPTH, "erst Zebrastreifen, dann Haltelinie" def test_haltelinie_deckt_nur_die_rechte_haelfte(): halt = [p for p, art in junction_marks(kreuzung(), 0) if art == "haltelinie"] ost = next(p for p in halt if p.centroid.x > 0 and abs(p.centroid.y) < 20) # Zufahrt von Osten nach Westen, also Fahrtrichtung (-1, 0): Rechts davon liegt y > 0 - per # offset_curve()-Konvention nachgerechnet (negativ = rechts der Linienrichtung), nicht nur # behauptet. Der Auftrag ging von y < 0 aus; das war die Gegenspur (Fixrunde zu Task 9). assert ost.centroid.y > 0 assert round(ost.bounds[3] - ost.bounds[1], 1) == round(SECTION["hauptstrasse"].carriageway / 2, 1) def test_arm_der_eine_gasse_ist_bekommt_keinen_zebrastreifen(): graph = kreuzung(("hauptstrasse", "hauptstrasse", "hauptstrasse", "gasse")) marks = junction_marks(graph, 0) # Wie bei test_zebra_liegt_am_kreuzungsrand: |x| < 20 wäre zu locker und träfe auch Ost- und # Westarm-Balken nahe der Ecke, obwohl deren Querachse (|x| bis 9,5 m für Zebra, 12,5 m für # die Haltelinie) klar außerhalb der Südarm-Fahrbahnhälfte liegt (Fixrunde zu Task 9). halb = SECTION["hauptstrasse"].carriageway / 2 süd = [art for p, art in marks if p.centroid.y < -1 and abs(p.centroid.x) < halb] assert süd == [], "der Gassenarm bleibt blank" def test_kreuzung_aus_lauter_gassen_bekommt_nichts(): assert junction_marks(kreuzung(("gasse",) * 4), 0) == [] def test_markings_sammelt_laengs_und_kreuzung(): arten = {art for _, art in markings(kreuzung())} assert "gelb_doppelt" in arten and "zebra" in arten and "haltelinie" in arten def test_schleife_bekommt_haltelinie_fuer_beide_richtungen(): # Wie test_schleife_bekommt_zwei_stummel_kein_loch_im_belag in test_junction.py: Eine # Schleife hängt mit beiden Enden am selben Knoten. graph.incident() liefert sie nur einmal, # aber sie stellt zwei getrennte Abgangsrichtungen dar (Verkehr kann von beiden Seiten in die # Kreuzung einfahren) - am echten Netz betrifft das 28 von 690 Kreuzungen mit einer # bordsteintragenden Schleife. Ohne die zweite Richtung fehlten Haltelinie und Zebrastreifen # auf der einen Seite der Schleife komplett. 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)], SECTION["hauptstrasse"].corridor, "hauptstrasse"), Edge(0, 1, [(0.0, 0.0), (-100.0, 0.0)], SECTION["hauptstrasse"].corridor, "hauptstrasse"), Edge(0, 2, [(0.0, 0.0), (0.0, -100.0)], SECTION["hauptstrasse"].corridor, "hauptstrasse")]) zahl = Counter(art for _, art in junction_marks(graph, 0)) assert zahl["haltelinie"] == 4, "zwei gerade Arme plus zwei Richtungen der Schleife"