aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeddy Wing2018-03-23 12:26:04 +0100
committerTeddy Wing2018-03-27 16:46:18 +0200
commit4bc33f14a148feb4056db1a2839303b07c67a6b3 (patch)
tree7e0c057da62ed5efb6d2376bd1e45726b2a79e25
parentd3ab2606ffd3dcb94a27d7b4e7c899f1238d3a53 (diff)
downloadchouette-core-4bc33f14a148feb4056db1a2839303b07c67a6b3.tar.bz2
Add `TomTom::Matrix`
A new component to the `TomTom` module that asks TomTom's Matrix API endpoint (https://developer.tomtom.com/online-routing/online-routing-documentation/matrix-routing) to compute `WayCost`s. The matrix API will give us all costs between each pair of coordinates. This will enable us to provide costs for any combination of points in a journey pattern. Given a list of `WayCost`s, it will send all points from those costs to the matrix API and return a list of all non-zero `WayCost`s between all pairs of coordinates. `points_from_way_costs()` extracts unique coordinates from the `WayCost`s. `points_as_params()` builds a list of points in the format expected by the matrix API. The response from the matrix API is formatted as a two-dimensional array consisting of rows and columns that pair each "origin" point with each "destination" point. We loop through this matrix and construct new `WayCost` objects for each pair of coordinates. At the moment, I haven't figured out how I want to save `WayCost` IDs when creating the new pairs. Leaving that for later. Refs #6222
-rw-r--r--lib/tom_tom.rb4
-rw-r--r--lib/tom_tom/matrix.rb89
-rw-r--r--spec/fixtures/tom_tom_matrix.json123
-rw-r--r--spec/lib/tom_tom/matrix_spec.rb127
4 files changed, 343 insertions, 0 deletions
diff --git a/lib/tom_tom.rb b/lib/tom_tom.rb
index a1a2bda43..fcebcc7ac 100644
--- a/lib/tom_tom.rb
+++ b/lib/tom_tom.rb
@@ -19,4 +19,8 @@ module TomTom
def self.batch(way_costs)
TomTom::Batch.new(@connection).batch(way_costs)
end
+
+ def self.matrix(way_costs)
+ TomTom::Matrix.new(@connection).matrix(way_costs)
+ end
end
diff --git a/lib/tom_tom/matrix.rb b/lib/tom_tom/matrix.rb
new file mode 100644
index 000000000..985055dae
--- /dev/null
+++ b/lib/tom_tom/matrix.rb
@@ -0,0 +1,89 @@
+module TomTom
+ class Matrix
+ def initialize(connection)
+ @connection = connection
+ end
+
+ def matrix(way_costs)
+ points = points_from_way_costs(way_costs)
+ points = points_as_params(points)
+
+ response = @connection.post do |req|
+ req.url '/routing/1/matrix/json'
+ req.headers['Content-Type'] = 'application/json'
+
+ req.params[:routeType] = 'shortest'
+ req.params[:travelMode] = 'bus'
+
+ req.body = {
+ origins: points,
+ destinations: points
+ }.to_json
+ end
+
+ extract_costs_to_way_costs!(
+ way_costs,
+ points,
+ JSON.parse(response.body)
+ )
+ end
+
+ def points_from_way_costs(way_costs)
+ points = Set.new
+
+ way_costs.each do |way_cost|
+ points.add(way_cost.departure)
+ points.add(way_cost.arrival)
+ end
+
+ points
+ end
+
+ def points_as_params(points)
+ points.map do |latlng|
+ {
+ point: {
+ latitude: latlng.lat,
+ longitude: latlng.lng
+ }
+ }
+ end
+ end
+
+ # TODO: We actually need to create new WayCost objects to hold the new dot connections given to us by the matrix API.
+ 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[:point][:latitude],
+ departure[:point][:longitude]
+ ),
+ arrival: Geokit::LatLng.new(
+ arrival[:point][:latitude],
+ arrival[:point][:longitude]
+ ),
+ distance: distance,
+ time: column['response']['routeSummary']['travelTimeInSeconds']
+ # id: 'TODO: figure out how to add combined stop IDs'
+ )
+ end
+ end
+
+ way_costs
+ end
+ end
+end
diff --git a/spec/fixtures/tom_tom_matrix.json b/spec/fixtures/tom_tom_matrix.json
new file mode 100644
index 000000000..30048576c
--- /dev/null
+++ b/spec/fixtures/tom_tom_matrix.json
@@ -0,0 +1,123 @@
+{
+ "formatVersion": "0.0.1",
+ "matrix": [
+ [
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 0,
+ "travelTimeInSeconds": 0,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T11:20:17+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 117947,
+ "travelTimeInSeconds": 8356,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T13:39:32+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 999088,
+ "travelTimeInSeconds": 62653,
+ "trafficDelayInSeconds": 298,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-24T04:44:30+01:00"
+ }
+ }
+ }
+ ],
+ [
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 117231,
+ "travelTimeInSeconds": 9729,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T14:02:25+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 0,
+ "travelTimeInSeconds": 0,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:17+01:00",
+ "arrivalTime": "2018-03-23T11:20:17+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 1114635,
+ "travelTimeInSeconds": 72079,
+ "trafficDelayInSeconds": 298,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-24T07:21:36+01:00"
+ }
+ }
+ }
+ ],
+ [
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 997232,
+ "travelTimeInSeconds": 63245,
+ "trafficDelayInSeconds": 179,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-24T04:54:23+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 1113108,
+ "travelTimeInSeconds": 68485,
+ "trafficDelayInSeconds": 52,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-24T06:21:43+01:00"
+ }
+ }
+ },
+ {
+ "statusCode": 200,
+ "response": {
+ "routeSummary": {
+ "lengthInMeters": 344,
+ "travelTimeInSeconds": 109,
+ "trafficDelayInSeconds": 0,
+ "departureTime": "2018-03-23T11:20:18+01:00",
+ "arrivalTime": "2018-03-23T11:22:07+01:00"
+ }
+ }
+ }
+ ]
+ ],
+ "summary": {
+ "successfulRoutes": 9,
+ "totalRoutes": 9
+ }
+}
diff --git a/spec/lib/tom_tom/matrix_spec.rb b/spec/lib/tom_tom/matrix_spec.rb
new file mode 100644
index 000000000..257bbd5fd
--- /dev/null
+++ b/spec/lib/tom_tom/matrix_spec.rb
@@ -0,0 +1,127 @@
+RSpec.describe TomTom::Matrix do
+ let(:matrix) { TomTom::Matrix.new(nil) }
+
+ describe "#points_from_way_costs" do
+ it "extracts a set of lat/lng coordinates from a list of WayCosts" do
+ way_costs = [
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606)
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879)
+ )
+ ]
+
+ expect(
+ matrix.points_from_way_costs(way_costs)
+ ).to eq(Set.new([
+ Geokit::LatLng.new(48.85086, 2.36143),
+ Geokit::LatLng.new(47.91231, 1.87606),
+ Geokit::LatLng.new(52.50867, 13.42879)
+ ]))
+ end
+ end
+
+ describe "#points_as_params" do
+ it "transforms a set of LatLng points into a hash for use by TomTom Matrix" do
+ points = Set.new([
+ Geokit::LatLng.new(48.85086, 2.36143),
+ Geokit::LatLng.new(47.91231, 1.87606),
+ Geokit::LatLng.new(52.50867, 13.42879)
+ ])
+
+ expect(
+ matrix.points_as_params(points)
+ ).to eq([
+ {
+ point: {
+ latitude: 48.85086,
+ longitude: 2.36143
+ },
+ },
+ {
+ point: {
+ latitude: 47.91231,
+ longitude: 1.87606
+ },
+ },
+ {
+ point: {
+ latitude: 52.50867,
+ longitude: 13.42879
+ },
+ }
+ ])
+ end
+ end
+
+ describe "#extract_costs_to_way_costs!" do
+ it "puts distance & time costs in way_costs" do
+ way_costs = [
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606)
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879)
+ )
+ ]
+
+ expected_way_costs = [
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606),
+ distance: 117947,
+ time: 8356
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(48.85086, 2.36143),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ distance: 999088,
+ time: 62653
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(48.85086, 2.36143),
+ distance: 117231,
+ time: 9729
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(47.91231, 1.87606),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ distance: 1114635,
+ time: 72079
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(52.50867, 13.42879),
+ arrival: Geokit::LatLng.new(48.85086, 2.36143),
+ distance: 997232,
+ time: 63245
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(52.50867, 13.42879),
+ arrival: Geokit::LatLng.new(47.91231, 1.87606),
+ distance: 1113108,
+ time: 68485
+ ),
+ WayCost.new(
+ departure: Geokit::LatLng.new(52.50867, 13.42879),
+ arrival: Geokit::LatLng.new(52.50867, 13.42879),
+ distance: 344,
+ time: 109
+ )
+ ]
+
+ matrix_response = JSON.parse(read_fixture('tom_tom_matrix.json'))
+
+ points = matrix.points_as_params(matrix.points_from_way_costs(way_costs))
+
+ expect(
+ matrix.extract_costs_to_way_costs!(way_costs, points, matrix_response)
+ ).to match_array(expected_way_costs)
+ end
+ end
+end