Commit Graph
4 Commits
Author SHA1 Message Date
knsoandClaude Sonnet 5 bab83bce5f fix(extract): insert_edge() knickt Kanten zu ihrem Knoten, verliert keine Kreuzung mehr bei Überlappung
Fixrunde 2 zu Task 11, zwei von der Koordination beim Bau von Gegenbeispielen gefundene Fehler in
insert_edge() aus Fixrunde 1, einer davon schon in den echten Daten nachweisbar:

1. attach() darf einen bis zu JUNCTION_RADIUS (8 m) entfernten Knoten wiederverwenden, aber
   _add_piece() übernahm dessen ID in a/b, ohne den zugehörigen Endpunkt in points auf die
   Knotenkoordinate zu ziehen - die Kante hing dadurch an einem Punkt, an dem sie geometrisch gar
   nicht lag. Am echten Netz endete die Jefferson Avenue so 6,65 m neben Knoten 561, an dem sie
   laut Graph hängt; section.py baut die Kreuzungsarme in junction_arms() aus genau diesen
   Endpunkten. Behoben wie _contract_short() es seit Task 4 vormacht: den betroffenen Endpunkt
   durch die Knotenkoordinate ersetzen, die Kante bekommt dadurch einen kurzen Knick zum Knoten.
   Neue Invariante über den ganzen Graphen (test_roads_insert.py): points[0] == nodes[a].point und
   points[-1] == nodes[b].point für jede Kante.

2. _crossing_points() kannte nur Point und MultiPoint. Überlappt die neue Linie eine Bestandskante
   ein Stück weit und kreuzt sie danach an einer anderen Stelle wirklich, liefert intersection()
   eine GeometryCollection aus der Überlappung (LineString) und dem echten Kreuzungspunkt (Point) -
   die wurde bisher komplett verworfen, samt Kreuzungspunkt. Jetzt werden aus jeder Geometrie
   (Point, MultiPoint oder GeometryCollection) nur die Point-Anteile behalten.

Festgehalten, nicht geändert: Kreuzt die neue Linie zwei verschiedene Kanten näher als
JUNCTION_RADIUS zueinander, verschmelzen beide zu einem Knoten - dieselbe Regel, mit der
_junction_clusters() beim Netzaufbau aus dem Skelett benachbarte Kreuzungspixel zusammenfasst.
Mit Fix 1 knickt die zweite beteiligte Kante jetzt sauber zu diesem gemeinsamen Knoten statt
knapp daneben zu enden. test_park_street.py zieht die Kreuzungsgrad-Zusicherung von ">= 3" auf
"== 4" an, weil an einer echten Kreuzung zweier gespaltener Straßen genau das gilt.

Am echten Netz nachgemessen: Die Jefferson Avenue endet jetzt exakt auf Knoten 561 (1551,67 |
332,67), nicht mehr 6,65 m daneben; die vier von insert_edge() neu geschriebenen Kanten haben
0,000000 m Lücke zu ihrem Knoten. Eine bereits vor Flamingo Park bestehende Rundungsdifferenz
zwischen unrundeten Knotenkoordinaten aus _junction_clusters() und den auf 2 Dezimalstellen
gerundeten Kantenpunkten aus clean() betrifft ~1.250 von 1.790 Kanten mit höchstens 7,1 mm
Abstand, unverändert vorher wie nachher - das ist keine Folge dieser beiden Fixe und liegt
außerhalb des heutigen Umfangs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 17:59:01 +02:00
knsoandClaude Sonnet 5 1f09e7b1d9 fix(extract): cut_edges() bildet keine Schleifen mehr, Straßen kreuzen das Netz statt es zu überlagern
Fixrunde 1 zu Task 11, zwei von der Koordination gefundene und am echten Netz nachgemessene
Fehler, beide durch die Senkung von MIN_CENTERLINE (15 m) auf MIN_EDGE (5 m) erst möglich:

1. cut_edges() hängte beide Enden eines Schnittstücks über attach() an - dessen 8-m-Umkreis fand
   bei einem Reststück zwischen 5 und 8 m Länge für beide Enden denselben ursprünglichen Knoten
   und machte aus dem geraden Stummel eine Schleife. section.py hält jeden Knoten ab Grad 3 für
   eine Kreuzung; an einem Sackgassenende wären dort Kreuzungsfläche, Zebrastreifen und
   Haltelinie entstanden. Die neue _cut_end() sucht nicht mehr im Umkreis, sondern nur noch nach
   exakter Übereinstimmung mit einem der beiden ursprünglichen Kantenenden.

2. apply_street() hängte die Jefferson Avenue nur mit einem attach()-Paar an ihre beiden Enden.
   Am echten Netz liegt ihr Nordende 23,2 m von jedem Knoten entfernt, aber die Straße kreuzt auf
   dem Weg zu ihrem Südende (6,6 m von Knoten 561) die 11th Street mittendrin, ohne dass dort ein
   Knoten entsteht - zwei Fahrbahnen hätten sich unverbunden überlagert. Die neue insert_edge()
   bricht jede gekreuzte Kante am Kreuzungspunkt auf und teilt sich dort einen Knoten mit der
   neuen Straße (mit attach() für die Knotenwahl, wie gehabt); apply_street() benutzt sie jetzt
   statt des attach()-Paars.

Am echten Netz (1.537 Knoten, 1.790 Kanten vor Flamingo Park) bestätigt: Jefferson Avenue bricht
jetzt korrekt an der 11th Street auf (neuer Knoten, Grad 4) und hängt am Südende weiterhin an
Knoten 561 (Grad 3 -> 4); die Meldezeile und die Sperrflächen-Warnung blieben wörtlich. Die
Schleifenzahl im Gesamtnetz sank von 196 auf 193 (die Park-Anlage schneidet alte Schleifen im
Parkgebiet weg), keine der verbliebenen 193 entstand neu - jede hat ein Vor-Park-Gegenstück im
5-m-Umkreis. Keine Kante unter 1 m, weder vorher noch nachher.

Neue Tests: test_roads_cut.py (Schleifenregression), test_roads_insert.py (drei Szenarien:
Kreuzung in der Mitte, Anhängen an einen Grad-3-Knoten, Kreuzungspunkt nahe einem vorhandenen
Knoten), test_park_street.py (Knotengrad an beiden Kreuzungen der Park-Zufahrtsstraße).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 17:26:42 +02:00
knsoandClaude Sonnet 5 8bc8ca19fc feat(extract): Flamingo Park hängt seine Straße an den Graphen
World.centerlines wird zu World.graph: apply_street() und apply_park() schneiden und
verlängern jetzt den Straßengraphen statt einer losen Liste von Mittellinien, mit den neuen
roads.cut_edges()/attach() als Gegenstück zum bestehenden Schneiden der Straßenflächen.
apply_park() schneidet dabei einmal am Parkviereck (nicht je Sperrfläche - das schneidet
tatsächlich nichts, was der Parkumriss nicht schon geschnitten hätte). check_corners() bleibt
unverändert gegen world.roads (den Korridor) messen, mit einem erklärenden Docstring-Absatz und
einer Regressionswache dafür in den Tests.

MIN_CENTERLINE (15 m) entfällt zugunsten von MIN_EDGE (5 m): Der alte Grund - Laternen wählten
ihre Straßenseite über den Listenindex - fällt weg, sobald eine spätere Aufgabe die Seite aus
der Geometrie bestimmt. test_park_street.py dokumentiert das jetzt: die beiden 11-m-Enden der
geschnittenen Gasse bleiben deshalb stehen, statt wie vorher zu verschwinden.

Beim Nachrechnen der Prüfkörper aus dem Auftrag fielen zwei echte Fehler auf: Der Reste-unter-
Mindestlänge-Test schnitt ursprünglich bis übers Streckenende hinaus, sodass der Rest schon
geometrisch verschwand und der MIN_EDGE-Filter nie geprüft wurde - jetzt schneidet er innerhalb
der Strecke, sodass tatsächlich ein zu kurzes Stück verworfen wird. Und die neue
Korridor-Regressionswache verglich (12-7)/2 > 2.5, was mit den echten Maßen immer falsch ist
(2.5 > 2.5); korrigiert auf >=, denn die Halbdifferenz trifft die Toleranz exakt.

Ausserdem musste eine Test-Fixture in test_park_apply.py von einem 5-m-Reststück (exakt an der
neuen MIN_EDGE-Grenze, hätte also entgegen der ursprünglichen Testabsicht überlebt) auf 3 m
verkürzt werden, damit der Test weiterhin prüft, dass echte Kurzreste wegfallen.

run.py bleibt unangetastet: Es ist bereits durch eine frühere Aufgabe gebrochen und wird von der
nächsten Aufgabe repariert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 10:41:08 +02:00
knsoandClaude Sonnet 5 cfc9fc0690 feat(extract): Jefferson Avenue als gerade Straße statt der Gasse
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 07:28:27 +02:00