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:
1 parent
a3504df43d
commit
3c74202b5e
10 files changed
+333
-102
No files matched your search
@@ -72,12 +72,33 @@ def test_same_seed_gives_the_same_palms():
|
||||
assert apply_park(world(), layout(), seed=7).palms == apply_park(world(), layout(), seed=7).palms
|
||||
|
||||
|
||||
def test_corners_must_lie_at_a_road():
|
||||
def corners(**moved):
|
||||
game = {"sw": (0.0, 0.0), "se": (100.0, 0.0), "ne": (100.0, 200.0), "nw": (0.0, 200.0)}
|
||||
return SimpleNamespace(game={**game, **moved})
|
||||
|
||||
|
||||
def test_corners_must_lie_at_a_road_when_it_pulls_back():
|
||||
"""Die eine Richtung: die Straße bleibt, wo sie ist, die Ecke steht daneben."""
|
||||
roads = [(box(-20, -20, 0, 220), False), (box(100, -20, 120, 220), False)]
|
||||
check_corners(SimpleNamespace(game={"sw": (0.0, 0.0), "se": (100.0, 0.0), "ne": (100.0, 200.0), "nw": (0.0, 200.0)}), roads)
|
||||
far = SimpleNamespace(game={"sw": (0.0, 0.0), "se": (100.0, 0.0), "ne": (100.0, 200.0), "nw": (30.0, 200.0)})
|
||||
check_corners(corners(), roads)
|
||||
with pytest.raises(ParkError, match="Spielecke nw"):
|
||||
check_corners(far, roads)
|
||||
check_corners(corners(nw=(30.0, 200.0)), roads)
|
||||
|
||||
|
||||
def test_a_road_that_grew_inward_over_the_corner_is_caught():
|
||||
"""Die andere Richtung, und der Grund für die Messung gegen den Rand statt gegen die Fläche.
|
||||
|
||||
Die Westkante der linken Straße ist von x = 0 auf x = 6 nach innen gewandert; die SW- und die
|
||||
NW-Ecke liegen jetzt 6 m tief in der Fahrbahn. apply_park() schnitte die verbreiterte Straße
|
||||
wortlos wieder auf das alte Viereck zurück — deshalb muss die Prüfung hier abbrechen. Mit dem
|
||||
Flächenabstand tut sie das nicht: er ist für eine Ecke mitten in der Straße 0 (die Zusicherung
|
||||
unten misst genau das), und die alte Bedingung poly.distance(corner) <= 2.0 ließ den Fall
|
||||
durchlaufen.
|
||||
"""
|
||||
grown = [(box(-20, -20, 6, 220), False), (box(100, -20, 120, 220), False)]
|
||||
assert min(poly.distance(Point(0, 0)) for poly, _ in grown) == 0
|
||||
with pytest.raises(ParkError, match="Spielecke sw"):
|
||||
check_corners(corners(), grown)
|
||||
|
||||
|
||||
def test_flamingo_park_goes_into_a_world_with_roads_around_the_park():
|
||||
@@ -93,4 +114,10 @@ def test_flamingo_park_goes_into_a_world_with_roads_around_the_park():
|
||||
# Bekannter Zustand: die Zufahrt zum Parkplatz quert die Sperrfläche der Tribüne und wird dort
|
||||
# ausgeschnitten (Task 5). Jede weitere oder andere Warnung ist ein echter Befund.
|
||||
assert [m for m in messages if m.startswith("Warnung")] == ["Warnung: Zufahrt schneidet die Sperrfläche tribuene"]
|
||||
assert any(message.startswith("Flamingo Park:") for message in messages)
|
||||
# Die Flächenzahl gehört mit in den Vergleich: _surface_kind() kennt nur die Sportarten aus
|
||||
# FIELD_SPORTS und COURT_SPORTS und lässt jede andere wortlos weg. Taggt OpenStreetMap beim
|
||||
# nächsten Abruf ein Feld um — aus "tennis" wird "pickleball", aus "soccer" ein "multi" —,
|
||||
# verschwindet es aus dem Park, ohne dass eine Warnung oder ein anderer Test das bemerkt;
|
||||
# hier sinkt dann die 14, und der Abzug wird geprüft statt der Park stillschweigend kleiner.
|
||||
assert [m for m in messages if m.startswith("Flamingo Park:")] == [
|
||||
"Flamingo Park: 2 Wegflächen, 14 Flächen, 11 Gebäude, Jefferson Avenue 12 m breit"]
|
||||
Reference in new issue
Block a user