fix(extract): Mindestbreite gegen papierdünne und wandartige Gebäudegrundrisse

727 von 2.286 rohen Grundrissen (31,8 %, aber nur 1,8 % der bebauten Fläche) sind
unter 4 m an der kurzen Seite ihres gedrehten Hüllrechtecks — extrudiert werden
daraus Wände, keine Häuser. Eine Mindestbreite allein reicht nicht: einzelne Formen
bleiben bei >= 4 m Breite trotzdem extrem lang und dünn (z.B. 4,27 m x 153 m,
Verhältnis 35,9). MAX_ASPECT_RATIO=12 ergänzt die Mindestbreite MIN_BUILDING_WIDTH=4,
an einer echten Lücke in den gemessenen Verhältnissen festgemacht (größte plausible
große Grundfläche: Verhältnis 10,8 bei 7.196 m²; nächste eindeutig wandartige Form:
Verhältnis 12,0 bei 60,5 m²). Zusammen fallen 766 Grundrisse (33,5 %, 36.986 m² =
3,3 % der Fläche) weg.

Bestehende Fixture in test_small_staircase_building_is_not_dropped vergrößert (4x4-
auf 6x6-Block): ihre kurze Seite lag zufällig bei ~3,7 m, unter der neuen Schwelle,
und hätte den Test sonst für den falschen Grund bestehen lassen (Ruling 74 statt
Ruling 28).

Pipeline neu durchgelaufen: eindeutige Gebäude 2.282 -> 1.520 (-33,4 %), Dreiecke
über alle 144 Chunks 327.181 -> 307.678 (-6,0 %, vollständig aus der Gebäude-Ebene),
public/data/vice-a 2,73 M -> 2,50 M.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Sonnet 5 committed 2026-09-13 05:14:42 +02:00
1 parent ad9bf63b36
commit 30cf64642b
129 files changed
+205 -138

No files matched your search

+40 -10
View File
@@ -36,17 +36,47 @@ def test_small_staircase_building_is_not_dropped():
MIN_AREA. Ein Grundriss mit gestuftem (Treppenrand-)Eck, der die Rechteckquote
von 0,85 verfehlt, wurde bisher ein zweites Mal - und gröber, mit 1,0 m -
vereinfacht und damit unter MIN_AREA gedrückt, obwohl er die 0,5-m-Vereinfachung
klar übersteht. An zwei gegenüberliegenden Ecken eines 4x4-Blocks fehlt je ein
Gebäudepixel, wie bei einer gefasten Ecke auf dem echten Mosaik.
klar übersteht. An zwei gegenüberliegenden Ecken eines 6x6-Blocks fehlt je ein
2x2-Gebäudepixelquadrat, wie bei einer gefasten Ecke auf dem echten Mosaik.
Fix-Runde 3 / Befund C: die Fixture war ursprünglich ein 4x4-Block mit je einem
fehlenden Eckpixel (kurze Seite danach ca. 3,7 m) - das kollidierte zufällig mit
der neuen Mindestbreite (Ruling 74, MIN_BUILDING_WIDTH=4.0) und hätte diesen Test
in einen Test für den falschen Grund verwandelt (weggefallen wegen Breite statt
weil die Rechteckquote verfehlt wird). Größer gewählt (kurze Seite ca. 5,8 m),
damit dieser Test weiterhin ausschließlich Ruling 28 prüft, unabhängig von 74.
"""
labels = np.full((40, 40), CLASSES["stadt"], dtype=np.uint8)
block = np.array([
[1, 1, 1, 0],
[1, 1, 1, 1],
[1, 1, 1, 1],
[0, 1, 1, 1],
], dtype=bool)
labels[10:14, 10:14][block] = CLASSES["gebaeude"]
labels = np.full((60, 60), CLASSES["stadt"], dtype=np.uint8)
block = np.ones((6, 6), dtype=bool)
block[0:2, 4:6] = False # obere rechte 2x2-Ecke fehlt
block[4:6, 0:2] = False # untere linke 2x2-Ecke fehlt
labels[10:16, 10:16][block] = CLASSES["gebaeude"]
assert len(building_polygons(labels)) == 1
# Befund C (Ruling 74, Fix-Runde 3): 727 von 2.286 rohen Grundrissen (31,8 %, aber nur 1,8 %
# der bebauten Flaeche) sind unter 4 m an der kurzen Seite ihres gedrehten Huellrechtecks -
# extrudiert werden daraus Waende, keine Haeuser. Zahlen an den echten Daten nachgemessen,
# s. Bericht.
def test_thin_sliver_building_is_dropped():
"""Unter MIN_BUILDING_WIDTH (4 m) kurzer Seite faellt ein Grundriss weg, unabhaengig von
der Flaeche (hier 60 m2, weit ueber MIN_AREA=12 - die Flaeche allein rettet es nicht)."""
labels = np.full((60, 60), CLASSES["stadt"], dtype=np.uint8)
labels[10:12, 10:40] = CLASSES["gebaeude"] # 2 m breit, 30 m lang
assert building_polygons(labels) == []
def test_long_narrow_building_just_above_min_width_is_still_dropped():
"""Ruling 74: 'eine 4x100-m-Form ist immer noch eine Wand' - Mindestbreite allein reicht
nicht; ab MIN_BUILDING_WIDTH greift zusaetzlich eine Obergrenze fuer laenge/breite."""
labels = np.full((150, 150), CLASSES["stadt"], dtype=np.uint8)
labels[10:15, 10:110] = CLASSES["gebaeude"] # 5 m breit, 100 m lang -> Verhaeltnis 20
assert building_polygons(labels) == []
def test_moderately_elongated_building_above_min_width_is_kept():
"""Regression: die Verhaeltnis-Obergrenze darf normale, nur maessig lang gestreckte
Gebaeude nicht treffen (10 m breit, 30 m lang -> Verhaeltnis 3, klar unter der Schwelle)."""
labels = np.full((60, 60), CLASSES["stadt"], dtype=np.uint8)
labels[10:20, 10:40] = CLASSES["gebaeude"]
assert len(building_polygons(labels)) == 1
def test_speculative_flag_matches_pixel_class():