81c62e1831252b31c3dff3b2457ef1bead75020b
[rails.git] / app / assets / javascripts / index / directions / osrm.js
1 // OSRM car engine
2 // Doesn't yet support hints
3
4 function OSRMEngine() {
5   var cachedHints = [];
6
7   return {
8     id: "osrm_car",
9     creditline: '<a href="http://project-osrm.org/" target="_blank">OSRM</a>',
10     draggable: true,
11
12     _transformSteps: function(input_steps, line) {
13       var INSTRUCTION_TEMPLATE = {
14         'continue': 'javascripts.directions.instructions.continue',
15         'merge right': 'javascripts.directions.instructions.merge_right',
16         'merge left': 'javascripts.directions.instructions.merge_left',
17         'off ramp right': 'javascripts.directions.instructions.offramp_right',
18         'off ramp left': 'javascripts.directions.instructions.offramp_left',
19         'on ramp right': 'javascripts.directions.instructions.onramp_right',
20         'on ramp left': 'javascripts.directions.instructions.onramp_left',
21         'fork right': 'javascripts.directions.instructions.fork_right',
22         'fork left': 'javascripts.directions.instructions.fork_left',
23         'end of road right': 'javascripts.directions.instructions.endofroad_right',
24         'end of road left': 'javascripts.directions.instructions.endofroad_left',
25         'turn straight': 'javascripts.directions.instructions.continue',
26         'turn slight right': 'javascripts.directions.instructions.slight_right',
27         'turn right': 'javascripts.directions.instructions.turn_right',
28         'turn sharp right': 'javascripts.directions.instructions.sharp_right',
29         'turn uturn': 'javascripts.directions.instructions.uturn',
30         'turn sharp left': 'javascripts.directions.instructions.sharp_left',
31         'turn left': 'javascripts.directions.instructions.turn_left',
32         'turn slight left': 'javascripts.directions.instructions.slight_left',
33         'roundabout': 'javascripts.directions.instructions.roundabout',
34         'rotary': 'javascripts.directions.instructions.roundabout',
35         'exit roundabout': 'javascripts.directions.instructions.exit_roundabout',
36         'exit rotary': 'javascripts.directions.instructions.exit_roundabout',
37         'depart': 'javascripts.directions.instructions.start',
38         'arrive': 'javascripts.directions.instructions.destination',
39       };
40       var ICON_MAP = {
41         'continue': 0,
42         'merge right': 21,
43         'merge left': 20,
44         'off ramp right': 24,
45         'off ramp left': 25,
46         'on ramp right': 2,
47         'on ramp left': 6,
48         'fork right': 18,
49         'fork left': 19,
50         'end of road right': 22,
51         'end of road left': 23,
52         'turn straight': 0,
53         'turn slight right': 1,
54         'turn right': 2,
55         'turn sharp right': 3,
56         'turn uturn': 4,
57         'turn slight left': 5,
58         'turn left': 6,
59         'turn sharp left': 7,
60         'roundabout': 10,
61         'rotary': 10,
62         'exit roundabout': 10,
63         'exit rotary': 10,
64         'depart': 8,
65         'arrive': 14
66       };
67       var numToWord = function(num) {
68         return ["first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth"][num-1];
69       };
70       var transformed_steps = input_steps.map(function(step, idx) {
71         var maneuver_id;
72
73         // special case handling
74         switch (step.maneuver.type) {
75           case 'on ramp':
76           case 'off ramp':
77           case 'merge':
78           case 'end of road':
79           case 'fork':
80             maneuver_id = step.maneuver.type + ' ' + (step.maneuver.modifier.indexOf('left') >= 0 ? 'left' : 'right');
81             break;
82           case 'depart':
83           case 'arrive':
84           case 'roundabout':
85           case 'rotary':
86           case 'exit roundabout':
87           case 'exit rotary':
88             maneuver_id = step.maneuver.type;
89             break;
90           case 'roundabout turn':
91           case 'turn':
92             maneuver_id = "turn " + step.maneuver.modifier;
93             break;
94           // for unknown types the fallback is turn
95           default:
96             maneuver_id = "turn " + step.maneuver.modifier;
97             break;
98         }
99         var template = INSTRUCTION_TEMPLATE[maneuver_id];
100
101         // convert lat,lng pairs to LatLng objects
102         var step_geometry = L.PolylineUtil.decode(step.geometry, { precision: 5 }).map(function(a) { return L.latLng(a); }) ;
103         // append step_geometry on line
104         Array.prototype.push.apply(line, step_geometry);
105
106         var instText = "<b>" + (idx + 1) + ".</b> ";
107         var destinations = "<b>" + step.destinations + "</b>";
108         var namedRoad = true;
109         var name;
110
111         if (step.name && step.ref) {
112           name = "<b>" + step.name + " (" + step.ref + ")</b>";
113         } else if (step.name) {
114           name = "<b>" + step.name + "</b>";
115         } else if (step.ref) {
116           name = "<b>" + step.ref + "</b>";
117         } else {
118           name = I18n.t('javascripts.directions.instructions.unnamed');
119           namedRoad = false;
120         }
121
122         if (step.maneuver.type.match(/exit (rotary|roundabout)/)) {
123           instText += I18n.t(template, { name: name });
124         } else if (step.maneuver.type.match(/rotary|roundabout/)) {
125           if (step.maneuver.exit) {
126             if (step.maneuver.exit <= 10) {
127               instText += I18n.t(template + '_with_exit_ordinal', { exit: I18n.t('javascripts.directions.instructions.exit_counts.' + numToWord(step.maneuver.exit)), name: name });
128             } else {
129               instText += I18n.t(template + '_with_exit', { exit: step.maneuver.exit, name: name });
130             }
131           } else {
132             instText += I18n.t(template + '_without_exit', { name: name });
133           }
134         } else if (step.maneuver.type.match(/on ramp|off ramp/)) {
135           var params = {};
136           if (step.exits && step.maneuver.type.match(/off ramp/)) params.exit = step.exits;
137           if (step.destinations) params.directions = destinations;
138           if (namedRoad) params.directions = name;
139           if (Object.keys(params).length > 0) {
140             template = template + "_with_" + Object.keys(params).join("_");
141           }
142           instText += I18n.t(template, params);
143         } else {
144           instText += I18n.t(template + '_without_exit', { name: name });
145         }
146         return [[step.maneuver.location[1], step.maneuver.location[0]], ICON_MAP[maneuver_id], instText, step.distance, step_geometry];
147       });
148
149       return transformed_steps;
150     },
151
152     getRoute: function (points, callback) {
153
154       var params = [
155         { name: "overview", value: "false" },
156         { name: "geometries", value: "polyline" },
157         { name: "steps", value: true }
158       ];
159
160
161       if (cachedHints.length === points.length) {
162         params.push({name: "hints", value: cachedHints.join(";")});
163       } else {
164         // invalidate cache
165         cachedHints = [];
166       }
167
168       var encoded_coords = points.map(function(p) {
169         return p.lng + ',' + p.lat;
170       }).join(';');
171
172       var req_url = OSM.OSRM_URL + encoded_coords;
173
174       var onResponse = function (data) {
175         if (data.code !== 'Ok')
176           return callback(true);
177
178         cachedHints = data.waypoints.map(function(wp) {
179           return wp.hint;
180         });
181
182         var line = [];
183         var transformLeg = function (leg) {
184           return this._transformSteps(leg.steps, line);
185         };
186
187         var steps = [].concat.apply([], data.routes[0].legs.map(transformLeg.bind(this)));
188
189         callback(false, {
190           line: line,
191           steps: steps,
192           distance: data.routes[0].distance,
193           time: data.routes[0].duration
194         });
195       };
196
197       return $.ajax({
198         url: req_url,
199         data: params,
200         dataType: "json",
201         success: onResponse.bind(this),
202         error: function () {
203           callback(true);
204         }
205       });
206     }
207   };
208 }
209
210 OSM.Directions.addEngine(new OSRMEngine(), true);