fix(extract): Kontraktion zieht jetzt alle anliegenden Kanten mit
_contract_short() ließ Kanten, die schon vorher am überlebenden Knoten hingen, auf ihrer alten Koordinate stehen - nur die vom eingezogenen Knoten umgehängten folgten dem neuen Mittelpunkt. Da Kontraktion beidseitig Grad >= 3 voraussetzt, hat der überlebende Knoten danach immer mindestens Grad 4, und _merge_chains() fasst nur Grad-2-Knoten an: Der Fehler konnte sich nie von selbst heilen. Am heutigen Mosaik wirkungslos (die Funktion greift dort kein einziges Mal), aber real für jeden Ausschnitt mit zwei nahen Kreuzungen - genau der Fall, den die Funktion behandeln soll. Dazu zwei Kleinigkeiten: _median_width() deckte den Kurzkanten-Fallback (unter 2 * WIDTH_MARGIN) bisher nicht ab, und ein Kommentar in _merge_chains() war ein Zeichen zu weit eingerückt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f809eedf71
commit
2d8f4e730c
2 files changed
+48
-3
No files matched your search
@@ -4,7 +4,8 @@ from __future__ import annotations
|
||||
import numpy as np
|
||||
|
||||
from leonida.coords import SLICE
|
||||
from leonida.roads import (CLASS_WIDTH, Edge, Graph, Node, SPUR_MIN, clean, measure_widths)
|
||||
from leonida.roads import (CLASS_WIDTH, CONTRACT_MAX, Edge, Graph, Node, SPUR_MIN, clean,
|
||||
measure_widths)
|
||||
|
||||
|
||||
def punkt(x: float, y: float) -> tuple[float, float]:
|
||||
@@ -103,6 +104,20 @@ def test_klasse_ist_die_naechstliegende_regelbreite():
|
||||
assert CLASS_WIDTH["hauptstrasse"] == 18.0
|
||||
|
||||
|
||||
def test_kurze_kante_ohne_randregel_misst_sich_ganz():
|
||||
# Kante unter 2 * WIDTH_MARGIN (16 m): "end <= start" bei der Randregel greift ins Leere -
|
||||
# _median_width() muss dann auf die ganze Kante zurückfallen statt auf ein leeres Stück.
|
||||
# Alle anderen Breitentests laufen mit Kanten ab 160 m und erreichen diesen Zweig nie.
|
||||
distance = np.full((50, 50), 3.0) # halbe Breite 3 m -> 6 m
|
||||
graph = Graph(nodes=[Node(punkt(SLICE["x0"] + 5, SLICE["y1"] - 20)),
|
||||
Node(punkt(SLICE["x0"] + 15, SLICE["y1"] - 20))],
|
||||
edges=[Edge(0, 1, gerade(punkt(SLICE["x0"] + 5, SLICE["y1"] - 20),
|
||||
punkt(SLICE["x0"] + 15, SLICE["y1"] - 20)))])
|
||||
out = measure_widths(graph, distance)
|
||||
assert out.edges[0].width == 6.0
|
||||
assert out.edges[0].klass == "gasse"
|
||||
|
||||
|
||||
# Die folgenden zwei Fälle hat erst der Gegencheck am echten Mosaik gezeigt (Schritt 5): Kein
|
||||
# synthetischer Test bildet sie ab, aber am echten Netz treten sie zuverlässig auf.
|
||||
|
||||
@@ -134,3 +149,29 @@ def test_schleife_an_einem_durchgangsknoten_bringt_clean_nicht_zum_absturz():
|
||||
Edge(1, 1, ring)])
|
||||
out = clean(graph)
|
||||
assert any(e.a == e.b for e in out.edges), "die Schleife bleibt erhalten, statt clean() abstürzen zu lassen"
|
||||
|
||||
|
||||
# Diesen Fall hat nicht das Mosaik gezeigt, sondern die Codeprüfung von Fixrunde 1: Die
|
||||
# Kontraktion griff bisher an keiner Stelle im heutigen Netz, ihr Fehler blieb dort wirkungslos.
|
||||
|
||||
|
||||
def test_kontraktion_zieht_alle_anliegenden_kanten_zum_neuen_knoten():
|
||||
# Zwei Kreuzungen mit je Grad 3, 3 m auseinander (unter CONTRACT_MAX) - _contract_short()
|
||||
# zieht sie zu einem Knoten in der Mitte zusammen. Nicht nur die zwei Kanten, die vorher am
|
||||
# eingezogenen Knoten hingen, müssen dem neuen Punkt folgen, sondern auch die zwei, die schon
|
||||
# vorher am überlebenden Knoten hingen: Der ist gerade selbst auf den Mittelpunkt gewandert.
|
||||
assert 3 < CONTRACT_MAX
|
||||
graph = Graph(nodes=[Node(punkt(0, 0)), Node(punkt(3, 0)),
|
||||
Node(punkt(-50, 0)), Node(punkt(0, 50)),
|
||||
Node(punkt(53, 0)), Node(punkt(3, 50))],
|
||||
edges=[Edge(0, 1, gerade(punkt(0, 0), punkt(3, 0))),
|
||||
Edge(0, 2, gerade(punkt(0, 0), punkt(-50, 0))),
|
||||
Edge(0, 3, gerade(punkt(0, 0), punkt(0, 50))),
|
||||
Edge(1, 4, gerade(punkt(3, 0), punkt(53, 0))),
|
||||
Edge(1, 5, gerade(punkt(3, 0), punkt(3, 50)))])
|
||||
out = clean(graph)
|
||||
ziel = out.nodes[0].point
|
||||
assert ziel == punkt(1.5, 0.0), "der überlebende Knoten steht auf dem Mittelpunkt beider Kreuzungen"
|
||||
fehler = [(edge.a, edge.b, edge.points) for edge in out.edges
|
||||
if (edge.a == 0 and edge.points[0] != ziel) or (edge.b == 0 and edge.points[-1] != ziel)]
|
||||
assert not fehler, f"Kanten mit veralteter Koordinate am zusammengezogenen Knoten: {fehler}"
|
||||
Reference in new issue
Block a user