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>
This commit is contained in:
1 parent
8283109ceb
commit
8bc8ca19fc
5 files changed
+162
-23
No files matched your search
@@ -6,6 +6,7 @@ from shapely.geometry import LineString, Point, Polygon, box
|
||||
|
||||
from leonida.heights import PALM_CLEARANCE
|
||||
from leonida.park import ParkError, ParkLayout, World, apply_flamingo_park, apply_park, check_corners, load_park
|
||||
from leonida.roads import Edge, Graph, Node
|
||||
|
||||
DATA = Path(__file__).resolve().parents[1] / "data"
|
||||
|
||||
@@ -23,7 +24,12 @@ def world():
|
||||
land=[box(-100, -100, 200, 300)],
|
||||
surfaces=[(box(-30, -30, 130, 230), "urban")],
|
||||
roads=[(box(-50, 95, 150, 105), False), (box(-20, -20, 0, 220), False)],
|
||||
centerlines=[LineString([(-50, 100), (150, 100)]), LineString([(-5, 150), (10, 150)])],
|
||||
# Der zweite Ast endet 3 m vor dem Parkviereck (statt der ehemals 5 m): Genau 5 m wäre
|
||||
# MIN_EDGE selbst, und der Rest bliebe stehen (5.0 < MIN_EDGE ist falsch) statt zu
|
||||
# verschwinden - dieser Test prüft aber, dass ein echter Kurzrest wegfällt.
|
||||
graph=Graph(nodes=[Node((-50.0, 100.0)), Node((150.0, 100.0)), Node((-3.0, 150.0)), Node((10.0, 150.0))],
|
||||
edges=[Edge(0, 1, [(-50.0, 100.0), (150.0, 100.0)], 12.0, "wohnstrasse"),
|
||||
Edge(2, 3, [(-3.0, 150.0), (10.0, 150.0)], 6.0, "gasse")]),
|
||||
buildings=[(box(20, 60, 30, 70), {"height": 9.0, "source": "district"}, False), # ganz im Park
|
||||
(box(-6, 80, 4, 90), {"height": 9.0, "source": "district"}, False), # 40 % im Park
|
||||
(box(-4, 120, 6, 130), {"height": 9.0, "source": "district"}, False)], # 60 % im Park
|
||||
@@ -36,7 +42,8 @@ def test_old_roads_and_centerlines_end_at_the_park_edge():
|
||||
assert all(poly.intersection(QUAD).area < 1e-6 for poly, _ in result.roads if not poly.equals(SERVICE))
|
||||
assert any(poly.contains(Point(-30, 100)) for poly, _ in result.roads)
|
||||
assert any(poly.contains(Point(120, 100)) for poly, _ in result.roads)
|
||||
assert sorted(line.bounds for line in result.centerlines) == [(-50.0, 100.0, 0.0, 100.0), (100.0, 100.0, 150.0, 100.0)]
|
||||
lines = [LineString(e.points) for e in result.graph.edges]
|
||||
assert sorted(line.bounds for line in lines) == [(-50.0, 100.0, 0.0, 100.0), (100.0, 100.0, 150.0, 100.0)]
|
||||
|
||||
|
||||
def test_old_surfaces_buildings_and_palms_in_the_park_go():
|
||||
@@ -101,16 +108,30 @@ def test_a_road_that_grew_inward_over_the_corner_is_caught():
|
||||
check_corners(corners(), grown)
|
||||
|
||||
|
||||
def test_eckenpruefung_misst_den_korridor_nicht_die_fahrbahn():
|
||||
"""Regressionswache: world.roads ist der Korridor, und die Prüfung gehört an ihn.
|
||||
|
||||
Eine 12-m-Straße bekommt 7 m Fahrbahn. Gegen die Fahrbahn gemessen lägen die Ecken des
|
||||
Parks 2,5 m weiter draußen als erlaubt, und jeder Lauf bräche ab.
|
||||
"""
|
||||
from leonida.section import SECTION
|
||||
assert SECTION["wohnstrasse"].corridor - SECTION["wohnstrasse"].carriageway == 5.0
|
||||
# Die Halbdifferenz trifft die Standardtoleranz von check_corners (2.5 m) exakt: schon der
|
||||
# kleinste real gemessene Eckenabstand (0,14 m, siehe check_corners()) reicht addiert, um
|
||||
# jeden Lauf abbrechen zu lassen, wenn hier gegen die Fahrbahn statt den Korridor gemessen wird.
|
||||
assert (SECTION["wohnstrasse"].corridor - SECTION["wohnstrasse"].carriageway) / 2 >= 2.5
|
||||
|
||||
|
||||
def test_flamingo_park_goes_into_a_world_with_roads_around_the_park():
|
||||
config = load_park(DATA / "flamingo-park.toml")
|
||||
park = Polygon([config.game[key] for key in ("sw", "se", "ne", "nw")])
|
||||
ring = park.buffer(15, join_style="mitre").difference(park)
|
||||
start = World(land=[park.buffer(400)], surfaces=[], roads=[(ring, False)], centerlines=[], buildings=[], palms=[])
|
||||
start = World(land=[park.buffer(400)], surfaces=[], roads=[(ring, False)], graph=Graph(), buildings=[], palms=[])
|
||||
result, messages = apply_flamingo_park(start, DATA / "flamingo-park.toml", seed=20260912)
|
||||
assert {"park", "field", "court", "pool", "parking", "sand"} <= {kind for _, kind in result.surfaces}
|
||||
assert len([b for b in result.buildings if b[1]["source"] == "osm"]) == 11
|
||||
assert result.paths and result.palms
|
||||
assert LineString([config.street.start, config.street.end]) in result.centerlines
|
||||
assert any(e.points == [tuple(config.street.start), tuple(config.street.end)] for e in result.graph.edges)
|
||||
# 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"]
|
||||
|
||||
Reference in new issue
Block a user