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
|
module TomTom
class Matrix
def initialize(connection)
@connection = connection
end
def matrix(way_costs)
points_with_ids = points_from_way_costs(way_costs)
points = points_as_params(points_with_ids)
Rails.logger.info "Invoke TomTom for #{points.size} points"
response = @connection.post do |req|
req.url '/routing/1/matrix/json'
req.headers['Content-Type'] = 'application/json'
req.params[:routeType] = 'shortest'
req.params[:traffic] = 'false'
req.params[:travelMode] = 'bus'
req.body = build_request_body(points)
end
extract_costs_to_way_costs!(
way_costs,
points_with_ids,
JSON.parse(response.body)
)
end
def points_from_way_costs(way_costs)
points = []
way_costs.each do |way_cost|
departure_id, arrival_id = way_cost.id.split('-')
departure = TomTom::Matrix::Point.new(
way_cost.departure,
departure_id
)
arrival = TomTom::Matrix::Point.new(
way_cost.arrival,
arrival_id
)
# Don't add duplicate coordinates. This assumes that
# `way_costs` consists of an ordered route of points where
# each departure coordinate is the same as the preceding
# arrival coordinate.
if points.empty? ||
points.last.coordinates != departure.coordinates
points << departure
end
points << arrival
end
points
end
def points_as_params(points)
points.map do |point|
{
point: {
latitude: point.coordinates.lat,
longitude: point.coordinates.lng
}
}
end
end
def build_request_body(points)
# Serialize `BigDecimal` values as floats to please the TomTom API
RequestJSONSerializer.dump({
origins: points,
destinations: points
})
end
def extract_costs_to_way_costs!(way_costs, points, matrix_json)
way_costs = []
# `row` and `column` order is the same as `points`
matrix_json['matrix'].each_with_index do |row, row_i|
row.each_with_index do |column, column_i|
next if column['statusCode'] != 200
distance = column['response']['routeSummary']['lengthInMeters']
# Ignore costs between a point and itself (e.g. from A to A)
next if distance == 0
departure = points[row_i]
arrival = points[column_i]
way_costs << WayCost.new(
departure: Geokit::LatLng.new(
departure.coordinates.lat,
departure.coordinates.lng
),
arrival: Geokit::LatLng.new(
arrival.coordinates.lat,
arrival.coordinates.lng
),
distance: distance,
time: column['response']['routeSummary']['travelTimeInSeconds'],
id: "#{departure.id}-#{arrival.id}"
)
end
end
way_costs
end
end
end
|