fix(extract): Kreuzungsstummel schmaeler wachsen lassen, GeometryCollection-Absturz beheben

_walk_source() nahm bisher die ganze junction_area() eines Knotens auf, sobald
irgendein anliegender Arm Bordstein hat - auch den Anteil, der zu einer dort
einmuendenden Gasse gehoert. Der Gehweg wuchs dadurch die volle Laenge des
Kreuzungsstummels die Gasse hinauf (bis zu setback+1, beim Boulevard 11 m)
statt nur um die Ecke. _kerbless_zone() bekommt den extra-Parameter zurueck
(_kerb_lines() und _walk_source() rufen sie beide mit 0.2 auf) und beschneidet
jetzt auch den Kreuzungsanteil der Gehwegquelle.

Gemessen: an einer T-Kreuzung (2 Gassen + 1 Wohnstrasse) verschwindet der
fehlerhafte Gassenstummel-Gehweg vollstaendig (53,11 -> 0,0 m2). An einer
4-Wege-Kreuzung mit 3 Gassen + 1 Boulevard reicht der Fix nicht aus: der Rest
in der Nische zwischen zwei Gassenstummeln schrumpft nur auf 27,14 statt der
erwarteten rund 2 m2, und seine maximale Ausdehnung von der Kreuzungsmitte
(11,14 m) liegt weit ueber der als Grenze genannten 3-4 m - dokumentiert als
bekannter, nicht ausreichend geloester Punkt, siehe Fixbericht.

Unabhaengig davon: MultiPolygon([result]) wirft TypeError, wenn result eine
GeometryCollection ist (intersection()/difference() liefern das gelegentlich
an Beruehrstellen, mit einer entarteten Nulllinie neben echten Polygonen).
Neuer Helfer to_multipolygon() in section.py filtert auf echte Polygone,
statt daran abzustuerzen; carriageway_area() und _sidewalk() nutzen ihn beide.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Sonnet 5 committed 2026-09-17 06:52:30 +02:00
1 parent ca5710730e
commit 685ac666e6
4 files changed
+127 -28

No files matched your search

+15 -4
View File
@@ -110,15 +110,26 @@ def junction_area(graph: Graph, node: int):
return star.union(rounded.intersection(near))
def to_multipolygon(geom) -> MultiPolygon:
"""Nur die Flächenanteile einer Geometrie, als MultiPolygon.
intersection()/difference() liefern nicht immer sauber ein (Multi)Polygon: An einer
Berührstelle kann eine GeometryCollection mit zusätzlichen entarteten Linien oder Punkten
herauskommen (Fixrunde 4 zu Task 7, am Gehweg reproduziert). MultiPolygon(geoms) nimmt aber
nur Polygone entgegen und wirft eine TypeError, sobald ein Element davon abweicht - hier
werden die Nicht-Flächen-Teile deshalb verworfen, statt dass die Rechnung daran abstürzt.
"""
if geom.is_empty:
return MultiPolygon()
return MultiPolygon([g for g in getattr(geom, "geoms", [geom]) if g.geom_type == "Polygon"])
def carriageway_area(graph: Graph):
"""Alle Bänder und alle Kreuzungsflächen als eine Geometrie."""
parts = [band for k in range(len(graph.edges)) if (band := ribbon(graph, k)) is not None]
parts += [area for node in range(len(graph.nodes)) if graph.degree(node) >= 3
and (area := junction_area(graph, node)) is not None]
vereint = unary_union(parts)
if vereint.is_empty:
return MultiPolygon()
# Zusage der Schnittstelle: immer MultiPolygon. unary_union() liefert bei zusammenhängender
# Eingabe ein einfaches Polygon, und Task 7 iteriert über .geoms — das bricht dann an kleinen
# Prüfkörpern, aber nicht am echten Netz, also genau da, wo niemand damit rechnet.
return vereint if vereint.geom_type == "MultiPolygon" else MultiPolygon([vereint])
return to_multipolygon(unary_union(parts))