]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index_modules/directions/route-output.js
Merge pull request #7191 from CommanderStorm/fix-attribution-listener-leak
[rails.git] / app / assets / javascripts / index_modules / directions / route-output.js
1 /* exported RouteOutput */
2 function RouteOutput(map) {
3   const popup = L.popup({ autoPanPadding: [100, 100] });
4
5   const polyline = L.polyline([], {
6     color: "#03f",
7     opacity: 0.3,
8     weight: 10
9   });
10
11   const highlight = L.polyline([], {
12     color: "#ff0",
13     opacity: 0.5,
14     weight: 12
15   });
16
17   let distanceUnits = "km_m";
18   let downloadURL = null;
19
20   function translateDistanceUnits(m) {
21     if (distanceUnits === "mi_ft") {
22       return [m / 0.3048, "ft", m / 1609.344, "mi"];
23     } else if (distanceUnits === "mi_yd") {
24       return [m / 0.9144, "yd", m / 1609.344, "mi"];
25     } else {
26       return [m, "m", m / 1000, "km"];
27     }
28   }
29
30   function formatTotalDistance(minorValue, minorName, majorValue, majorName) {
31     const scope = "javascripts.directions.distance_in_units";
32
33     if (minorValue < 1000 || majorValue < 0.25) {
34       return OSM.i18n.t(minorName, { scope, distance: Math.round(minorValue) });
35     } else if (majorValue < 10) {
36       return OSM.i18n.t(majorName, { scope, distance: majorValue.toFixed(1) });
37     } else {
38       return OSM.i18n.t(majorName, { scope, distance: Math.round(majorValue) });
39     }
40   }
41
42   function formatStepDistance(minorValue, minorName, majorValue, majorName) {
43     const scope = "javascripts.directions.distance_in_units";
44
45     if (minorValue < 5) {
46       return "";
47     } else if (minorValue < 200) {
48       return OSM.i18n.t(minorName, { scope, distance: Math.round(minorValue / 10) * 10 });
49     } else if (minorValue < 1500 || majorValue < 0.25) {
50       return OSM.i18n.t(minorName, { scope, distance: Math.round(minorValue / 100) * 100 });
51     } else if (majorValue < 5) {
52       return OSM.i18n.t(majorName, { scope, distance: majorValue.toFixed(1) });
53     } else {
54       return OSM.i18n.t(majorName, { scope, distance: Math.round(majorValue) });
55     }
56   }
57
58   function formatHeight(minorValue, minorName) {
59     const scope = "javascripts.directions.distance_in_units";
60
61     return OSM.i18n.t(minorName, { scope, distance: Math.round(minorValue) });
62   }
63
64   function formatTime(s) {
65     let m = Math.round(s / 60);
66     const h = Math.floor(m / 60);
67
68     m -= h * 60;
69
70     return h + ":" + (m < 10 ? "0" : "") + m;
71   }
72
73   function writeSummary(route) {
74     $("#directions_route_distance").val(formatTotalDistance(...translateDistanceUnits(route.distance)));
75     $("#directions_route_time").val(formatTime(route.time));
76
77     if (typeof route.ascend !== "undefined" && typeof route.descend !== "undefined") {
78       $("#directions_route_ascend_descend").prop("hidden", false);
79       $("#directions_route_ascend").val(formatHeight(...translateDistanceUnits(route.ascend)));
80       $("#directions_route_descend").val(formatHeight(...translateDistanceUnits(route.descend)));
81     } else {
82       $("#directions_route_ascend_descend").prop("hidden", true);
83       $("#directions_route_ascend").val("");
84       $("#directions_route_descend").val("");
85     }
86   }
87
88   function writeSteps(route) {
89     $("#directions_route_steps").empty();
90
91     for (const [i, [direction, instruction, dist, lineseg]] of route.steps.entries()) {
92       const row = $("<tr class='turn'/>").appendTo($("#directions_route_steps"));
93
94       if (direction) {
95         row.append("<td class='ps-3'><svg width='20' height='20' class='d-block'><use href='#routing-sprite-" + direction + "' /></svg></td>");
96       } else {
97         row.append("<td class='ps-3'>");
98       }
99
100       row.append(`<td class="text-break"><b>${i + 1}.</b> ${instruction}`);
101       row.append("<td class='pe-3 distance text-body-secondary text-end'>" + formatStepDistance(...translateDistanceUnits(dist)));
102
103       row.on("click", function () {
104         popup
105           .setLatLng(lineseg[0])
106           .setContent(`<p><b>${i + 1}.</b> ${instruction}</p>`)
107           .openOn(map);
108       });
109
110       row
111         .on("mouseenter", function () {
112           highlight
113             .setLatLngs(lineseg)
114             .addTo(map);
115         })
116         .on("mouseleave", function () {
117           map.removeLayer(highlight);
118         });
119     }
120   }
121
122   const routeOutput = {};
123
124   routeOutput.write = function (route) {
125     polyline
126       .setLatLngs(route.line)
127       .addTo(map);
128
129     writeSummary(route);
130     writeSteps(route);
131
132     $("#directions_distance_units_settings input").off().on("change", function () {
133       distanceUnits = this.value;
134       writeSummary(route);
135       writeSteps(route);
136     });
137
138     const blob = new Blob([JSON.stringify(polyline.toGeoJSON())], { type: "application/geo+json" });
139
140     URL.revokeObjectURL(downloadURL);
141     downloadURL = URL.createObjectURL(blob);
142     $("#directions_route_download").prop("href", downloadURL);
143
144     $("#directions_route_credit")
145       .text(route.credit)
146       .prop("href", route.creditlink);
147     $("#directions_route_demo")
148       .text(route.credit)
149       .prop("href", route.demolink);
150   };
151
152   routeOutput.fit = function () {
153     map.fitBounds(polyline.getBounds().pad(0.05));
154   };
155
156   routeOutput.isVisible = function () {
157     return map.hasLayer(polyline);
158   };
159
160   routeOutput.remove = function () {
161     map
162       .removeLayer(popup)
163       .removeLayer(polyline);
164
165     $("#directions_distance_units_settings input").off();
166
167     $("#directions_route_steps").empty();
168
169     URL.revokeObjectURL(downloadURL);
170     $("#directions_route_download").prop("href", "");
171   };
172
173   return routeOutput;
174 };