-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlalamove-vrp.py
195 lines (152 loc) · 5.79 KB
/
lalamove-vrp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
from shipment import Shipment
from location import Location
from address import Address
import random
PICKUP = 1
DELIVERY = 2
MAX_DRIVERS = 2
MAX_DELIVERY = 5
def createOrder(order):
""" This method actually creates the order object that is handed off to the driver
:param order: The order to be pickedup and delivered
:return orders: A new list of orders where pickup/dropoff are just stops
"""
orders = []
orders.append([order.service_type,
Location(order.pickup_loc,
order.pickup_time),
PICKUP])
orders.append([order.service_type,
Location(order.dropoff_loc,
order.dropoff_time),
DELIVERY])
return orders
def removeOrderFromDriver(service_type, drivers):
""" Removing an order from the list.
:param service_type: Name of the order, this is the key
:param drivers: List of drivers
"""
# iterate through all the driver's packages
for driver in drivers:
# We need to loop twice in removing the package since there's pickup and dropoff
for i in range(0,2):
# For each driver, look for the package
for order in driver:
if order[0] == service_type:
driver.remove(order)
break
def removeOrder(service_type, orders):
""" Removes an order from the orders
:param service_type: Name of the order, this is the key
:param orders: List of all orders
"""
# We need to loop twice in removing the package since there's pickup and dropoff
for i in range(0,2):
# For each driver, look for the package
for order in orders:
if order.service_type == service_type:
orders.remove(order)
break
def getTime(order):
""" Small operator function to assist in sorting
"""
return order[1].time.time
def createShipmentSplit(orders):
""" We order all the shipments by start, stop times. Once ordered, this method will
identify where in the list to split the shipments between the drivers
"""
splitPoints = []
t_orders = []
# First, we order by pickup time and delivery time (but we can't use standard sort since we have 2 keys)
for order in orders:
i = 0
while i < len(t_orders) \
and order.pickup_time.time > t_orders[i].pickup_time.time:
i += 1
# If startime is equal
while i < len(t_orders) \
and order.pickup_time.time == t_orders[i].pickup_time.time\
and order.dropoff_time.time > t_orders[i].dropoff_time.time:
i += 1
# I create a split point when start times are equal
if (len(splitPoints) < MAX_DRIVERS-1):
splitPoints.append(i)
t_orders.insert(i, order)
if MAX_DRIVERS > 1 and len(splitPoints) < MAX_DRIVERS-1:
# Try to find a where to split the routes between 2 drivers.
# Decided on a very simple approach, if there's 2 pick up points where the travel time exceeds the time window,
# I split it here.
for order_idx in range(0, len(t_orders)-1):
dist, dur = t_orders[order_idx].pickup_loc.dist(t_orders[order_idx+1].pickup_loc)
time_delta = t_orders[order_idx+1].pickup_time.time - t_orders[order_idx].pickup_time.time
# Make sure time is always positive
if time_delta < 0:
time_delta *= -1
if len(splitPoints) < MAX_DRIVERS-1 and dur >= time_delta:
splitPoints.append(order_idx)
if MAX_DRIVERS > 1 and len(splitPoints) < MAX_DRIVERS-1:
splitPoints.append(int(len(t_orders)/2)-1)
return splitPoints, t_orders
def sendToDrivers(splits, orders):
""" Assigns orders to drivers
:param splits: The list of split points
:param orders: All orders
:return: List of drivers with packages assigned to each driver
"""
drivers = [[] for i in range(len(splits)+1)]
driver = 0
for i in range(0, len(orders)):
drivers[driver] += createOrder(orders[i])
if i in splits:
driver += 1
# Order the pickup and drop offs
for driver in drivers:
driver.sort(key=getTime)
return drivers
def printOrders(drivers):
"""
Print the orders and delivery order
:param drivers: Drivers and their packages
:return:
"""
stops = 0
i = 0
for driver in drivers:
stops += len(driver)
print "Driver " + str(i+1) + ":",
j = 0
for order in driver:
print "Service " + order[0],
if order[2] == PICKUP:
print ": Pickup (",
else:
print ": Deliver (",
print order[1].time.time_string + " ) ",
if j < len(driver)-1:
print "--",
dist, dur = Address().dist(drivers[i][j][1].address,
drivers[i][j+1][1].address)
print str(int(dur/60)) + " --> ",
j = j + 1
print ""
i = i + 1
print "Total Shipments: " + str(int(stops/2))
print ""
def main():
orders = []
# Create random number of orders up to MAX_DELIVERY constant
# Using i as the service type/name
for i in range(0, random.randrange(1, MAX_DELIVERY)):
#for i in range(0, MAX_DELIVERY):
orders.append(Shipment(str(i)))
splits, sorted_orders = createShipmentSplit(orders)
drivers = sendToDrivers(splits, sorted_orders)
printOrders(drivers)
# Remove a package
#removeOrder('0', drivers)
removeOrder('0', orders)
splits, sorted_orders = createShipmentSplit(orders)
drivers = sendToDrivers(splits, sorted_orders)
printOrders(drivers)
if __name__ == '__main__':
main()