fix(extract): Gehwegquelle nur noch aus bordsteintragenden Armstummeln, ungerundet

Zuschneiden (Fixrunde 4) war der falsche Hebel: Egal wie breit die abgezogene
Zone, die Nische zwischen zwei Gassenstummeln entsteht erst durch die
FILLET-Rundung in junction_area() und liegt abseits der Gassenachse - ein
linearer Abzug entlang dieser Achse kann sie grundsaetzlich nicht erreichen.

Die Stummelbildung aus junction_area() in einen eigenen Helfer gezogen,
junction_arms(graph, node, kerb_only=False), mit einem Filter fuer Kanten ohne
Bordstein. junction_area() nutzt ihn ungefiltert und rundet wie bisher -
verhaelt sich dadurch identisch (913.566,7 m² in 213 Teilen am echten Netz,
bit-genau nachgemessen). _walk_source() in street.py nimmt jetzt nur noch die
rohen, ungerundeten Armstummel mit Bordstein (kerb_only=True) als Quelle,
ohne den kerbless_zone-Abzug aus Fixrunde 4 - die Nische entsteht dadurch gar
nicht erst, weil die Kreuzung ohne Rundung nur noch ueber ihre eigenen,
bordsteintragenden Arme in die Quelle eingeht.

Gemessen: an der Kreuzung mit drei Gassen und einem Boulevard verschwindet
der Rest in der Nische vollstaendig (0,0 statt der 27,14 m² aus Fixrunde 4)
- weiter als die fuer diese Runde erwartete Obergrenze von rund 6 m
Ausdehnung. Kriterium 4 damit erfuellt, keine weitere Variante noetig.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Sonnet 5 committed 2026-09-17 07:02:50 +02:00
1 parent 685ac666e6
commit f2f5db94fe
3 files changed
+60 -60

No files matched your search

+25 -15
View File
@@ -78,21 +78,18 @@ def ribbon(graph: Graph, k: int):
FILLET = 6.0
def junction_area(graph: Graph, node: int):
"""Die Fläche einer Kreuzung: die Stummel aller Arme, mit gerundeten einspringenden Ecken.
def junction_arms(graph: Graph, node: int, kerb_only: bool = False) -> list:
"""Die Armstummel einer Kreuzung, je auf ihre eigene Fahrbahnbreite gepuffert.
Gerundet wird durch Schließen (aufblasen, dann wieder schrumpfen) - und ausdrücklich nur
innerhalb einer Scheibe um den Knoten. Auf das ganze Netz angewandt verklebte dieselbe
Operation zwei parallele Straßen, deren Fahrbahnen weniger als 2 * FILLET auseinanderliegen.
Zweimal gebraucht (Fixrunde 5 zu Task 7): junction_area() vereinigt alle und rundet die
einspringenden Ecken; die Gehwegquelle in street.py nimmt nur die Arme mit Bordstein, ohne
Rundung. Deshalb hier einmal, statt die Stummelbildung zu wiederholen.
"""
incident = graph.incident(node)
if not incident:
return None
reach = setback(graph, node)
arms = []
for k in incident:
arme = []
for k in graph.incident(node):
edge = graph.edges[k]
if len(edge.points) < 2:
if len(edge.points) < 2 or (kerb_only and not SECTION[edge.klass].kerb):
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
@@ -101,10 +98,23 @@ def junction_area(graph: Graph, node: int):
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)
stumpf = substring(line, 0.0, min(reach + 1.0, line.length))
arme.append(stumpf.buffer(SECTION[edge.klass].carriageway / 2,
cap_style="flat", join_style="mitre", mitre_limit=1.5))
return arme
def junction_area(graph: Graph, node: int):
"""Die Fläche einer Kreuzung: die Stummel aller Arme, mit gerundeten einspringenden Ecken.
Gerundet wird durch Schließen (aufblasen, dann wieder schrumpfen) - und ausdrücklich nur
innerhalb einer Scheibe um den Knoten. Auf das ganze Netz angewandt verklebte dieselbe
Operation zwei parallele Straßen, deren Fahrbahnen weniger als 2 * FILLET auseinanderliegen.
"""
if not graph.incident(node):
return None
reach = setback(graph, node)
star = unary_union(junction_arms(graph, node))
near = Point(graph.nodes[node].point).buffer(reach + FILLET + 2.0)
rounded = star.buffer(FILLET, join_style="round").buffer(-FILLET, join_style="round")
return star.union(rounded.intersection(near))