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
|
module TomTom
class Matrix
def initialize(connection)
@connection = connection
end
# Exceptions:
#
# * This raises a `TomTom::Errors::MatrixRemoteError` when the API responds
# with an error.
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
check_for_error_response(response)
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 check_for_error_response(response)
if response.status != 200
raise TomTom::Errors::MatrixRemoteError,
"status: #{response.status}, body: #{response.body}"
end
json = JSON.parse(response.body)
if json.has_key?('error')
raise TomTom::Errors::MatrixRemoteError,
"status: #{response.status}, message: #{json['error']['description']}"
end
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
|