Presentation work
[rails.git] / app / assets / javascripts / routing.js.erb
1 /*
2         osm.org routing interface
3         
4         See also:
5         https://github.com/apmon/openstreetmap-website/tree/routing2
6         https://github.com/apmon/openstreetmap-website/compare/routing2
7         https://github.com/apmon/openstreetmap-website/blob/9755c3ae0a8d0684d43760f91dc864ff42d8477a/app/views/routing/start.js.erb
8
9         *** draggable start/end markers
10         *** click each part
11         *** translation (including all alerts and presentation)
12         *** export GPX
13 */
14
15 var TURN_INSTRUCTIONS=["",
16         "Continue on ",                         // 1
17         "Slight right onto ",           // 2
18         "Turn right onto ",                     // 3
19         "Sharp right onto ",            // 4
20         "U-turn along ",                        // 5
21         "Sharp left onto ",                     // 6
22         "Turn left onto ",                      // 7
23         "Slight left onto ",            // 8
24         "(via point) ",                         // 9
25         "Follow ",                                      // 10
26         "At roundabout take ",          // 11
27         "Leave roundabout - ",          // 12
28         "Stay on roundabout - ",        // 13
29         "Start at end of ",                     // 14
30         "Reach destination",            // 15
31         "Go against one-way on ",       // 16
32         "End of one-way on "]           // 17
33
34 var ROUTING_POLYLINE={
35         color: '#03f',
36         opacity: 0.3,
37         weight: 10
38 };
39
40
41 OSM.Routing=function(map,name,jqSearch) {
42         var r={};
43         r.map=map;                              // Leaflet map
44         r.name=name;                    // global variable name of this instance (needed for JSONP)
45         r.jqSearch=jqSearch;    // JQuery object for search panel
46
47         r.route_from=null;
48         r.route_to=null;
49         r.viaPoints=[];
50         r.polyline=null;
51
52         // Geocoding
53
54         r.geocode=function(id,event) { var _this=this;
55                 var field=event.target;
56                 var v=event.target.value;
57                 // *** do something if v==''
58                 var querystring = '<%= NOMINATIM_URL %>search?q=' + encodeURIComponent(v) + '&format=json';
59                 // *** &accept-language=<%#= request.user_preferred_languages.join(',') %>
60                 // *** prefer current viewport
61                 $.getJSON(querystring, function(json) { _this._gotGeocode(json,field); });
62         };
63         
64         r._gotGeocode=function(json,field) {
65                 if (json.length==0) {
66                         alert("Sorry, couldn't find that place.");      // *** internationalise
67                         r[field.id]=null;
68                         return;
69                 }
70                 field.value=json[0].display_name;
71                 var lat=Number(json[0].lat), lon=Number(json[0].lon);
72                 r[field.id]=[lat,lon];
73                 // ** update markers
74         };
75         
76         // Route-fetching UI
77
78         r.requestRoute=function() {
79                 if (r.route_from && r.route_to) {
80                         var chosen=jqSearch.find('select.routing_engines :selected').val();
81                         r.engines[chosen].getRoute(true,[r.route_from,r.route_to]);
82                         // then, when the route has been fetched, it'll call the engine's gotRoute function
83                 }
84         };
85
86         // Take an array of Leaflet LatLngs and draw it as a polyline
87         r.setPolyline=function(line) {
88                 if (r.polyline) map.removeLayer(r.polyline);
89                 r.polyline=L.polyline(line, ROUTING_POLYLINE).addTo(r.map);
90                 r.map.fitBounds(r.polyline.getBounds());
91         };
92
93         // Take an array of directions and write it out
94         // (we use OSRM's route_instructions format)
95         // *** translations?
96         r.setItinerary=function(steps) {
97                 $("#content").removeClass("overlay-sidebar");
98                 $('#sidebar_content').empty();
99                 var html='<h2><a class="geolink" href="#"><span class="icon close"></span></a>Directions</h2>';
100                 html+="<table>";
101                 for (var i=0; i<steps.length; i++) {
102                         var step=steps[i];
103                         var instCodes=step[0].split('-');
104                         // Assemble instruction text
105                         var instText="<b>"+(i+1)+".</b> ";
106                         instText+=TURN_INSTRUCTIONS[instCodes[0]];
107                         if (instCodes[1]) { instText+="exit "+instCodes[1]+" "; }
108                         if (instCodes[0]!=15) { instText+=step[1] ? "<b>"+step[1]+"</b>" : "(unnamed)"; }
109                         // Add to table
110                         html+="<tr>";
111                         html+="<td class='direction i"+instCodes[0]+"'> ";
112                         html+="<td>"+instText;
113                 }
114                 html+="</table>";
115         $('#sidebar_content').html(html);
116         };
117
118
119         // Add engines
120         
121         r.engines=[];
122         r.addEngine=function(engine) {
123                 // Save engine
124                 var i=r.engines.length;
125                 engine.subscript=i;
126                 r['engine'+i]=engine;
127                 r.engines.push(engine);
128
129                 // Add generic JSONP function
130                 engine.requestJSONP=function(url) {
131                         var script = document.createElement('script');
132                         script.src = url+"&jsonp="+r.name+".engine"+this.subscript+".gotRoute";
133                         // OSRM doesn't like non-alphanumeric, otherwise we could just do OSM.routing.engines["+engine.subscript+"].gotRoute
134                         document.body.appendChild(script); 
135                 };
136
137                 // Populate dropdown
138                 var select=jqSearch.find('select.routing_engines');
139                 select.append("<option value='"+i+"'>"+engine.name+"</option>");
140         };
141
142         // OSRM car engine
143         // *** this should all be shared from an OSRM library somewhere
144         // *** need to clear hints at some point
145
146         r.addEngine({
147                 name: 'Car (OSRM)',
148                 draggable: true,
149                 _hints: {},
150                 getRoute: function(final,points) {
151                         var url="http://router.project-osrm.org/viaroute?z=14&output=json";
152                         for (var i=0; i<points.length; i++) {
153                                 var pair=points[i].join(',');
154                                 url+="&loc="+pair;
155                                 if (this._hints[pair]) url+= "&hint="+this._hints[pair];
156                         }
157                         if (final) url+="&instructions=true";
158                         this.requestJSONP(url);
159                 },
160                 gotRoute: function(data) {
161                         if (data.status==207) {
162                                 alert("Couldn't find route between those two places");
163                                 return false;
164                         }
165                         // *** store hints
166                         var line=L.PolylineUtil.decode(data.route_geometry);
167                         for (i=0; i<line.length; i++) { line[i].lat/=10; line[i].lng/=10; }
168                         r.setPolyline(line);
169                         r.setItinerary(data.route_instructions);
170                 }
171         });
172
173         return r;
174 };