]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5901'
authorTom Hughes <tom@compton.nu>
Sun, 6 Apr 2025 21:27:52 +0000 (22:27 +0100)
committerTom Hughes <tom@compton.nu>
Sun, 6 Apr 2025 21:27:52 +0000 (22:27 +0100)
app/assets/javascripts/index/directions.js
app/assets/javascripts/index/directions/fossgis_osrm.js
app/assets/javascripts/index/directions/fossgis_valhalla.js
app/assets/javascripts/index/directions/graphhopper.js
test/system/directions_test.rb

index 8b3047b68e5d4802b0ce586a6eb326e5db958fa8..45ff960e231acd6b488dc0611f13b41c926ffa8b 100644 (file)
@@ -184,9 +184,7 @@ OSM.Directions = function (map) {
         );
 
       // Add each row
-      for (const [i, step] of route.steps.entries()) {
-        const [ll, direction, instruction, dist, lineseg] = step;
-
+      turnByTurnTable.append(route.steps.map(([direction, instruction, dist, lineseg], i) => {
         const row = $("<tr class='turn'/>");
         if (direction) {
           row.append("<td class='border-0'><svg width='20' height='20' class='d-block'><use href='#routing-sprite-" + direction + "' /></svg></td>");
@@ -198,8 +196,8 @@ OSM.Directions = function (map) {
 
         row.on("click", function () {
           popup
-            .setLatLng(ll)
-            .setContent("<p>" + instruction + "</p>")
+            .setLatLng(lineseg[0])
+            .setContent(`<p><b>${i + 1}.</b> ${instruction}</p>`)
             .openOn(map);
         });
 
@@ -211,8 +209,8 @@ OSM.Directions = function (map) {
           map.removeLayer(highlight);
         });
 
-        turnByTurnTable.append(row);
-      }
+        return row;
+      }));
 
       const blob = new Blob([JSON.stringify(polyline.toGeoJSON())], { type: "application/json" });
       URL.revokeObjectURL(downloadURL);
index b5faca510617d6031f35beb5f3622ab1ebad43df..40181e588e7679382d47f72a397ff3a5c5fd2eae 100644 (file)
@@ -5,7 +5,7 @@
   function FOSSGISOSRMEngine(modeId, vehicleType) {
     let cachedHints = [];
 
-    function _processDirections(route) {
+    function getInstructionText(step) {
       const INSTRUCTION_TEMPLATE = {
         "continue": "continue",
         "merge right": "merge_right",
         "depart": "start",
         "arrive": "destination"
       };
-      const ICON_MAP = {
-        "continue": "straight",
-        "merge right": "merge-right",
-        "merge left": "merge-left",
-        "off ramp right": "exit-right",
-        "off ramp left": "exit-left",
-        "on ramp right": "right",
-        "on ramp left": "left",
-        "fork right": "fork-right",
-        "fork left": "fork-left",
-        "end of road right": "end-of-road-right",
-        "end of road left": "end-of-road-left",
-        "turn straight": "straight",
-        "turn slight right": "slight-right",
-        "turn right": "right",
-        "turn sharp right": "sharp-right",
-        "turn uturn": "u-turn-left",
-        "turn slight left": "slight-left",
-        "turn left": "left",
-        "turn sharp left": "sharp-left",
-        "roundabout": "roundabout",
-        "rotary": "roundabout",
-        "exit roundabout": "roundabout",
-        "exit rotary": "roundabout",
-        "depart": "start",
-        "arrive": "destination"
-      };
       function numToWord(num) {
         return ["first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth"][num - 1];
       }
+
+      const instrPrefix = "javascripts.directions.instructions.";
+      let template = instrPrefix + INSTRUCTION_TEMPLATE[step.maneuverId];
+
+      const destinations = "<b>" + step.destinations + "</b>";
+      let namedRoad = true;
+      let name;
+
+      if (step.name && step.ref) {
+        name = "<b>" + step.name + " (" + step.ref + ")</b>";
+      } else if (step.name) {
+        name = "<b>" + step.name + "</b>";
+      } else if (step.ref) {
+        name = "<b>" + step.ref + "</b>";
+      } else {
+        name = OSM.i18n.t(instrPrefix + "unnamed");
+        namedRoad = false;
+      }
+
+      if (step.maneuver.type.match(/^exit (rotary|roundabout)$/)) {
+        return OSM.i18n.t(template, { name: name });
+      }
+      if (step.maneuver.type.match(/^(rotary|roundabout)$/)) {
+        if (!step.maneuver.exit) {
+          return OSM.i18n.t(template + "_without_exit", { name: name });
+        }
+        if (step.maneuver.exit > 10) {
+          return OSM.i18n.t(template + "_with_exit", { exit: step.maneuver.exit, name: name });
+        }
+        return OSM.i18n.t(template + "_with_exit_ordinal", { exit: OSM.i18n.t(instrPrefix + "exit_counts." + numToWord(step.maneuver.exit)), name: name });
+      }
+      if (!step.maneuver.type.match(/^(on ramp|off ramp)$/)) {
+        return OSM.i18n.t(template + "_without_exit", { name: name });
+      }
+      const params = {};
+      if (step.exits && step.maneuver.type.match(/^(off ramp)$/)) params.exit = step.exits;
+      if (step.destinations) params.directions = destinations;
+      if (namedRoad) params.directions = name;
+      if (Object.keys(params).length > 0) {
+        template = template + "_with_" + Object.keys(params).join("_");
+      }
+      return OSM.i18n.t(template, params);
+    }
+
+    function _processDirections(leg) {
       function getManeuverId(maneuver) {
         // special case handling
         switch (maneuver.type) {
             return "turn " + maneuver.modifier;
         }
       }
+      const ICON_MAP = {
+        "continue": "straight",
+        "merge right": "merge-right",
+        "merge left": "merge-left",
+        "off ramp right": "exit-right",
+        "off ramp left": "exit-left",
+        "on ramp right": "right",
+        "on ramp left": "left",
+        "fork right": "fork-right",
+        "fork left": "fork-left",
+        "end of road right": "end-of-road-right",
+        "end of road left": "end-of-road-left",
+        "turn straight": "straight",
+        "turn slight right": "slight-right",
+        "turn right": "right",
+        "turn sharp right": "sharp-right",
+        "turn uturn": "u-turn-left",
+        "turn slight left": "slight-left",
+        "turn left": "left",
+        "turn sharp left": "sharp-left",
+        "roundabout": "roundabout",
+        "rotary": "roundabout",
+        "exit roundabout": "roundabout",
+        "exit rotary": "roundabout",
+        "depart": "start",
+        "arrive": "destination"
+      };
+
+      for (const step of leg.steps) step.maneuverId = getManeuverId(step.maneuver);
 
-      const steps = route.legs.flatMap(
-        leg => leg.steps.map(function (step) {
-          const maneuver_id = getManeuverId(step.maneuver);
-
-          const instrPrefix = "javascripts.directions.instructions.";
-          let template = instrPrefix + INSTRUCTION_TEMPLATE[maneuver_id];
-
-          const step_geometry = L.PolylineUtil.decode(step.geometry, { precision: 5 }).map(L.latLng);
-
-          let instText;
-          const destinations = "<b>" + step.destinations + "</b>";
-          let namedRoad = true;
-          let name;
-
-          if (step.name && step.ref) {
-            name = "<b>" + step.name + " (" + step.ref + ")</b>";
-          } else if (step.name) {
-            name = "<b>" + step.name + "</b>";
-          } else if (step.ref) {
-            name = "<b>" + step.ref + "</b>";
-          } else {
-            name = OSM.i18n.t(instrPrefix + "unnamed");
-            namedRoad = false;
-          }
-
-          if (step.maneuver.type.match(/^exit (rotary|roundabout)$/)) {
-            instText = OSM.i18n.t(template, { name: name });
-          } else if (step.maneuver.type.match(/^(rotary|roundabout)$/)) {
-            if (step.maneuver.exit) {
-              if (step.maneuver.exit <= 10) {
-                instText = OSM.i18n.t(template + "_with_exit_ordinal", { exit: OSM.i18n.t(instrPrefix + "exit_counts." + numToWord(step.maneuver.exit)), name: name });
-              } else {
-                instText = OSM.i18n.t(template + "_with_exit", { exit: step.maneuver.exit, name: name });
-              }
-            } else {
-              instText = OSM.i18n.t(template + "_without_exit", { name: name });
-            }
-          } else if (step.maneuver.type.match(/^(on ramp|off ramp)$/)) {
-            const params = {};
-            if (step.exits && step.maneuver.type.match(/^(off ramp)$/)) params.exit = step.exits;
-            if (step.destinations) params.directions = destinations;
-            if (namedRoad) params.directions = name;
-            if (Object.keys(params).length > 0) {
-              template = template + "_with_" + Object.keys(params).join("_");
-            }
-            instText = OSM.i18n.t(template, params);
-          } else {
-            instText = OSM.i18n.t(template + "_without_exit", { name: name });
-          }
-          return [[step.maneuver.location[1], step.maneuver.location[0]], ICON_MAP[maneuver_id], instText, step.distance, step_geometry];
-        })
-      );
+      const steps = leg.steps.map(step => [
+        ICON_MAP[step.maneuverId],
+        getInstructionText(step),
+        step.distance,
+        L.PolylineUtil.decode(step.geometry, { precision: 5 })
+      ]);
 
       return {
-        line: steps.flatMap(step => step[4]),
+        line: steps.flatMap(step => step[3]),
         steps,
-        distance: route.distance,
-        time: route.duration
+        distance: leg.distance,
+        time: leg.duration
       };
     }
 
           .then(response => {
             if (response.code !== "Ok") throw new Error();
             cachedHints = response.waypoints.map(wp => wp.hint);
-            return _processDirections(response.routes[0]);
+            return _processDirections(response.routes[0].legs[0]);
           });
       }
     };
index 48b3ad64abeb532dc60a5dad6cf96c5844b36fbf..2e12956577339b61fda00d1af843cea1c3890be6 100644 (file)
       "merge-left" // kMergeLeft = 38;
     ];
 
-    function _processDirections(tripLegs) {
-      let line = [];
-      let steps = [];
-      let distance = 0;
-      let time = 0;
+    function _processDirections(leg) {
+      const line = L.PolylineUtil.decode(leg.shape, { precision: 6 });
 
-      for (const leg of tripLegs) {
-        const legLine = L.PolylineUtil.decode(leg.shape, {
-          precision: 6
-        });
-
-        const legSteps = leg.maneuvers.map(function (manoeuvre) {
-          const lineseg = legLine
-            .slice(manoeuvre.begin_shape_index, manoeuvre.end_shape_index + 1)
-            .map(([lat, lng]) => ({ lat, lng }));
-          return [
-            lineseg[0],
-            INSTR_MAP[manoeuvre.type],
-            manoeuvre.instruction,
-            manoeuvre.length * 1000,
-            lineseg
-          ];
-        });
-
-        line = line.concat(legLine);
-        steps = steps.concat(legSteps);
-        distance += leg.summary.length;
-        time += leg.summary.time;
-      }
+      const steps = leg.maneuvers.map(manoeuvre => [
+        INSTR_MAP[manoeuvre.type],
+        manoeuvre.instruction,
+        manoeuvre.length * 1000,
+        line.slice(manoeuvre.begin_shape_index, manoeuvre.end_shape_index + 1)
+      ]);
 
       return {
-        line: line,
-        steps: steps,
-        distance: distance * 1000,
-        time: time
+        line,
+        steps,
+        distance: leg.summary.length * 1000,
+        time: leg.summary.time
       };
     }
 
           .then(response => response.json())
           .then(({ trip }) => {
             if (trip.status !== 0) throw new Error();
-            return _processDirections(trip.legs);
+            return _processDirections(trip.legs[0]);
           });
       }
     };
index 20de38e37c288a483ba821b93d6d6ba544565101..182be4497d38da9481f9a7720de66a4adec2da43 100644 (file)
     function _processDirections(path) {
       const line = L.PolylineUtil.decode(path.points);
 
-      const steps = path.instructions.map(function (instr) {
-        const lineseg = line
-          .slice(instr.interval[0], instr.interval[1] + 1)
-          .map(([lat, lng]) => ({ lat, lng }));
-        return [
-          lineseg[0],
-          GH_INSTR_MAP[instr.sign],
-          instr.text,
-          instr.distance,
-          lineseg
-        ]; // TODO does graphhopper map instructions onto line indices?
-      });
-      steps.at(-1)[1] = "destination";
+      const steps = path.instructions.map(instr => [
+        GH_INSTR_MAP[instr.sign],
+        instr.text,
+        instr.distance,
+        line.slice(instr.interval[0], instr.interval[1] + 1)
+      ]);
+      steps.at(-1)[0] = "destination";
 
       return {
-        line: line,
-        steps: steps,
+        line,
+        steps,
         distance: path.distance,
         time: path.time / 1000,
         ascend: path.ascend,
index 1c4edb60c023875ed1e3495fb16a860730a8c2c6..8ff78736b731b3a1e137a1215ec1046bf479b683 100644 (file)
@@ -38,8 +38,8 @@ class DirectionsSystemTest < ApplicationSystemTestCase
       return Promise.resolve({
         line: points,
         steps: [
-          [points[0], "start", "<b>1.</b> #{start_instruction}", distance, points],
-          [points[1], "destination", "<b>2.</b> #{finish_instruction}", 0, [points[1]]]
+          ["start", "<b>1.</b> #{start_instruction}", distance, points],
+          ["destination", "<b>2.</b> #{finish_instruction}", 0, [points[1]]]
         ],
         distance,
         time