In this tutorial, I cover vehicle route optimization using openrouteservice with the openrouteservice-py wrapper for Python. If you simply need to optimize a single route for a single vehicle with no service time restrictions, you can do this by using the directions API as described in my tutorial about getting directions with Python and ensuring you pass the optimize_waypoints=True
parameter. The below notebook will guide you through doing some more complex optimization examples.
Imports
import folium
import openrouteservice as ors
import math
Here we lay out some coordinates that we can use for our optimizations.
# Simple single route optimization
coords = [
[-87.7898356, 41.8879452],
[-87.7808524, 41.8906422],
[-87.7895149, 41.8933762],
[-87.7552925, 41.8809087],
[-87.7728134, 41.8804058],
[-87.7702890, 41.8802231],
[-87.7787924, 41.8944518],
[-87.7732345, 41.8770663],
]
# visualize the points on a map
m = folium.Map(location=list(reversed([-87.787984, 41.8871616])), tiles="cartodbpositron", zoom_start=14)
for coord in coords:
folium.Marker(location=list(reversed(coord))).add_to(m)
m
Here we do a simple two vehicle optimization with each vehicle being able to take a maximum of 5 tasks.
client = ors.Client(key='YOUR_KEY_HERE')
coords = [
[-87.7898356, 41.8879452],
[-87.7808524, 41.8906422],
[-87.7895149, 41.8933762],
[-87.7552925, 41.8809087],
[-87.7728134, 41.8804058],
[-87.7702890, 41.8802231],
[-87.7787924, 41.8944518],
[-87.7732345, 41.8770663],
]
vehicle_start = [-87.800701, 41.876214]
m = folium.Map(location=list(reversed([-87.787984, 41.8871616])), tiles="cartodbpositron", zoom_start=14)
for coord in coords:
folium.Marker(location=list(reversed(coord))).add_to(m)
folium.Marker(location=list(reversed(vehicle_start)), icon=folium.Icon(color="red")).add_to(m)
m
vehicles = [
ors.optimization.Vehicle(id=0, profile='driving-car', start=vehicle_start, end=vehicle_start, capacity=[5]),
ors.optimization.Vehicle(id=1, profile='driving-car', start=vehicle_start, end=vehicle_start, capacity=[5])
]
jobs = [ors.optimization.Job(id=index, location=coords, amount=[1]) for index, coords in enumerate(coords)]
optimized = client.optimization(jobs=jobs, vehicles=vehicles, geometry=True)
line_colors = ['green', 'orange', 'blue', 'yellow']
for route in optimized['routes']:
folium.PolyLine(locations=[list(reversed(coords)) for coords in ors.convert.decode_polyline(route['geometry'])['coordinates']], color=line_colors[route['vehicle']]).add_to(m)
m
In the below example, we add service durations to our jobs and give each vehicle an 8-hour shift. One vehicle takes 3 jobs, while the other vehicles picks up the remaining 5. There are more options to add restrictions to jobs/vehicles, check out the remaining options at the VROOM API. Note that as the Vroom API changes options may differ slightly from what is available on openrouteservice and implemented in openrouteservice-py
coords = [
{ 'location': [-87.7898356, 41.8879452], 'service': 1*60*60 },
{ 'location': [-87.7808524, 41.8906422], 'service': 0.5*60*60 },
{ 'location': [-87.7895149, 41.8933762], 'service': 1*60*60 },
{ 'location': [-87.7552925, 41.8809087], 'service': 6*60*60 },
{ 'location': [-87.7728134, 41.8804058], 'service': 1*60*60 },
{ 'location': [-87.7702890, 41.8802231], 'service': 1*60*60 },
{ 'location': [-87.7787924, 41.8944518], 'service': 1*60*60 },
{ 'location': [-87.7732345, 41.8770663], 'service': 0.25*60*60},
]
vehicle_start = [-87.800701, 41.876214]
m = folium.Map(location=list(reversed([-87.787984, 41.8871616])), tiles="cartodbpositron", zoom_start=14)
folium.Marker(location=list(reversed(vehicle_start)), icon=folium.Icon(color="red")).add_to(m)
m
vehicles = [
ors.optimization.Vehicle(id=0, profile='driving-car', start=vehicle_start, end=vehicle_start, time_window=[0, 8*60*60]),
ors.optimization.Vehicle(id=1, profile='driving-car', start=vehicle_start, end=vehicle_start, time_window=[0, 8*60*60])
]
jobs = [ors.optimization.Job(id=index, **job) for index, job in enumerate(coords)]
optimized = client.optimization(jobs=jobs, vehicles=vehicles, geometry=True)
line_colors = ['green', 'orange', 'blue', 'yellow']
for route in optimized['routes']:
folium.PolyLine(locations=[list(reversed(coords)) for coords in ors.convert.decode_polyline(route['geometry'])['coordinates']], color=line_colors[route['vehicle']]).add_to(m)
for step in route['steps']:
if not step['type'] == 'job':
continue
folium.Marker(location=list(reversed(step['location'])), popup=f"Arrival time: {math.floor(step['arrival'] / (60*60))} hours {math.floor((step['arrival'] % (60*60)) / 60)} minutes", icon=folium.Icon(color=line_colors[route['vehicle']])).add_to(m)
m