docs: Task 12 gegen das echte Netz berichtigen
Vor dem Auftrag die Kette am echten Netz nachgemessen. Vier Fehler im Plan: - Der Zuschnitt auf Chunks verwirft Stücke bis 0,5 m², ein Strich ist 0,3 m² - keiner der 4.363 Striche hätte überlebt. Markierungen bekommen eine eigene Splittergrenze. - _classified() stand in der Chunk-Schleife: 144 x 5,8 s, rund 14 Minuten. - Der Entwurf setzt Palmen nach dem Straßenbau neu, der Plan nicht: 24 Palmen stünden auf der Fahrbahn. - Der erste Test verglich einen Aufruf mit sich selbst. Dazu Messskript und Erwartungen auf den Stand nach dem Flamingo Park. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6c73b7f816
commit
892558fc50
1 file changed
+175
-49
@@ -1985,11 +1985,21 @@ git commit -m "feat(extract): Flamingo Park hängt seine Straße an den Graphen"
|
||||
|
||||
**Dateien:**
|
||||
- Ändern: `tools/extract/run.py`
|
||||
- Ändern: `tools/extract/leonida/emit.py` (nur ein Parameter)
|
||||
- Test: `tools/extract/tests/test_pipeline_roads.py` (neu)
|
||||
|
||||
**Schnittstellen:**
|
||||
- Verbraucht: alles aus den Tasks 3–11.
|
||||
- Liefert: einen Lauf, der die neuen Schichten schreibt.
|
||||
- Liefert: einen Lauf, der die neuen Schichten schreibt. In `run.py` zwei testbare Helfer,
|
||||
`fit_to_carriageway(world, carriage) -> World` und `chunk_markings(marks, i, j) -> list[tuple[Polygon, str]]`;
|
||||
in `emit.py` `split_by_chunk_all(polys, i, j, min_area=0.5)`.
|
||||
|
||||
> **Vor dem Auftrag am echten Netz nachgemessen, vier Fehler dieser Aufgabe sind hier schon berichtigt:**
|
||||
> (1) `split_by_chunk_all()` verwirft Stücke bis 0,5 m². Ein Strich der gestrichelten Linien ist 3 m × 10 cm = 0,3 m² — alle 4.363 Striche der Stadt wären beim Zuschnitt verschwunden, nachgezählt: 0 übrig. Markierungen laufen deshalb über `chunk_markings()` mit eigener Grenze.
|
||||
> (2) `_classified()` stand in der Chunk-Schleife und hätte 144-mal je 5,8 s gerechnet, rund 14 Minuten. Es läuft jetzt einmal davor.
|
||||
> (3) Der Entwurf setzt die Palmen nach dem Straßenbau neu (Abschnitt Ablauf, Schritt 7), der Plan hatte das nicht. `palm_points()` hält nur Abstand zum gemessenen Korridor, und die Fahrbahn ragt an Kurven und Kreuzungen darüber hinaus: 24 Palmen stünden auf der Fahrbahn, 32 weitere näher als `PALM_KERB`.
|
||||
> (4) Der erste Test verglich denselben Aufruf mit sich selbst und konnte nicht fehlschlagen. Er prüft jetzt den Helfer, den `run.py` wirklich benutzt.
|
||||
> Die Erwartungszahlen in Schritt 4 und 5 sind **nach** dem Flamingo Park gemessen, so wie der Lauf sie erzeugt.
|
||||
|
||||
- [ ] **Schritt 1: Den fehlschlagenden Test schreiben**
|
||||
|
||||
@@ -2002,32 +2012,79 @@ from __future__ import annotations
|
||||
from shapely.geometry import box
|
||||
from shapely.ops import unary_union
|
||||
|
||||
from leonida.emit import chunk_box_of
|
||||
from leonida.park import World
|
||||
from leonida.roads import Edge, Graph, Node
|
||||
from leonida.street import build_street, punch
|
||||
from run import chunk_markings, fit_to_carriageway
|
||||
|
||||
|
||||
def _gerade_wohnstrasse():
|
||||
"""100 m Wohnstraße entlang y = 0: Fahrbahn 7 m breit, also von y = -3,5 bis 3,5."""
|
||||
graph = Graph(nodes=[Node((0.0, 0.0)), Node((100.0, 0.0))],
|
||||
edges=[Edge(0, 1, [(0.0, 0.0), (100.0, 0.0)], 12.0, "wohnstrasse")])
|
||||
return graph, build_street(graph, box(0, -6, 100, 6), bridges=[])
|
||||
|
||||
|
||||
def _welt(graph, *, surfaces=(), palms=()):
|
||||
return World(land=[box(-20, -50, 120, 50)], surfaces=list(surfaces), roads=[], graph=graph,
|
||||
buildings=[], palms=list(palms))
|
||||
|
||||
|
||||
def test_boden_und_belag_bekommen_dasselbe_loch():
|
||||
graph = Graph(nodes=[Node((0.0, 0.0)), Node((100.0, 0.0))],
|
||||
edges=[Edge(0, 1, [(0.0, 0.0), (100.0, 0.0)], 12.0, "wohnstrasse")])
|
||||
street = build_street(graph, box(0, -6, 100, 6), bridges=[])
|
||||
land = punch([box(-20, -50, 120, 50)], street.carriageway)
|
||||
belag = punch([box(-20, -50, 120, 50)], street.carriageway)
|
||||
assert abs(unary_union(land).area - unary_union(belag).area) < 0.01
|
||||
assert unary_union(land).intersection(street.carriageway).area < 0.01
|
||||
"""Vergisst run.py eine der beiden Schichten, liegt dort ein Belag über der Mulde oder die
|
||||
Bodenplatte schließt sie."""
|
||||
graph, street = _gerade_wohnstrasse()
|
||||
platte = box(-20, -50, 120, 50)
|
||||
welt = _welt(graph, surfaces=[(box(-20, -50, 50, 50), "urban"), (box(50, -50, 120, 50), "park")])
|
||||
out = fit_to_carriageway(welt, street.carriageway)
|
||||
boden = unary_union(out.land)
|
||||
assert boden.intersection(street.carriageway).area < 0.01, "die Fahrbahn liegt im Loch"
|
||||
loch = street.carriageway.intersection(platte).area
|
||||
assert abs(platte.area - boden.area - loch) < 0.01, "das Loch ist nicht größer als die Fahrbahn"
|
||||
for kind in ("urban", "park"):
|
||||
belag = unary_union([poly for poly, k in out.surfaces if k == kind])
|
||||
assert belag.intersection(street.carriageway).area < 0.01, f"kein Belag {kind} über der Mulde"
|
||||
assert abs(unary_union([poly for poly, _ in out.surfaces]).area - boden.area) < 0.01, "dasselbe Loch"
|
||||
|
||||
|
||||
def test_gehweg_liegt_nicht_im_loch():
|
||||
graph = Graph(nodes=[Node((0.0, 0.0)), Node((100.0, 0.0))],
|
||||
edges=[Edge(0, 1, [(0.0, 0.0), (100.0, 0.0)], 12.0, "wohnstrasse")])
|
||||
street = build_street(graph, box(0, -6, 100, 6), bridges=[])
|
||||
graph, street = _gerade_wohnstrasse()
|
||||
land = unary_union(punch([box(-20, -50, 120, 50)], street.carriageway))
|
||||
assert street.sidewalk.difference(land).area < 0.5, "der Gehweg liegt auf der Bodenplatte"
|
||||
|
||||
|
||||
def test_keine_palme_auf_oder_dicht_an_der_fahrbahn():
|
||||
"""Entwurf, Ablauf Schritt 7: Palmen werden nach dem Straßenbau neu gesetzt. Am echten Netz
|
||||
standen ohne diesen Schritt 24 Palmen auf der Fahrbahn und 32 weitere näher als PALM_KERB."""
|
||||
graph, street = _gerade_wohnstrasse()
|
||||
welt = _welt(graph, palms=[(50.0, 0.0), (50.0, 4.5), (50.0, 5.5)])
|
||||
out = fit_to_carriageway(welt, street.carriageway)
|
||||
assert out.palms == [(50.0, 5.5)], "auf der Fahrbahn und 1 m daneben weg, 2 m daneben bleibt"
|
||||
|
||||
|
||||
def test_striche_ueberleben_den_zuschnitt():
|
||||
"""Ein Strich der gestrichelten Mitte ist 3 m × 10 cm = 0,3 m². Mit der Splittergrenze der
|
||||
Flächen (0,5 m²) fiel am echten Netz jeder einzelne der 4.363 Striche weg."""
|
||||
zelle = chunk_box_of(0, 0)
|
||||
x, y = zelle.centroid.x, zelle.centroid.y
|
||||
stuecke = chunk_markings([(box(x, y, x + 3.0, y + 0.1), "gelb_gestrichelt")], 0, 0)
|
||||
assert [art for _, art in stuecke] == ["gelb_gestrichelt"]
|
||||
assert abs(stuecke[0][0].area - 0.3) < 1e-6
|
||||
|
||||
|
||||
def test_strich_ueber_der_chunkgrenze_wird_geteilt_nicht_verworfen():
|
||||
zelle = chunk_box_of(0, 0)
|
||||
grenze, y = zelle.bounds[2], zelle.centroid.y # rechte Kante von Chunk (0, 0)
|
||||
strich = [(box(grenze - 1.0, y, grenze + 2.0, y + 0.1), "weiss_gestrichelt")]
|
||||
assert [round(poly.area, 6) for poly, _ in chunk_markings(strich, 0, 0)] == [0.1]
|
||||
assert [round(poly.area, 6) for poly, _ in chunk_markings(strich, 1, 0)] == [0.2]
|
||||
```
|
||||
|
||||
- [ ] **Schritt 2: Test laufen lassen**
|
||||
|
||||
Ausführen: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_pipeline_roads.py -q`
|
||||
Erwartet: 2 bestanden — der Test prüft schon gebauten Code aus Task 7 und darf grün sein.
|
||||
Erwartet: FAIL schon beim Sammeln, `ImportError: cannot import name 'chunk_markings' from 'run'` — die Helfer entstehen erst in Schritt 3. (`pythonpath = ["."]` in `pyproject.toml` macht `run.py` für die Tests importierbar; `main()` läuft beim Import nicht.)
|
||||
|
||||
- [ ] **Schritt 3: `run.py` umbauen**
|
||||
|
||||
@@ -2070,15 +2127,15 @@ Nach `world, messages = apply_flamingo_park(...)` die Straßen bauen:
|
||||
bridge_polys = [p for p, bridge in world.roads if bridge]
|
||||
street = build_street(world.graph, corridor, bridge_polys)
|
||||
marks = markings(world.graph)
|
||||
world = replace(world,
|
||||
land=punch(world.land, street.carriageway),
|
||||
surfaces=[(part, kind) for poly, kind in world.surfaces
|
||||
for part in punch([poly], street.carriageway)])
|
||||
palmen = len(world.palms)
|
||||
world = fit_to_carriageway(world, street.carriageway)
|
||||
arten = dict(sorted(Counter(art for _, art in marks).items()))
|
||||
print(f"Straßen: {len(street.kerb)} Bordsteinstücke, {len(street.lots)} Restflächen, "
|
||||
f"{len(marks)} Markierungen")
|
||||
f"{len(marks)} Markierungen {arten}, {palmen - len(world.palms)} Palmen an der Fahrbahn entfernt")
|
||||
classified = _classified(street, world.graph) # einmal, nicht je Chunk: 5,8 s am echten Netz
|
||||
```
|
||||
|
||||
`replace` aus `dataclasses` und `unary_union` aus `shapely.ops` importieren.
|
||||
`replace` aus `dataclasses`, `Counter` aus `collections`, `unary_union` aus `shapely.ops` und `PALM_KERB` aus `leonida.heights` importieren (dort steht der Import von `assign_heights, palm_points` schon).
|
||||
|
||||
Die Chunk-Schleife auf die neuen Schlüssel umstellen:
|
||||
|
||||
@@ -2088,13 +2145,12 @@ Die Chunk-Schleife auf die neuen Schlüssel umstellen:
|
||||
land=list(split_by_chunk_all(world.land, i, j)),
|
||||
surfaces=[(part, kind) for (p, kind) in world.surfaces
|
||||
for part in split_by_chunk_all([p], i, j)],
|
||||
carriageway=[(part, klass) for poly, klass in _classified(street, world.graph)
|
||||
carriageway=[(part, klass) for poly, klass in classified
|
||||
for part in split_by_chunk_all([poly], i, j)],
|
||||
curb=[part for line in street.kerb
|
||||
for part in _clip_lines(line, chunk_box_of(i, j))],
|
||||
sidewalk=list(split_by_chunk_all(_pieces(street.sidewalk), i, j)),
|
||||
markings=[(part, art) for poly, art in marks
|
||||
for part in split_by_chunk_all([poly], i, j)],
|
||||
markings=chunk_markings(marks, i, j),
|
||||
lots=list(split_by_chunk_all(street.lots, i, j)),
|
||||
bridges=list(split_by_chunk_all(bridge_polys, i, j)),
|
||||
edges=[(part, e.klass, e.width, SECTION[e.klass].carriageway)
|
||||
@@ -2107,9 +2163,12 @@ Die Chunk-Schleife auf die neuen Schlüssel umstellen:
|
||||
))
|
||||
```
|
||||
|
||||
Dazu drei kleine Helfer oben in `run.py`:
|
||||
Dazu die Helfer oben in `run.py`:
|
||||
|
||||
```python
|
||||
MARKING_MIN_AREA = 0.001 # m² — ein Strich ist 3 m × 10 cm = 0,3 m², die Flächengrenze 0,5 m² verwürfe ihn
|
||||
|
||||
|
||||
def _pieces(geometry) -> list:
|
||||
return [g for g in getattr(geometry, "geoms", [geometry]) if not g.is_empty]
|
||||
|
||||
@@ -2120,24 +2179,66 @@ def _clip_lines(line, cell) -> list:
|
||||
return [g for g in _pieces(part) if g.geom_type == "LineString" and g.length > 0.5]
|
||||
|
||||
|
||||
def _classified(street, graph) -> list:
|
||||
"""Jede Fahrbahnfläche mit der Klasse der Kante, die in ihr liegt.
|
||||
def fit_to_carriageway(world: World, carriage) -> World:
|
||||
"""Die Welt an die gebaute Fahrbahn anpassen (Entwurf, Ablauf Schritte 3 und 7).
|
||||
|
||||
Die Fahrbahn ist eine einzige verschmolzene Geometrie; die Klasse hängt aber an der Kante.
|
||||
Zugeordnet wird über die Kante, deren Mittelpunkt in der Fläche liegt — bei mehreren gewinnt
|
||||
die breiteste, weil an einer Kreuzung die größere Straße das Bild bestimmt.
|
||||
Bodenplatte und Beläge bekommen dasselbe Loch — fehlt es in einer Schicht, liegt dort ein Belag
|
||||
über der Mulde oder die Platte schließt sie. Palmen näher als PALM_KERB an der Fahrbahn fallen
|
||||
weg, nach derselben Regel, mit der apply_street() sie von der Jefferson Avenue fernhält:
|
||||
palm_points() hält nur Abstand zum gemessenen Korridor, und die Fahrbahn ragt an Kurven und
|
||||
Kreuzungen darüber hinaus.
|
||||
"""
|
||||
from leonida.section import SECTION
|
||||
return replace(world,
|
||||
land=punch(world.land, carriage),
|
||||
surfaces=[(part, kind) for poly, kind in world.surfaces
|
||||
for part in punch([poly], carriage)],
|
||||
palms=[p for p in world.palms if carriage.distance(Point(p)) >= PALM_KERB])
|
||||
|
||||
|
||||
def chunk_markings(marks, i: int, j: int) -> list:
|
||||
"""Die Markierungen in Chunk (i, j) — an der Chunkgrenze geteilt, nicht verworfen."""
|
||||
return [(part, art) for poly, art in marks
|
||||
for part in split_by_chunk_all([poly], i, j, min_area=MARKING_MIN_AREA)]
|
||||
|
||||
|
||||
def _classified(street, graph) -> list:
|
||||
"""Jede Fahrbahnfläche mit der Klasse der breitesten Kante, deren Mittelpunkt in ihr liegt.
|
||||
|
||||
Vorsicht beim Lesen des Felds: carriageway_area() verschmilzt das Netz zu wenigen
|
||||
zusammenhängenden Teilen, und ein Teil trägt dann die breiteste Klasse aller Straßen darin. Am
|
||||
echten Netz sind das 5 von 213 Teilen, aber 91 % der Fahrbahnfläche tragen "boulevard". Über
|
||||
die einzelne Straße sagt das Feld also wenig — die Klasse je Straße steht in edges. Heute liest
|
||||
es kein Verbraucher; Browser und Minimap färben die Fahrbahn einheitlich.
|
||||
"""
|
||||
mitten = [(e.klass, LineString(e.points).interpolate(0.5, normalized=True)) for e in graph.edges]
|
||||
out = []
|
||||
for poly in _pieces(street.carriageway):
|
||||
treffer = [e.klass for e in graph.edges
|
||||
if poly.contains(LineString(e.points).interpolate(0.5, normalized=True))]
|
||||
treffer = [klass for klass, mitte in mitten if poly.contains(mitte)]
|
||||
klass = max(treffer, key=lambda k: SECTION[k].carriageway) if treffer else "wohnstrasse"
|
||||
out.append((poly, klass))
|
||||
return out
|
||||
```
|
||||
|
||||
`SECTION` aus `leonida.section` importieren.
|
||||
`SECTION` aus `leonida.section` und `Point` aus `shapely.geometry` importieren.
|
||||
|
||||
In `tools/extract/leonida/emit.py` bekommt `split_by_chunk_all` die Splittergrenze als Parameter, mit dem bisherigen Wert als Vorgabe — alle anderen Aufrufer bleiben unverändert:
|
||||
|
||||
```python
|
||||
def split_by_chunk_all(polys, i: int, j: int, min_area: float = 0.5):
|
||||
"""Alle Anteile der übergebenen Polygone, die in Chunk (i, j) liegen.
|
||||
|
||||
min_area verwirft die Splitter, die beim Zuschneiden an der Chunkgrenze entstehen. 0,5 m² passt
|
||||
für Flächen; Markierungen sind kleiner und brauchen eine eigene Grenze (run.py, chunk_markings).
|
||||
"""
|
||||
cell = chunk_box(i, j)
|
||||
for poly in polys:
|
||||
part = poly.intersection(cell)
|
||||
if not part.is_empty and part.area > min_area:
|
||||
if part.geom_type == "Polygon":
|
||||
yield part
|
||||
else:
|
||||
yield from (g for g in part.geoms if g.geom_type == "Polygon" and g.area > min_area)
|
||||
```
|
||||
|
||||
Die Namensnennung im Manifest um den Bordstein erweitern:
|
||||
|
||||
@@ -2151,7 +2252,7 @@ Die Namensnennung im Manifest um den Bordstein erweitern:
|
||||
- [ ] **Schritt 4: Pipeline laufen lassen**
|
||||
|
||||
Ausführen: `cd tools/extract && PYTHONUTF8=1 uv run python run.py`
|
||||
Erwartet: Exit 0, die Zeile „Straßennetz: … Knoten, … Kanten", die Zeile „Straßen: …", die bekannte Parkzeile und **genau eine** Warnung (`Zufahrt schneidet die Sperrfläche tribuene`).
|
||||
Erwartet: Exit 0 nach rund vier Minuten (davon etwa 70 s für das Zuschneiden der Markierungen in der Chunk-Schleife), die Zeile „Straßennetz: 1537 Knoten, 1790 Kanten", die Zeile „Straßen: 830 Bordsteinstücke, 171 Restflächen, 22394 Markierungen {…}, 56 Palmen an der Fahrbahn entfernt", die bekannte Parkzeile „Flamingo Park: 2 Wegflächen, 14 Flächen, 11 Gebäude, Jefferson Avenue 12 m breit" und **genau eine** Warnung (`Zufahrt schneidet die Sperrfläche tribuene`). Die Zahlen hat der Controller mit derselben Kette gemessen; weichen sie ab, gehört das in den Bericht.
|
||||
|
||||
Bricht der Lauf in `check_corners` ab, ist das der im Entwurf vorhergesagte Stolperstein: Dann misst die Prüfung gegen die Fahrbahn statt gegen den Korridor. Nachsehen, ob `world.roads` beim Aufruf noch der Korridor ist — nicht die Toleranz hochsetzen.
|
||||
|
||||
@@ -2162,42 +2263,67 @@ cd tools/extract && PYTHONUTF8=1 uv run python - <<'EOF'
|
||||
import json
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from shapely.geometry import Polygon
|
||||
from shapely.ops import unary_union
|
||||
from shapely.geometry import LineString, Point, Polygon
|
||||
|
||||
ROOT = Path("../../public/data/vice-a")
|
||||
zahl = Counter()
|
||||
flaeche = Counter()
|
||||
stuecke, flaeche, arten, klassen = Counter(), Counter(), Counter(), Counter()
|
||||
bordstein, palmen_auf_fahrbahn = 0.0, 0
|
||||
for path in sorted(ROOT.glob("chunk_*.json")):
|
||||
c = json.loads(path.read_text(encoding="utf-8"))
|
||||
for key in ("carriageway", "sidewalk", "lots", "markings", "curb", "edges"):
|
||||
zahl[key] += len(c.get(key, []))
|
||||
stuecke[key] += len(c.get(key, []))
|
||||
for key in ("carriageway", "sidewalk", "lots"):
|
||||
for item in c.get(key, []):
|
||||
flaeche[key] += Polygon(item["poly"][0], item["poly"][1:]).area
|
||||
klassen = Counter()
|
||||
for path in sorted(ROOT.glob("chunk_*.json")):
|
||||
c = json.loads(path.read_text(encoding="utf-8"))
|
||||
bordstein += sum(LineString(line).length for line in c.get("curb", []))
|
||||
arten.update(m["art"] for m in c.get("markings", []))
|
||||
klassen.update(e["klasse"] for e in c.get("edges", []))
|
||||
print("Stücke:", dict(zahl))
|
||||
print("Flächen km²:", {k: round(v / 1e6, 3) for k, v in flaeche.items()})
|
||||
print("Kanten je Klasse:", dict(klassen))
|
||||
fahrbahn = [Polygon(item["poly"][0], item["poly"][1:]) for item in c.get("carriageway", [])]
|
||||
palmen_auf_fahrbahn += sum(1 for x, y in c.get("palms", [])
|
||||
if any(f.contains(Point(x, y)) for f in fahrbahn))
|
||||
print("Stücke, je Chunk gezählt:", dict(stuecke))
|
||||
print("Flächen km²:", {k: round(v / 1e6, 4) for k, v in flaeche.items()})
|
||||
print("Bordstein km:", round(bordstein / 1e3, 2))
|
||||
print("Markierungsstücke je Art:", dict(sorted(arten.items())))
|
||||
print("Kantenstücke je Klasse:", dict(klassen))
|
||||
print("Palmen auf der Fahrbahn:", palmen_auf_fahrbahn)
|
||||
EOF
|
||||
```
|
||||
|
||||
Erwartet, am echten Netz vom Controller nach Task 7 gemessen: Fahrbahn **0,914 km²** in 213 Teilen, Gehweg **0,295 km²**, Restflächen **0,093 km²** in 163 Stücken, Bordstein **120,3 km** in 832 Stücken, alle vier Klassen besetzt. Abweichungen von mehr als etwa 5 % gehören in den Bericht.
|
||||
Erwartet, vom Controller mit derselben Kette nach dem Flamingo Park gemessen:
|
||||
|
||||
Zur Einordnung: Der gemessene Korridor ist 1,3156 km². Fahrbahn, Gehweg und Restflächen ergeben zusammen 1,3018 km²; die fehlenden 0,0138 km² sind die Splitter unter 200 m², die absichtlich wegfallen. 6,8 % der Fahrbahn liegen außerhalb des gemessenen Korridors — das sind die Bänder, die an Kurven und Kreuzungen über die alte Rasterkontur hinausragen, und das ist gewollt.
|
||||
| Größe | Erwartet |
|
||||
|---|---|
|
||||
| Fahrbahn | **0,9084 km²** |
|
||||
| Gehweg | **0,2983 km²** |
|
||||
| Restflächen | **0,0973 km²** |
|
||||
| Bordstein | **120,61 km** |
|
||||
| Markierungsstücke je Art | je Art mindestens so viele wie in der Zeile „Straßen:" aus Schritt 4 — Zuschneiden teilt nur. `gelb_gestrichelt` und `weiss_gestrichelt` dürfen auf keinen Fall 0 sein. |
|
||||
| Kantenstücke je Klasse | alle vier Klassen besetzt |
|
||||
| Palmen auf der Fahrbahn | **0** |
|
||||
|
||||
- [ ] **Schritt 6: Commit**
|
||||
Die Stückzahlen im Skript sind je Chunk gezählt: Was über eine Chunkgrenze läuft, zählt doppelt. Mit den ungeteilten Zahlen (213 Fahrbahnteile, 171 Restflächen, 830 Bordsteinstücke, 1.777 Kanten nach dem Park) sind sie deshalb nicht vergleichbar — die ungeteilten stehen in der Zeile „Straßen:". Flächen und Bordsteinlänge sind dagegen vergleichbar; der Zuschnitt verliert nur Splitter unter 0,5 m² beziehungsweise 0,5 m. **Weicht eine Fläche oder die Bordsteinlänge um mehr als 1 % ab, ist etwas falsch verdrahtet** — dann nicht die Erwartung anpassen, sondern im Bericht melden.
|
||||
|
||||
Zur Einordnung: Der Korridor ohne Brücken ist nach dem Park 1,3140 km². Fahrbahn, Gehweg und Restflächen liegen darunter, weil Splitter unter 200 m² absichtlich wegfallen, und ein Teil der Fahrbahn liegt außerhalb des Korridors — die Bänder ragen an Kurven und Kreuzungen über die alte Rasterkontur hinaus, und das ist gewollt. Ohne den Park waren es 0,9136 / 0,2976 / 0,0944 km² und 120,27 km; der Unterschied ist die herausgeschnittene Parkfläche und die eingefügte Jefferson Avenue.
|
||||
|
||||
- [ ] **Schritt 6: Erzeugte Daten zurückstellen**
|
||||
|
||||
Der Lauf hat `public/data/vice-a` neu geschrieben. Die Daten kommen erst in Task 16 in einen eigenen Commit, und bis dahin sollen die Arbeitsbäume von Task 13 und 14 sauber bleiben:
|
||||
|
||||
```bash
|
||||
cd ../.. && git status --short
|
||||
git add tools/extract/run.py tools/extract/tests/test_pipeline_roads.py
|
||||
cd ../.. && git checkout -- public/data/vice-a && git status --short
|
||||
```
|
||||
|
||||
Erwartet: unversioniert nur die beiden Kit-Ordner des Nutzers (`modular_city_road_pack__game_ready/`, `modular_sidewalk_curb_kit/`), geändert nur `run.py` und `emit.py`, neu nur der Test. Liegen unter `public/data/vice-a` neue, unversionierte Dateien, stammen sie aus diesem Lauf: im Bericht nennen und nur diese entfernen. Die Kit-Ordner werden nie angefasst.
|
||||
|
||||
- [ ] **Schritt 7: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/extract/run.py tools/extract/leonida/emit.py tools/extract/tests/test_pipeline_roads.py
|
||||
git commit -m "feat(extract): Pipeline baut Straßen aus dem Graphen"
|
||||
```
|
||||
|
||||
Die erzeugten Daten kommen erst in Task 16 in einen eigenen Commit — hier **nicht** `public/data` mitcommitten.
|
||||
Hier **nicht** `public/data` mitcommitten.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user