Mit Kartendaten lassen sich tolle Analysen und Anwendungen bauen. Ich bin ein großer Verfechter der Open Street Map (merkt man kaum in diesem Blog). Mit deren Hilfe habe ich auch eine etwas andere Innenraumkarte für die Münchner öffentlichen Verkehrsmittel gebaut.
Inspiriert wurde ich durch eine Veröffentlichung der Londoner Verkehrsgesellschaft. Die hat mit ihrem Routenplaner die Zeiten ermittelt, die man zu Fuß zwischen den einzelnen Stationen zurücklegen muss – und das auf eine Karte gepackt (Mehr dazu hier). Dabei zeigt sich: Manchmal lohnt es sich sehr, mal kurz ein paar Minuten zu Fuß zu gehen – wenn die U-Bahn gerade erst weggefahren ist, oder es Probleme gibt.
Von London nach München
Und da kommt München ins Spiel. Immer wieder gibt es auch hier Probleme mit den Öffis, die die Pendler nerven. Wenn die eine Karte hätten, könnten sie sich selbst helfen. Ich wollte also die Gehzeiten aller Stationen des Münchner Nahverkehrsnetzes (S-Bahn und U-Bahn) ermitteln. Weil die Stationen außerhalb des Innenraums fast nur noch S-Bahnen sind – und gehwegemässig sehr weit auseinander liegen, habe ich mich auf den Innenraum beschränkt. Wie ich das grundsätzlich gemacht habe, findet ihr in diesem Blogartikel.
Die Daten kamen aus der Open Street Map (OSM). Dort gibt es eine Wiki-Seite in der Münchner Orte vertaggt sind. So auch die Transportmittel mit ihren Linienverläufen und Haltestellen. Jedes Objekt in der OSM hat eine ID – und lässt sich damit supereasy abfragen.

Ein Blick ins OSM-Wiki: Hier stehen die Relationen der Münchner U- und S-Bahnen
So kann ich für jede U-Bahnlinie, die als Zusammenhang (Relation) festgelegt ist, die dazugehörigen Punkte abfragen. Die Stationen.
Für die U1 sehen die Daten in der Open Street Map beispielsweise so aus:

Die U1 mit den Daten der Linie. Jede Station hat eigene Daten (z.B. Name). © OpenStreetMap-Mitwirkende
Für jede U-Bahnlinie habe ich mir dann die Relation herausgesucht und mit Liniennummer in eine CSV-Datei geschrieben. Das hätte ich auch locker automatisieren können, denn die Abfrage über die Open Street Map ist ja ganz leicht. War aber kein großer Unterschied für mich.
So sehen die Linien und ihre IDs aus:
Ein Ausschnitt aus der CSV:
line | relation_id |
u1 | 3502654 |
u2 | 3502584 |
u3 | 3484046 |
s1 | 1773068 |
s2 | 1854799 |
s3 | 1792663 |
Die CSV-Datei habe ich dann in R geladen und für jede Linie die Stationskoordinaten abgefragt. Die wurden dann an die Google Distance Matrix geschickt (mehr dazu hier). Aus jeder Linie entstand dann eine Matrix, in der die Gehzeiten zwischen den einzelnen Stationen beschrieben waren. Die Linienmatrizen habe ich einzeln als CSV (z.B. S1.csv oder U6.csv) abgespeichert und händisch in einer Innenraumvorlage ergänzt. Auch das könnte man automatisieren, wenn festlegt, wo zwischen den Stationen die Zeiten erscheinen sollen.
In R die Stationen abgefragt, dann die Distanz berechnet
Ein Blick in den Code (den API-Key habe ich entfernt):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
library(dplyr) library(tidyr) library(RCurl) library(jsonlite) ids <- read.csv2("route_ids.csv", stringsAsFactors = FALSE) id_i <- ids$relation_id df_names <- ids$line # Funktion, um Stunden in Minuten umzurechnen - brauchen wir später getminutes <- function(x) {x[1] / 60} # Haltestellenpunkte für jede Linie bekommen - Overpass API-Abfrage for (i_id in seq_along(id_i)){ print(paste0("Trying: ", df_names[i_id])) myQuery <- paste0("[out:csv(::id,\"name\",::lat,::lon)];relation(", id_i[i_id], ");(node(r););out%20body;>;out%20skel%20qt;") url <- paste0("http://overpass-api.de/api/interpreter?data=", myQuery) #API-Call ausführen response_osm <- getURL(url, .encoding = "UTF-8") print("Got OSM-Results") #response als CSV laden d <- read.csv(text = response_osm, head = TRUE, sep="\t", stringsAsFactors = FALSE) # Matrx für jede Linie bekommen d$lon <- as.numeric(d$X.lon) d$lat <- as.numeric(d$X.lat) lat_lon_pairs <- purrr::by_row(d, ~ paste0(.x$lat, ",", .x$lon))$.out %>% unlist sources <- paste0(lat_lon_pairs, collapse = "|") mymatrix <- data.frame() # Jedes lat_lon_pair abfragen mit For-Loop for (i_pairs in seq_along(lat_lon_pairs)){ destination <- lat_lon_pairs[[i_pairs]] # Google Distance Matrix-Abfrage ab hier print("Start Matrix") Sys.sleep(2) url <- paste0("https://maps.googleapis.com/maps/api/distancematrix/json?origins=", destination, "&destinations=", sources, "&mode=walking&key=HIER_STEHT_DER_API_KEY") response_test <- jsonlite::fromJSON(url) print(paste0("Google-Status: ", response_test$status)) mymatrix <- rbind(mymatrix, as.vector(response_test$rows$elements[[1]]$duration$value)) } #Hier wird aus der Matrix ein Dataframe erstellt print("Dataframe wird erstellt") df <- as.data.frame(mymatrix) df <- as.data.frame(lapply(df[, 1:ncol(df)], FUN = function(x) {sapply(x, FUN = getminutes)})) rownames(df) <- d$name colnames(df) <- d$name df$line <- ids$line[i_id] write.csv2(df, file = paste0(df_names[i_id], ".csv"), row.names = TRUE) print(paste0("Dataframe ", df_names[i_id], " gespeichert")) } |
Noch ein bisschen professioneller wäre der Code, wenn wir die Abfrage mit der Bibliothek httr machen würden. Wie die funktioniert, hat der Entwickler hier beschrieben.
So sah es am Ende aus
Das Ergebnis habe ich dann in Inkscape auf einem Innenraumplan weiterbearbeitet. Es lief bei tz.de: „Gehzeitenplan: So kommen Sie zur Fuß durchs U-Bahnetz“.