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>
_walk_source() nahm bisher die ganze junction_area() eines Knotens auf, sobald
irgendein anliegender Arm Bordstein hat - auch den Anteil, der zu einer dort
einmuendenden Gasse gehoert. Der Gehweg wuchs dadurch die volle Laenge des
Kreuzungsstummels die Gasse hinauf (bis zu setback+1, beim Boulevard 11 m)
statt nur um die Ecke. _kerbless_zone() bekommt den extra-Parameter zurueck
(_kerb_lines() und _walk_source() rufen sie beide mit 0.2 auf) und beschneidet
jetzt auch den Kreuzungsanteil der Gehwegquelle.
Gemessen: an einer T-Kreuzung (2 Gassen + 1 Wohnstrasse) verschwindet der
fehlerhafte Gassenstummel-Gehweg vollstaendig (53,11 -> 0,0 m2). An einer
4-Wege-Kreuzung mit 3 Gassen + 1 Boulevard reicht der Fix nicht aus: der Rest
in der Nische zwischen zwei Gassenstummeln schrumpft nur auf 27,14 statt der
erwarteten rund 2 m2, und seine maximale Ausdehnung von der Kreuzungsmitte
(11,14 m) liegt weit ueber der als Grenze genannten 3-4 m - dokumentiert als
bekannter, nicht ausreichend geloester Punkt, siehe Fixbericht.
Unabhaengig davon: MultiPolygon([result]) wirft TypeError, wenn result eine
GeometryCollection ist (intersection()/difference() liefern das gelegentlich
an Beruehrstellen, mit einer entarteten Nulllinie neben echten Polygonen).
Neuer Helfer to_multipolygon() in section.py filtert auf echte Polygone,
statt daran abzustuerzen; carriageway_area() und _sidewalk() nutzen ihn beide.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
incident() liefert eine Schleife (a == b) nur einmal, und edge.a == node ist bei ihr immer
wahr - die zweite Abgangsrichtung bekam deshalb nie einen eigenen Arm im Kreuzungsfächer.
Das war kein Schönheitsfehler, sondern ein Loch in der Fahrbahn genau dort, wo das
zurückgeschnittene Schleifenband auf die Kreuzungsfläche trifft: 179 von 196 Schleifen
sitzen an einem Knoten mit Grad >= 3 und waren betroffen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Der Korridor, den Task 4 misst, ist auf der Karte eine einzige graue
Fläche - Fahrbahn und Gehweg tragen dort dieselbe Farbe. Der Querschnitt
teilt sie deshalb nur auf, statt sie zu vergrößern: Fahrbahn plus zweimal
Gehweg ergibt für jede Klasse genau die gemessene Breite, und der
Stadtgrundriss bleibt unberührt. Die Maße (Fahrspur 3,5 m, Parkspur
2,0-2,5 m, Gehweg 2,5-3,0 m) stehen im Entwurf; eine Assertion auf
Modulebene hält die Aufteilung für alle vier Klassen fest.
Das Fahrbahnband entsteht aus der Mittellinie: An jedem Kreuzungsende
(Grad >= 3) weicht sie um die halbe Fahrbahn der breitesten anliegenden
Kante plus 2 m zurück, freie Enden bleiben stehen. Eine Kante, die ganz
in den Kreuzungsflächen verschwindet, liefert kein Band - das füllt Task 6.
Testkorrektur: test_kante_ganz_innerhalb_der_kreuzung_liefert_kein_band
hatte an beiden "Kreuzungsknoten" nur je zwei Kanten, also Grad 2 statt
Grad 3 - keine Kreuzung nach der Definition, die trimmed_line() selbst
verwendet. Gegen die fertige Implementierung geprüft, lieferte sie eine
echte Linie statt None. Je ein zweiter Arm an beiden Knoten (wie schon in
test_kontraktion_zieht_alle_anliegenden_kanten_mit für denselben
3-m-Kreuzungsabstand) behebt das.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>