2 osm.org routing interface
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
9 *** draggable start/end markers
11 *** translation (including all alerts and presentation)
13 *** URL history (or do we consciously not want to support that?)
16 var TURN_INSTRUCTIONS=["",
18 "Slight right onto ", // 2
19 "Turn right onto ", // 3
20 "Sharp right onto ", // 4
22 "Sharp left onto ", // 6
23 "Turn left onto ", // 7
24 "Slight left onto ", // 8
27 "At roundabout take ", // 11
28 "Leave roundabout - ", // 12
29 "Stay on roundabout - ", // 13
30 "Start at end of ", // 14
31 "Reach destination", // 15
32 "Go against one-way on ", // 16
33 "End of one-way on "] // 17
35 var ROUTING_POLYLINE={
42 OSM.Routing=function(map,name,jqSearch) {
44 r.map=map; // Leaflet map
45 r.name=name; // global variable name of this instance (needed for JSONP)
46 r.jqSearch=jqSearch; // JQuery object for search panel
56 r.geocode=function(id,event) { var _this=this;
57 var field=event.target;
58 var v=event.target.value;
59 // *** do something if v==''
60 var querystring = '<%= NOMINATIM_URL %>search?q=' + encodeURIComponent(v) + '&format=json';
61 // *** &accept-language=<%#= request.user_preferred_languages.join(',') %>
62 // *** prefer current viewport
63 $.getJSON(querystring, function(json) { _this._gotGeocode(json,field); });
66 r._gotGeocode=function(json,field) {
68 alert("Sorry, couldn't find that place."); // *** internationalise
72 field.value=json[0].display_name;
73 var lat=Number(json[0].lat), lon=Number(json[0].lon);
74 r[field.id]=[lat,lon];
80 r.selectEngine=function(e) {
81 r.chosenEngine=r.engines[e.target.selectedIndex];
83 r.requestRoute=function() {
84 if (r.route_from && r.route_to) {
85 r.chosenEngine.getRoute(true,[r.route_from,r.route_to]);
86 // then, when the route has been fetched, it'll call the engine's gotRoute function
90 // Take an array of Leaflet LatLngs and draw it as a polyline
91 r.setPolyline=function(line) {
92 if (r.polyline) map.removeLayer(r.polyline);
93 r.polyline=L.polyline(line, ROUTING_POLYLINE).addTo(r.map);
94 r.map.fitBounds(r.polyline.getBounds());
97 // Take an array of directions and write it out
98 // (we use OSRM's route_instructions format)
100 r.setItinerary=function(steps) {
102 $("#content").removeClass("overlay-sidebar");
103 $('#sidebar_content').empty();
104 var html='<h2><a class="geolink" href="#" onclick="$(~.close_directions~).click();return false;"><span class="icon close"></span></a>Directions</h2>'.replace(/~/g,"'");
105 html+="<table id='turnbyturn' />";
106 $('#sidebar_content').html(html);
109 for (var i=0; i<steps.length; i++) {
111 var instCodes=step[0].split('-');
112 // Assemble instruction text
113 var instText="<b>"+(i+1)+".</b> ";
114 instText+=TURN_INSTRUCTIONS[instCodes[0]];
115 if (instCodes[1]) { instText+="exit "+instCodes[1]+" "; }
116 if (instCodes[0]!=15) { instText+=step[1] ? "<b>"+step[1]+"</b>" : "(unnamed)"; }
119 if (dist<5) { dist=""; }
120 else if (dist<200) { dist=Math.round(dist/10)*10+"m"; }
121 else if (dist<1500) { dist=Math.round(dist/100)*100+"m"; }
122 else if (dist<5000) { dist=Math.round(dist/100)/10+"km"; }
123 else { dist=Math.round(dist/1000)+"km"; }
125 var row=$("<tr class='turn'/>");
126 row.append("<td class='direction i"+instCodes[0]+"'> ");
127 row.append("<td class='instruction'>"+instText);
128 row.append("<td class='distance'>"+dist);
129 with ({num: i, dist: step[3]}) {
130 row.on('click',function(e) {
131 r.clickTurn(num, r.polyline.getLatLngs()[dist]);
134 $('#turnbyturn').append(row);
138 r.clickTurn=function(num,latlng) {
139 L.popup().setLatLng(latlng).setContent("<p>"+(num+1)+"</p>").openOn(r.map);
143 // Close all routing UI
146 $("#content").addClass("overlay-sidebar");
147 if (r.polyline) map.removeLayer(r.polyline);
153 r.addEngine=function(engine) {
155 var i=r.engines.length;
157 r['engine'+i]=engine;
158 r.engines.push(engine);
160 // Add generic JSONP function
161 engine.requestJSONP=function(url) {
162 var script = document.createElement('script');
163 script.src = url+r.name+".engine"+this.subscript+".gotRoute";
164 // OSRM doesn't like non-alphanumeric, otherwise we could just do OSM.routing.engines["+engine.subscript+"].gotRoute
165 document.body.appendChild(script);
169 var select=jqSearch.find('select.routing_engines');
170 select.append("<option value='"+i+"'>"+engine.name+"</option>");
174 // *** this should all be shared from an OSRM library somewhere
175 // *** need to clear hints at some point
181 getRoute: function(final,points) {
182 var url="http://router.project-osrm.org/viaroute?z=14&output=json";
183 for (var i=0; i<points.length; i++) {
184 var pair=points[i].join(',');
186 if (this._hints[pair]) url+= "&hint="+this._hints[pair];
188 if (final) url+="&instructions=true";
189 this.requestJSONP(url+"&jsonp=");
191 gotRoute: function(data) {
192 if (data.status==207) {
193 alert("Couldn't find route between those two places");
197 var line=L.PolylineUtil.decode(data.route_geometry);
198 for (i=0; i<line.length; i++) { line[i].lat/=10; line[i].lng/=10; }
200 r.setItinerary(data.route_instructions);
204 // CloudMade foot engine
205 // *** again, this should be shared from a Cloudmade library somewhere
206 // *** this API key is taken from some example code, not for real live use!
209 name: 'Foot (CloudMade)',
211 getRoute: function(final,points) {
212 var url="http://routes.cloudmade.com/8ee2a50541944fb9bcedded5165f09d9/api/0.3/";
214 for (var i=0; i<points.length; i++) {
215 p.push(points[i][0]);
216 p.push(points[i][1]);
220 this.requestJSONP(url+"?callback=");
222 gotRoute: function(data) {
225 // *** will require some degree of refactoring because instruction text is pre-assembled
226 // *** otherwise largely like OSRM (funny that)
232 // *** move into separate files
234 r.chosenEngine=r.engines[0];