fix(extract): carriageway_area() liefert immer MultiPolygon, Kreuzungstest schärfen
Zwei Befunde aus der Prüfung: unary_union() vereinte eine einzelne zusammenhängende Kreuzung zu einem einfachen Polygon statt des zugesagten MultiPolygon (bricht an kleinen Prüfkörpern, nicht am echten Netz mit seinen 213 Teilen - deshalb bisher unbemerkt). Normalisiert jetzt auf MultiPolygon, mit Testzusicherung an der zusammenhängenden kreuzung()-Fixture. test_zwei_parallele_strassen_verschmelzen_nicht bewachte nichts: alle vier Knoten hatten Grad 1, junction_area() lief nie. Fixture bekommt jetzt an beiden Straßen einen Querarm (Grad 3), damit das Schließen tatsächlich ausgeführt wird, während die Straßen getrennt bleiben. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f91b5ab7c6
commit
ee07a6bf2a
2 files changed
+27
-6
No files matched your search
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from shapely.geometry import LineString, Point
|
from shapely.geometry import LineString, MultiPolygon, Point
|
||||||
from shapely.ops import substring, unary_union
|
from shapely.ops import substring, unary_union
|
||||||
|
|
||||||
from .roads import CLASS_WIDTH, Graph
|
from .roads import CLASS_WIDTH, Graph
|
||||||
@@ -115,4 +115,10 @@ def carriageway_area(graph: Graph):
|
|||||||
parts = [band for k in range(len(graph.edges)) if (band := ribbon(graph, k)) is not None]
|
parts = [band for k in range(len(graph.edges)) if (band := ribbon(graph, k)) is not None]
|
||||||
parts += [area for node in range(len(graph.nodes)) if graph.degree(node) >= 3
|
parts += [area for node in range(len(graph.nodes)) if graph.degree(node) >= 3
|
||||||
and (area := junction_area(graph, node)) is not None]
|
and (area := junction_area(graph, node)) is not None]
|
||||||
return unary_union(parts)
|
vereint = unary_union(parts)
|
||||||
|
if vereint.is_empty:
|
||||||
|
return MultiPolygon()
|
||||||
|
# Zusage der Schnittstelle: immer MultiPolygon. unary_union() liefert bei zusammenhängender
|
||||||
|
# Eingabe ein einfaches Polygon, und Task 7 iteriert über .geoms — das bricht dann an kleinen
|
||||||
|
# Prüfkörpern, aber nicht am echten Netz, also genau da, wo niemand damit rechnet.
|
||||||
|
return vereint if vereint.geom_type == "MultiPolygon" else MultiPolygon([vereint])
|
||||||
@@ -29,16 +29,31 @@ def test_einspringende_ecke_ist_gerundet():
|
|||||||
|
|
||||||
def test_zwei_parallele_strassen_verschmelzen_nicht():
|
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.
|
# Zwei Wohnstraßen mit 10 m Abstand der Mittellinien: Fahrbahn je 7 m, dazwischen 3 m Lücke.
|
||||||
# Ein global angewandtes Schließen mit 6 m würde sie zu einer Fläche verkleben.
|
# Je ein Querarm macht aus jeder Straße eine echte Kreuzung (Grad 3) - ohne ihn hätte keiner
|
||||||
graph = Graph(nodes=[Node((0.0, 0.0)), Node((100.0, 0.0)), Node((0.0, 10.0)), Node((100.0, 10.0))],
|
# der vier Knoten Grad >= 3, junction_area() liefe nie, und der Test bewachte nichts (siehe
|
||||||
edges=[Edge(0, 1, [(0.0, 0.0), (100.0, 0.0)], 12.0, "wohnstrasse"),
|
# Fixbericht). Ein auf die ganze Karte statt je Knoten angewandtes Schließen würde die beiden
|
||||||
Edge(2, 3, [(0.0, 10.0), (100.0, 10.0)], 12.0, "wohnstrasse")])
|
# 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)
|
area = carriageway_area(graph)
|
||||||
assert len(area.geoms) == 2, "getrennte Straßen bleiben getrennt"
|
assert len(area.geoms) == 2, "getrennte Straßen bleiben getrennt"
|
||||||
|
|
||||||
|
|
||||||
def test_fahrbahn_enthaelt_baender_und_kreuzung():
|
def test_fahrbahn_enthaelt_baender_und_kreuzung():
|
||||||
area = carriageway_area(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(0, 0)), "die Kreuzung selbst"
|
||||||
assert area.contains(Point(40, 0)), "ein Band"
|
assert area.contains(Point(40, 0)), "ein Band"
|
||||||
assert not area.contains(Point(40, 40)), "die Ecke zwischen zwei Armen bleibt frei"
|
assert not area.contains(Point(40, 40)), "die Ecke zwischen zwei Armen bleibt frei"
|
||||||
|
|||||||
Reference in new issue
Block a user