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:
knsoandClaude Sonnet 5 committed 2026-09-16 07:28:27 +02:00
1 parent d756eccd77
commit cfc9fc0690
2 files changed
+125 -2

No files matched your search

+63 -2
View File
@@ -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)