feat(extract): Jefferson Avenue als gerade Straße statt der Gasse
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d756eccd77
commit
cfc9fc0690
2 files changed
+125
-2
No files matched your search
@@ -10,12 +10,14 @@ from __future__ import annotations
|
||||
import json
|
||||
import math
|
||||
import tomllib
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass, field, replace
|
||||
from pathlib import Path
|
||||
|
||||
from shapely.geometry import LineString, Polygon
|
||||
from shapely.geometry import LineString, Point, Polygon
|
||||
from shapely.ops import unary_union
|
||||
|
||||
from .heights import PALM_KERB
|
||||
|
||||
|
||||
class ParkError(ValueError):
|
||||
"""Formfehler in der Parkdatei oder eine Welt, zu der sie nicht mehr passt: der Lauf bricht ab."""
|
||||
@@ -424,3 +426,62 @@ def _points(geometry) -> list[tuple[float, float]]:
|
||||
elif g.geom_type in ("LineString", "LinearRing"):
|
||||
out.extend(g.coords)
|
||||
return out
|
||||
|
||||
|
||||
# --- Welt verändern ----------------------------------------------------------------------------
|
||||
|
||||
MIN_CENTERLINE = 15.0 # m, wie centerlines() in vectors.py: kürzere Stummel setzten Laternen auf die Kreuzung
|
||||
MIN_ROAD_AREA = 20.0 # m², wie road_polygons() in vectors.py
|
||||
|
||||
|
||||
@dataclass
|
||||
class World:
|
||||
"""Weltgeometrie zwischen Extraktion und Chunk-Ausgabe, in Welt-x/Welt-y."""
|
||||
land: list[Polygon]
|
||||
surfaces: list[tuple[Polygon, str]]
|
||||
roads: list[tuple[Polygon, bool]]
|
||||
centerlines: list[LineString]
|
||||
buildings: list[tuple[Polygon, dict, bool]] # Grundriss, {"height", "source"}, spekulativ
|
||||
palms: list[tuple[float, float]]
|
||||
paths: list[Polygon] = field(default_factory=list)
|
||||
|
||||
|
||||
def _lines(geometry, min_length: float = MIN_CENTERLINE) -> list[LineString]:
|
||||
return [g for g in _pieces(geometry) if g.geom_type == "LineString" and g.length >= min_length]
|
||||
|
||||
|
||||
def apply_street(world: World, street: Street) -> World:
|
||||
"""Gebäude und Gasse weg, gerade Straße mit Mittellinie, frei gewordene Fläche wird Stadtfläche."""
|
||||
cut = Polygon(street.alley_cut)
|
||||
|
||||
def hit(building) -> bool:
|
||||
return any(building[0].contains(Point(p)) for p in street.remove_buildings)
|
||||
|
||||
removed = [b[0] for b in world.buildings if hit(b)]
|
||||
buildings = [b for b in world.buildings if not hit(b)]
|
||||
line = LineString([street.start, street.end])
|
||||
corridor = line.buffer(street.width / 2, cap_style="flat")
|
||||
|
||||
kept, joined = [], [corridor]
|
||||
for poly, bridge in world.roads:
|
||||
rest = poly.difference(cut) if poly.intersects(cut) else poly
|
||||
for piece in _polygons(rest, MIN_ROAD_AREA):
|
||||
if not bridge and piece.intersects(corridor):
|
||||
joined.append(piece)
|
||||
else:
|
||||
kept.append((piece, bridge))
|
||||
roads = kept + [(piece, False) for piece in _polygons(unary_union(joined), MIN_ROAD_AREA)]
|
||||
|
||||
centerlines = [piece for l in world.centerlines
|
||||
for piece in (_lines(l.difference(cut)) if l.intersects(cut) else [l])] + [line]
|
||||
|
||||
freed = unary_union(removed + [cut]).difference(corridor)
|
||||
covered = [b[0] for b in buildings if b[0].intersects(freed)] + \
|
||||
[poly for poly, kind in world.surfaces if kind == "urban" and poly.intersects(freed)]
|
||||
if covered:
|
||||
freed = freed.difference(unary_union(covered))
|
||||
surfaces = world.surfaces + [(poly, "urban") for poly in _polygons(freed)]
|
||||
|
||||
palms = [p for p in world.palms if corridor.distance(Point(p)) >= PALM_KERB]
|
||||
return replace(world, roads=roads, centerlines=centerlines, buildings=buildings,
|
||||
surfaces=surfaces, palms=palms)
|
||||
Reference in new issue
Block a user