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

+24 -27
View File
@@ -178,12 +178,16 @@ def test_wohnstrassen_gehweg_bleibt_vollstaendig_an_gasseneinmuendung():
def test_gehweg_waechst_nicht_ueber_den_gassenstummel_an_geteilter_kreuzung():
# Fixrunde 4: _walk_source() nahm bisher die ganze junction_area() auf, sobald irgendein Arm
# Bordstein hat - auch den Anteil, der zu einer dort einmündenden Gasse gehört. T-Kreuzung:
# zwei durchgehende Gassenarme (West/Ost) plus ein Wohnstraßenarm (Süd). Ohne Beschneidung
# wächst ein Gehwegstreifen die volle Länge beider Gassenstummel entlang (hier: 53,11 m² im
# Nordstreifen y∈[3,6]), obwohl mitten auf der Gasse (Kontrollpunkt, weit von der Kreuzung)
# kein Gehweg entsteht - der Fehler sitzt eindeutig am Kreuzungsanteil.
# Fixrunde 4/5: _walk_source() nahm ursprünglich die ganze junction_area() auf, sobald
# irgendein Arm Bordstein hat - auch den Anteil, der zu einer dort einmündenden Gasse gehört.
# Ein linearer Zuschnitt (Fixrunde 4) reichte nicht, weil die FILLET-Rundung die Nische
# zwischen Gassenstummeln abseits der Gassenachse aufweitet. Seit Fixrunde 5 geht die
# Kreuzung nur noch über ihre bordsteintragenden Armstummel (junction_arms(kerb_only=True),
# ungerundet) in die Quelle ein - die Nische entsteht dadurch gar nicht erst. T-Kreuzung:
# zwei durchgehende Gassenarme (West/Ost) plus ein Wohnstraßenarm (Süd). Ohne den Fix wächst
# ein Gehwegstreifen die volle Länge beider Gassenstummel entlang (53,11 m² im Nordstreifen
# y∈[3,6]), obwohl mitten auf der Gasse (Kontrollpunkt, weit von der Kreuzung) kein Gehweg
# entsteht - der Fehler sitzt eindeutig am Kreuzungsanteil.
graph = Graph(nodes=[Node((-50.0, 0.0)), Node((0.0, 0.0)), Node((50.0, 0.0)), Node((0.0, -50.0))],
edges=[Edge(0, 1, [(-50.0, 0.0), (0.0, 0.0)], SECTION["gasse"].corridor, "gasse"),
Edge(1, 2, [(0.0, 0.0), (50.0, 0.0)], SECTION["gasse"].corridor, "gasse"),
@@ -198,21 +202,16 @@ def test_gehweg_waechst_nicht_ueber_den_gassenstummel_an_geteilter_kreuzung():
"kein Gehweg entlang der Gassenstummel, nur der Wohnstraßen-Gehweg im Süden bleibt"
def test_gehwegrest_an_kreuzung_mit_mehreren_gassen_bleibt_teilproblem():
# Fixrunde 4, Abnahmepunkt 3+4: Kreuzung mit drei Gassenarmen (West/Ost/Süd) und einem
# Boulevard-Arm (Nord). setback = max(9, 3, 3, 3) + 2 = 11 m - der Boulevard zieht den
# Kreuzungsstummel aller vier Arme, auch der schmalen Gassen, auf 12 m Länge hoch.
# _kerbless_zone(graph, 0.2) schneidet nur einen schmalen Streifen (Gassenbreite + 0,2 m)
# entlang der jeweiligen Gassen-Mittellinie heraus, nicht die durch die FILLET-Rundung
# aufgeweitete Nische zwischen zwei benachbarten Gassenstummeln - dort bleibt ein Rest, der
# weiter reicht als die für eine reine Eckenrundung erwartete Größenordnung.
#
# Bericht an den Koordinator (Abnahmepunkt 4): Der Rest in der Nische zwischen West- und
# Süd-Gassenstummel schrumpft zwar von 53,3 auf 27,14 m², aber seine maximale Ausdehnung von
# der Kreuzungsmitte reicht bis 11,14 m - weit über die als Grenze genannten 3-4 m. Der
# Ansatz aus Fixrunde 4 behebt den Kreuzungsstummel-Fehler also nur teilweise; dieser Test
# hält den tatsächlich erreichten (nicht den gewünschten) Zwischenstand fest, bis eine
# weitere Entscheidung vorliegt - siehe Fixbericht.
def test_gehwegrest_an_kreuzung_mit_mehreren_gassen_bleibt_aus():
# Fixrunde 5, Abnahmepunkt 4 - jetzt Zielverhalten statt Zwischenstand: Kreuzung mit drei
# Gassenarmen (West/Ost/Süd) und einem Boulevard-Arm (Nord). setback = max(9,3,3,3)+2 = 11 m
# - der Boulevard zieht den Kreuzungsstummel aller vier Arme, auch der schmalen Gassen, auf
# 12 m Länge hoch. Ein linearer Zuschnitt (Fixrunde 4, _kerbless_zone entlang der
# Gassen-Mittellinie) erreichte die durch die FILLET-Rundung aufgeweitete Nische zwischen
# zwei benachbarten Gassenstummeln nicht (Rest blieb bei 27,14 m², Ausdehnung bis 11,14 m).
# Seit Fixrunde 5 geht die Kreuzung nur noch über ihren bordsteintragenden Arm (den
# Boulevard) in die Gehwegquelle ein, ungerundet - die drei Gassenarme tragen gar nichts
# mehr bei, die Nische zwischen ihnen kann also gar nicht erst entstehen.
graph = Graph(nodes=[Node((0.0, 0.0)), Node((-50.0, 0.0)), Node((50.0, 0.0)),
Node((0.0, -50.0)), Node((0.0, 50.0))],
edges=[Edge(0, 1, [(0.0, 0.0), (-50.0, 0.0)], SECTION["gasse"].corridor, "gasse"),
@@ -224,9 +223,7 @@ def test_gehwegrest_an_kreuzung_mit_mehreren_gassen_bleibt_teilproblem():
street = build_street(graph, corridor, bridges=[])
nische = box(-15, -15, -3, -3)
rest = street.sidewalk.intersection(nische)
# 40 liegt klar zwischen der ungefixten Fassung (53,30) und der jetzigen (27,14) - die
# Schranke trennt beide Fassungen sauber, statt nur knapp unter dem alten Wert zu liegen.
assert rest.area < 40, "die Nische schrumpft gegenüber der ungefixten Fassung"
assert rest.area > 20, ("Regressionsschutz für den unten dokumentierten Zwischenstand (rund "
"27,14 m²) - stimmt diese Zahl plötzlich nicht mehr, hat sich am "
"Fix etwas geändert, das im Bericht nachgezogen werden muss")
# Gemessen: 0,0 m² exakt - deutlich strenger als die von der Abnahme geforderte Obergrenze
# von rund WALK_MAX + halbe Gassenbreite (~6 m Ausdehnung); 1,0 m² lässt Spielraum für
# künftige, geometrisch bedingte Rundungsreste, ohne die Kernaussage zu verwässern.
assert rest.area < 1.0, "die Nische zwischen zwei Gassenstummeln bleibt frei von Gehweg"