Eine Routen-Relation enthält die Ways einer Linie in ihrer Reihenfolge. Fügen Sie ihre Geometrie zu einem Pfad zusammen und schicken Sie ihn an /elevation/profile: Sie erhalten alle 90 m eine Höhe mit der Distanz vom Start, den gesamten Anstieg und den tiefsten und höchsten Punkt.
Die Pilatusbahn bei Luzern ist die steilste Zahnradbahn der Welt, mit Steigungen bis 48%.
Die Abfrage
[out:json][timeout:25]; rel["route"="train"]["ref"="CC83"]["to"="Pilatus Kulm"]; out geom;
Ausführen
Profil als Tabelle
Live-Ergebnis von overpass.ch, bis zu 12 Stunden zwischengespeichert. Kartendaten © OpenStreetMap-Mitwirkende (ODbL). Heights produced using Copernicus WorldDEM™-90 © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved. Meerestiefen: GEBCO Compilation Group (2026) GEBCO 2026 Grid.
So funktioniert es
rel["route"="train"]["ref"="CC83"]["to"="Pilatus Kulm"]wählt die Bergfahrt der Linie CC83. Die Talfahrt ist eine eigene Relation.out geom;liefert die Relation mit den Koordinaten jedes Mitglieds.- Die Mitglieder mit leerer Rolle sind das Gleis; Mitglieder
stopundplatformfallen weg. Fügen Sie die Ways der Reihe nach zusammen, und drehen Sie einen Way um, wenn sein Ende statt seines Anfangs den vorherigen berührt. - Schicken Sie den Pfad als
{"path": [[lat, lon], …]}an/elevation/profile. In diesen Daten ist die Linie etwa 4,3 km lang und steigt etwa 1'590 m.
Varianten
- Die Gornergratbahn in Zermatt:
rel["route"="train"]["name"="R48 (CC): Zermatt GGB => Gornergrat"];(9,3 km, etwa 1'490 m Anstieg). - Den Abstand ändern Sie mit
interval(in Metern, mindestens 10) odersamples, wie in der Dokumentation beschrieben. - Ein GPX-Track statt einer OSM-Route: Schicken Sie die Datei selbst an
/elevation/profile, siehe Höhen zu einer GPX-Datei hinzufügen.
Aus Ihrem Code ausführen
Zwei Anfragen: Die Abfrage liefert die Route, und ihre zusammengesetzte Geometrie geht an /elevation/profile.
import math
import requests
API = "https://api.overpass.ch/v1/IHR_API_SCHLÜSSEL"
query = open("query.overpassql").read()
r = requests.post(f"{API}/interpreter", data={"data": query}, timeout=120)
r.raise_for_status()
route = r.json()["elements"][0]
def gap(a, b): # Abstand in Grad, genügt zum Vergleichen
return math.hypot(a[0] - b[0], (a[1] - b[1]) * math.cos(math.radians(a[0])))
ways = [[[p["lat"], p["lon"]] for p in m["geometry"]] for m in route["members"]
if m["type"] == "way" and m["role"] in ("", "forward", "backward")]
path = list(ways[0])
for i, way in enumerate(ways[1:]):
near = lambda p: min(gap(p, way[0]), gap(p, way[-1]))
if i == 0 and near(path[0]) < near(path[-1]):
path.reverse() # der erste Weg zeigt rückwärts
if gap(path[-1], way[-1]) < gap(path[-1], way[0]):
way = way[::-1] # dieser auch
path += way
r = requests.post(f"{API}/elevation/profile", json={"path": path}, timeout=60)
r.raise_for_status()
answer = r.json()
print(answer["profile"]) # distance_m, ascent_m, descent_m, min_m, max_m, …
for p in answer["results"][:5]:
print(p["distance"], p["elevation"])
const API = "https://api.overpass.ch/v1/IHR_API_SCHLÜSSEL";
const res = await fetch(`${API}/interpreter`, { method: "POST", body: new URLSearchParams({ data: query }) });
if (!res.ok) throw new Error(await res.text());
const route = (await res.json()).elements[0];
// Abstand in Grad, genügt zum Vergleichen
const gap = (a, b) => Math.hypot(a[0] - b[0], (a[1] - b[1]) * Math.cos((a[0] * Math.PI) / 180));
const ways = route.members
.filter((m) => m.type === "way" && ["", "forward", "backward"].includes(m.role))
.map((m) => m.geometry.map((p) => [p.lat, p.lon]));
const path = [...ways[0]];
ways.slice(1).forEach((way, i) => {
const near = (p) => Math.min(gap(p, way[0]), gap(p, way.at(-1)));
if (i === 0 && near(path[0]) < near(path.at(-1))) path.reverse(); // der erste Weg zeigt rückwärts
if (gap(path.at(-1), way.at(-1)) < gap(path.at(-1), way[0])) way.reverse(); // dieser auch
path.push(...way);
});
const pr = await fetch(`${API}/elevation/profile`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ path }),
});
if (!pr.ok) throw new Error(await pr.text());
const { profile, results } = await pr.json();
console.log(profile, results.slice(0, 5));
Speichern Sie die Abfrage als query.overpassql. Sie funktioniert genauso in Overpass Turbo, QGIS und JOSM.