-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathtsp.py
129 lines (108 loc) · 4.41 KB
/
tsp.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
from __future__ import print_function
from six.moves import xrange
from ortools.constraint_solver import pywrapcp
from ortools.constraint_solver import routing_enums_pb2
import urllib.request
import json
from geopy.distance import vincenty
def return_lambda_gateway_response(code, body):
return {"statusCode": code, "body": json.dumps(body)}
def vincenty_distance(pos_1, pos_2):
pos_1 = (pos_1[0], pos_1[1])
pos_2 = (pos_2[0], pos_2[1])
return vincenty(pos_1, pos_2).meters
def create_distance_matrix(locations, transport_mode, distance_calculation):
# Create the distance matrix.
dist_matrix = {}
# complete distance matrix
# precompute distance between location to have distance callback in O(1)
if distance_calculation == "OSRM":
url = "https://bi.ahamove.com/osrm/table/v1/driving/"
for loc in locations:
url += str(loc[1]) + "," + str(loc[0]) + ";"
url = url[:-1] + "?annotations=distance"
response = urllib.request.urlopen(url).read().decode('UTF-8')
contents = json.loads(response)["distances"]
if transport_mode == "N1":
for index in xrange(len(locations)):
contents[0][index] = 0
if transport_mode == "1N":
for index in xrange(len(locations)):
contents[index][0] = 0
dist_matrix = contents
else:
for from_node in xrange(len(locations)):
dist_matrix[from_node] = {}
for to_node in xrange(len(locations)):
if (from_node == to_node) or (transport_mode == "1N" and to_node == 0) or (transport_mode == "N1" and from_node == 0):
dist_matrix[from_node][to_node] = 0
else:
distance = (vincenty_distance(
locations[from_node],
locations[to_node]))
dist_matrix[from_node][to_node] = distance
return dist_matrix
def create_distance_callback(dist_matrix):
# Create the distance callback.
def distance_callback(from_node, to_node):
return int(dist_matrix[from_node][to_node])
return distance_callback
def tsp(event, context):
# Create the data.
try:
body = event.get('body')
event = json.loads(body)
locations = event["points"]
transport_mode = event["transport_mode"]
distance_calculation = event.get("distance_calculation", "VINCENTY")
# Error handling
except KeyError as e:
print("Missing required input: " + str(e))
cluster = {"title": "Missing required input: " + str(e)}
return return_lambda_gateway_response(400, cluster)
if transport_mode != "1N" and transport_mode != "N1" and transport_mode != "1N1":
cluster = {"title": "Invalid transport_mode"}
return return_lambda_gateway_response(400, cluster)
if distance_calculation != "VINCENTY" and distance_calculation != "OSRM":
cluster = {"title": "Invalid distance_calculation"}
return return_lambda_gateway_response(400, cluster)
if distance_calculation == "OSRM" and len(locations) > 100:
cluster = {"title": "Bad request: OSRM cannot be used with more than 100 points"}
return return_lambda_gateway_response(400, cluster)
dist_matrix = create_distance_matrix(locations, transport_mode, distance_calculation)
dist_callback = create_distance_callback(dist_matrix)
tsp_size = len(locations)
num_routes = 1
depot = 0
# Create routing model.
if tsp_size > 0:
routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
# Solve the problem.
assignment = routing.SolveWithParameters(search_parameters)
if assignment:
# Solution cost.
print("Total distance: " + str(assignment.ObjectiveValue()) + "\n")
# Inspect solution.
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
route_number = 0
node = routing.Start(route_number)
if transport_mode == "N1":
node = assignment.Value(routing.NextVar(node))
start_node = node
route = ''
cluster = []
while not routing.IsEnd(node):
cluster.append(locations[node])
route += str(node) + ' -> '
node = assignment.Value(routing.NextVar(node))
if transport_mode != "1N":
route += '0'
cluster.append([locations[0]])
print("Route:\n\n" + route)
return return_lambda_gateway_response(200, {"route": cluster})
else:
print('No solution found.')
else:
print('Specify an instance greater than 0.')