feat(extract): Mulde, Bordsteinlinien, Gehweg und Restflächen
Ein Prüfkörper korrigiert: Der Splitter in test_kleine_reststuecke_fallen_weg_grosse_bleiben lag direkt an derselben Korridorkante wie die Ausbuchtung und verschmolz dort schon bei unary_union() mit ihr, bevor der LOT_MIN-Filter überhaupt etwas zu tun bekam. Mit einer kleinen Lücke zum Rand bleibt er ein eigenständiges 2-m²-Polygon und fällt durch den Filter - der Test prüft jetzt tatsächlich das Wegfallen kleiner Reststücke, nicht nur, dass große bleiben. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ee07a6bf2a
commit
45a1a62103
2 files changed
+151
No files matched your search
@@ -0,0 +1,82 @@
|
||||
"""Die Straße als Mulde: Loch in der Bodenplatte, Fahrbahn darunter, Bordstein dazwischen.
|
||||
|
||||
Warum die Fahrbahn nach unten und nicht der Gehweg nach oben (Entwurf, Abschnitt "Die Mulde"):
|
||||
Läge der Gehweg 15 cm hoch, hätte er überall dort eine Kante ins Nichts, wo kein Haus
|
||||
anschließt - an jedem Park, jeder Baulücke, jedem Strandzugang. So bleibt die ganze Welt auf
|
||||
y = 0, und der Bordstein ist genau das, was er in Wirklichkeit ist: die Wand zwischen Gehweg
|
||||
und Fahrbahn.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from shapely.geometry import LineString, Polygon
|
||||
from shapely.ops import unary_union
|
||||
|
||||
from .roads import Graph
|
||||
from .section import SECTION, WALK_MAX, carriageway_area, ribbon
|
||||
|
||||
# Die Tiefe steht hier als dokumentierter Wert; gebaut wird sie im Browser (geometry.js).
|
||||
DEPTH = 0.15
|
||||
# Kürzere Bordsteinstücke sind Rechenreste an Kreuzungsecken, keine Kante, die jemand sieht.
|
||||
KERB_MIN = 2.0
|
||||
# Ab hier ist ein Reststück eine Fläche (Parkplatz, Vorplatz, Rampe) und keine Zacke.
|
||||
LOT_MIN = 200.0
|
||||
|
||||
|
||||
@dataclass
|
||||
class Street:
|
||||
carriageway: object
|
||||
kerb: list = field(default_factory=list)
|
||||
sidewalk: object = None
|
||||
lots: list = field(default_factory=list)
|
||||
|
||||
|
||||
def build_street(graph: Graph, corridor, bridges) -> Street:
|
||||
"""Fahrbahn, Bordsteinlinien, Gehweg und Restflächen aus Graph und gemessenem Korridor."""
|
||||
carriage = carriageway_area(graph)
|
||||
return Street(
|
||||
carriageway=carriage,
|
||||
kerb=_kerb_lines(graph, carriage, bridges),
|
||||
sidewalk=_sidewalk(carriage, corridor),
|
||||
lots=_lots(corridor, carriage),
|
||||
)
|
||||
|
||||
|
||||
def _sidewalk(carriage, corridor):
|
||||
"""Der Streifen zwischen Fahrbahnkante und Korridorrand.
|
||||
|
||||
Ein einziger Puffer für alle Klassen reicht: Weil am Korridor beschnitten wird, bleibt bei
|
||||
12 m Korridor und 7 m Fahrbahn genau der 2,5-m-Gehweg der Wohnstraße übrig, bei 18 m und
|
||||
12 m genau die 3,0 m der Hauptstraße. Die Gasse füllt ihren Korridor ganz aus und bekommt
|
||||
dadurch von selbst keinen Gehweg.
|
||||
"""
|
||||
return carriage.buffer(WALK_MAX, join_style="round").intersection(corridor).difference(carriage)
|
||||
|
||||
|
||||
def _kerb_lines(graph: Graph, carriage, bridges) -> list[LineString]:
|
||||
"""Die Fahrbahnkante, ohne die Stücke an Gassen und auf Brücken."""
|
||||
ohne = [band.buffer(0.2) for k, edge in enumerate(graph.edges)
|
||||
if not SECTION[edge.klass].kerb and (band := ribbon(graph, k)) is not None]
|
||||
ohne += [b.buffer(0.2) for b in bridges]
|
||||
rand = carriage.boundary
|
||||
if ohne:
|
||||
rand = rand.difference(unary_union(ohne))
|
||||
teile = getattr(rand, "geoms", [rand])
|
||||
return [g for g in teile if g.geom_type == "LineString" and g.length >= KERB_MIN]
|
||||
|
||||
|
||||
def _lots(corridor, carriage) -> list[Polygon]:
|
||||
rest = corridor.difference(carriage)
|
||||
return [g for g in getattr(rest, "geoms", [rest])
|
||||
if g.geom_type == "Polygon" and g.area >= LOT_MIN]
|
||||
|
||||
|
||||
def punch(polygons, carriage) -> list[Polygon]:
|
||||
"""Die Fahrbahn aus Bodenplatte oder Belag herausschneiden."""
|
||||
out = []
|
||||
for poly in polygons:
|
||||
rest = poly.difference(carriage)
|
||||
out += [g for g in getattr(rest, "geoms", [rest])
|
||||
if g.geom_type == "Polygon" and g.area > 0.5]
|
||||
return out
|
||||
Reference in new issue
Block a user