fix(extract): Chunk-Ausgabe lauf-stabil, Eckenprüfung in beide Richtungen

emit.py normalisiert jede ausgegebene Fläche und sortiert die Listen nach der
Ausgabestruktur selbst. unary_union() in apply_street() gab das kartenweite
Straßenpolygon mit anderem Ringstartpunkt und an anderer Listenstelle zurück und
änderte damit 88 Chunkdateien, in denen sich geometrisch nichts bewegt hatte
(symmetrische Differenz 0,00 m²). Belagsarten und Mittellinien behalten ihre
Reihenfolge: overview.py malt die Beläge in Listenreihenfolge übereinander, und
vergeLampPoints() hängt an Reihenfolge und Laufrichtung der Mittellinien —
Sortieren verschöbe dort 1.848 von 3.921 Laternen auf die andere Straßenseite.

check_corners() misst gegen den Straßenrand statt gegen die Straßenfläche. Alle
vier Ecken liegen in der Fahrbahn, ihr Flächenabstand ist 0,000 m; die alte
Prüfung schlug deshalb nur an, wenn eine Straße sich zurückzieht. Eine nach
innen verbreiterte Straße hätte apply_park() still auf das veraltete Viereck
zurückgeschnitten.

Die Parkwege werden am Parkviereck abgeschnitten — die runden Kappen von
LineString.buffer() lagen mit 95,96 m² darüber hinaus, rund 41 m² davon sichtbar
als Betonzungen auf den umgebenden Belägen. load_elements() prüft den OSM-Abzug
so streng wie die Parkdatei, mit Dateiname statt nacktem KeyError.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5 committed 2026-09-16 13:43:01 +02:00
1 parent a3504df43d
commit 3c74202b5e
10 files changed
+333 -102

No files matched your search

+25 -1
View File
@@ -2,7 +2,7 @@ from pathlib import Path
import pytest
from leonida.park import Anchor, Keepout, ParkError, load_park, parse_park
from leonida.park import Anchor, Keepout, ParkError, load_elements, load_park, parse_park
DATA = Path(__file__).resolve().parents[1] / "data"
@@ -92,3 +92,27 @@ def test_committed_park_file_loads():
assert [k.name for k in config.keepouts] == ["hood-yard", "tribuene"]
assert (config.scale_u, config.offset_u) == (1.0, 6.25)
assert len(config.street.alley_cut) == 12
assert len(load_elements(config.osm_path)) > 0
@pytest.mark.parametrize("text, message", [
("{ abgeschnitten", "kein gültiges JSON"),
('{"license": "ODbL 1.0"}', "Feld 'elements' fehlt oder ist keine Liste"),
('{"elements": {"1": {}}}', "Feld 'elements' fehlt oder ist keine Liste"),
('{"elements": [42]}', "elements[1] braucht eine Tabelle 'tags'"),
('{"elements": [{"id": 7, "geometry": [[25.78, -80.14], [25.79, -80.14]]}]}',
"elements[1] (id 7) braucht eine Tabelle 'tags'"),
('{"elements": [{"id": 7, "tags": {"highway": "footway"}}]}',
"elements[1] (id 7) braucht 'geometry' als Liste von Punkten"),
('{"elements": [{"id": 7, "tags": {}, "geometry": [[25.78]]}]}',
"elements[1] (id 7) braucht 'geometry' als Liste von Punkten"),
])
def test_a_broken_osm_dump_aborts_with_the_file_name(tmp_path, text, message):
"""osm.py wird von Hand aufgerufen; ein alter oder halb geschriebener Abzug ist der Normalfall,
nicht die Ausnahme. Er soll denselben ParkError mit Dateinamen geben wie die Parkdatei, statt
als KeyError irgendwo in build_layout() zu landen."""
path = tmp_path / "flamingo-park.json"
path.write_text(text, encoding="utf-8")
with pytest.raises(ParkError, match="flamingo-park.json") as err:
load_elements(path)
assert message in str(err.value)