//= require_self //= require_tree ./directions_engines OSM.Directions = function (map) { var awaitingGeocode; // true if the user has requested a route, but we're waiting on a geocode result var awaitingRoute; // true if we've asked the engine for a route and are waiting to hear back var dragging; // true if the user is dragging a start/end point var chosenEngine; var popup = L.popup(); var polyline = L.polyline([], { color: '#03f', opacity: 0.3, weight: 10 }); var highlight = L.polyline([], { color: '#ff0', opacity: 0.5, weight: 12 }); var endpoints = [ Endpoint($("input[name='route_from']"), <%= asset_path('marker-green.png').to_json %>), Endpoint($("input[name='route_to']"), <%= asset_path('marker-red.png').to_json %>) ]; function Endpoint(input, iconUrl) { var endpoint = {}; endpoint.marker = L.marker([0, 0], { icon: L.icon({ iconUrl: iconUrl, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowUrl: <%= asset_path('images/marker-shadow.png').to_json %>, shadowSize: [41, 41] }), draggable: true }); endpoint.marker.on('drag dragend', function (e) { dragging = (e.type == 'drag'); if (dragging && !chosenEngine.draggable) return; if (dragging && awaitingRoute) return; endpoint.setLatLng(e.target.getLatLng()); if (map.hasLayer(polyline)) { getRoute(); } }); input.on("change", function (e) { // make text the same in both text boxes var value = e.target.value; endpoint.setValue(value) }); endpoint.setValue = function(value) { endpoint.value = value; delete endpoint.latlng; input.val(value); endpoint.getGeocode(); } endpoint.getGeocode = function() { // if no one has entered a value yet, then we can't geocode, so don't // even try. if (!endpoint.value) { return; } endpoint.awaitingGeocode = true; $.getJSON('<%= NOMINATIM_URL %>search?q=' + encodeURIComponent(endpoint.value) + '&format=json', function (json) { endpoint.awaitingGeocode = false; endpoint.hasGeocode = true; if (json.length == 0) { alert(I18n.t('javascripts.directions.errors.no_place')); return; } input.val(json[0].display_name); endpoint.latlng = L.latLng(json[0]); endpoint.marker .setLatLng(endpoint.latlng) .addTo(map); if (awaitingGeocode) { awaitingGeocode = false; getRoute(); } }); } endpoint.setLatLng = function (ll) { var precision = OSM.zoomPrecision(map.getZoom()); input.val(ll.lat.toFixed(precision) + ", " + ll.lng.toFixed(precision)); endpoint.hasGeocode = true; endpoint.latlng = ll; endpoint.marker .setLatLng(ll) .addTo(map); }; return endpoint; } $(".directions_form a.directions_close").on("click", function(e) { e.preventDefault(); var route_from = endpoints[0].value; if (route_from) { OSM.router.route("/?query=" + encodeURIComponent(route_from) + OSM.formatHash(map)); } else { OSM.router.route("/" + OSM.formatHash(map)); } }); function formatDistance(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"; } } function formatTime(s) { var m = Math.round(s / 60); var h = Math.floor(m / 60); m -= h * 60; return h + ":" + (m < 10 ? '0' : '') + m; } function setEngine(id) { engines.forEach(function(engine, i) { if (engine.id == id) { chosenEngine = engine; select.val(i); } }); } function getRoute() { // go fetch geocodes for any endpoints which have not already // been geocoded. for (var ep_i = 0; ep_i < 2; ++ep_i) { var endpoint = endpoints[ep_i]; if (!endpoint.hasGeocode && !endpoint.awaitingGeocode) { endpoint.getGeocode(); awaitingGeocode = true; } } if (endpoints[0].awaitingGeocode || endpoints[1].awaitingGeocode) { awaitingGeocode = true; return; } var o = endpoints[0].latlng, d = endpoints[1].latlng; if (!o || !d) return; var precision = OSM.zoomPrecision(map.getZoom()); OSM.router.replace("/directions?" + querystring.stringify({ engine: chosenEngine.id, route: o.lat.toFixed(precision) + ',' + o.lng.toFixed(precision) + ';' + d.lat.toFixed(precision) + ',' + d.lng.toFixed(precision) })); // copy loading item to sidebar and display it. we copy it, rather than // just using it in-place and replacing it in case it has to be used // again. $('#sidebar_content').html($('.directions_form .loader_copy').html()); awaitingRoute = true; map.setSidebarOverlaid(false); chosenEngine.getRoute([o, d], function (err, route) { awaitingRoute = false; if (err) { map.removeLayer(polyline); if (!dragging) { alert(I18n.t('javascripts.directions.errors.no_route')); } return; } polyline .setLatLngs(route.line) .addTo(map); if (!dragging) { map.fitBounds(polyline.getBounds().pad(0.05)); } var html = '

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

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

' + ''; $('#sidebar_content') .html(html); // Add each row var cumulative = 0; route.steps.forEach(function (step) { var ll = step[0], direction = step[1], instruction = step[2], dist = step[3], lineseg = step[4]; cumulative += dist; 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"; } var row = $(""); row.append(" "); row.append("
" + instruction); row.append("" + dist); row.on('click', function () { popup .setLatLng(ll) .setContent("

" + instruction + "

") .openOn(map); }); row.hover(function () { highlight .setLatLngs(lineseg) .addTo(map); }, function () { map.removeLayer(highlight); }); $('#turnbyturn').append(row); }); $('#sidebar_content').append('

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

'); $('#sidebar_content a.geolink').on('click', function(e) { e.preventDefault(); map.removeLayer(polyline); $('#sidebar_content').html(''); map.setSidebarOverlaid(true); // TODO: collapse width of sidebar back to previous }); }); } var engines = OSM.Directions.engines; engines.sort(function (a, b) { a = I18n.t('javascripts.directions.engines.' + a.id); b = I18n.t('javascripts.directions.engines.' + b.id); return a.localeCompare(b); }); var select = $('select.routing_engines'); engines.forEach(function(engine, i) { select.append(""); }); setEngine('osrm_car'); select.on("change", function (e) { chosenEngine = engines[e.target.selectedIndex]; if (map.hasLayer(polyline)) { getRoute(); } }); $(".directions_form").on("submit", function(e) { e.preventDefault(); $("header").addClass("closed"); getRoute(); }); $(".routing_marker").on('dragstart', function (e) { e.originalEvent.dataTransfer.effectAllowed = 'move'; e.originalEvent.dataTransfer.setData('id', this.id); var xo = e.originalEvent.clientX - $(e.target).offset().left; var yo = e.originalEvent.clientY - $(e.target).offset().top; e.originalEvent.dataTransfer.setData('offsetX', e.originalEvent.target.width / 2 - xo); e.originalEvent.dataTransfer.setData('offsetY', e.originalEvent.target.height - yo); }); var page = {}; page.pushstate = page.popstate = function() { $(".search_form").hide(); $(".directions_form").show(); $("#map").on('dragend dragover', function (e) { e.preventDefault(); }); $("#map").on('drop', function (e) { e.preventDefault(); 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); endpoints[id === 'marker_from' ? 0 : 1].setLatLng(ll); getRoute(); }); var params = querystring.parse(location.search.substring(1)), route = (params.route || '').split(';'); if (params.engine) { setEngine(params.engine); } if (params.from) { endpoints[0].setValue(params.from); endpoints[1].setValue(""); } else { endpoints[0].setValue(""); endpoints[1].setValue(""); } var o = route[0] && L.latLng(route[0].split(',')), d = route[1] && L.latLng(route[1].split(',')); if (o) endpoints[0].setLatLng(o); if (d) endpoints[1].setLatLng(d); map.setSidebarOverlaid(!o || !d); getRoute(); }; page.load = function() { page.pushstate(); }; page.unload = function() { $(".search_form").show(); $(".directions_form").hide(); $("#map").off('dragend dragover drop'); map .removeLayer(popup) .removeLayer(polyline) .removeLayer(endpoints[0].marker) .removeLayer(endpoints[1].marker); }; return page; }; OSM.Directions.engines = []; OSM.Directions.addEngine = function (engine, supportsHTTPS) { if (document.location.protocol == "http:" || supportsHTTPS) { OSM.Directions.engines.push(engine); } };