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