1 OSM.DirectionsRouteOutput = function (map) {
2 const popup = L.popup({ autoPanPadding: [100, 100] });
4 const polyline = L.polyline([], {
10 const highlight = L.polyline([], {
16 let distanceUnits = "km";
17 let downloadURL = null;
20 function translateDistanceUnits(m) {
21 const scope = "javascripts.directions.distance_in_units.";
22 if (distanceUnits === "mi") return [scope + "miles", m / 0.3048, "ft", m / 1609.344, "mi"];
23 return [scope + "meters", m, "m", m / 1000, "km"];
26 function formatTotalDistance(m) {
27 const [scope, minorValue, minorName, majorValue, majorName] = translateDistanceUnits(m);
29 if (minorValue < 1000 || majorValue < 0.25) {
30 return OSM.i18n.t(minorName, { scope, distance: Math.round(minorValue) });
31 } else if (majorValue < 10) {
32 return OSM.i18n.t(majorName, { scope, distance: majorValue.toFixed(1) });
34 return OSM.i18n.t(majorName, { scope, distance: Math.round(majorValue) });
38 function formatStepDistance(m) {
39 const [scope, minorValue, minorName, majorValue, majorName] = translateDistanceUnits(m);
43 } else if (minorValue < 200) {
44 return OSM.i18n.t(minorName, { scope, distance: Math.round(minorValue / 10) * 10 });
45 } else if (minorValue < 1500 || majorValue < 0.25) {
46 return OSM.i18n.t(minorName, { scope, distance: Math.round(minorValue / 100) * 100 });
47 } else if (majorValue < 5) {
48 return OSM.i18n.t(majorName, { scope, distance: majorValue.toFixed(1) });
50 return OSM.i18n.t(majorName, { scope, distance: Math.round(majorValue) });
54 function formatHeight(m) {
55 const [scope, value, name] = translateDistanceUnits(m);
57 return OSM.i18n.t(name, { scope, distance: Math.round(value) });
60 function formatTime(s) {
61 let m = Math.round(s / 60);
62 const h = Math.floor(m / 60);
64 return h + ":" + (m < 10 ? "0" : "") + m;
67 function writeContent() {
68 if (this?.dataset?.unit) distanceUnits = this.dataset.unit;
70 $("#directions_route_distance").val(formatTotalDistance(route.distance));
71 $("#directions_route_time").val(formatTime(route.time));
72 $("#directions_route_ascend_descend").prop("hidden", typeof route.ascend === "undefined" || typeof route.descend === "undefined");
73 $("#directions_route_ascend").val(formatHeight(route.ascend ?? 0));
74 $("#directions_route_descend").val(formatHeight(route.descend ?? 0));
75 $("#directions_route_steps").empty();
77 for (const [i, step] of route.steps.entries()) {
78 writeStep(step, i).appendTo("#directions_route_steps");
82 function writeStep([direction, instruction, dist, lineseg], i) {
83 const popupText = `<b>${i + 1}.</b> ${instruction}`;
85 if (direction) icon = `<svg width="20" height="20" class="d-block"><use href="#routing-sprite-${direction}" /></svg>`;
87 return $("<tr class='turn'/>")
88 .append(`<td class="ps-3">${icon}</td>`)
89 .append(`<td>${popupText}</td>`)
90 .append(`<td class="pe-3 distance text-body-secondary text-end">${formatStepDistance(dist)}</td>`)
91 .on("click", function () {
93 .setLatLng(lineseg[0])
94 .setContent(`<p>${popupText}</p>`)
97 .on("mouseenter", function () {
102 .on("mouseleave", function () {
103 map.removeLayer(highlight);
107 const routeOutput = {};
109 routeOutput.write = function (r) {
112 .setLatLngs(route.line)
116 $("#directions_route input[data-unit]").off().on("change", writeContent);
118 const blob = new Blob([JSON.stringify(polyline.toGeoJSON())], { type: "application/json" });
119 URL.revokeObjectURL(downloadURL);
120 downloadURL = URL.createObjectURL(blob);
121 $("#directions_route_download").prop("href", downloadURL);
123 $("#directions_route_credit")
125 .prop("href", route.creditlink);
128 routeOutput.fit = function () {
129 map.fitBounds(polyline.getBounds().pad(0.05));
132 routeOutput.isVisible = function () {
133 return map.hasLayer(polyline);
136 routeOutput.remove = function () {
139 .removeLayer(polyline);
141 $("#directions_route input[data-unit]").off();
143 $("#directions_route_steps").empty();
145 URL.revokeObjectURL(downloadURL);
146 $("#directions_route_download").prop("href", "");