Nacharbeit zu Befund 3, beim Nachmessen am echten Netz entdeckt: Die erste
Korrektur (Richtung aus origin -> interpolate(reach) statt aus points[1])
reichte an zwei Stellen nicht.
Erstens kann eine Schleife geradewegs von ihrem Knoten weg- und auf
demselben Weg wieder zurücklaufen - am echten Netz eine 21,63 m lange
Boulevard-Schleife, deren Punktliste den Hinweg exakt rückwärts
wiederholt. Landet reach nahe dieser Kehre, zeigt die Sehne vom Ursprung
weiter nach außen, obwohl die Kante dort längst umkehrt: Die Markierung
lag an dieser einen Schleife weiterhin vollständig (100 %) neben der
Fahrbahn. Die Richtung kommt jetzt aus dem letzten Teilstück von
substring(line, 0, reach) - der tatsächlichen Anfahrtsrichtung an der
Schnittstelle, gleich wohin die Kante von dort aus weiterläuft.
Zweitens blieb "origin" (der Kreuzungsknoten) der Anker für Balken und
Haltelinie. origin und der Punkt bei Streckenlänge reach fallen nur auf
einer geraden Kante zusammen - mit der neuen Anfahrtsrichtung, aber dem
Knoten als Anker, läge selbst eine gerade Fortsetzung ab reach
systematisch daneben, sobald die Kante zwischen Knoten und reach von der
reinen Ursprungsrichtung abweicht. Anker ist jetzt der tatsächliche Punkt
auf der Kante bei Streckenlänge min(reach, länge).
Nachgemessen am echten Netz: von 452,65 m² (vor Befund 3) über 342,66 m²
(nur die erste Korrektur) auf 110,06 m² (0,25 % der Gesamtfläche) - beide
Korrekturen zusammen eine Verbesserung um 75,7 %. Der Rest ist eine engere
Spielart desselben Problems: Ein Bogen kann auch noch zwischen reach und
reach + STOP_SETBACK + STOP_DEPTH abknicken, was ein einzelner
Richtungsstrahl nicht mehr auffangen kann - das verlangte die vom Befund
selbst genannte, größere Alternative ("die Markierung entlang der
tatsächlichen Kante aufbauen"), die diese Fixwelle nicht umsetzt. Siehe
Fixwelle-Bericht für die Einzelheiten und die verbleibenden Einzelfälle.
Neuer Test mit den echten Schleifenmaßen (9 m hin, 6 m quer, reach 11 m):
vor dieser Korrektur lag die gesamte Markierung an der Schleife daneben,
danach vollständig darauf.
Befund 8, zwei kleine, aber datenwirksame Funde:
- Zebrastreifen: Der Startversatz (-halb + BAR/2, Takt 2*BAR) liess die
Balkenreihe an einem Fahrbahnrand bündig sitzen und am gegenüber-
liegenden bis zu eine Taktbreite frei (0,6 m bei Hauptstraße/Boulevard,
0,4 m bei Wohnstraße), statt der laut Entwurf verlangten Aufteilung
"Balken 60 cm, Lücke 60 cm" über die ganze Breite. n = floor((2*halb +
BAR) / (2*BAR)) Balken, symmetrisch bei -(n-1)*BAR gestartet, lassen an
beiden Rändern denselben Rest frei (nachgemessen: 0,3 m statt 0/0,6 m
bei Hauptstraße, 0,2 m statt 0/0,4 m bei Wohnstraße), ohne die Balkenzahl
je Arm zu ändern.
- edges[].breite (Median aus der Distanztransformation) kam ungerundet an
(Werte wie 31.622776601683793), während jede andere Zahl in der Ausgabe
auf 2 Stellen gerundet ist. fahrbahn bleibt unverändert, weil es schon
eine der vier festen SECTION-Konstanten ist.
Zwei Tests, beide ohne die jeweilige Korrektur rot: die Zebrabalken einer
Kreuzungszufahrt lassen ungleiche Ränder (0,0/0,6 m statt 0,3/0,3 m), und
eine krumme Kantenbreite blieb ungerundet in der Ausgabe.
Befund 3: In junction_marks() kam die Richtung einer Zufahrt aus
origin -> points[1], also nur aus dem ersten Segment der Kante. Haltelinie
und Zebrastreifen wurden aber bis reach + STOP_SETBACK + STOP_DEPTH (bis
16 m) auf dieser Geraden aufgespannt - knickte die Kante innerhalb dieser
Strecke ab, landete die Markierung daneben statt darauf. Am echten Netz
(gemessen über alle 144 committeten Chunks): 452,65 m² von 43.906 m²
Markierungsfläche (1,03 %) liegen außerhalb der Fahrbahn, davon 291,44 m²
Zebra und 147,68 m² Haltelinie; die schlimmsten Einzelfälle sind
vollständige Haltelinien mit 5,41 m². Die Richtung kommt jetzt aus
line.interpolate(min(reach, länge)) - dem tatsächlichen Punkt auf der
Kante bei Streckenlänge reach - statt aus dem zweiten Stützpunkt.
Befund 4: offset_curve() liefert bei einem engen Zickzack eine
MultiLineString statt einer LineString; substring() in _dashed()
akzeptiert nur LineString und wirft dann GeometryTypeError. Am heutigen
Mosaik tritt das nicht auf, aber eine geänderte Korrektur oder eine
eingehängte Kante mit engem Knick hätte den Vier-Minuten-Lauf abgebrochen.
Zweiter, stiller Ast: Eine sehr spitze Kehre lässt offset_curve() leer
zurückkommen; _solid() puffert das ungeprüft zu einem Nullflächen-Polygon.
_offset() normalisiert jetzt auf eine Liste von LineStrings (leer -> [],
MultiLineString -> ihre geoms), _dashed() und _solid() iterieren darüber;
longitudinal() flacht die jetzt listenwertigen _solid()-Aufrufe ab.
Beide Befunde mit Tests, die ohne die jeweilige Korrektur rot sind: der
Zickzack-Fall bricht am unkorrigierten Code mit GeometryTypeError ab, der
Kehre-Fall lieferte zwei Nullflächen-Polygone unter den Rand-/
Doppellinien.
reach kommt vom breitesten Arm der ganzen Kreuzung, wurde aber fuer jeden Arm
gleich verwendet. An einer Kreuzung, wo ein kurzer Arm auf einen breiten trifft,
reicht der kurze Arm dann nicht mehr bis reach (Zebrastreifen) oder bis
reach + STOP_SETBACK + STOP_DEPTH (Haltelinie) - die Markierung entstand trotzdem
und ragte ueber das Kantenende hinaus. junction_arms() in section.py schuetzt sich
dagegen laengst; junction_marks() jetzt auch, mit getrennter Pruefung je Markierung,
weil beide unterschiedlich viel Platz brauchen.
Am echten Netz: 38 von 1.508 Armstummeln sind zu kurz fuer die Haltelinie, groesster
Ueberstand 10,6 m (Fixrunde 1 des Koordinators). Auf tatsaechliche Zufahrten
umgerechnet (Schleifen zaehlen doppelt) sind es 44 von 1.536 - die Differenz zur
Kantenzahl war anfangs unklar, ist jetzt aber nachgerechnet und im Kommentar
festgehalten. markings(graph) sinkt dadurch von 22.586 auf 22.336 Flaechen (-206
Zebrabalken, -44 Haltelinien) - genau das vorher falsch platzierte Material.
Dazu zwei bisher uebersehene Stellen mit demselben zu lockeren Testfilter
(abs(centroid.y) < 20) auf die halbe Fahrbahnbreite verengt, wie schon an den zwei
Schwesterstellen der vorigen Fixrunde - die Haltelinie des Suedarms erfuellte die
lockere Bedingung ebenfalls, next() traf bisher nur zufaellig zuerst auf den Ostarm.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Zebrastreifen im Miami-Balkenmuster und Haltelinie je Zufahrt, dazu markings(graph)
als Sammelfunktion fuer Task 10. Beim Nachrechnen der Pruefkoerper zwei Fehler im
Auftrag gefunden und vor der Uebernahme berichtigt:
Die Haltelinie lag auf der falschen Fahrbahnhaelfte. "across" ist schon als links von
"along" definiert (dieselbe Konvention wie offset_curve() in _offset()), und die Umkehr
der Fahrtrichtung an der Zufahrt braucht deshalb kein zusaetzliches Vorzeichen - der
Auftrag zog eins ab und landete damit auf der Gegenspur. Eine Probe mit offset_curve()
auf der Einfahrtlinie bestaetigt +across als die rechte Seite; alle vier Arme der
Testkreuzung ergeben danach das erwartete Windradmuster.
Zwei Testfilter (|x| bzw. |y| < 20) waren zu locker: An der vierarmigen Testkreuzung
liegen Balken benachbarter Arme nahe genug am Ursprung, um faelschlich mitgezaehlt zu
werden - falsches Gruen durch Verwechslung. Auf die halbe Fahrbahnbreite verengt.
Zusaetzlich gefunden und mit eigenem Test belegt: Eine Schleifenkante haengt mit beiden
Enden am selben Knoten und stellt zwei getrennte Zufahrten dar (wie junction_arms() in
section.py); ohne die zweite Richtung fehlten Haltelinie und Zebra auf einer Seite der
Schleife. Betrifft 28 von 690 Kreuzungen am echten Netz.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>