Created
July 1, 2021 06:07
-
-
Save Westlife1002/c6d9c40047723b1581690afcc7ee13b4 to your computer and use it in GitHub Desktop.
VRP 11.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Capacitated Vehicle Routing Problem""" | |
from six.moves import xrange | |
from ortools.constraint_solver import pywrapcp | |
from ortools.constraint_solver import routing_enums_pb2 | |
import networkx as nx | |
import matplotlib.pyplot as plt | |
import random | |
import math | |
import pandas as pd | |
import tkinter as tk | |
import sys | |
# Problem Data Definition # | |
class Vehicle(): | |
def __init__(self): | |
self._capacity = 9000 | |
# self.id = id | |
self.routes = [] | |
self.time_min = [] | |
self.time_max = [] | |
self.load = [] | |
self.cm3 = [] | |
self._speed = 0.31 #mile/min | |
@property | |
def capacity(self): | |
"""Gets vehicle capacity""" | |
return self._capacity | |
def routes(self): | |
return self.routes | |
def speed(self): | |
return self._speed | |
class TextRedirector(object): | |
def __init__(self, widget, tag="stdout"): | |
self.widget = widget | |
self.tag = tag | |
def write(self, str): | |
self.widget.configure(state="normal") | |
self.widget.insert("end", str, (self.tag,)) | |
self.widget.configure(state="disabled") | |
class DataProblem(): | |
"""Stores the data for the problem""" | |
def __init__(self): | |
"""Initializes the data for the problem""" | |
self._vehicle = Vehicle() | |
self._num_vehicles = num_vehicles() | |
self._locations = [] | |
self._demands = [] | |
self._time_windows = [] | |
self._cm3 = [] | |
self._depot = 0 | |
# self._starts = [26, 39, 15, 39, 20, 1, 36, 49, 29, | |
# 22, 7, 34, 39, 32, 1, 22, 48, 9, 9, | |
# 39, 4, 49, 37, 9, 34, 25, 44, 15, 6, | |
# 35, 42, 20, 16, 26, 31, 33, 20, 42, 50, | |
# 47, 42, 42, 17, 11, 40, 2, 51, 27, 12, | |
# 13, 32, 19, 27, 6, 11, 4, 34, 4, 24, 12, | |
# 38, 48, 15, 5, 44, 47, 29, 50, 32, 1, 35, | |
# 9, 8, 40, 43, 1, 29, 18, 6, 15, 2, 14, 51, | |
# 21, 35, 44, 30, 36, 44, 35, 32, 7, 22, 8, 41, | |
# 46, 22, 48, 13, 45, 3, 5, 29, 11, 33, 24, 40, | |
# 18, 29, 21, 41, 1, 51, 36, 19, 23, 18, 35, 33, | |
# 27, 24, 2, 27, 3, 20, 45, 1, 29, 27, 29, 2, 47, | |
# 24, 2, 8, 46, 23, 5, 48, 13, 35, 43, 44, 12, 17, 28, 5, 23, 7, 14] | |
self._starts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] | |
# self._starts = [85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85] | |
# self._starts = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] | |
self._ends = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] | |
df = pd.read_csv("VRP_input_6.27.csv", encoding="cp1252") | |
self.dataframe = df | |
# print(df) | |
self._demands = df["Chargeable Weight"].tolist() | |
self._cm3 = df["cm3"].tolist() | |
lat = df["Lat"].tolist() | |
lon = df["Long"].tolist() | |
self._locations = list(zip(lat,lon)) | |
# print(self._locations ) | |
earliest = df['earliest'].tolist() | |
latest = df['latest'].tolist() | |
self._time_windows = list(zip(earliest,latest)) | |
# print(self._time_windows) | |
@property | |
def getvehicle(self): | |
"""Gets a vehicle""" | |
return self._vehicle | |
@property | |
def num_vehicles(self): | |
"""Gets number of vehicles""" | |
return self._num_vehicles | |
@property | |
def locations(self): | |
"""Gets locations""" | |
return self._locations | |
@property | |
def num_locations(self): | |
"""Gets number of locations""" | |
return len(self.locations) | |
@property | |
def depot(self): | |
"""Gets depot location index""" | |
return self._depot | |
@property | |
def demands(self): | |
"""Gets demands at each location""" | |
return self._demands | |
@property | |
def cm3(self): | |
"""Gets cm3 at each location""" | |
return self._cm3 | |
@property | |
def time_per_demand_unit(self): | |
"""Gets the time (in min) to load a demand""" | |
return 20 # average 20 minutes | |
@property | |
def time_windows(self): | |
"""Gets (start time, end time) for each locations""" | |
return self._time_windows | |
@property | |
def starts(self): | |
"""Gets start location for each truck""" | |
return self._starts | |
@property | |
def ends(self): | |
"""Gets end location for each truck""" | |
return self._ends | |
def distance(lat1, long1, lat2, long2): | |
# Note: The formula used in this function is not exact, as it assumes | |
# the Earth is a perfect sphere. | |
# Mean radius of Earth in miles | |
radius_earth = 3959 | |
# Convert latitude and longitude to | |
# spherical coordinates in radians. | |
degrees_to_radians = math.pi/180.0 | |
phi1 = lat1 * degrees_to_radians | |
phi2 = lat2 * degrees_to_radians | |
lambda1 = long1 * degrees_to_radians | |
lambda2 = long2 * degrees_to_radians | |
dphi = phi2 - phi1 | |
dlambda = lambda2 - lambda1 | |
a = haversine(dphi) + math.cos(phi1) * math.cos(phi2) * haversine(dlambda) | |
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) | |
d = radius_earth * c | |
return d | |
def haversine(angle): | |
h = math.sin(angle / 2) ** 2 | |
return h | |
# print(DataProblem().locations) | |
# print(DataProblem().demands) | |
####################### | |
# Problem Constraints # | |
####################### | |
class CreateDistanceEvaluator(object): # pylint: disable=too-few-public-methods | |
"""Creates callback to return distance between points.""" | |
def __init__(self, data, manager): | |
"""Initializes the distance matrix.""" | |
self._distances = {} | |
self.manager = manager | |
self.dist_matrix = [] | |
# @property | |
# def manager(self): | |
# """Gets routing model""" | |
# return self._manager | |
# precompute distance between location to have distance callback in O(1) | |
for from_node in xrange(data.num_locations): | |
self._distances[from_node] = {} | |
temp = [] | |
for to_node in xrange(data.num_locations): | |
if from_node == to_node: | |
self._distances[from_node][to_node] = 0 | |
temp.append(0) | |
else: | |
x1 = data.locations[from_node][0] # Ycor of end1 | |
# print (x1) | |
y1 = data.locations[from_node][1] # Xcor of end1 | |
# print(y1) | |
x2 = data.locations[to_node][0] # Ycor of end2 | |
y2 = data.locations[to_node][1] # Xcor of end2 | |
self._distances[from_node][to_node] = int( | |
distance(x1, y1, x2, y2)) | |
temp.append(int(distance(x1, y1, x2, y2))) | |
self.dist_matrix.append(temp) | |
# def distance_evaluator(self, from_index, to_index): | |
# """Returns the Harversine Distance between the two nodes""" | |
# # Convert from routing variable Index to time matrix NodeIndex. | |
# # print("from_index: ", from_index) | |
# # print("self.dist_matrix: ", self.dist_matrix) | |
# # print("type(from_index)", from_index) | |
# from_node = self.manager.IndexToNode(from_index) | |
# # print("type(from_node)", from_node) | |
# # print("") | |
# # print("from_node: ", from_node) | |
# to_node = self.manager.IndexToNode(to_index) | |
# # return self._distances[from_node][to_node] | |
# return self.dist_matrix[from_node][to_node] | |
# def distance_evaluator(self, from_index, to_index): | |
# """Returns the Harversine Distance between the two nodes""" | |
# return self._distances[from_index][to_index] | |
class CreateDemandEvaluator(object): | |
"""Creates callback to get demands at each location.""" | |
def __init__(self, data, manager): | |
"""Initializes the demand array.""" | |
self._demands = data.demands | |
self._manager = manager | |
def demand_evaluator(self, from_index): | |
"""Returns the demand of the current node""" | |
from_node = self._manager.IndexToNode(from_index) | |
return self._demands[from_node] | |
class CreateCM3Evaluator(object): | |
"""Creates callback to get cm3 at each location.""" | |
def __init__(self, data, manager): | |
"""Initializes the demand array.""" | |
self._cm3 = data.cm3 | |
self._manager = manager | |
def cm3_evaluator(self, from_index): | |
"""Returns the cm3 of the current node""" | |
from_node = self._manager.IndexToNode(from_index) | |
return self._cm3[from_node] | |
class CreateTimeEvaluator(object): | |
"""Creates callback to get total times between locations.""" | |
@staticmethod | |
def service_time(data, node): | |
"""Gets the service time for the specified location.""" | |
# return data.demands[node] * data.time_per_demand_unit #function of volume at this node | |
return data.time_per_demand_unit #constant service time for all nodes | |
@staticmethod | |
def travel_time(data, from_node, to_node): | |
"""Gets the travel times between two locations.""" | |
if from_node == to_node: | |
travel_time = 0 | |
else: | |
x1=data.locations[from_node][0] | |
y1=data.locations[from_node][1] | |
x2=data.locations[to_node][0] | |
y2=data.locations[to_node][1] | |
travel_time = distance( | |
x1,y1,x2,y2) / data.getvehicle.speed() | |
return travel_time | |
def __init__(self, data, manager): | |
"""Initializes the total time matrix.""" | |
self._total_time = {} | |
self._manager = manager | |
# precompute total time to have time callback in O(1) | |
for from_node in xrange(data.num_locations): | |
self._total_time[from_node] = {} #under total_time {}, add key = this from_node, value ={} | |
for to_node in xrange(data.num_locations): | |
if from_node == to_node: | |
self._total_time[from_node][to_node] = 0 #under this from_node {}, add key = this to_node, value = 0 | |
else: | |
self._total_time[from_node][to_node] = int( #under this from_node {}, add key = this to_node, value = service_time + travel_time | |
self.service_time(data, from_node) + | |
self.travel_time(data, from_node, to_node)) | |
# print(self._total_time) | |
# print(self._total_time) | |
def time_evaluator(self, from_index, to_index): | |
"""Returns the total time between the two nodes""" | |
from_node = self._manager.IndexToNode(from_index) | |
to_node = self._manager.IndexToNode(to_index) | |
return self._total_time[from_node][to_node] | |
def add_distance_dimension(routing, transit_callback_index): | |
"""Add Global Span constraint""" | |
distance = "Distance" | |
maximum_distance = 360 | |
routing.AddDimension( | |
transit_callback_index, | |
0, # null slack | |
maximum_distance, # maximum distance per vehicle | |
True, # start cumul to zero | |
distance) | |
# distance_dimension = routing.GetDimensionOrDie(distance) | |
# Try to minimize the max distance among vehicles. | |
# /!\ It doesn't mean the standard deviation is minimized | |
# distance_dimension.SetGlobalSpanCostCoefficient(100) | |
# def add_capacity_constraints(routing, data, demand_evaluator): | |
# """Adds capacity constraint""" | |
# capacity = "Capacity" | |
# routing.AddDimension( | |
# demand_evaluator, | |
# 0, # null capacity slack | |
# data.getvehicle.capacity, # vehicle maximum capacity | |
# True, # start cumul to zero | |
# capacity) | |
def add_capacity_constraints(routing, data, demand_callback_index): | |
"""Adds capacity constraint""" | |
capacity = "Capacity" | |
routing.AddDimensionWithVehicleCapacity( | |
demand_callback_index, | |
0, # null capacity slack | |
capacity_vector(), # vector vehicle_capacity | |
True, # start cumul to zero | |
capacity) | |
def add_cm3_constraints(routing, data, demand_callback_index): | |
"""Adds capacity constraint""" | |
cm3 = "cm3" | |
routing.AddDimensionWithVehicleCapacity( | |
demand_callback_index, | |
0, # null capacity slack | |
cm3_vector(), # vector vehicle_capacity | |
True, # start cumul to zero | |
cm3) | |
def add_time_window_constraints(routing, data, time_callback_index, manager): | |
"""Add Global Span constraint""" | |
time = "Time" | |
# horizon = 2000 | |
horizon = 1200 | |
routing.AddDimension( | |
time_callback_index, | |
horizon, # allow waiting time | |
horizon, # maximum time per vehicle | |
True, # don't force start cumul to zero since we are giving TW to start nodes | |
time) | |
time_dimension = routing.GetDimensionOrDie(time) | |
for location_idx, time_window in enumerate(data.time_windows): | |
if location_idx == 0: | |
continue | |
index = manager.NodeToIndex(location_idx) | |
print("location idx: ", location_idx) | |
print("node to index: ", index) | |
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1]) | |
# print(time_dimension.CumulVar(index)) | |
routing.AddToAssignment(time_dimension.SlackVar(index)) | |
for vehicle_id in xrange(data.num_vehicles): | |
index = routing.Start(vehicle_id) | |
# set a constraint on the start or the end node for each vehicle, which is not included in above | |
time_dimension.CumulVar(index).SetRange(data.time_windows[0][0], data.time_windows[0][1]) | |
routing.AddToAssignment(time_dimension.SlackVar(index)) | |
########### | |
# Printer # | |
########### | |
vlist=[] | |
load_list=[] | |
time_list_min=[] | |
time_list_max=[] | |
class ConsolePrinter(): | |
"""Print solution to console""" | |
def __init__(self, data, manager, routing, assignment): | |
"""Initializes the printer""" | |
self._data = data | |
self._routing = routing | |
self._assignment = assignment | |
self._manager = manager | |
@property | |
def data(self): | |
"""Gets problem data""" | |
return self._data | |
@property | |
def routing(self): | |
"""Gets routing model""" | |
return self._routing | |
@property | |
def assignment(self): | |
"""Gets routing model""" | |
return self._assignment | |
@property | |
def manager(self): | |
"""Gets routing model""" | |
return self._manager | |
def print(self): | |
"""Prints assignment on console""" | |
# Inspect solution. | |
capacity_dimension = self.routing.GetDimensionOrDie('Capacity') | |
cm3_dimension = self.routing.GetDimensionOrDie('cm3') | |
time_dimension = self.routing.GetDimensionOrDie('Time') | |
total_dist = 0 | |
total_time = 0 | |
global vlist | |
global load_list | |
global time_list_min | |
global time_list_max | |
for vehicle_id in xrange(self.data.num_vehicles): | |
index = self.routing.Start(vehicle_id) | |
plan_output = 'Vehicle ID {0}, route plan details:\n'.format(vehicle_id) | |
route_dist = 0 | |
this_vehicle = Vehicle() | |
while not self.routing.IsEnd(index): | |
node_index = self.manager.IndexToNode(index) | |
# print(node_index) | |
this_vehicle.routes.append(self.manager.IndexToNode(index)) | |
# print(this_vehicle.routes) | |
# print(self.data.vehicle.routes) | |
# print(self.data.vehicle.routes.append(temp_id)) | |
next_node_index = self.manager.IndexToNode( | |
self.assignment.Value(self.routing.NextVar(index))) | |
route_dist += distance( | |
self.data.locations[node_index][0], | |
self.data.locations[node_index][1], | |
self.data.locations[next_node_index][0], | |
self.data.locations[next_node_index][1]) | |
load_var = capacity_dimension.CumulVar(index) | |
cm3_var = cm3_dimension.CumulVar(index) | |
# print("load_var: ", load_var) | |
route_load = self.assignment.Value(load_var) | |
# route_load += self.data.demands[node_index] | |
cm3_load = self.assignment.Value(cm3_var) | |
time_var = time_dimension.CumulVar(index) | |
time_min = self.assignment.Min(time_var) | |
time_max = self.assignment.Max(time_var) | |
this_vehicle.load.append(route_load) | |
this_vehicle.cm3.append(cm3_load) | |
this_vehicle.time_min.append(time_min) | |
this_vehicle.time_max.append(time_max) | |
slack_var = time_dimension.SlackVar(index) | |
slack_min = self.assignment.Min(slack_var) | |
slack_max = self.assignment.Max(slack_var) | |
plan_output += ' address:{0} load({1}) earliest/latest({2},{3}) idle time({4},{5}) ->'.format( | |
node_index, | |
route_load, | |
time_min, time_max, | |
slack_min, slack_max) | |
index = self.assignment.Value(self.routing.NextVar(index)) | |
node_index = self.manager.IndexToNode(index) | |
load_var = capacity_dimension.CumulVar(index) | |
cm3_var = cm3_dimension.CumulVar(index) | |
# print("truck type: ", str(load_var).split("..")[1][:-1]) | |
route_load = self.assignment.Value(load_var) | |
cm3_load = self.assignment.Value(cm3_var) | |
time_var = time_dimension.CumulVar(index) | |
route_time = self.assignment.Value(time_var) | |
time_min = self.assignment.Min(time_var) | |
time_max = self.assignment.Max(time_var) | |
total_dist += route_dist | |
total_time += route_time | |
if route_load != 0: | |
plan_output += ' address:{0} load({1}) earliest/latest({2},{3})\n'.format(node_index, route_load, time_min, time_max) | |
plan_output += 'truck type: {0} \n'.format(GetTruckType(int(str(load_var).split("..")[1][:-1]))) | |
plan_output += 'total dist: {0} km\n'.format(int(route_dist)) | |
plan_output += 'total weight: {0}\n'.format(route_load) | |
plan_output += 'total cm3: {0}\n'.format(cm3_load) | |
plan_output += 'total time: {0} min\n'.format(route_time) | |
vlist.append(this_vehicle.routes) | |
print(plan_output) | |
this_vehicle.routes.append(0) | |
this_vehicle.load.append(route_load) | |
this_vehicle.cm3.append(cm3_load) | |
this_vehicle.time_min.append(time_min) | |
this_vehicle.time_max.append(time_max) | |
# vlist.append(this_vehicle.routes) | |
load_list.append(this_vehicle.load) | |
time_list_min.append(this_vehicle.time_min) | |
time_list_max.append(this_vehicle.time_max) | |
# print(vlist) | |
print('total dist(all routes): {0} km'.format(int(total_dist))) | |
print('total time(all routes): {0} min'.format(int(total_time))) | |
def PickupColor(count): | |
default_cl=["red", "blue","yellowgreen","orange","hotpink","orchid", | |
"black","purple","slateblue","olive","steelblue","magenta", | |
"cadetblue","teal","lightseagreen","deepskyblue", | |
"olivedrab","darkkhaki", "sandybrown", "tomato"] | |
if count <= len(default_cl) - 1: | |
color=default_cl[count] | |
else: | |
# temp=random.choice(list(matplotlib.colors.cnames.items()))[0] | |
# while "white" in temp or "gray" in temp and temp in default_cl: | |
# temp = random.choice(list(matplotlib.colors.cnames.items()))[0] | |
# continue | |
# cl=temp | |
rand = lambda: random.randint(70, 170) | |
color='#%02X%02X%02X' % (rand(), rand(), rand()) | |
return color | |
def DrawNetwork(): | |
G = nx.DiGraph() | |
locations = DataProblem()._locations | |
# print(locations) | |
x = 0 | |
for vehicle_id in vlist: | |
n = 0 | |
e = [] | |
node = [] | |
cl=PickupColor(x) | |
# print(cl) | |
# print(data.num_vehicles) | |
# print(this_vehicle.id) | |
# print(this_vehicle.routes) | |
for i in vehicle_id: | |
G.add_node(i, pos=(locations[i][0], locations[i][1])) | |
# a= [locations[i][0], locations[i][1]] | |
# print(a) | |
if n > 0: | |
# print(n) | |
# print(vehicle_id.routes[n]) | |
# print (vehicle_id.routes[n-1]) | |
u = (vehicle_id[n - 1], vehicle_id[n]) | |
e.append(u) | |
node.append(i) | |
G.add_edge(vehicle_id[n - 1], vehicle_id[n]) | |
# nx.draw(G, nx.get_node_attributes(G, 'pos'), nodelist=node, edgelist=e, with_labels=True, | |
# node_color=cl, width=2, edge_color=cl, | |
# style='dashed', font_color='w', font_size=12, font_family='sans-serif') | |
n += 1 | |
nx.draw(G, nx.get_node_attributes(G, 'pos'), nodelist=node, edgelist=e, with_labels=True, | |
node_color=cl, width=2, edge_color=cl, | |
style='dashed', font_color='w', font_size=12, font_family='sans-serif') | |
x += 1 | |
# let's color the node 0 in black | |
nx.draw_networkx_nodes(G, locations, nodelist=[0], node_color='k') | |
plt.axis('on') | |
# plt.show() | |
def Reporting(data): | |
df = data.dataframe | |
df['Vehicle_ID'] = 0 | |
df['Pos_in_route'] = 0 | |
df['arr_min'] = 0 | |
df['arr_max'] = 0 | |
df['total_load'] = 0 | |
df['prior_lat'] = 0 | |
df['prior_lon'] = 0 | |
veh = vlist | |
global prior_list | |
prior_list=[] | |
lst_GPS = df[['Lat','Long']].to_dict() | |
df1 = pd.DataFrame(columns=df.columns) #Create an empty dataframe | |
for i in range(len(veh)): | |
veh_id_dict = dict.fromkeys(veh[i][:-1], i) | |
veh_pos_dict = dict(zip(veh[i][:-1], range(len(veh[i]) - 1))) | |
veh_arrmin = dict(zip(veh[i][:-1], time_list_min[i][:-1])) | |
veh_arrmax = dict(zip(veh[i][:-1], time_list_max[i][:-1])) | |
veh_load = dict.fromkeys(veh[i][:-1], load_list[i][-1]) | |
# print(veh[i]) | |
veh_prior = veh[i].copy() | |
veh_prior.insert(1,0) | |
veh_prior = veh_prior[:-1] | |
# print(veh_prior) | |
lat = list(zip(*list(zip(veh_prior, [lst_GPS['Lat'][i] for i in veh_prior]))))[1] | |
lon = list(zip(*list(zip(veh_prior, [lst_GPS['Long'][i] for i in veh_prior]))))[1] | |
# print(lat) | |
prior_list.append([lat,lon]) | |
veh_prior_lat = dict(zip(veh[i][:-1], lat)) | |
veh_prior_lon = dict(zip(veh[i][:-1], lon)) | |
# print(veh_prior_lat) | |
df.loc[veh[i][:-1], 'Vehicle_ID'] = df.loc[veh[i][:-1]].ID.map(veh_id_dict) | |
df.loc[veh[i][:-1], 'Pos_in_route'] = df.loc[veh[i][:-1]].ID.map(veh_pos_dict) | |
df.loc[veh[i][:-1], 'arr_min'] = df.loc[veh[i][:-1]].ID.map(veh_arrmin) | |
df.loc[veh[i][:-1], 'arr_max'] = df.loc[veh[i][:-1]].ID.map(veh_arrmax) | |
df.loc[veh[i][:-1], 'total_load'] = df.loc[veh[i][:-1]].ID.map(veh_load) | |
df.loc[veh[i][:-1], 'prior_lat'] = df.loc[veh[i][:-1]].ID.map(veh_prior_lat) | |
df.loc[veh[i][:-1], 'prior_lon'] = df.loc[veh[i][:-1]].ID.map(veh_prior_lon) | |
# print(df.loc[veh[i][:-1], ['Vehicle_ID', 'Pos_in_route','prior_lat']]) | |
df1 = df1.append(df.loc[veh[i][:-1]]) | |
df1.sort_values(['ID', 'Vehicle_ID'], inplace=True) | |
for i in range(len(veh)): | |
df.loc[veh[i][-1], 'Vehicle_ID'] = i | |
df.loc[veh[i][-1], 'Pos_in_route'] = len(veh[i])-1 | |
df.loc[veh[i][-1], 'arr_min'] = time_list_min[i][-1] | |
df.loc[veh[i][-1], 'arr_max'] = time_list_max[i][-1] | |
df.loc[veh[i][-1], 'total_load'] = load_list[i][-1] | |
df.loc[veh[i][:-1], 'prior_lat'] = prior_list[i][0][-1] | |
df.loc[veh[i][:-1], 'prior_lon'] = prior_list[i][1][-1] | |
df1 = df1.append(df.loc[veh[i][-1]]) | |
df1['Utilization_Rate'] = df1['total_load'] / data.getvehicle.capacity | |
# print(df1) | |
# Create a Pandas Excel writer using XlsxWriter as the engine. | |
writer = pd.ExcelWriter('VRP_result.xlsx', engine='xlsxwriter') | |
# Convert the dataframe to an XlsxWriter Excel object. | |
df1.to_excel(writer, sheet_name='Sheet1') | |
writer.save() | |
######## | |
# Main # | |
######## | |
def main(): | |
"""Entry point of the program""" | |
# Instantiate the data problem. | |
data = DataProblem() | |
# Create Routing Model | |
manager = pywrapcp.RoutingIndexManager(data.num_locations, data.num_vehicles, data.starts, data.ends) | |
# manager = pywrapcp.RoutingIndexManager(data.num_locations, data.num_vehicles, data.depot) | |
routing = pywrapcp.RoutingModel(manager) | |
# Define weight of each edge | |
dist_evaluator = CreateDistanceEvaluator(data,manager) | |
# print(dist_evaluator.dist_matrix) | |
def distance_evaluator(from_index, to_index): | |
"""Returns the Harversine Distance between the two nodes""" | |
from_node = manager.IndexToNode(from_index) | |
to_node = manager.IndexToNode(to_index) | |
# print("from_index: ", from_index) | |
# print("to_index: ", to_index) | |
# print("from_node: ", from_node) | |
# print("to_node: ", to_node) | |
# print("dist: ", dist_evaluator.dist_matrix[from_node][to_node]) | |
return dist_evaluator.dist_matrix[from_node][to_node] | |
distance_callback_index = routing.RegisterTransitCallback(distance_evaluator) | |
routing.SetArcCostEvaluatorOfAllVehicles(distance_callback_index) | |
addTruckCost(routing) | |
# Add Distance constraint | |
add_distance_dimension(routing, distance_callback_index) | |
# Add Capacity constraint | |
demand_evaluator = CreateDemandEvaluator(data, manager).demand_evaluator | |
demand_callback_index = routing.RegisterUnaryTransitCallback(demand_evaluator) | |
add_capacity_constraints(routing, data, demand_callback_index) | |
# Add Cubic Meters constraint | |
cm3_evaluator = CreateCM3Evaluator(data, manager).cm3_evaluator | |
cm3_callback_index = routing.RegisterUnaryTransitCallback(cm3_evaluator) | |
add_cm3_constraints(routing, data, cm3_callback_index) | |
# Add Time Window constraint | |
time_evaluator = CreateTimeEvaluator(data, manager).time_evaluator | |
time_callback_index = routing.RegisterTransitCallback(time_evaluator) | |
add_time_window_constraints(routing, data, time_callback_index, manager) | |
# Setting first solution heuristic (cheapest addition). | |
search_parameters = pywrapcp.DefaultRoutingSearchParameters() | |
# 1).First Solution Heuristic | |
search_parameters.first_solution_strategy = ( | |
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) | |
search_parameters.time_limit.seconds = 10 | |
# search_parameters.time_limit.seconds = 20 | |
# 2).Guided Local Search Heuristics | |
# search_parameters.local_search_metaheuristic = ( | |
# routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) | |
# search_parameters.log_search = True | |
# search_parameters.time_limit.FromSeconds(10) | |
# search_parameters.time_limit_ms = 3000 | |
# 3).Simulated Annealing Heuristics | |
# search_parameters.local_search_metaheuristic = ( | |
# routing_enums_pb2.LocalSearchMetaheuristic.SIMULATED_ANNEALING) | |
# search_parameters.time_limit_ms = 1800000 | |
# 4).TABU_SEARCH Heuristics | |
# search_parameters.local_search_metaheuristic = ( | |
# routing_enums_pb2.LocalSearchMetaheuristic.TABU_SEARCH) | |
# search_parameters.time_limit_ms = 200000 | |
# search_parameters.time_limit.seconds = 10 | |
# Solve the problem. | |
# assignment = routing.SolveWithParameters(search_parameters) | |
solution = routing.SolveWithParameters(search_parameters) | |
if solution: | |
print("solution found!\n") | |
printer = ConsolePrinter(data, manager, routing, solution) | |
# print_solution(data, manager, routing, solution) | |
printer.print() | |
DrawNetwork() | |
Reporting(data) | |
plt.show() | |
else: | |
print("no solution!\n") | |
# window.quit() | |
# if __name__ == '__main__': | |
# main() | |
window = tk.Tk() | |
window.title('VRP 11.0') | |
window.geometry('1000x507') | |
# window2 = tk.Tk() | |
# window.title('VRP 2.0') | |
# window.geometry('500x500') | |
l1 = tk.Label(window, bg='light gray', width=50, text='empty') | |
l1.pack() | |
# l2 = tk.Label(window, bg='yellow', width=50, text='empty') | |
# l2.pack() | |
mode = tk.StringVar() | |
mode.set("auto") | |
l4 = tk.Radiobutton(text='manual', variable=mode, value='manual') | |
l4.place(x=800, y=60) | |
l5 = tk.Radiobutton(text='auto', variable=mode, value='auto') | |
l5.place(x=750, y=60) | |
l6 = tk.Label(window,text="planning mode:", justify = tk.LEFT, padx = 20) | |
l6.place(x=750, y=40) | |
l3 = tk.Text() | |
l3.pack(side=tk.RIGHT) | |
def quite(): | |
window.quit() | |
def print_selection1(v): | |
l1.config(text='you have chosen ' + str(num_vehicles()) +' trucks') | |
# def print_selection2(v): | |
# l2.config(text='Truck Capacity is: ' + v + ' kg') | |
def num_vehicles(): | |
global t1 | |
global t2 | |
global t3 | |
global t4 | |
global t5 | |
which_button_is_selected = mode.get() | |
if which_button_is_selected == "manual": | |
t1=s1.get() | |
t2=s2.get() | |
t3=s3.get() | |
t4=s4.get() | |
t5=s5.get() | |
if which_button_is_selected == "auto": | |
t1=int(s1.cget("to")) | |
t2=int(s2.cget("to")) | |
t3=int(s3.cget("to")) | |
t4=int(s4.cget("to")) | |
t5=int(s5.cget("to")) | |
return t1+t2+t3+t4+t5 | |
def capacity_vector(): | |
return [1660]*t1+[2324]*t2+[4980]*t3+[7470]*t4+[14940]*t5 | |
def cm3_vector(): | |
return [8]*t1+[17]*t2+[34]*t3+[49]*t4+[82]*t5 | |
def addTruckCost(routing): | |
for index,item in enumerate(capacity_vector()): | |
# print("index: ", index, " capacity: ", item) | |
if item == 1660: | |
routing.SetFixedCostOfVehicle(3000, index) | |
if item == 2324: | |
routing.SetFixedCostOfVehicle(4500, index) | |
if item == 4980: | |
routing.SetFixedCostOfVehicle(6000, index) | |
if item == 7470: | |
routing.SetFixedCostOfVehicle(8000, index) | |
if item == 14940: | |
routing.SetFixedCostOfVehicle(12000, index) | |
def GetTruckType(item): | |
if item == 1660: | |
return "Sprinter" | |
if item == 2324: | |
return "LutonVan" | |
if item == 4980: | |
return "7.5 Tonner" | |
if item == 7470: | |
return "17.5 Tonner" | |
if item == 14940: | |
return "Trailer" | |
s1 = tk.Scale(window, label='Sprinter', from_=0, to=30, orient=tk.HORIZONTAL, | |
length=400, showvalue=1, tickinterval=5, resolution=1, command=print_selection1) | |
s1.pack() | |
s2 = tk.Scale(window, label='LutonVan', from_=0, to=30, orient=tk.HORIZONTAL, | |
length=400, showvalue=1, tickinterval=5, resolution=1, command=print_selection1) | |
s2.pack() | |
s3 = tk.Scale(window, label='7.5 Tonner', from_=0, to=30, orient=tk.HORIZONTAL, | |
length=400, showvalue=1, tickinterval=5, resolution=1, command=print_selection1) | |
s3.pack() | |
s4 = tk.Scale(window, label='17.5 Tonner', from_=0, to=30, orient=tk.HORIZONTAL, | |
length=400, showvalue=1, tickinterval=5, resolution=1, command=print_selection1) | |
s4.pack() | |
s5 = tk.Scale(window, label='Trailer', from_=0, to=30, orient=tk.HORIZONTAL, | |
length=400, showvalue=1, tickinterval=5, resolution=1, command=print_selection1) | |
s5.pack() | |
b = tk.Button(window, text='run', width=15, | |
height=2, command=main) | |
b.pack() | |
b = tk.Button(window, text='exit', width=15, | |
height=2, command=quite) | |
b.place(x=700, y=440) | |
sys.stdout = TextRedirector(l3, "stdout") | |
window.mainloop() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ID | Data | Docket_No | Jobtype | HUB | Hub Lat | Hub Long | UDN | Col_Postcode | Del_Postcode | Customer Name | Postcode | Lat | Long | Date | Chargeable Weight | earliest | latest | cm3 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | EI LHR DC | 51.4457678 | -0.45613 | 0 | 0 | 0 | 0 | ||||||||||||
1 | DelSpec | 664373 | Delivery | LHR | 51.45682 | -0.452107 | 444393 | TW14 8QH | AL4 9LP | AL49LP | AL4 | 51.799089 | -0.29716 | 3/4/2016 | 472 | 480 | 960 | 10 | |
2 | DelSpec | 664203 | Delivery | LHR | 51.45682 | -0.452107 | 444246 | TW14 8QH | B19 1DS | B191DS | B19 | 52.497911 | -1.903832 | 3/4/2016 | 1488 | 480 | 960 | 3 | |
3 | DelSpec | 664717 | Delivery | LHR | 51.45682 | -0.452107 | 444693 | TW14 8QH | BB5 5YJ | BB55YJ | BB5 | 53.74589 | -2.379778 | 3/4/2016 | 5652 | 480 | 960 | 10 | |
4 | DelSpec | 664524 | Delivery | LHR | 51.45682 | -0.452107 | 444528 | TW14 8QH | CV23 0WA | CV230WA | CV23 | 52.332395 | -1.346753 | 3/4/2016 | 2348 | 480 | 960 | 9 | |
5 | DelSpec | 665329 | Delivery | LHR | 51.45682 | -0.452107 | 445250 | TW14 8QH | CV34 4RL | CV344RL | CV34 | 52.276323 | -1.579845 | 3/4/2016 | 12 | 480 | 960 | 10 | |
6 | DelSpec | 664519 | Delivery | LHR | 51.45682 | -0.452107 | 444524 | TW14 8QH | PE19 8JJ | PE198JJ | PE19 | 52.254674 | -0.265726 | 3/4/2016 | 2940 | 480 | 960 | 5 | |
7 | DelSpec | 665207 | Delivery | LHR | 51.45682 | -0.452107 | 445144 | TW14 8QH | RM17 6SU | RM176SU | RM17 | 51.480381 | 0.332106 | 3/4/2016 | 2533 | 480 | 960 | 4 | |
8 | DelSpec | 665355 | Delivery | LHR | 51.45682 | -0.452107 | 445272 | TW14 8QH | TW6 3ET | TW63ET | TW6 | 51.466844 | -0.452952 | 3/4/2016 | 1142 | 480 | 960 | 7 | |
9 | DelSpec | 664677 | Delivery | LHR | 51.45682 | -0.452107 | 444659 | TW14 8QH | WD4 8GW | WD48GW | WD4 | 51.714144 | -0.462949 | 3/4/2016 | 115 | 480 | 960 | 1 | |
10 | DelDaily | 664378 | Delivery | LHR | 51.45682 | -0.452107 | 444398 | TW14 8QH | BN6 8SG | BN68SG | BN6 | 50.918412 | -0.14951 | 3/4/2016 | 325 | 480 | 960 | 5 | |
11 | DelDaily | 664673 | Delivery | LHR | 51.45682 | -0.452107 | 444656 | TW14 8QH | CB10 1NY | CB101NY | CB10 | 52.042318 | 0.311799 | 3/4/2016 | 886 | 480 | 960 | 5 | |
12 | DelDaily | 664693 | Delivery | LHR | 51.45682 | -0.452107 | 444672 | TW14 8QH | CB9 7AE | CB97AE | CB9 | 52.084242 | 0.457063 | 3/4/2016 | 412 | 480 | 960 | 6 | |
13 | DelDaily | 664691 | Delivery | LHR | 51.45682 | -0.452107 | 444670 | TW14 8QH | CB9 7AE | CB97AE | CB9 | 52.084242 | 0.457063 | 3/4/2016 | 142 | 480 | 960 | 5 | |
14 | DelDaily | 664689 | Delivery | LHR | 51.45682 | -0.452107 | 444669 | TW14 8QH | CM1 2UP | CM12UP | CM1 | 51.746003 | 0.403812 | 3/4/2016 | 4 | 480 | 960 | 9 | |
15 | DelDaily | 664655 | Delivery | LHR | 51.45682 | -0.452107 | 444639 | TW14 8QH | CM8 3DJ | CM83DJ | CM8 | 51.818885 | 0.632586 | 3/4/2016 | 1531 | 480 | 960 | 10 | |
16 | DelDaily | 664308 | Delivery | LHR | 51.45682 | -0.452107 | 444336 | TW14 8QH | E16 4ES | E164ES | E16 | 51.509748 | 0.029329 | 3/4/2016 | 785 | 480 | 960 | 9 | |
17 | DelDaily | 664266 | Delivery | LHR | 51.45682 | -0.452107 | 444301 | TW14 8QH | EC4 4DE | EC44DE | EC4 | 51.4145682 | -0.0964522 | 3/4/2016 | 27 | 480 | 960 | 10 | |
18 | DelDaily | 664770 | Delivery | LHR | 51.45682 | -0.452107 | 444738 | TW14 8QH | EN11 9BU | EN119BU | EN11 | 51.76252 | -0.017755 | 3/4/2016 | 216 | 480 | 960 | 7 | |
19 | DelDaily | 664503 | Delivery | LHR | 51.45682 | -0.452107 | 444509 | TW14 8QH | GU34 4QE | GU344QE | GU34 | 51.13352 | -0.981982 | 3/4/2016 | 616 | 480 | 960 | 2 | |
20 | DelDaily | 664322 | Delivery | LHR | 51.45682 | -0.452107 | 444348 | TW14 8QH | GU35 9JA | GU359JA | GU35 | 51.124085 | -0.851958 | 3/4/2016 | 35 | 480 | 960 | 3 | |
21 | DelDaily | 664815 | Delivery | LHR | 51.45682 | -0.452107 | 444775 | TW14 8QH | HP12 3ST | HP123ST | HP12 | 51.622838 | -0.789451 | 3/4/2016 | 514 | 480 | 960 | 2 | |
22 | DelDaily | 664657 | Delivery | LHR | 51.45682 | -0.452107 | 444641 | TW14 8QH | HP12 3TA | HP123TA | HP12 | 51.622838 | -0.789451 | 3/4/2016 | 13 | 480 | 960 | 1 | |
23 | DelDaily | 664370 | Delivery | LHR | 51.45682 | -0.452107 | 444390 | TW14 8QH | KT9 2NY | KT92NY | KT9 | 51.35185 | -0.310037 | 3/4/2016 | 240 | 480 | 960 | 6 | |
24 | DelDaily | 664694 | Delivery | LHR | 51.45682 | -0.452107 | 444673 | TW14 8QH | KT9 2NY | KT92NY | KT9 | 51.35185 | -0.310037 | 3/4/2016 | 207 | 480 | 960 | 5 | |
25 | DelDaily | 664224 | Delivery | LHR | 51.45682 | -0.452107 | 444265 | TW14 8QH | LU7 4TB | LU74TB | LU7 | 51.913652 | -0.695049 | 3/4/2016 | 1 | 480 | 960 | 2 | |
26 | DelDaily | 664656 | Delivery | LHR | 51.45682 | -0.452107 | 444640 | TW14 8QH | LU7 4TB | LU74TB | LU7 | 51.913652 | -0.695049 | 3/4/2016 | 1 | 480 | 960 | 7 | |
27 | DelDaily | 664678 | Delivery | LHR | 51.45682 | -0.452107 | 444660 | TW14 8QH | MK12 5QL | MK125QL | MK12 | 52.055821 | -0.813985 | 3/4/2016 | 1113 | 480 | 960 | 3 | |
28 | DelDaily | 664353 | Delivery | LHR | 51.45682 | -0.452107 | 444377 | TW14 8QH | MK18 1SW | MK181SW | MK18 | 51.953891 | -0.986966 | 3/4/2016 | 807 | 480 | 960 | 8 | |
29 | DelDaily | 664288 | Delivery | LHR | 51.45682 | -0.452107 | 444320 | TW14 8QH | MK42 9XE | MK429XE | MK42 | 52.110728 | -0.463547 | 3/4/2016 | 340 | 480 | 960 | 2 | |
30 | DelDaily | 664808 | Delivery | LHR | 51.45682 | -0.452107 | 444768 | TW14 8QH | NN14 3JW | NN143JW | NN14 | 52.423667 | -0.609697 | 3/4/2016 | 1016 | 480 | 960 | 1 | |
31 | DelDaily | 664659 | Delivery | LHR | 51.45682 | -0.452107 | 444643 | TW14 8QH | NN17 5XZ | NN175XZ | NN17 | 52.519549 | -0.624125 | 3/4/2016 | 500 | 480 | 960 | 2 | |
32 | DelDaily | 664768 | Delivery | LHR | 51.45682 | -0.452107 | 444737 | TW14 8QH | NP44 3AX | NP443AX | NP44 | 51.655073 | -3.026117 | 3/4/2016 | 100 | 480 | 960 | 10 | |
33 | DelDaily | 664675 | Delivery | LHR | 51.45682 | -0.452107 | 444657 | TW14 8QH | NW10 0US | NW100US | NW10 | 51.541023 | -0.253094 | 3/4/2016 | 39 | 480 | 960 | 5 | |
34 | DelDaily | 664676 | Delivery | LHR | 51.45682 | -0.452107 | 444658 | TW14 8QH | NW10 0US | NW100US | NW10 | 51.541023 | -0.253094 | 3/4/2016 | 54 | 480 | 960 | 9 | |
35 | DelDaily | 664816 | Delivery | LHR | 51.45682 | -0.452107 | 444776 | TW14 8QH | NW8 9LE | NW89LE | NW8 | 51.53328 | -0.173443 | 3/4/2016 | 435 | 480 | 960 | 3 | |
36 | DelDaily | 664760 | Delivery | LHR | 51.45682 | -0.452107 | 444730 | TW14 8QH | OX18 3LX | OX183LX | OX18 | 51.736346 | -1.561643 | 3/4/2016 | 77 | 480 | 960 | 5 | |
37 | DelDaily | 664463 | Delivery | LHR | 51.45682 | -0.452107 | 444474 | TW14 8QH | OX29 5UT | OX295UT | OX29 | 51.780381 | -1.417656 | 3/4/2016 | 96 | 480 | 960 | 9 | |
38 | DelDaily | 664377 | Delivery | LHR | 51.45682 | -0.452107 | 444397 | TW14 8QH | OX9 3RR | OX93RR | OX9 | 51.731977 | -0.99119 | 3/4/2016 | 250 | 480 | 960 | 8 | |
39 | DelDaily | 664744 | Delivery | LHR | 51.45682 | -0.452107 | 444714 | TW14 8QH | RG2 0TG | RG20TG | RG2 | 51.411466 | -0.938145 | 3/4/2016 | 593 | 480 | 960 | 3 | |
40 | DelDaily | 664291 | Delivery | LHR | 51.45682 | -0.452107 | 444322 | TW14 8QH | RG24 8WF | RG248WF | RG24 | 51.284542 | -1.06454 | 3/4/2016 | 50 | 480 | 960 | 7 | |
41 | DelDaily | 664793 | Delivery | LHR | 51.45682 | -0.452107 | 444755 | TW14 8QH | SG1 2EE | SG12EE | SG1 | 51.90495 | -0.202641 | 3/4/2016 | 1434 | 480 | 960 | 9 | |
42 | DelDaily | 664453 | Delivery | LHR | 51.45682 | -0.452107 | 444464 | TW14 8QH | SL1 4NH | SL14NH | SL1 | 51.539466 | -0.656017 | 3/4/2016 | 26.4 | 480 | 960 | 4 | |
43 | DelDaily | 664564 | Delivery | LHR | 51.45682 | -0.452107 | 444564 | TW14 8QH | SL1 4QZ | SL14QZ | SL1 | 51.539466 | -0.656017 | 3/4/2016 | 567 | 480 | 960 | 2 | |
44 | DelDaily | 664805 | Delivery | LHR | 51.45682 | -0.452107 | 444765 | TW14 8QH | SS2 5RX | SS25RX | SS2 | 51.560225 | 0.704165 | 3/4/2016 | 81 | 480 | 960 | 5 | |
45 | DelDaily | 664803 | Delivery | LHR | 51.45682 | -0.452107 | 444763 | TW14 8QH | TW15 1AX | TW151AX | TW15 | 51.435291 | -0.464249 | 3/4/2016 | 185 | 480 | 960 | 1 | |
46 | DelDaily | 664809 | Delivery | LHR | 51.45682 | -0.452107 | 444769 | TW14 8QH | TW15 1AX | TW151AX | TW15 | 51.435291 | -0.464249 | 3/4/2016 | 560 | 480 | 960 | 10 | |
47 | DelDaily | 664814 | Delivery | LHR | 51.45682 | -0.452107 | 444774 | TW14 8QH | TW15 1BL | TW151BL | TW15 | 51.435291 | -0.464249 | 3/4/2016 | 269 | 480 | 960 | 6 | |
48 | DelDaily | 664818 | Delivery | LHR | 51.45682 | -0.452107 | 444778 | TW14 8QH | TW15 1BL | TW151BL | TW15 | 51.435291 | -0.464249 | 3/4/2016 | 446 | 480 | 960 | 8 | |
49 | DelDaily | 664616 | Delivery | LHR | 51.45682 | -0.452107 | 444605 | TW14 8QH | TW4 6BL | TW46BL | TW4 | 51.467057 | -0.393889 | 3/4/2016 | 149 | 480 | 960 | 4 | |
50 | DelDaily | 664351 | Delivery | LHR | 51.45682 | -0.452107 | 444375 | TW14 8QH | TW4 6ER | TW46ER | TW4 | 51.467057 | -0.393889 | 3/4/2016 | 25 | 480 | 960 | 10 | |
51 | DelDaily | 663689 | Delivery | LHR | 51.45682 | -0.452107 | 443780 | TW14 8QH | TW6 2EQ | TW62EQ | TW6 | 51.466844 | -0.452952 | 3/4/2016 | 100 | 480 | 960 | 9 | |
52 | DelDaily | 663847 | Delivery | LHR | 51.45682 | -0.452107 | 443921 | TW14 8QH | W1 8ZU | W18ZU | W18 | 51.275018 | -0.745621 | 3/4/2016 | 263 | 480 | 960 | 8 | |
53 | Coldaily | 665143 | Collection | LHR | 51.45682 | -0.452107 | 445089 | BN17 7EH | TW14 8QH | BN177EH | BN17 | 50.813176 | -0.540751 | 3/4/2016 | 9.6 | 480 | 960 | 10 | |
54 | Coldaily | 665274 | Collection | LHR | 51.45682 | -0.452107 | 445202 | BN17 7LT | TW14 8QH | BN177LT | BN17 | 50.813176 | -0.540751 | 3/4/2016 | 291.6 | 480 | 960 | 3 | |
55 | Coldaily | 665131 | Collection | LHR | 51.45682 | -0.452107 | 445079 | BN43 5PA | TW14 8QH | BN435PA | BN43 | 50.85574 | -0.26425 | 3/4/2016 | 448.8 | 480 | 960 | 10 | |
56 | Coldaily | 664603 | Collection | LHR | 51.45682 | -0.452107 | 444595 | CB3 OQH | TW14 8QH | CB3OQH | CB3 | 52.207126 | 0.090273 | 3/4/2016 | 192 | 480 | 960 | 6 | |
57 | Coldaily | 664749 | Collection | LHR | 51.45682 | -0.452107 | 444719 | CB8 9RG, | TW14 8QH | CB89RG, | CB8 | 52.21158 | 0.459165 | 3/4/2016 | 240 | 480 | 960 | 9 | |
58 | Coldaily | 664506 | Collection | LHR | 51.45682 | -0.452107 | 444512 | CM20 2HS | TW14 8QH | CM202HS | CM20 | 51.783585 | 0.100775 | 3/4/2016 | 126 | 480 | 960 | 10 | |
59 | Coldaily | 665130 | Collection | LHR | 51.45682 | -0.452107 | 445078 | CT5 2RR | TW14 8QH | CT52RR | CT5 | 51.352817 | 1.049264 | 3/4/2016 | 186 | 480 | 960 | 9 | |
60 | Coldaily | 664548 | Collection | LHR | 51.45682 | -0.452107 | 444549 | CT5 2RR | TW14 8QH | CT52RR | CT5 | 51.352817 | 1.049264 | 3/4/2016 | 603.6 | 480 | 960 | 7 | |
61 | Coldaily | 664538 | Collection | LHR | 51.45682 | -0.452107 | 444540 | CT9 4ED | TW14 8QH | CT94ED | CT9 | 51.375632 | 1.379217 | 3/4/2016 | 3.6 | 480 | 960 | 1 | |
62 | Coldaily | 664428 | Collection | LHR | 51.45682 | -0.452107 | 444442 | GU2 7YF | TW14 8QH | GU27YF | GU2 | 51.24599 | -0.590318 | 3/4/2016 | 64.8 | 480 | 960 | 7 | |
63 | Coldaily | 664636 | Collection | LHR | 51.45682 | -0.452107 | 444621 | GU31 5HZ | TW14 8QH | GU315HZ | GU31 | 50.987958 | -0.90645 | 3/4/2016 | 314.4 | 480 | 960 | 9 | |
64 | Coldaily | 665269 | Collection | LHR | 51.45682 | -0.452107 | 445198 | HP12 3ST | TW14 8QH | HP123ST | HP12 | 51.622838 | -0.789451 | 3/4/2016 | 134.4 | 480 | 960 | 6 | |
65 | Coldaily | 665236 | Collection | LHR | 51.45682 | -0.452107 | 445170 | HP12 3ST | TW14 8QH | HP123ST | HP12 | 51.622838 | -0.789451 | 3/4/2016 | 166.8 | 480 | 960 | 1 | |
66 | Coldaily | 664663 | Collection | LHR | 51.45682 | -0.452107 | 444647 | HP19 8RY | TW14 8QH | HP198RY | HP19 | 51.822933 | -0.831316 | 3/4/2016 | 165.6 | 480 | 960 | 10 | |
67 | Coldaily | 665316 | Collection | LHR | 51.45682 | -0.452107 | 445238 | IP6 0LW | TW14 8QH | IP60LW | IP6 | 52.122052 | 1.139937 | 3/4/2016 | 180 | 480 | 960 | 10 | |
68 | Coldaily | 665118 | Collection | LHR | 51.45682 | -0.452107 | 445067 | LE19 1WX | TW14 8QH | LE191WX | LE19 | 52.595156 | -1.203261 | 3/4/2016 | 316.8 | 480 | 960 | 7 | |
69 | Coldaily | 665129 | Collection | LHR | 51.45682 | -0.452107 | 445077 | LE8 6NU | TW14 8QH | LE86NU | LE8 | 52.548921 | -1.008372 | 3/4/2016 | 60 | 480 | 960 | 9 | |
70 | Coldaily | 664390 | Collection | LHR | 51.45682 | -0.452107 | 444409 | LU3 4BU | TW14 8QH | LU34BU | LU3 | 51.930713 | -0.448537 | 3/4/2016 | 16.8 | 480 | 960 | 10 | |
71 | Coldaily | 664619 | Collection | LHR | 51.45682 | -0.452107 | 444607 | MK41 OHR | TW14 8QH | MK41OHR | MK41 | 52.158447 | -0.470404 | 3/4/2016 | 18 | 480 | 960 | 2 | |
72 | Coldaily | 665314 | Collection | LHR | 51.45682 | -0.452107 | 445236 | OX14 4SD | TW14 8QH | OX144SD | OX14 | 51.655547 | -1.265938 | 3/4/2016 | 79.2 | 480 | 960 | 3 | |
73 | Coldaily | 665235 | Collection | LHR | 51.45682 | -0.452107 | 445169 | OX18 3LX | TW14 8QH | OX183LX | OX18 | 51.736346 | -1.561643 | 3/4/2016 | 9.6 | 480 | 960 | 6 | |
74 | Coldaily | 665209 | Collection | LHR | 51.45682 | -0.452107 | 445146 | OX18 3LX | TW14 8QH | OX183LX | OX18 | 51.736346 | -1.561643 | 3/4/2016 | 3.6 | 480 | 960 | 9 | |
75 | Coldaily | 664414 | Collection | LHR | 51.45682 | -0.452107 | 444431 | OX26 4UL | TW14 8QH | OX264UL | OX26 | 51.904814 | -1.14932 | 3/4/2016 | 144 | 480 | 960 | 2 | |
76 | Coldaily | 664466 | Collection | LHR | 51.45682 | -0.452107 | 444476 | RG6 1AZ | TW14 8QH | RG61AZ | RG6 | 51.432553 | -0.926931 | 3/4/2016 | 426 | 480 | 960 | 8 | |
77 | Coldaily | 664328 | Collection | LHR | 51.45682 | -0.452107 | 444353 | RH15 9TJ | TW14 8QH | RH159TJ | RH15 | 50.960119 | -0.126666 | 3/4/2016 | 14.4 | 480 | 960 | 1 | |
78 | Coldaily | 664765 | Collection | LHR | 51.45682 | -0.452107 | 444735 | RH4 1XF | TW14 8QH | RH41XF | RH4 | 51.226034 | -0.343712 | 3/4/2016 | 116.4 | 480 | 960 | 9 | |
79 | Coldaily | 664773 | Collection | LHR | 51.45682 | -0.452107 | 444741 | RH6 9UU | TW14 8QH | RH69UU | RH6 | 51.168867 | -0.174104 | 3/4/2016 | 32.4 | 480 | 960 | 7 | |
80 | Coldaily | 665272 | Collection | LHR | 51.45682 | -0.452107 | 445200 | SL3 9LE | TW14 8QH | SL39LE | SL3 | 51.482282 | -0.530137 | 3/4/2016 | 4.8 | 480 | 960 | 6 | |
81 | Coldaily | 665141 | Collection | LHR | 51.45682 | -0.452107 | 445088 | SL7 1TF | TW14 8QH | SL71TF | SL7 | 51.585958 | -0.794384 | 3/4/2016 | 48 | 480 | 960 | 3 | |
82 | Coldaily | 664823 | Collection | LHR | 51.45682 | -0.452107 | 444783 | SN3 4TN | TW14 8QH | SN34TN | SN3 | 51.5705 | -1.740761 | 3/4/2016 | 74.4 | 480 | 960 | 4 | |
83 | Coldaily | 664780 | Collection | LHR | 51.45682 | -0.452107 | 444746 | SN3 4TN | TW14 8QH | SN34TN | SN3 | 51.5705 | -1.740761 | 3/4/2016 | 360 | 480 | 960 | 6 | |
84 | Coldaily | 664605 | Collection | LHR | 51.45682 | -0.452107 | 444596 | SS14 3ES | TW14 8QH | SS143ES | SS14 | 51.582404 | 0.478576 | 3/4/2016 | 883.2 | 480 | 960 | 1 | |
85 | Coldaily | 665315 | Collection | LHR | 51.45682 | -0.452107 | 445237 | UB3 3BS | TW14 8QH | UB33BS | UB3 | 51.509008 | -0.418135 | 3/4/2016 | 8.4 | 480 | 960 | 8 | |
86 | Coldaily | 665271 | Collection | LHR | 51.45682 | -0.452107 | 445199 | WC1 2NP | TW14 8QH | WC12NP | WC1 | 51.5008172 | -0.1215212 | 3/4/2016 | 4.8 | 480 | 960 | 8 | |
87 | ColSpec | 664822 | Collection | LHR | 51.45682 | -0.452107 | 444782 | CO10 7QS | TW14 8QH | CO107QS | CO10 | 52.061712 | 0.723033 | 3/4/2016 | 981 | 480 | 960 | 6 | |
88 | ColSpec | 665313 | Collection | LHR | 51.45682 | -0.452107 | 445235 | HP2 7SU | TW14 8QH | HP27SU | HP2 | 51.798452 | -0.488284 | 3/4/2016 | 400 | 480 | 960 | 7 | |
89 | ColSpec | 664651 | Collection | LHR | 51.45682 | -0.452107 | 444635 | LE17 4XN | TW14 8QH | LE174XN | LE17 | 52.462251 | -1.154013 | 3/4/2016 | 79 | 480 | 960 | 3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment