buildings.py regularisiert Gebäude-Maskenpolygone (gebaeude/spekulativ) zu
Rechtecken, wenn sie ihr gedrehtes Hüllrechteck zu mindestens 85 % ausfüllen,
sonst zu vereinfachten Grundrissen; zu kleine oder entartete Polygone liefern
None. heights.py weist Gebäuden eine Höhe zu (Wahrzeichen-Treffer schlägt
Viertel-Bereich, deterministisch geseedet) und platziert Palmenpunkte entlang
strandnaher Mittellinien: der Versatz vom Fahrbahnrand wird von innen nach
außen gesucht (PALM_VERGE), bis eine Position sowohl PALM_KERB von der
Fahrbahn als auch PALM_CLEARANCE von Gebäuden einhält; findet sich keine,
entfällt die Palme. Start bei halber PALM_SPACING verhindert doppelte Palmen
an Kreuzungen, wo Task 4 Mittellinien pro Ast trennt.
pyproject.toml ergänzt einen gezielten filterwarnings-Eintrag: Shapelys
oriented_envelope (minimum_rotated_rectangle) meldet für bereits achsen-
parallele/rechtwinklige Polygone eine RuntimeWarning aus GEOS (Winkelterm
wird 0/0), obwohl das Ergebnis nachweislich korrekt bleibt – u.a. löst der
Briefs eigener L-Shape-Test das aus. Nur dieser eine Text+Kategorie-
kombinierte Fall wird gefiltert, jede andere Warnung bleibt sichtbar.
Auf dem echten Mosaik: 2141 Gebäudepolygone (258 spekulativ, 211 von
regularize verworfen), Höhen 6.0-15.0 m (nur "district", da data/skyline.json
und data/districts.json erst in Task 6 kommen), 2433 Palmenpunkte. Straßen-
und Mittellinienzahlen (475 Stücke/40 Brücken, 2323 Linien/Median 27 m)
decken sich exakt mit den in Task 4 unabhängig ermittelten Referenzwerten.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
Fix-Runde 1, drei Befunde:
- road_polygons: Straßennetz ist ein zusammenhängendes Polygon über den ganzen
Ausschnitt; eine Fensterprüfung um seine Bounding-Box sieht daher zwangsläufig
Wasser und meldet fast alles als Brücke. Ersetzt durch Verschneidung jedes
Straßenpolygons mit der Landfläche (Ruling 13). Die wörtlich vorgegebene
Landberechnung (unary_union(land_polygons(labels))) besteht die bestehenden
Tests nicht: eine Straße ist nie "wasser", also zählt land_polygons() sie
selbst als Land, und ein Damm quer durchs Meer überlappt dann mit sich selbst
statt als Brücke aufzufallen (siehe Fix-Bericht für den Nachweis). Land wird
stattdessen ohne die Straßenklasse berechnet und um LAND_REACH_PX gewachsen,
damit eine Straße, die direkt an Land beginnt, dort noch erkannt wird. Der
Radius wurde am echten Mosaik kalibriert: unter 8 px verkettet sich eine
gewöhnliche Küstenstraße mangels Berührung mit "Land" erneut zu einem
kartenweiten Brückenstück; 10 px behebt das mit Sicherheitsabstand zur
Wasserlücke des bestehenden Tests.
- centerlines: eine Kontur um das ein Pixel breite Skelett lief auf beiden
Seiten des Bandes hin und zurück und lieferte jede Linie doppelt; an
Kreuzungen ergab das eine einzelne Schleife statt getrennter Äste (Ruling 14,
neue Tests zuerst rot gesehen). Ersetzt durch einen Pixelgraphen (networkx):
Skelettpixel als Knoten, 8er-Nachbarschaft als Kanten, Pfade zwischen Knoten
mit Grad ungleich 2 je Zusammenhangskomponente, reine Ringe einmal umlaufen.
- mask_polygons: Polygon(ring).buffer(0) kann bei selbstschneidenden Konturen
ein MultiPolygon liefern; das schlug später mit AttributeError fehl. Beim
Einsortieren in outers/holes wird jetzt nur noch ein sauberes Polygon
akzeptiert (Ruling 15).
16/16 Tests grün. Auf dem echten Mosaik: 469 Straßenstücke (190 Brücken,
größtes Brückenstück 26.294 m² auf einer lokalen Fläche von 643 x 564 m,
nicht mehr kartenweit).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ruling 10: wasser war auf die tiles.py-Kompositionsfarbe (44,103,164) gesetzt statt auf die
tatsächlich gerenderte, im Tiefenverlauf variierende Wasserfarbe. Auf (48,144,192) korrigiert,
MAX_DELTA_E von 14 auf 22 angehoben, um den Verlauf abzudecken. Unbekannter Pixelanteil im
echten Mosaik faellt damit von 37,4 % auf 3,5 %, unter die 5-%-Abbruchschwelle aus Aufgabe 6.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Einfarbige Fake-Kacheln erkannten nur die Herkunftskachel eines Pixels,
nicht den Versatz innerhalb der Kachel (72px Spielraum je Achse zwischen
3072px Kachelraster und 3000px Zuschnitt). fake_tile kodiert jetzt die
globale Pixelposition, ein neuer Test nagelt mosaic_origin_px() == (24, 60)
fest und prüft drei Pixel gegen den daraus berechneten globalen Index.
Produktionscode unverändert (Ruling 8).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>