import routingpy as rp
import geopandas
import contextily as cx
import matplotlib.pyplot as plt
from shapely.geometry import Polygon
26 Using Other APIs with RoutingPy - Valhalla
The Valhalla API is a free and open source routing engine that works with OpenStreetMap data.
Like the previous API we used, we can also pull back both travel matrices and isochrones.
It can both be run locally, or the free web-based instance can be accessed.
Experiment with a simple isochrone
= rp.Valhalla(base_url='https://valhalla1.openstreetmap.de')
valhalla_api
= valhalla_api.isochrones(
isochrone =[-3.5127907292783873,50.72593717697725], # note long lat, not lat long
locations="multimodal", # this is transit + pedestrian mix
profile=[10*60,20*60,30*60] # in seconds, so 10 * 60 = 10 minutes, etc.
intervals
)
# set colours to iterate through (from longest time to shortest)
= ["darkorange", "deepskyblue", "chartreuse"]
color_list
= plt.subplots(figsize=(10,10))
fig, ax
= len(isochrone)
number_of_isochrones
for i in range(len(isochrone)):
= isochrone[number_of_isochrones-i-1].geometry # this improves order isochrones are plotted in
lon_lat_list
= Polygon(lon_lat_list)
polygon_geom = geopandas.GeoDataFrame(index=[0], crs='epsg:4326', geometry=[polygon_geom])
polygon
=ax, alpha=0.15, edgecolor='black', color=color_list[i])
polygon.plot(ax
'off')
ax.axis(
=14, crs=polygon.crs.to_string()) cx.add_basemap(ax,zoom
Turn this into an easy function for creating an isochrone from any lat-long pair.
def get_isochrone(long_lat_pair, profile="auto", intervals=[10*60,20*60,30*60], basemap_zoom=12, figsize=(10,10), alpha=0.15, options_dict=None):
'''
locations – One pair of lng/lat values. Takes the form [Longitude, Latitude].
profile – Specifies the mode of transport to use when calculating directions. One of [“auto”, “bicycle”, “multimodal”, “pedestrian”.
intervals – Time ranges to calculate isochrones for. In seconds or meters, depending on interval_type.
'''
= rp.Valhalla(base_url='https://valhalla1.openstreetmap.de')
valhalla_api
= valhalla_api.isochrones(
isochrone =long_lat_pair,
locations=profile,
profile=intervals,
intervals=options_dict
options
)
= ["darkorange", "deepskyblue", "chartreuse"]
color_list
= plt.subplots(figsize=figsize)
fig, ax
= len(isochrone)
number_of_isochrones
for i in range(len(isochrone)):
= isochrone[number_of_isochrones-i-1].geometry
lon_lat_list
= Polygon(lon_lat_list)
polygon_geom = geopandas.GeoDataFrame(index=[0], crs='epsg:4326', geometry=[polygon_geom])
polygon
=ax, alpha=alpha, edgecolor='black', color=color_list[i])
polygon.plot(ax
'off')
ax.axis(
=basemap_zoom, crs=polygon.crs.to_string()) cx.add_basemap(ax,zoom
26.1 Car
# auto = car in this api, not 'automatic' choice of routing
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="auto",
profile=12
basemap_zoom )
Valhalla instances can have traffic data integrated into it - though only for ‘routes’ and isochrones, and seemingly not via their web service (i.e. it has to be done with a local Valhalla instance).
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="auto",
profile=12
basemap_zoom )
26.2 Bicycle
# bike
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="bicycle",
profile=12
basemap_zoom )
26.3 Public Transit
Some level of public transport service seemes to be available in the service.
It appears that it may be through a partnership with Transitland: see https://www.transit.land/places/United%20Kingdom
# 'bus' profile doesn't appear to work correctly - repeats car map
# same seems to be true on their demo site (https://valhalla.openstreetmap.de/)
# use 'multimodal' instead for public transport - see below
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="bus",
profile=12
basemap_zoom )
# multimodal is effectively a mix of pedestrian + transit
# I believe transit data comes from the gtfs service (general transit feed specification)
# some detail about what gtfs data is held for the uk can be found here: https://gtfs.pro/en/uk
# gtfs is a google project - don't know if it's what powers google map's transit services (at least partly) or not
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="multimodal",
profile=15
basemap_zoom )
26.3.1 Look at options relating to public transit
API suggests we can set preference for bus vs rail and how willing users are to make use of transfers.
https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#transit-costing-options
We can set the parameter ‘dry run’ to see what will be send to the API and check that the formatting matches the examples given in the docs.
valhalla_api.isochrones(=[-3.5127907292783873,50.72593717697725],
locations="multimodal",
profile=[10*60,20*60,30*60],
intervals={"use_bus": 0.0, "use_rail": 1.0,"use_transfers":0.3},
options=True
dry_run )
url:
https://valhalla1.openstreetmap.de/isochrone
Parameters:
{
"headers": {
"User-Agent": "routingpy/v1.2.1",
"Content-Type": "application/json"
},
"timeout": 60,
"json": {
"locations": [
{
"lon": -3.5127907292783873,
"lat": 50.72593717697725
}
],
"costing": "multimodal",
"contours": [
{
"time": 10.0
},
{
"time": 20.0
},
{
"time": 30.0
}
],
"costing_options": {
"transit": {
"use_bus": 0.0,
"use_rail": 1.0,
"use_transfers": 0.3
}
}
}
}
Isochrones(None, None)
# Let's look at preferring bus usage
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="multimodal",
profile=15,
basemap_zoom={"use_bus": "1.0", "use_rail": "0.0","use_transfers":"0.3"}
options_dict )
# Now at preferring rail usage
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="multimodal",
profile=15,
basemap_zoom={"use_bus": "0.0", "use_rail": "1.0","use_transfers":"0.3"}
options_dict )
We can see that the output is no different. My theory is that current gtfs data for the uk does not include rail data. Downloading the geojson from this page and overlaying it in QGIS with the google maps hybrid map shows stops only on bus stops, not train stations. https://gtfs.pro/en/uk/Department-for-Transport-UK/dft-gtfs
More investigation needed!
Trying it again in European countries still doesn’t seem to yield different results - but uncertain which do and don’t have rail data in this form.
get_isochrone(= [7.458000835300172, 46.96831183806674],
long_lat_pair="multimodal",
profile=14,
basemap_zoom=[15*60,30*60,45*60],
intervals={"use_bus": "0.0", "use_rail": "1.0"}
options_dict )
get_isochrone(= [7.458000835300172, 46.96831183806674],
long_lat_pair="multimodal",
profile=14,
basemap_zoom=[15*60,30*60,45*60],
intervals={"use_bus": "1.0", "use_rail": "0.0"}
options_dict )
26.4 Impact of walking speed
We can see that changing the walking speed does have an impact on the results, so the options dict is definitely working.
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="pedestrian",
profile=15,
basemap_zoom={"walking_speed": "4.1"}
options_dict )
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="pedestrian",
profile=15,
basemap_zoom={"walking_speed": "2.5"}
options_dict )
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="pedestrian",
profile=15,
basemap_zoom={"walking_speed": "5.5"}
options_dict )
Experiment with impact of changing how comfortable users are with using cycle lanes vs on-road
https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#bicycle-costing-options
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="bicycle",
profile=12,
basemap_zoom={"use_roads":"1.0"}
options_dict )
get_isochrone(= [-3.5280937949744335,50.72494617349739],
long_lat_pair="bicycle",
profile=12,
basemap_zoom={"use_roads":"0.1"}
options_dict )