import requests from pprint import pprint OVERPASS_URL = "https://overpass-api.de/api/interpreter" def fetch_overpass(overpass_query: str, bbox: tuple) -> dict: """ Fragt die Overpass API mit einem beliebigen Query nach POIs in der angegebenen Bounding Box ab. Sendet einen HTTP-POST-Request an die Overpass API und gibt die geparste JSON-Antwort zurück. Die Funktion prüft den HTTP-Status-Code, einen allfälligen Fehler im Response-Body sowie die JSON-Struktur der Antwort. Args: overpass_query (str): Overpass-QL-Query mit dem Platzhalter {bbox}. Beispiel: '[out:json][timeout:5]; (node["aerialway"="station"]({bbox});); out center body;' bbox (tuple): Bounding Box als 4-Tuple in Dezimalgrad: (south, west, north, east) Beispiel Davos: (46.72, 9.70, 46.92, 10.00) Beispiel Schweiz: (45.8, 5.9, 47.8, 10.5) Returns: dict: Geparste JSON-Antwort der Overpass API. Die Antwort enthält unter dem Schlüssel "elements" eine Liste von OSM-Objekten (nodes und ways) mit ihren Tags und Koordinaten. Beispiel: { "elements": [ { "type": "node", "id": 123456, "lat": 46.8, "lon": 9.8, "tags": {"aerialway": "station", "name": "Jakobshorn"} }, ... ] } Raises: RuntimeError: Wenn die API einen HTTP-Fehlercode zurückgibt (z.B. 429 Too Many Requests, 504 Gateway Timeout). RuntimeError: Wenn der Response-Body kein gültiges JSON enthält (z.B. bei leerem Body nach einem Server-Timeout). RuntimeError: Wenn Overpass einen fachlichen Fehler meldet (HTTP 200, aber "remark" im Body, z.B. bei Speicher- oder Timeout-Überschreitung). requests.Timeout: Wenn die API nicht innerhalb des Client-Timeouts antwortet. """ bbox_str = ",".join(map(str, bbox)) query = overpass_query.format(bbox=bbox_str) resp = requests.post( OVERPASS_URL, data={"data": query}, timeout=40, # clientseitiges timeout ist nicht serverseitiges timeout (das ist im query selbst auf z.B. 3 Sekunden gestellt) headers={"User-Agent": "GeoService/1.0 (poi-generator)"}, ) if resp.status_code != 200: raise RuntimeError( f"Overpass API Fehler: {resp.status_code}\n{resp.text}" ) try: data = resp.json() except requests.exceptions.JSONDecodeError: raise RuntimeError( f"Antwort ist kein gültiges JSON:\n{resp.text[:200]}" ) # HTTP 200 bedeutet nicht immer Erfolg — Overpass meldet Fehler im Body! if "remark" in data: raise RuntimeError( f"Overpass Query-Fehler: {data['remark']}" ) return data if __name__ == "__main__": BERGBAHN_QUERY = """ [out:json][timeout:2][maxsize:500000]; ( node["aerialway"="station"]({bbox}); way["aerialway"="station"]({bbox}); node["railway"="funicular"]({bbox}); way["railway"="funicular"]({bbox}); node["railway"="station"]["funicular"="yes"]({bbox}); ); out center body; """ bbox = (46.72, 9.70, 46.92, 10.00) # bbox = (45.8, 5.9, 47.8, 10.5) result = fetch_overpass(overpass_query=BERGBAHN_QUERY, bbox=bbox) pprint(result)