Visualization in Python: Finding Routes between Points | by Wei-Meng Lee | Towards Data Science

In my previous article, I talked about visualizing your geospatial data using the folium library.
towardsdatascience.com
Another task that developers often have to perform with geospatial data is to map out the routing paths between various points of interests. And so in this article I am going to show you how to:
Geocode your locations
Find the shortest distance between two locations
The first step to routing is to install the OSMnx package.
The OSMnx is a Python package that lets you download geospatial data from OpenStreetMap and model, project, visualize, and analyze real-world street networks and any other geospatial geometries.
Installing the OSMnx package is a little tricky — before you perform the usual pip/conda install, you have to use the following step:
$ conda config --prepend channels conda-forge$ conda install osmnx
Note: If conda install osmnx fails, use pip install osmnx
Once the above steps are performed, OSMnx should now be available for use.
You can now make use of the OSMnx package together with the NetworkX package to find the route between two points.
NetworkX is a Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks.
The following code snippet finds the shortest walking distance between two locations in San Francisco:
import osmnx as oximport networkx as nxox.config(log_console=True, use_cache=True)# define the start and end locations in latlngstart_latlng = (37.78497,-122.43327)end_latlng = (37.78071,-122.41445)# location where you want to find your routeplace     = 'San Francisco, California, United States'# find shortest route based on the mode of travelmode      = 'walk'        # 'drive', 'bike', 'walk'# find shortest path based on distance or timeoptimizer = 'time'        # 'length','time'# create graph from OSM within the boundaries of some # geocodable place(s)graph = ox.graph_from_place(place, network_type = mode)# find the nearest node to the start locationorig_node = ox.get_nearest_node(graph, start_latlng)# find the nearest node to the end locationdest_node = ox.get_nearest_node(graph, end_latlng)#  find the shortest pathshortest_route = nx.shortest_path(graph,                                  orig_node,                                  dest_node,                                  weight=optimizer)
The default method for finding the shortest path is ‘dijkstra’. You can change this by setting the method parameter of the shortest_path() function to ’bellman-ford’.
The shortest_route variable now holds a collection of path to walk from one point to another in the shortest time:
[5287124093, 65314192, 258759765, 65314189, 5429032435, 65303568, 65292734, 65303566, 2220968863, 4014319583, 65303561, 65303560, 4759501665, 65303559, 258758548, 4759501667, 65303556, 65303554, 65281835, 65303553, 65303552, 65314163, 65334128, 65317951, 65333826, 65362158, 65362154, 5429620634, 65308268, 4064226224, 7240837048, 65352325, 7240837026, 7240837027]
Obviously, having a list of paths is not very useful by itself. A more meaning way to interpret the result is to plot the paths using the plot_route_folium() function:
shortest_route_map = ox.plot_route_folium(graph, shortest_route)shortest_route_map
The plot_route_folium() function returns a folium map (folium.folium.Map). When displayed in Jupyter Notebook, it looks like this:
The default tileset used is cartodbpositron. If you want to change it, you can set the tiles argument to the tileset that you want to use. The following code snippet shows the map displayed using the openstreetmap tileset:
shortest_route_map = ox.plot_route_folium(graph, shortest_route,                                           tiles='openstreetmap')shortest_route_map
Here is the map displayed using the openstreetmap tileset:
If you want to let the user choose their preferred tileset during runtime, use the following code snippet:
import foliumfolium.TileLayer('openstreetmap').add_to(shortest_route_map)folium.TileLayer('Stamen Terrain').add_to(shortest_route_map)folium.TileLayer('Stamen Toner').add_to(shortest_route_map)folium.TileLayer('Stamen Water Color').add_to(shortest_route_map)folium.TileLayer('cartodbpositron').add_to(shortest_route_map)folium.TileLayer('cartodbdark_matter').add_to(shortest_route_map)folium.LayerControl().add_to(shortest_route_map)shortest_route_map
The user can now choose their own tileset to display the map:
Besides finding the shortest path for walking, you can also plot the shortest path for driving:
# find shortest route based on the mode of travelmode      = 'drive'       # 'drive', 'bike', 'walk'# find shortest path based on distance or timeoptimizer = 'time'        # 'length','time'
Here is the path for driving:
How about biking?
# find shortest route based on the mode of travelmode      = 'bike'       # 'drive', 'bike', 'walk'# find shortest path based on distance or timeoptimizer = 'time'       # 'length','time'
Here is the shortest path for biking:
You can also find the shortest path based on distance, instead of time:
# find shortest route based on the mode of travelmode      = 'bike'       # 'drive', 'bike', 'walk'# find shortest path based on distance or timeoptimizer = 'length'     # 'length','time'
This is the shortest distance for biking:
I will leave the rest of the combinations for you to try out.
When finding routes between two points, it is not very convenient to specify the latitude and longitude of the locations (unless you already have the coordinates in your dataset). Instead, it would be far easier to just specify their friendly names. You can actually perform this step (called geocoding) using the geopy module.
Geocoding is the process of converting an address into its coordinates. Reverse geocoding, on the other hand, turns a pair of coordinates into a friendly address.
To install the geopy module, type the following command in Terminal:
$ pip install geopy
The following code snippet creates an instance of the Nominatim geocoder class for OpenStreetMap data. It then calls the geocode()method to geocode the location for Golden Gate Bridge. Using the geocoded location, you can now extract the latitude and longitude of the location:
from geopy.geocoders import Nominatimlocator = Nominatim(user_agent = "myapp")location = locator.geocode("Golden Gate Bridge")print(location.latitude, location.longitude)# 37.8303213 -122.4797496print(location.point)# 37 49m 49.1567s N, 122 28m 47.0986s Wprint(type(location.point))# <class 'geopy.point.Point'>
You can verify the result by going to https://www.google.com/maps and pasting the latitude and longitude into the search box:
Let’s now modify our original code so that we can geocode the start and end points:
import osmnx as oximport networkx as nxox.config(log_console=True, use_cache=True)from geopy.geocoders import Nominatimlocator = Nominatim(user_agent = "myapp")# define the start and end locations in latlng# start_latlng = (37.78497,-122.43327)# end_latlng = (37.78071,-122.41445)start_location = "Hilton San Francisco Union Square"end_location = "Golden Gateway Tennis & Swim Club"# stores the start and end points as geopy.point.Point objectsstart_latlng = locator.geocode(start_location).pointend_latlng = locator.geocode(end_location).point# location where you want to find your routeplace     = 'San Francisco, California, United States'# find shortest route based on the mode of travelmode      = 'bike'       # 'drive', 'bike', 'walk'# find shortest path based on distance or timeoptimizer = 'length'        # 'length','time'# create graph from OSM within the boundaries of some # geocodable place(s)graph = ox.graph_from_place(place, network_type = mode)# find the nearest node to the start locationorig_node = ox.get_nearest_node(graph, start_latlng)# find the nearest node to the end locationdest_node = ox.get_nearest_node(graph, end_latlng)...
Note that the get_nearest_node() function can accept the location coordinates either as a tuple containing latitude and longitude, or a geopy.point.Point object.
Here is the shortest biking distance from Hilton hotel in Union Square to the Golden Gateway Tennis & Swim Club in San Francisco:
It would be clearer if you could display markers indicating the starting point as well as the ending point. As I described in my previous article, you can use the Marker class in folium to display a marker with a popup.
The following code snippet displays two markers — green indicating the starting point, and red indicating the end point:
import folium# Marker class only accepts coordinates in tuple formstart_latlng = (start_latlng[0],start_latlng[1])end_latlng   = (end_latlng[0],end_latlng[1])start_marker = folium.Marker(            location = start_latlng,            popup = start_location,            icon = folium.Icon(color='green'))end_marker = folium.Marker(            location = end_latlng,            popup = end_location,            icon = folium.Icon(color='red'))# add the circle marker to the mapstart_marker.add_to(shortest_route_map)end_marker.add_to(shortest_route_map)shortest_route_map
Note that the Marker class only accepts coordinates in tuple form. Hence you need to modify start_latlng and end_latlng to become tuples containing latitude and longitude.
Here are the two markers indicating the start and end of the path:
In the earlier section, you used theplot_route_folium() function to plot the shortest path of two points on a folium map:
shortest_route_map = ox.plot_route_folium(graph, shortest_route)shortest_route_map
There is one more function that you might be interested — plot_graph_route(). Instead of outputting an interactive map, it produces a static graph. This is useful if you want to produce a poster/image showing the path of two points.
The following code snippet produces a static graph using the points used in the previous section:
import osmnx as oximport networkx as nxox.config(log_console=True, use_cache=True)graph = ox.graph_from_place(place, network_type = mode)orig_node = ox.get_nearest_node(graph, start_latlng)dest_node = ox.get_nearest_node(graph, end_latlng)shortest_route = nx.shortest_path(graph,                                   orig_node,                                   dest_node,                                   weight=optimizer)fig, ax = ox.plot_graph_route(graph,                              shortest_route,                              save=True)
You will see the following output:
I hope you find inspiration from this article and start using it to create routes for points of interest. You might want to let users enter their current locations, and then plot the routes to show them how to go to their destinations. In any case, have fun!
weimenglee.medium.com
Reference: Boeing, G. 2017. OSMnx: New Methods for Acquiring, Constructing, Analyzing, and Visualizing Complex Street Networks. Computers, Environment and Urban Systems 65, 126–139. doi:10.1016/j.compenvurbsys.2017.05.004