81 lines
3.3 KiB
Python
81 lines
3.3 KiB
Python
"""Holt die OpenStreetMap-Daten für Flamingo Park einmal und legt sie ins Repo.
|
|
|
|
cd tools/extract && PYTHONUTF8=1 uv run python osm.py
|
|
|
|
Spec: docs/superpowers/specs/2026-09-15-flamingo-park-design.md
|
|
run.py liest nur die geschriebene Datei und braucht kein Netz; ein neuer Abruf ist eine bewusste
|
|
Handlung mit eigenem Commit.
|
|
"""
|
|
import json
|
|
import urllib.parse
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
URL = "https://overpass-api.de/api/interpreter"
|
|
UA = {"User-Agent": "leonida-extract/0.1 (local hobby project)"}
|
|
BBOX = (25.7812, -80.1392, 25.7872, -80.1358) # Süd, West, Nord, Ost: Flamingo Park mit Rand
|
|
OUT = Path(__file__).resolve().parent / "data" / "osm" / "flamingo-park.json"
|
|
PATH_HIGHWAYS = ("footway", "path", "pedestrian", "steps", "cycleway", "service")
|
|
LEISURE = ("pitch", "playground", "dog_park", "sports_centre", "swimming_pool")
|
|
|
|
|
|
def overpass_query(bbox=BBOX) -> str:
|
|
box = "({},{},{},{})".format(*bbox)
|
|
return (f"[out:json][timeout:60];("
|
|
f"way{box}[highway];way{box}[leisure];way{box}[amenity=parking];way{box}[building];"
|
|
f");out geom;")
|
|
|
|
|
|
def keep(element: dict) -> bool:
|
|
"""Nur was der Park braucht: Wege, Sportflächen, Pool, Parkplatz, Gebäude."""
|
|
if element.get("type") != "way" or not element.get("geometry"):
|
|
return False
|
|
tags = element.get("tags", {})
|
|
if "building" in tags:
|
|
return True
|
|
highway = tags.get("highway")
|
|
if highway:
|
|
if highway not in PATH_HIGHWAYS:
|
|
return False
|
|
# Gehwege an Straßen gehören zu Teil 2; ein Stück des 13th-St-Wegs ist trotzdem als sidewalk getaggt.
|
|
return tags.get("footway") != "sidewalk" or tags.get("name", "").startswith("Flamingo Park Path")
|
|
return tags.get("leisure") in LEISURE or tags.get("amenity") == "parking"
|
|
|
|
|
|
def snapshot(raw: dict) -> dict:
|
|
"""Gefilterter Stand mit Herkunft, Lizenz und Abrufzeit; Geometrie als [Breite, Länge]."""
|
|
elements = [{"id": e["id"], "tags": e.get("tags", {}),
|
|
"geometry": [[g["lat"], g["lon"]] for g in e["geometry"]]}
|
|
for e in raw.get("elements", []) if keep(e)]
|
|
elements.sort(key=lambda e: e["id"])
|
|
return {
|
|
"source": "https://www.openstreetmap.org",
|
|
"license": "ODbL 1.0, © OpenStreetMap-Mitwirkende",
|
|
"fetched": raw.get("osm3s", {}).get("timestamp_osm_base", ""),
|
|
"elements": elements,
|
|
}
|
|
|
|
|
|
def dumps(snap: dict) -> str:
|
|
"""Eine Zeile je Element, damit ein neuer Abruf als lesbarer Diff erscheint."""
|
|
head = [f' "{key}": {json.dumps(value, ensure_ascii=False)}' for key, value in snap.items() if key != "elements"]
|
|
rows = ",\n".join(" " + json.dumps(e, ensure_ascii=False, separators=(",", ":")) for e in snap["elements"])
|
|
return "{\n" + ",\n".join(head) + ',\n "elements": [\n' + rows + "\n ]\n}\n"
|
|
|
|
|
|
def fetch(query: str) -> dict:
|
|
request = urllib.request.Request(URL, data=urllib.parse.urlencode({"data": query}).encode(), headers=UA)
|
|
with urllib.request.urlopen(request, timeout=90) as response:
|
|
return json.loads(response.read().decode("utf-8"))
|
|
|
|
|
|
def main() -> None:
|
|
snap = snapshot(fetch(overpass_query()))
|
|
OUT.parent.mkdir(parents=True, exist_ok=True)
|
|
OUT.write_text(dumps(snap), encoding="utf-8")
|
|
print(f"{len(snap['elements'])} Elemente, Stand {snap['fetched']} -> {OUT}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|