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
Good morning,
We are Master’s students at University Ca Foscari (Venice) and we enjoyed your videos! We wanted to ask you some clarifications about the API keys: we are working with a VRP optimization about a picking up service and we would like to insert the capacity of the trucks and how much each job collects in order to give it to the vehicle which will bring it to the warehouse. We tried to do it, but we did not succeed and we don’t know how to recall this variable in the vehicles and in the job variables. Could you help us in solving this problem please?
Thank you so much.
Best regards.
Hi, thanks for reaching out! In this case you would probably want to use capacity on the vehicle and amount on the job. Set capacity on the vehicle to the number of parcels the truck can pickup, and the amount on the job to the number of parcels to be picked up at the job site. Of course you may want to create a subunit or something to handle different parcel sizes. In that case, set 1 to the size of the smallest parcel and use 2 for a parcels twice the size etc.