How to find path and distance between two coordinates using OSM and python?

10.6k views Asked by At

I need to get the driving time and distance between two sets of coordinates using Python and an open source mapping service ( preferably OSM).

I found a lot of different python libraries that can calculate the distance between two given points (locations) but it is not the driving distance.

I also noticed that using the Google Distance Matrix API and a JSON interpreter I can pretty much do this but I don't want to use google for this project. Please advise on an appropriate library that uses Open Street Map network and calculates the travel time and distance and preferably allows for creating a map of the selected route.

p.s. I noticed that a similar task is done using OSM but not with python

2

There are 2 answers

1
Loïc On

Just did search for you, I didn't test it, but this seems to be what you are looking for : http://wiki.openstreetmap.org/wiki/PyrouteLib

3
Rivers On

You could use OSMnx.

Here is a sample code that do what you're asking for:

import osmnx as ox
import networkx as nx
from datetime import timedelta


# The place where your 2 points are located. It will be used to create a graph from the OSM data
# In this example, the 2 points are two addresses in Manhattan, so we choose "Manhattan"
# It could be a bounding box too, or an area around a point
graph_area = ("Manhattan, New York, USA")

# Create the graph of the area from OSM data. It will download the data and create the graph
G = ox.graph_from_place(graph_area, network_type='drive')
# (For a better accuracy, create a graph with lot more nodes:)
#G = ox.graph_from_place(graph_area, network_type='drive', simplify=False)

# OSM data are sometime incomplete so we use the speed module of osmnx to add missing edge speeds and travel times
G = ox.add_edge_speeds(G)
G = ox.add_edge_travel_times(G)

# Save graph to disk if you want to reuse it
ox.save_graphml(G, "Manhattan.graphml")

# Load the graph
#G = ox.load_graphml("Manhattan.graphml")

# Plot the graph
fig, ax = ox.plot_graph(G, figsize=(10, 10), node_size=0, edge_color='y', edge_linewidth=0.2)

# Two pairs of (lat,lng) coordinates
origin_coordinates = (40.70195053163349, -74.01123198479581)
destination_coordinates = (40.87148739347057, -73.91517498611597)

# If you want to take an address (osmx will use Nominatim service for this)
# origin_coordinates = ox.geocode("2 Broad St, New York, NY 10005")

# In the graph, get the nodes closest to the points
origin_node = ox.nearest_nodes(G, Y=origin_coordinates[0], X=origin_coordinates[1])
destination_node = ox.nearest_nodes(G, Y=destination_coordinates[0], X=destination_coordinates[1])


# Get the shortest route by distance
shortest_route_by_distance = ox.shortest_path(G, origin_node, destination_node, weight='length')

# Plot the shortest route by distance
fig, ax = ox.plot_graph_route(G, shortest_route_by_distance, route_color='y', route_linewidth=6, node_size=0)

# Get the shortest route by travel time
shortest_route_by_travel_time = ox.shortest_path(G, origin_node, destination_node, weight='travel_time')

# Plot the shortest route by travel time
fig, ax = ox.plot_graph_route(G, shortest_route_by_travel_time, route_color='y', route_linewidth=6, node_size=0)

# Plot the 2 routes
fig, ax = ox.plot_graph_routes(G, routes=[shortest_route_by_distance, shortest_route_by_travel_time], route_colors=['r', 'y'], route_linewidth=6, node_size=0)

# Get the travel time, in seconds
# Note here that we use "nx" (networkx), not "ox" (osmnx)
travel_time_in_seconds = nx.shortest_path_length(G, origin_node, destination_node, weight='travel_time')
print("travel time in seconds", travel_time_in_seconds)

#The travel time in "HOURS:MINUTES:SECONDS" format
travel_time_in_hours_minutes_seconds = str(timedelta(seconds=travel_time_in_seconds))
print("travel time in hours minutes seconds", travel_time_in_hours_minutes_seconds)

# Get the distance in meters
distance_in_meters = nx.shortest_path_length(G, origin_node, destination_node, weight='length')
print("distance in meters", distance_in_meters)
# Distance in kilometers
distance_in_kilometers = distance_in_meters / 1000
print("distance in kilometers", distance_in_kilometers)

And by the way thanks to Geoff Boeing for this great library!

Update 2023 relative to @KBurchfiel comment:

In fact, it will depend on the shape of the path. Sometimes it will be more accurate, but sometimes it will be even more inaccurate. Look at these images (they are from https://github.com/nathanrooy/taxicab):

enter image description here

enter image description here

enter image description here

You can do 2 things to produce a more accurate distance measurement:

  1. Add simplify=False when creating the graph: G = ox.graph_from_place(graph_area, network_type='drive', simplify=False). Your graph will have lot more nodes.
  2. Use Taxicab from nathanrooy

Here is an example using Taxicab:

import osmnx as ox
import networkx as nx
import taxicab as tc

# Load the graph
G = ox.load_graphml("Manhattan.graphml")
origin_coordinates = (40.70195053163349, -74.01123198479581)
destination_coordinates = (40.87148739347057, -73.91517498611597)
route = tc.distance.shortest_path(G, origin_coordinates, destination_coordinates)
tc.plot.plot_graph_route(G, route)

Note that Taxicab has this function for distances only, not for travel times. I have no time at the moment to code it, but if you or another reader have it, feel free to add it here and/or to contribute to Taxicab/Osmnx ;-)