Files
gta6/tools/extract/tests/test_vectors.py
T
knsoandClaude Sonnet 5 120cafd7bf fix(extract): Brücke braucht Nähe zu Wasser, nicht nur Ferne von Land
Fix-Runde 2 (Ruling 17). Nachmessung am echten Mosaik zeigte: 143 der 190 aus
Fix-Runde 1 markierten Brücken (137.288 von 208.744 m² Fläche) hatten unter
10 % Wasser in ihrer Umgebung, darunter die beiden größten Stücke mit 2,4 %
und 0,1 % Wasser. Das waren breite Kreuzungs- und Parkflächen an Land: ihre
Mitte liegt allein deshalb weiter als LAND_REACH_PX von straßenfreiem Land
entfernt, weil dort ringsum nur Straße ist – kein Bezug zu Wasser. Ein
größeres LAND_REACH_PX heilt das nicht (ab 16 kippt der bestehende Test).

Straßenstücke jenseits von "Land" zählen jetzt nur noch als Brücke, wenn sie
zusätzlich innerhalb WATER_REACH_PX von Wasser liegen; neue Konstante
WATER_REACH_PX = 8, am echten Mosaik kalibriert (6-10 ein stabiles Plateau
bei ~40 Brücken, ab 14 bricht es weg und reißt wieder trockene Flächen mit).

Regressionstest zuerst rot gesehen (test_wide_road_area_on_land_is_not_a_bridge),
bestehende Tests unverändert und weiterhin grün. 17/17 Tests grün. Auf dem
echten Mosaik: 475 Straßenstücke, 40 Brücken, größtes Brückenstück 12.588 m² –
deckt sich mit den unabhängig ermittelten Referenzwerten der Prüfung.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:14:12 +02:00

59 lines
2.6 KiB
Python

import numpy as np
from leonida.classify import CLASSES
from leonida.vectors import centerlines, land_polygons, mask_polygons, road_polygons
def test_square_becomes_one_polygon_with_right_area():
mask = np.zeros((100, 100), dtype=bool)
mask[20:60, 30:80] = True # 40 x 50 Pixel = 2000 m²
polys = mask_polygons(mask, min_area=10, simplify=1.0)
assert len(polys) == 1
assert abs(polys[0].area - 2000) < 60 # Vereinfachung darf etwas abweichen
def test_island_keeps_its_lagoon():
labels = np.full((80, 80), CLASSES["wasser"], dtype=np.uint8)
labels[10:70, 10:70] = CLASSES["land"]
labels[30:50, 30:50] = CLASSES["wasser"] # Lagune
polys = land_polygons(labels)
assert len(polys) == 1
assert len(polys[0].interiors) == 1
def test_road_over_water_is_flagged_as_bridge():
labels = np.full((60, 60), CLASSES["wasser"], dtype=np.uint8)
labels[0:10, :] = CLASSES["land"]
labels[25:31, :] = CLASSES["strasse"] # Damm quer durchs Wasser
roads = road_polygons(labels)
assert len(roads) == 1
assert roads[0][1] is True
def test_crossing_roads_form_a_single_polygon():
labels = np.full((60, 60), CLASSES["land"], dtype=np.uint8)
labels[28:32, :] = CLASSES["strasse"]
labels[:, 28:32] = CLASSES["strasse"]
roads = road_polygons(labels)
assert len(roads) == 1 # Kreuzung, kein Zerfall in vier Arme
def test_centerline_runs_once_along_a_straight_road():
labels = np.full((60, 300), CLASSES["land"], dtype=np.uint8)
labels[28:38, 50:250] = CLASSES["strasse"] # 200 lang, 10 breit
lines = centerlines(labels)
assert len(lines) == 1
assert 180 <= lines[0].length <= 215 # einmal entlang, nicht hin und zurück
def test_crossing_roads_yield_separate_centerlines():
labels = np.full((120, 120), CLASSES["land"], dtype=np.uint8)
labels[55:65, :] = CLASSES["strasse"]
labels[:, 55:65] = CLASSES["strasse"]
assert len(centerlines(labels)) >= 3 # Arme werden an der Kreuzung getrennt
def test_wide_road_area_on_land_is_not_a_bridge():
"""Eine breite Kreuzungsfläche ohne Wasser in der Nähe ist nie eine Brücke.
Ihre Mitte liegt weiter als LAND_REACH_PX von straßenfreiem Land entfernt, weil
dort nur Straße ist — das allein darf sie nicht zur Brücke machen.
"""
labels = np.full((120, 120), CLASSES["land"], dtype=np.uint8)
labels[30:90, 30:90] = CLASSES["strasse"] # 60x60 Straßenfläche, kein Wasser
pieces = road_polygons(labels)
assert pieces
assert not any(bridge for _, bridge in pieces)