diff --git a/tools/extract/leonida/section.py b/tools/extract/leonida/section.py index 011b231..1f268cb 100644 --- a/tools/extract/leonida/section.py +++ b/tools/extract/leonida/section.py @@ -92,11 +92,18 @@ def junction_area(graph: Graph, node: int): arms = [] for k in incident: edge = graph.edges[k] - points = edge.points if edge.a == node else list(reversed(edge.points)) - line = LineString(points) - stub = substring(line, 0.0, min(reach + 1.0, line.length)) - arms.append(stub.buffer(SECTION[edge.klass].carriageway / 2, - cap_style="flat", join_style="mitre", mitre_limit=1.5)) + if len(edge.points) < 2: + continue # Punktkanten haben keine Richtung; LineString wirft an ihnen + # Eine Schleife hängt mit beiden Enden am selben Knoten und braucht deshalb zwei Stummel. + # incident() liefert sie nur einmal, und edge.a == node ist bei ihr immer wahr — ohne den + # zweiten Stummel bliebe an ihrem anderen Ende ein Loch in der Fahrbahn. + richtungen = ([edge.points, list(reversed(edge.points))] if edge.a == edge.b + else [edge.points if edge.a == node else list(reversed(edge.points))]) + for points in richtungen: + line = LineString(points) + stub = substring(line, 0.0, min(reach + 1.0, line.length)) + arms.append(stub.buffer(SECTION[edge.klass].carriageway / 2, + cap_style="flat", join_style="mitre", mitre_limit=1.5)) star = unary_union(arms) near = Point(graph.nodes[node].point).buffer(reach + FILLET + 2.0) rounded = star.buffer(FILLET, join_style="round").buffer(-FILLET, join_style="round") diff --git a/tools/extract/tests/test_junction.py b/tools/extract/tests/test_junction.py index 725bb73..f7589a1 100644 --- a/tools/extract/tests/test_junction.py +++ b/tools/extract/tests/test_junction.py @@ -42,3 +42,20 @@ def test_fahrbahn_enthaelt_baender_und_kreuzung(): 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))