//= require_self //= require_tree ./directions_engines var TURN_INSTRUCTIONS = []; var ROUTING_POLYLINE = { color: '#03f', opacity: 0.3, weight: 10 }; var ROUTING_POLYLINE_HIGHLIGHT = { color: '#ff0', opacity: 0.5, weight: 12 }; OSM.DirectionsEngines = { list: [], add: function (supportsHTTPS, engine) { if (document.location.protocol == "http:" || supportsHTTPS) this.list.push(engine); } }; OSM.Directions = function (map, name, jqSearch) { var r = {}; TURN_INSTRUCTIONS = [ "", I18n.t('javascripts.directions.instructions.continue_on'), // 1 I18n.t('javascripts.directions.instructions.slight_right'), // 2 I18n.t('javascripts.directions.instructions.turn_right'), // 3 I18n.t('javascripts.directions.instructions.sharp_right'), // 4 I18n.t('javascripts.directions.instructions.uturn'), // 5 I18n.t('javascripts.directions.instructions.sharp_left'), // 6 I18n.t('javascripts.directions.instructions.turn_left'), // 7 I18n.t('javascripts.directions.instructions.slight_left'), // 8 I18n.t('javascripts.directions.instructions.via_point'), // 9 I18n.t('javascripts.directions.instructions.follow'), // 10 I18n.t('javascripts.directions.instructions.roundabout'), // 11 I18n.t('javascripts.directions.instructions.leave_roundabout'), // 12 I18n.t('javascripts.directions.instructions.stay_roundabout'), // 13 I18n.t('javascripts.directions.instructions.start'), // 14 I18n.t('javascripts.directions.instructions.destination'), // 15 I18n.t('javascripts.directions.instructions.against_oneway'), // 16 I18n.t('javascripts.directions.instructions.end_oneway') // 17 ]; r.map = map; // Leaflet map r.name = name; // global variable name of this instance (needed for JSONP) r.jqSearch = jqSearch; // JQuery object for search panel r.route_from = null; // null=unset, false=awaiting response, [lat,lon]=geocoded r.route_to = null; // | r.awaitingGeocode = false;// true if the user has requested a route, but we're waiting on a geocode result r.awaitingRoute = false; // true if we've asked the engine for a route and are waiting to hear back r.dragging = false; // true if the user is dragging a start/end point r.viaPoints = []; // not yet used r.polyline = null; // Leaflet polyline object r.popup = null; // Leaflet popup object r.marker_from = null; // Leaflet from marker r.marker_to = null; // Leaflet to marker r.chosenEngine = null; // currently selected routing engine var icon_from = L.icon({ iconUrl: <%= asset_path('marker-green.png').to_json %>, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowUrl: <%= asset_path('images/marker-shadow.png').to_json %>, shadowSize: [41, 41] }); var icon_to = L.icon({ iconUrl: <%= asset_path('marker-red.png').to_json %>, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowUrl: <%= asset_path('images/marker-shadow.png').to_json %>, shadowSize: [41, 41] }); // Geocoding r.geocode = function (id, event) { var _this = this; var field = event.target; var v = event.target.value; var querystring = '<%= NOMINATIM_URL %>search?q=' + encodeURIComponent(v) + '&format=json'; // *** &accept-language=<%#= request.user_preferred_languages.join(',') %> r[field.id] = false; $.getJSON(querystring, function (json) { _this._gotGeocode(json, field); }); }; r._gotGeocode = function (json, field) { if (json.length == 0) { alert(I18n.t('javascripts.directions.errors.no_place')); r[field.id] = null; return; } field.value = json[0].display_name; var lat = Number(json[0].lat), lon = Number(json[0].lon); r[field.id] = [lat, lon]; r.updateMarker(field.id); if (r.awaitingGeocode) { r.awaitingGeocode = false; r.requestRoute(true, true); } }; // Drag and drop markers r.handleDrop = function (e) { var oe = e.originalEvent; var id = oe.dataTransfer.getData('id'); var pt = L.DomEvent.getMousePosition(oe, map.getContainer()); // co-ordinates of the mouse pointer at present pt.x += Number(oe.dataTransfer.getData('offsetX')); pt.y += Number(oe.dataTransfer.getData('offsetY')); var ll = map.containerPointToLatLng(pt); r.createMarker(ll, id); r.setNumericInput(ll, id); r.requestRoute(true, false); // update to/from field }; r.createMarker = function (latlng, id) { if (r[id]) r.map.removeLayer(r[id]); r[id] = L.marker(latlng, { icon: id == 'marker_from' ? icon_from : icon_to, draggable: true, name: id }).addTo(r.map); r[id].on('drag', r.markerDragged); r[id].on('dragend', r.markerDragged); }; // Update marker from geocoded route input r.updateMarker = function (id) { var m = id.replace('route', 'marker'); if (!r[m]) { r.createMarker(r[id], m); return; } var ll = r[m].getLatLng(); if (ll.lat != r[id][0] || ll.lng != r[id][1]) { r.createMarker(r[id], m); } }; // Marker has been dragged r.markerDragged = function (e) { r.dragging = (e.type == 'drag'); // true for drag, false for dragend if (r.dragging && !r.chosenEngine.draggable) return; if (r.dragging && r.awaitingRoute) return; r.setNumericInput(e.target.getLatLng(), e.target.options.name); r.requestRoute(!r.dragging, false); }; // Set a route input field to a numeric value r.setNumericInput = function (ll, id) { var routeid = id.replace('marker', 'route'); r[routeid] = [ll.lat, ll.lng]; $("[name=" + routeid + "]:visible").val(Math.round(ll.lat * 10000) / 10000 + " " + Math.round(ll.lng * 10000) / 10000); }; // Route-fetching UI r.requestRoute = function (isFinal, updateZoom) { if (r.route_from && r.route_to) { $(".query_wrapper.routing .spinner").show(); r.awaitingRoute = true; r.chosenEngine.getRoute(isFinal, [r.route_from, r.route_to]); if (updateZoom) { r.map.fitBounds(L.latLngBounds([r.route_from, r.route_to]).pad(0.05)); } // then, when the route has been fetched, it'll call the engine's gotRoute function } else if (r.route_from == false || r.route_to == false) { // we're waiting for a Nominatim response before we can request a route r.awaitingGeocode = true; } }; // Take an array of Leaflet LatLngs and draw it as a polyline r.setPolyline = function (line) { if (r.polyline) map.removeLayer(r.polyline); r.polyline = L.polyline(line, ROUTING_POLYLINE).addTo(r.map); }; // Take directions and write them out // data = { steps: array of [latlng, sprite number, instruction text, distance in metres, highlightPolyline] } // sprite numbers equate to OSRM's route_instructions turn values r.setItinerary = function (data) { // Create base table $("#content").removeClass("overlay-sidebar"); $('#sidebar_content').empty(); var html = ('

' + '' + I18n.t('javascripts.directions.directions') + '

' + I18n.t('javascripts.directions.distance') + ': ' + r.formatDistance(data.distance) + '. ' + I18n.t('javascripts.directions.time') + ': ' + r.formatTime(data.time) + '.

' + '').replace(/~/g, "'"); $('#sidebar_content').html(html); // Add each row var cumulative = 0; for (var i = 0; i < data.steps.length; i++) { var step = data.steps[i]; // Distance var dist = step[3]; if (dist < 5) { dist = ""; } else if (dist < 200) { dist = Math.round(dist / 10) * 10 + "m"; } else if (dist < 1500) { dist = Math.round(dist / 100) * 100 + "m"; } else if (dist < 5000) { dist = Math.round(dist / 100) / 10 + "km"; } else { dist = Math.round(dist / 1000) + "km"; } // Add to table var row = $(""); row.append("
"); row.append("" + step[2]); row.append("" + dist); with ({ instruction: step[2], ll: step[0], lineseg: step[4] }) { row.on('click', function (e) { r.clickTurn(instruction, ll); }); row.hover(function (e) { r.highlightSegment(lineseg); }, function (e) { r.unhighlightSegment(); }); } ; $('#turnbyturn').append(row); cumulative += step[3]; } $('#sidebar_content').append('

' + I18n.t('javascripts.directions.instructions.courtesy', {link: r.chosenEngine.creditline}) + '

'); }; r.clickTurn = function (instruction, latlng) { r.popup = L.popup().setLatLng(latlng).setContent("

" + instruction + "

").openOn(r.map); }; r.highlightSegment = function (lineseg) { if (r.highlighted) map.removeLayer(r.highlighted); r.highlighted = L.polyline(lineseg, ROUTING_POLYLINE_HIGHLIGHT).addTo(r.map); }; r.unhighlightSegment = function () { if (r.highlighted) map.removeLayer(r.highlighted); }; r.formatDistance = function (m) { if (m < 1000) { return Math.round(m) + "m"; } else if (m < 10000) { return (m / 1000.0).toFixed(1) + "km"; } else { return Math.round(m / 1000) + "km"; } }; r.formatTime = function (s) { var m = Math.round(s / 60); var h = Math.floor(m / 60); m -= h * 60; return h + ":" + (m < 10 ? '0' : '') + m; }; // Close all routing UI r.close = function () { $("#content").addClass("overlay-sidebar"); r.route_from = r.route_to = null; $(".query_wrapper.routing input").val(""); var remove = ['polyline', 'popup', 'marker_from', 'marker_to']; for (var i = 0; i < remove.length; i++) { if (r[remove[i]]) { map.removeLayer(r[remove[i]]); r[remove[i]] = null; } } }; // Routing engine handling // Add all engines var list = OSM.DirectionsEngines.list; list.sort(function (a, b) { return I18n.t(a.name) > I18n.t(b.name); }); var select = r.jqSearch.find('select.routing_engines'); for (var i = 0; i < list.length; i++) { // Set up JSONP callback with ({num: i}) { list[num].requestJSONP = function (url) { var script = document.createElement('script'); script.src = url + r.name + ".gotRoute" + num; document.body.appendChild(script); }; list[num].requestCORS = function (url) { $.ajax({ url: url, method: "GET", data: {}, dataType: 'json', success: r['gotRoute' + num] }); }; r['gotRoute' + num] = function (data) { r.awaitingRoute = false; $(".query_wrapper.routing .spinner").hide(); if (!list[num].gotRoute(r, data)) { // No route found if (r.polyline) { map.removeLayer(r.polyline); r.polyline = null; } if (!r.dragging) { alert(I18n.t('javascripts.directions.errors.no_route')); } } }; } select.append(""); } r.engines = list; r.chosenEngine = list[0]; // default to first engine // Choose an engine on dropdown change r.selectEngine = function (e) { r.chosenEngine = r.engines[e.target.selectedIndex]; if (r.polyline) { // and if a route is currently showing, must also refresh, else confusion r.requestRoute(true, false); } }; // Choose an engine by name r.chooseEngine = function (name) { for (var i = 0; i < r.engines.length; i++) { if (r.engines[i].name == name) { r.chosenEngine = r.engines[i]; r.jqSearch.find('select.routing_engines').val(i); } } }; $("input[name='route_from']").on("change", function (e) { r.geocode('route_from', e); }); $("input[name='route_to']").on("change", function (e) { r.geocode('route_to', e); }); $("select[name='routing_engines']").on("change", function (e) { r.selectEngine(e); }); return r; };