feat(extract): Chunk-Format für Fahrbahn, Bordstein, Gehweg und Markierung
chunk_payload() liefert jetzt carriageway, curb, sidewalk, markings, lots, bridges und edges statt roads und centerlines - die Ausgabe fuer Task 12 (Pipeline) und Task 13/14 (Zeichnen). curb ist ein Linienzug: Bordstein hat keine eigene Flaeche, Loch und Fahrbahn teilen sich den Umriss. _line() normalisiert die Laufrichtung wie _rings() die Wicklung - anders als bei den alten centerlines duerfen curb und edges das, weil die Laternen ihre Seite seit Teil 2 aus Kantenrichtung und Gehwegpruefung waehlen, nicht mehr aus der Listenposition. Selbstpruefung zur Lauf-Stabilitaet ging ueber die zwei mitgelieferten Tests hinaus, die bridges und edges nur leer durchreichen. Ad-hoc nachgerechnet und dabei eine Luecke gefunden: Der Sortierschluessel von edges deckte nur (line, klasse) ab, nicht auch breite und fahrbahn wie bei buildings ueblich (dort alle vier Felder). Bei gleichem line+klasse, aber unterschiedlicher breite/fahrbahn, waere die Ausgabe wieder an der Listenposition haengengeblieben - in der echten Pipeline legt SECTION[klasse] beide Werte fest und kann so nicht divergieren, der Schluessel deckt jetzt trotzdem alle vier Felder ab, wie bei buildings. test_centerlines_keep_their_order_and_direction geht weg: Sie bewies die jetzt bewusst umgekehrte Zusicherung (Reihenfolge und Richtung tragen Bedeutung); centerlines selbst entfaellt. Die zwei neuen Tests aus dem Auftrag belegen das Gegenteil fuer die Nachfolgefelder. Bricht wie angekuendigt run.py (ruft chunk_payload noch mit roads=/centerlines= auf) - das repariert Task 11/12. Anders als im Auftrag angenommen bleibt die Testsuite komplett gruen (248/248): test_park_apply.py und test_park_street.py haengen an park.py's eigenem World, nicht an chunk_payload, und kein anderer Test importiert emit.py. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4bf670e0d0
commit
bdc6a0ffe5
2 files changed
+81
-41
No files matched your search
@@ -71,21 +71,27 @@ def _sorted_surfaces(entries: list[dict]) -> list[dict]:
|
||||
for entry in sorted(run, key=lambda e: e["poly"])]
|
||||
|
||||
|
||||
def chunk_payload(i, j, *, land, surfaces, roads, centerlines, buildings, palms, paths=()) -> 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.
|
||||
"""
|
||||
ox = SLICE["x0"] + i * CHUNK_SIZE
|
||||
oy = SLICE["y1"] - j * CHUNK_SIZE
|
||||
return [[round(x - ox, 2), round(-y + oy, 2)] for x, y in shapely.normalize(line).coords]
|
||||
|
||||
|
||||
def chunk_payload(i, j, *, land, surfaces, carriageway, curb, sidewalk, markings, lots,
|
||||
bridges, edges, buildings, palms, paths=()) -> dict:
|
||||
"""Ein Chunk als Ausgabestruktur, deterministisch sortiert.
|
||||
|
||||
Sortiert wird nach der gerundeten Ausgabestruktur selbst, nicht nach Flaeche, Schwerpunkt oder
|
||||
Bounding-Box: nur sie ist total geordnet und ohne Sonderfall, denn zwei Eintraege mit gleichem
|
||||
Schluessel sind dann bytegleich und ihre Reihenfolge wieder egal. Die abgeleiteten Zahlen
|
||||
koennen sich dagegen zwei verschiedene Flaechen teilen, und die Ausgabe hinge fuer diese
|
||||
beiden weiterhin an der Listenposition, die GEOS vergibt.
|
||||
|
||||
centerlines bleiben bewusst unsortiert und unnormalisiert: dort traegt sowohl die Reihenfolge
|
||||
als auch die Laufrichtung Bedeutung. vergeLampPoints() in props.js setzt jede Laterne
|
||||
abwechselnd links und rechts der Fahrbahn (gerader Index links, ungerader rechts) und verwirft
|
||||
sie, wenn auf ihrer Seite kein freier Platz ist - an den ausgelieferten Chunks wechselten beim
|
||||
Sortieren 1.848 von 3.921 Laternen die Strassenseite. Die Mittellinien sind auch ohne
|
||||
Sortieren stabil: sie entstehen aus centerlines() und difference(), nie aus unary_union().
|
||||
Schluessel sind dann bytegleich und ihre Reihenfolge wieder egal.
|
||||
"""
|
||||
ox = SLICE["x0"] + i * CHUNK_SIZE
|
||||
oy = SLICE["y1"] - j * CHUNK_SIZE
|
||||
@@ -94,14 +100,21 @@ def chunk_payload(i, j, *, land, surfaces, roads, centerlines, buildings, palms,
|
||||
"origin": [ox, -oy],
|
||||
"land": sorted(_rings(p, i, j) for p in land),
|
||||
"surfaces": _sorted_surfaces([{"poly": _rings(p, i, j), "kind": k} for p, k in surfaces]),
|
||||
"roads": sorted(({"poly": _rings(p, i, j), "bridge": b} for p, b in roads),
|
||||
key=lambda r: (r["poly"], r["bridge"])),
|
||||
"centerlines": [[[round(x - ox, 2), round(-y + oy, 2)] for x, y in ln.coords] for ln in centerlines],
|
||||
"carriageway": sorted(({"poly": _rings(p, i, j), "klasse": k} for p, k in carriageway),
|
||||
key=lambda c: (c["poly"], c["klasse"])),
|
||||
"curb": sorted(_line(l, i, j) for l in curb),
|
||||
"sidewalk": sorted(({"poly": _rings(p, i, j)} for p in sidewalk), key=lambda s: s["poly"]),
|
||||
"markings": sorted(({"poly": _rings(p, i, j), "art": a} for p, a in markings),
|
||||
key=lambda m: (m["poly"], m["art"])),
|
||||
"lots": sorted(({"poly": _rings(p, i, j)} for p in lots), key=lambda l: l["poly"]),
|
||||
"bridges": sorted(({"poly": _rings(p, i, j)} for p in bridges), key=lambda b: b["poly"]),
|
||||
"edges": sorted(({"line": _line(l, i, j), "klasse": k, "breite": w, "fahrbahn": f}
|
||||
for l, k, w, f in edges),
|
||||
key=lambda e: (e["line"], e["klasse"], e["breite"], e["fahrbahn"])),
|
||||
"buildings": sorted(({"poly": _rings(p, i, j), "height": h["height"],
|
||||
"source": h["source"], "speculative": s} for p, h, s in buildings),
|
||||
key=lambda b: (b["poly"], b["height"], b["source"], b["speculative"])),
|
||||
"palms": sorted([round(x - ox, 2), round(-y + oy, 2)] for x, y in palms),
|
||||
# Parkwege (Flamingo Park, docs/superpowers/specs/2026-09-15-flamingo-park-design.md), aufgebaut wie roads.
|
||||
"paths": sorted(({"poly": _rings(p, i, j)} for p in paths), key=lambda p: p["poly"]),
|
||||
}
|
||||
|
||||
@@ -140,7 +153,7 @@ def write_debug_png(out_dir: Path, mosaic, payloads: list[dict]) -> None:
|
||||
for rings in payload["land"]:
|
||||
ring = rings[0] # nur der Aussenring; Loecher werden nicht gezeichnet
|
||||
draw.line([tuple(pt) for pt in ring] + [tuple(ring[0])], fill=(0, 255, 255), width=1)
|
||||
for road in payload["roads"]:
|
||||
for road in payload["carriageway"]:
|
||||
ring = road["poly"][0]
|
||||
draw.line([tuple(pt) for pt in ring], fill=(255, 0, 255), width=1)
|
||||
for b in payload["buildings"]:
|
||||
|
||||
Reference in new issue
Block a user