fix(extract): Regressionsschutz für edges-Sortierschlüssel, _line()-Docstring berichtigt

Fixrunde 1 zu Task 10 (Rückmeldung des Koordinators). Zwei Punkte:

Die edges-Korrektur aus dem Hauptcommit (Sortierschlüssel deckte nur (line, klasse) ab,
nicht breite/fahrbahn) hatte keinen committeten Regressionstest - nur eine Ad-hoc-Probe,
die nicht mitkam. Drei Tests ergänzt: zwei edges-Einträge mit gleicher Linie/Klasse aber
unterschiedlicher Breite in beiden Reihenfolgen (der eigentliche Regressionsschutz - mit
dem alten zweiteiligen Schlüssel probeweise reproduziert, um die Fehlermeldung zu belegen,
dann zurückgestellt), zwei bridges-Einträge in beiden Reihenfolgen, und eine Linie gegen
ihre exakte Umkehrung einmal als curb, einmal als edges - das ist die Zusicherung, auf der
_line()s ganze Normalisierung beruht, und stand bisher nirgends als Test.

_line()-Docstring behauptete im Präsens, Laternen wählten ihre Seite schon aus der
Kantenrichtung statt aus der Listenposition. Das stimmt nicht: src/world/props.js:107
wählt weiterhin über den geraden/ungeraden Index (vergeLampPoints(), liest
chunk.centerlines/chunk.roads). Docstring jetzt als Bedingung formuliert - was
vorausgesetzt wird und wo es eingelöst wird (eine geometriebasierte Nachfolgefunktion in
props.js, im Plan als sidewalkLampPoints() vorgesehen) - statt als Tatsachenbehauptung,
und ohne Aufgabennummer im Kommentar.

overview.py, geometry.js und props.js bewusst nicht angefasst (Koordinator: das ist
Sache der Aufgaben 13-15).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Sonnet 5 committed 2026-09-17 10:09:27 +02:00
1 parent f97b7d9c81
commit 887cb09433
2 files changed
+64 -5

No files matched your search

+12 -5
View File
@@ -74,11 +74,18 @@ def _sorted_surfaces(entries: list[dict]) -> list[dict]:
def _line(line, i: int, j: int) -> list[list[float]]:
"""Ein Linienzug in Chunk-Koordinaten, richtungsunabhängig festgelegt.
normalize() dreht eine Linie in eine feste Laufrichtung. Das ist hier anders als bei den
früheren centerlines erwünscht: Die Laternen wählen ihre Seite seit Teil 2 aus der
Kantenrichtung *und* prüfen gegen die Gehwegfläche, hängen also nicht mehr an der
Listenposition (siehe props.js). Damit darf die Ausgabe sortiert und normalisiert werden,
und ein Lauf erzeugt wieder Datei für Datei dasselbe.
normalize() dreht eine Linie in eine feste Laufrichtung. Normalisieren und Sortieren ist für
curb und edges nur zulässig, solange keine Laterne ihre Straßenseite aus der Listenposition
wählt, sondern aus der Geometrie - Kantenrichtung und eine Prüfung gegen die Gehwegfläche.
Bei den früheren centerlines war das nicht so, deshalb blieben sie unsortiert und
unnormalisiert.
Eingelöst wird die Voraussetzung erst mit einer geometriebasierten Nachfolgefunktion in
props.js (vorgesehen als sidewalkLampPoints(), als Ablösung von vergeLampPoints()). Heute
liest dort noch niemand curb oder edges: vergeLampPoints() wählt die Seite weiterhin über den
geraden/ungeraden Index (Zeile 107) und läuft auf chunk.centerlines/chunk.roads. Bis zur
Ablösung ist das hier deshalb folgenlos - nicht weil die Bedingung gälte, sondern weil noch
kein Verbraucher zuhört.
"""
ox = SLICE["x0"] + i * CHUNK_SIZE
oy = SLICE["y1"] - j * CHUNK_SIZE
+52
View File
@@ -139,3 +139,55 @@ def test_neue_schichten_sind_lauf_stabil():
curb=[l2, l1], sidewalk=[a], markings=[(b, "zebra"), (a, "zebra")],
lots=[b, a], bridges=[], edges=[], buildings=[], palms=[])
assert eins == zwei
def test_edges_sortierschluessel_deckt_alle_felder_ab():
"""Regressionsschutz fuer den Fund aus Fixrunde 1: Zwei Kanten mit gleicher Linie und
gleicher Klasse, aber unterschiedlicher Breite, duerfen nicht an der Eingabereihenfolge
haengen bleiben. Mit dem Schluessel (line, klasse) allein - wie im urspruenglichen
Auftragscode - loest keiner der beiden Faelle den Tie auf, sorted() bleibt dann stabil und
reicht die Eingabereihenfolge unveraendert durch; erst breite/fahrbahn im Schluessel machen
die zwei Eintraege wieder unterscheidbar und damit total geordnet."""
from shapely.geometry import LineString
linie = LineString([(70, 2400), (70, 2450)])
schmal = (linie, "gasse", 6.0, 6.0)
breit = (linie, "gasse", 99.0, 99.0)
eins = chunk_payload(0, 0, land=[], surfaces=[], carriageway=[], curb=[], sidewalk=[],
markings=[], lots=[], bridges=[], edges=[schmal, breit],
buildings=[], palms=[])
zwei = chunk_payload(0, 0, land=[], surfaces=[], carriageway=[], curb=[], sidewalk=[],
markings=[], lots=[], bridges=[], edges=[breit, schmal],
buildings=[], palms=[])
assert eins == zwei
def test_bridges_sind_lauf_stabil():
"""Gegenprobe zu test_neue_schichten_sind_lauf_stabil, die bridges leer laesst: zwei
Bruecken in vertauschter Reihenfolge muessen dieselbe Ausgabe ergeben."""
a = Polygon([(10, 2400), (20, 2400), (20, 2450), (10, 2450)])
b = Polygon([(30, 2400), (40, 2400), (40, 2450), (30, 2450)])
eins = chunk_payload(0, 0, land=[], surfaces=[], carriageway=[], curb=[], sidewalk=[],
markings=[], lots=[], bridges=[a, b], edges=[], buildings=[], palms=[])
zwei = chunk_payload(0, 0, land=[], surfaces=[], carriageway=[], curb=[], sidewalk=[],
markings=[], lots=[], bridges=[b, a], edges=[], buildings=[], palms=[])
assert eins == zwei
def test_curb_und_edges_sind_richtungsunabhaengig():
"""Die Zusicherung, auf der die ganze Normalisierung in _line() beruht (siehe dessen
Docstring): eine Linie und ihre exakte Umkehrung muessen fuer curb wie fuer edges dieselbe
Ausgabe ergeben. Einzeln geprueft (nur curb bzw. nur edges besetzt), damit ein kuenftiger
Bruch das betroffene Feld benennt statt nur "irgendwas stimmt nicht"."""
from shapely.geometry import LineString
hin = LineString([(10, 2400), (18, 2470), (22, 2500)])
zurueck = LineString(list(reversed(list(hin.coords))))
rest = dict(land=[], surfaces=[], carriageway=[], sidewalk=[], markings=[], lots=[],
bridges=[], buildings=[], palms=[])
eins_curb = chunk_payload(0, 0, curb=[hin], edges=[], **rest)
zwei_curb = chunk_payload(0, 0, curb=[zurueck], edges=[], **rest)
assert eins_curb["curb"] == zwei_curb["curb"]
eins_edges = chunk_payload(0, 0, curb=[], edges=[(hin, "wohnstrasse", 12.0, 7.0)], **rest)
zwei_edges = chunk_payload(0, 0, curb=[], edges=[(zurueck, "wohnstrasse", 12.0, 7.0)], **rest)
assert eins_edges["edges"] == zwei_edges["edges"]