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