fix(extract): set_precision statt Koordinatenrundung gegen selbstschneidende Ringe
_rings() rundete bislang jede Koordinate unabhängig auf 2 Stellen, nach normalize(). Das kann zwei Ringpunkte so verschieben, dass sich der Ring selbst schneidet, obwohl das Ausgangspolygon gültig war - Ursache der acht neuen ungültigen Polygone in den ausgelieferten Chunks (carriageway 3, land 2, sidewalk 2, surfaces 1). shapely.set_precision(poly, 0.01) läuft jetzt vor normalize() und vor der Rundung: Es ist eine echte Geometrieoperation (GEOS hält das Ergebnis mit mode="valid_output" aktiv gültig) statt eines blinden round() je Zahl. Gegen alle 29.037 Polygone der committeten (bereits einmal falsch gerundeten) Chunks probeweise gerechnet: kein Absturz, 8 der 9 ungültigen Polygone (die neunte ist die bekannte, von diesem Zweig unabhängige Vorbelastung in "paths", chunk_10_6, schon vor diesem Zweig ungültig) werden gültig, Flächenänderung insgesamt -24,35 m² von 11.711.354 m² (-0,00021 %). Zwei Sonderfälle brauchten eine eigene Behandlung, die die Zeilen im Befund nicht erwähnen, ohne die aber der Vier-Minuten-Lauf hätte abbrechen oder eine falsche Fläche ausliefern können: - An einer Engstelle unter 1 cm kann set_precision ein Polygon in mehrere Teile spalten (3 von 29.037 Fällen, größter abgeschnittener Teil 25,9 m²) - poly.exterior auf einer MultiPolygon wäre ein AttributeError gewesen. Es bleibt jetzt das größere Teilstück. - Ganz vereinzelt lehnt GEOS das Schnappen mit einer TopologyException ab (in der Stichprobe nur an einem bereits vorher ungültigen Polygon); dann bleibt das Polygon ungerundet, und die alte Rundung je Koordinate greift wie vor diesem Befund, statt den Lauf abzubrechen. Zwei Tests, beide ohne die Korrektur rot: ein valides Polygon, dessen unabhängig gerundete Ecken sich kreuzen (Beispiel selbst nachgerechnet, nicht nur behauptet), und eine Hantelform mit einem Steg unter 1 cm, die am unkorrigierten _rings() unter der Rundung selbst ungültig wird statt sich sauber zu teilen.
This commit is contained in:
1 parent
83140f89e1
commit
a397892847
2 files changed
+74
-2
No files matched your search
@@ -46,10 +46,35 @@ def _rings(poly: Polygon, i: int, j: int) -> list[list[list[float]]]:
|
||||
geometry.js stellt sie beim Laden ohnehin selbst her (clockwise(), Zeile 50), das
|
||||
Halbstrahl-Verfahren von pointInPolygon() in props.js kommt ohne sie aus, und PIL fuellt
|
||||
in overview.py unabhaengig von Wicklung und Startpunkt (nachgemessen, Bild bytegleich).
|
||||
|
||||
set_precision(poly, 0.01) laeuft vor normalize() und vor der Rundung selbst (Befund 5 der
|
||||
Fixwelle): Wer stattdessen jede Koordinate unabhaengig auf 2 Stellen rundet, wie es hier bis
|
||||
zu diesem Befund geschah, kann zwei Ringpunkte so verschieben, dass sich der Ring selbst
|
||||
schneidet - set_precision ist eine echte Geometrieoperation (GEOS haelt das Ergebnis mit
|
||||
mode="valid_output" aktiv gueltig) statt eines blinden Python-round() je Zahl. Acht von
|
||||
144 ausgelieferten Chunks hatten dadurch ein ungueltiges Polygon (carriageway 3, land 2,
|
||||
sidewalk 2, surfaces 1) - set_precision behebt alle acht (nachgemessen).
|
||||
|
||||
Zwei Sonderfaelle, an einer Stichprobe ueber alle 29.037 ausgelieferten Polygone gemessen:
|
||||
An einer Engstelle unter 1 cm spaltet set_precision ein Polygon in mehrere Teile (3 von
|
||||
29.037 Faellen, groesster abgeschnittener Teil 25,9 m²) - hier bleibt nur das groesste
|
||||
Teilstueck; das Polygon soll eine zusammenhaengende Flaeche bleiben, kein Zusatzeintrag,
|
||||
den kein Aufrufer erwartet. Ganz vereinzelt lehnt GEOS das Schnappen mit einer
|
||||
TopologyException ab (in der Stichprobe nur an einem bereits vorher ungueltigen Polygon);
|
||||
dann bleibt das Polygon ungerundet, und die anschliessende Rundung je Koordinate greift wie
|
||||
vor diesem Befund - kein Absturz des Vier-Minuten-Laufs an einer einzelnen Ausnahme.
|
||||
"""
|
||||
ox = SLICE["x0"] + i * CHUNK_SIZE
|
||||
oy = SLICE["y1"] - j * CHUNK_SIZE
|
||||
poly = shapely.normalize(poly)
|
||||
try:
|
||||
genau = shapely.set_precision(poly, 0.01)
|
||||
except shapely.errors.GEOSException:
|
||||
genau = poly
|
||||
if genau.geom_type == "MultiPolygon" and not genau.is_empty:
|
||||
genau = max(genau.geoms, key=lambda g: g.area)
|
||||
elif genau.is_empty or genau.geom_type != "Polygon":
|
||||
genau = poly
|
||||
poly = shapely.normalize(genau)
|
||||
# Welt (X, Y) -> three (x, z) -> relativ zur Chunk-Ecke
|
||||
return [[[round(x - ox, 2), round(-y - (-oy), 2)] for x, y in ring.coords]
|
||||
for ring in (poly.exterior, *poly.interiors)]
|
||||
|
||||
Reference in new issue
Block a user