docs: fünf Befunde des Vorabscans im Plan berichtigt
Signatur von road_graph, eine wirkungslose Hilfsfunktion, eine Zusicherung die der Vereinfachung widerspricht, ein doppelter Aufruf und ein sorted() über Wörterbücher ohne Schlüssel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b24714adf4
commit
a86a4c9601
1 file changed
+13
-14
@@ -388,12 +388,9 @@ def _junction_clusters(pixels: nx.Graph, shape) -> tuple[np.ndarray, list[tuple[
|
|||||||
for k in range(1, count + 1):
|
for k in range(1, count + 1):
|
||||||
rows, cols = np.nonzero(junction & (labelled == k))
|
rows, cols = np.nonzero(junction & (labelled == k))
|
||||||
points.append(to_world([(rows.mean(), cols.mean())])[0])
|
points.append(to_world([(rows.mean(), cols.mean())])[0])
|
||||||
return labelled * junction_reach(labelled, junction), points
|
# labelled traegt die Knotennummer auf jedem *gewachsenen* Pixel. Genau das braucht _attach():
|
||||||
|
# Das Ende einer Kette liegt neben einem gewachsenen Pixel, nicht neben einem Kreuzungspixel.
|
||||||
|
return labelled, points
|
||||||
def junction_reach(labelled: np.ndarray, junction: np.ndarray) -> np.ndarray:
|
|
||||||
"""Alle gewachsenen Pixel gehören zum Knoten, nicht nur die ursprünglichen."""
|
|
||||||
return labelled > 0
|
|
||||||
|
|
||||||
|
|
||||||
def _chain(sub: nx.Graph) -> list[tuple[int, int]]:
|
def _chain(sub: nx.Graph) -> list[tuple[int, int]]:
|
||||||
@@ -420,7 +417,7 @@ def _attach(graph: Graph, pixels: nx.Graph, clusters: np.ndarray, pixel) -> int:
|
|||||||
Ausführen: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_roads_graph.py -q`
|
Ausführen: `cd tools/extract && PYTHONUTF8=1 uv run pytest tests/test_roads_graph.py -q`
|
||||||
Erwartet: 5 bestanden.
|
Erwartet: 5 bestanden.
|
||||||
|
|
||||||
Bleibt etwas rot, ist der wahrscheinlichste Grund `_junction_clusters`: `clusters` muss für **jedes** gewachsene Pixel die Knotennummer tragen, damit `_attach` das Ende einer Kette einem Knoten zuordnen kann. Die Rückgabe `labelled * junction_reach(...)` ist genau dafür da — `labelled` allein ist außerhalb der gewachsenen Bereiche 0, und das ist gewollt.
|
Bleibt etwas rot, ist der wahrscheinlichste Grund `_junction_clusters`: `clusters` muss für **jedes** gewachsene Pixel die Knotennummer tragen, damit `_attach` das Ende einer Kette einem Knoten zuordnen kann. `ndi.label` auf der gewachsenen Maske leistet genau das; außerhalb der gewachsenen Bereiche steht 0, und das ist gewollt.
|
||||||
|
|
||||||
- [ ] **Schritt 6: Volle Suite und Commit**
|
- [ ] **Schritt 6: Volle Suite und Commit**
|
||||||
|
|
||||||
@@ -457,7 +454,9 @@ dort ein.
|
|||||||
- `CLASS_WIDTH = {"gasse": 6.0, "wohnstrasse": 12.0, "hauptstrasse": 18.0, "boulevard": 24.0}`
|
- `CLASS_WIDTH = {"gasse": 6.0, "wohnstrasse": 12.0, "hauptstrasse": 18.0, "boulevard": 24.0}`
|
||||||
- `clean(graph: Graph) -> Graph`
|
- `clean(graph: Graph) -> Graph`
|
||||||
- `measure_widths(graph: Graph, distance: np.ndarray) -> Graph` (setzt `width` und `klass` je Kante)
|
- `measure_widths(graph: Graph, distance: np.ndarray) -> Graph` (setzt `width` und `klass` je Kante)
|
||||||
- `road_graph(labels: np.ndarray) -> Graph` — der Aufruf für die Pipeline: Skelett, Graph, säubern, messen
|
- `road_graph(labels: np.ndarray, distance: np.ndarray) -> Graph` — der Aufruf für die Pipeline:
|
||||||
|
Skelett, Graph, säubern, Breite und Klasse. Die Distanztransformation kommt von außen, weil
|
||||||
|
`run.py` sie auch für die Palmen braucht.
|
||||||
|
|
||||||
- [ ] **Schritt 1: Den fehlschlagenden Test schreiben**
|
- [ ] **Schritt 1: Den fehlschlagenden Test schreiben**
|
||||||
|
|
||||||
@@ -508,7 +507,9 @@ def test_knoten_mit_nur_zwei_kanten_verschwindet():
|
|||||||
Edge(1, 2, gerade(punkt(50, 0), punkt(100, 0)))])
|
Edge(1, 2, gerade(punkt(50, 0), punkt(100, 0)))])
|
||||||
out = clean(graph)
|
out = clean(graph)
|
||||||
assert len(out.edges) == 1
|
assert len(out.edges) == 1
|
||||||
assert out.edges[0].points == [punkt(0, 0), punkt(50, 0), punkt(100, 0)]
|
# clean() vereinfacht mit 1,0 m, und der Zwischenpunkt liegt genau auf der Verbindungslinie -
|
||||||
|
# Douglas-Peucker wirft ihn weg. Geprüft wird das Verketten, nicht die Zahl der Stützpunkte.
|
||||||
|
assert out.edges[0].points == [punkt(0, 0), punkt(100, 0)]
|
||||||
|
|
||||||
|
|
||||||
def test_breite_ignoriert_die_kreuzungsenden():
|
def test_breite_ignoriert_die_kreuzungsenden():
|
||||||
@@ -1193,8 +1194,8 @@ def _sidewalk(carriage, corridor):
|
|||||||
|
|
||||||
def _kerb_lines(graph: Graph, carriage, bridges) -> list[LineString]:
|
def _kerb_lines(graph: Graph, carriage, bridges) -> list[LineString]:
|
||||||
"""Die Fahrbahnkante, ohne die Stücke an Gassen und auf Brücken."""
|
"""Die Fahrbahnkante, ohne die Stücke an Gassen und auf Brücken."""
|
||||||
ohne = [ribbon(graph, k).buffer(0.2) for k, edge in enumerate(graph.edges)
|
ohne = [band.buffer(0.2) for k, edge in enumerate(graph.edges)
|
||||||
if not SECTION[edge.klass].kerb and ribbon(graph, k) is not None]
|
if not SECTION[edge.klass].kerb and (band := ribbon(graph, k)) is not None]
|
||||||
ohne += [b.buffer(0.2) for b in bridges]
|
ohne += [b.buffer(0.2) for b in bridges]
|
||||||
rand = carriage.boundary
|
rand = carriage.boundary
|
||||||
if ohne:
|
if ohne:
|
||||||
@@ -1690,7 +1691,7 @@ def chunk_payload(i, j, *, land, surfaces, carriageway, curb, sidewalk, markings
|
|||||||
"carriageway": sorted(({"poly": _rings(p, i, j), "klasse": k} for p, k in carriageway),
|
"carriageway": sorted(({"poly": _rings(p, i, j), "klasse": k} for p, k in carriageway),
|
||||||
key=lambda c: (c["poly"], c["klasse"])),
|
key=lambda c: (c["poly"], c["klasse"])),
|
||||||
"curb": sorted(_line(l, i, j) for l in curb),
|
"curb": sorted(_line(l, i, j) for l in curb),
|
||||||
"sidewalk": sorted({"poly": _rings(p, i, j)} for p in sidewalk),
|
"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),
|
"markings": sorted(({"poly": _rings(p, i, j), "art": a} for p, a in markings),
|
||||||
key=lambda m: (m["poly"], m["art"])),
|
key=lambda m: (m["poly"], m["art"])),
|
||||||
"lots": sorted(({"poly": _rings(p, i, j)} for p in lots), key=lambda l: l["poly"]),
|
"lots": sorted(({"poly": _rings(p, i, j)} for p in lots), key=lambda l: l["poly"]),
|
||||||
@@ -1706,8 +1707,6 @@ def chunk_payload(i, j, *, land, surfaces, carriageway, curb, sidewalk, markings
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`sorted({"poly": ...} for ...)` ohne `key` schlägt bei Wörterbüchern fehl — bei `sidewalk` deshalb `key=lambda s: s["poly"]` ergänzen, wie bei `lots`.
|
|
||||||
|
|
||||||
`write_debug_png` liest `payload["roads"]`; dort auf `payload["carriageway"]` umstellen, Farbe unverändert.
|
`write_debug_png` liest `payload["roads"]`; dort auf `payload["carriageway"]` umstellen, Farbe unverändert.
|
||||||
|
|
||||||
- [ ] **Schritt 4: Test laufen lassen**
|
- [ ] **Schritt 4: Test laufen lassen**
|
||||||
|
|||||||
Reference in new issue
Block a user