Repository: graphhopper/graphhopper
Pull request #1844 (2019-12-31 15:15:46)

Use road class to determine if a fork should have an instruction
This PR solves #990 and #1047. 

Essentially, this will remove many unnecessary instructions on main roads, including the annoying continue instructions on motorways. For minor roads the difference should be rather small.

Something I am not sure about is how we should handle the tagged maxspeed. Often no maxspeed is tagged. Currently I decided to fallback to the average speed.

Let me know how this works out in your tests and if there are any cases we need to improve.

Here is a brief comparison of [GH maps route](https://graphhopper.com/maps/?point=49.498458%2C8.464966&point=47.657988%2C9.508667&locale=de-DE&vehicle=car&weighting=fastest&elevation=true&debug=true&use_miles=false&layer=OpenStreetMap) vs. the improved version, I think the difference is quite visible :)):
![Screenshot from 2019-12-31 16-14-30](https://user-images.githubusercontent.com/1553525/71625538-ae021300-2be8-11ea-9988-3776af7cb76d.png)
![Screenshot from 2019-12-31 16-14-36](https://user-images.githubusercontent.com/1553525/71625539-ae9aa980-2be8-11ea-991a-f3d38641283e.png)

BTW: I stumbled over #1843 while creating this PR



Patch:
diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java
index 2233689b9..b0a132ca7 100644
--- a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java
+++ b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java
@@ -23,7 +23,6 @@ import com.graphhopper.routing.util.DefaultEdgeFilter;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
-import com.graphhopper.storage.IntsRef;
 import com.graphhopper.storage.NodeAccess;
 import com.graphhopper.util.*;
 import com.graphhopper.util.shapes.GHPoint;
@@ -50,11 +49,13 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
     private final BooleanEncodedValue roundaboutEnc;
     private final BooleanEncodedValue accessEnc;
     private final BooleanEncodedValue getOffBikeEnc;
+    private final BooleanEncodedValue roadClassLinkEnc;
     private final EnumEncodedValue<RouteNetwork> bikeRouteEnc;
     private final EnumEncodedValue<RoadClass> roadClassEnc;
     private final EnumEncodedValue<RoadEnvironment> roadEnvEnc;
     private final EnumEncodedValue<RoadAccess> roadAccessEnc;
     private final EnumEncodedValue<Toll> tollEnc;
+    private final DecimalEncodedValue maxSpeedEnc;
 
     /*
      * We need three points to make directions
@@ -106,6 +107,8 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
         this.tollEnc = evLookup.hasEncodedValue(Toll.KEY) ? evLookup.getEnumEncodedValue(Toll.KEY, Toll.class) : null;
 
         this.roadClassEnc = evLookup.getEnumEncodedValue(RoadClass.KEY, RoadClass.class);
+        this.roadClassLinkEnc = evLookup.getBooleanEncodedValue(RoadClassLink.KEY);
+        this.maxSpeedEnc = evLookup.getDecimalEncodedValue(MaxSpeed.KEY);
         this.roadEnvEnc = evLookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class);
         this.roadAccessEnc = evLookup.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class);
         this.nodeAccess = graph.getNodeAccess();
@@ -144,13 +147,12 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
             prevLon = this.nodeAccess.getLongitude(baseNode);
         }
 
-        IntsRef flags = edge.getFlags();
         double adjLat = nodeAccess.getLatitude(adjNode);
         double adjLon = nodeAccess.getLongitude(adjNode);
         double latitude, longitude;
 
         PointList wayGeo = edge.fetchWayGeometry(3);
-        boolean isRoundabout = roundaboutEnc.getBool(false, flags);
+        boolean isRoundabout = edge.get(roundaboutEnc);
 
         if (wayGeo.getSize() <= 2) {
             latitude = adjLat;
@@ -220,8 +222,7 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
                     // check if there is an exit at the same node the roundabout was entered
                     EdgeIterator edgeIter = outEdgeExplorer.setBaseNode(baseNode);
                     while (edgeIter.next()) {
-                        if ((edgeIter.getAdjNode() != prevNode)
-                                && !roundaboutEnc.getBool(false, edgeIter.getFlags())) {
+                        if ((edgeIter.getAdjNode() != prevNode) && !edgeIter.get(roundaboutEnc)) {
                             roundaboutInstruction.increaseExitNumber();
                             break;
                         }
@@ -390,7 +391,7 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
             forceInstruction = true;
         }
 
-        InstructionsOutgoingEdges outgoingEdges = new InstructionsOutgoingEdges(prevEdge, edge, encoder, crossingExplorer, nodeAccess, prevNode, baseNode, adjNode);
+        InstructionsOutgoingEdges outgoingEdges = new InstructionsOutgoingEdges(prevEdge, edge, encoder, maxSpeedEnc, crossingExplorer, nodeAccess, prevNode, baseNode, adjNode);
         int nrOfPossibleTurns = outgoingEdges.nrOfAllowedOutgoingEdges();
 
         // there is no other turn possible
@@ -429,9 +430,6 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
             return sign;
         }
 
-        IntsRef flag = edge.getFlags();
-        IntsRef prevFlag = prevEdge.getFlags();
-
         boolean outgoingEdgesAreSlower = outgoingEdges.outgoingEdgesAreSlowerByFactor(1);
 
         // There is at least one other possibility to turn, and we are almost going straight
@@ -446,12 +444,26 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
         // Happens a lot for trunk_links
         // For _links, comparing flags works quite good, as links usually have different speeds => different flags
         if (otherContinue != null) {
-            //We are at a fork
+            // We are at a fork
             if (!InstructionsHelper.isNameSimilar(name, prevName)
                     || InstructionsHelper.isNameSimilar(otherContinue.getName(), prevName)
-                    || !prevFlag.equals(flag)
-                    || prevFlag.equals(otherContinue.getFlags())
                     || !outgoingEdgesAreSlower) {
+
+                final RoadClass roadClass = edge.get(roadClassEnc);
+                final RoadClass prevRoadClass = prevEdge.get(roadClassEnc);
+                final RoadClass otherRoadClass = otherContinue.get(roadClassEnc);
+                final boolean link = edge.get(roadClassLinkEnc);
+                final boolean prevLink = prevEdge.get(roadClassLinkEnc);
+                final boolean otherLink = otherContinue.get(roadClassLinkEnc);
+                // We know this is a fork, but we only need an instruction if highways are actually changing,
+                // this approach only works for major roads, for minor roads it can be hard to differentiate easily in real life
+                if (roadClass == RoadClass.MOTORWAY || roadClass == RoadClass.TRUNK || roadClass == RoadClass.PRIMARY || roadClass == RoadClass.SECONDARY || roadClass == RoadClass.TERTIARY) {
+                    if ((roadClass == prevRoadClass && link == prevLink) && (otherRoadClass != prevRoadClass || otherLink != prevLink)) {
+                        return returnForcedInstructionOrIgnore(forceInstruction, sign);
+                    }
+                }
+
+
                 GHPoint tmpPoint = InstructionsHelper.getPointForOrientationCalculation(otherContinue, nodeAccess);
                 double otherDelta = InstructionsHelper.calculateOrientationDelta(prevLat, prevLon, tmpPoint.getLat(), tmpPoint.getLon(), prevOrientation);
 
diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java b/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java
index d9bcb75ad..e05ae0ba2 100644
--- a/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java
+++ b/core/src/main/java/com/graphhopper/routing/InstructionsOutgoingEdges.java
@@ -19,7 +19,6 @@ package com.graphhopper.routing;
 
 import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
-import com.graphhopper.routing.profiles.MaxSpeed;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.storage.IntsRef;
 import com.graphhopper.storage.NodeAccess;
@@ -62,12 +61,14 @@ class InstructionsOutgoingEdges {
     private final List<EdgeIteratorState> allowedOutgoingEdges;
     // All outgoing edges, including oneways in the wrong direction
     private final List<EdgeIteratorState> allOutgoingEdges;
-    private final DecimalEncodedValue speedEnc;
+    private final DecimalEncodedValue maxSpeedEnc;
+    private final DecimalEncodedValue avgSpeedEnc;
     private final NodeAccess nodeAccess;
 
     public InstructionsOutgoingEdges(EdgeIteratorState prevEdge,
                                      EdgeIteratorState currentEdge,
                                      FlagEncoder encoder,
+                                     DecimalEncodedValue maxSpeedEnc,
                                      EdgeExplorer crossingExplorer,
                                      NodeAccess nodeAccess,
                                      int prevNode,
@@ -76,7 +77,8 @@ class InstructionsOutgoingEdges {
         this.prevEdge = prevEdge;
         this.currentEdge = currentEdge;
         BooleanEncodedValue accessEnc = encoder.getAccessEnc();
-        this.speedEnc = encoder.getAverageSpeedEnc();
+        this.maxSpeedEnc = maxSpeedEnc;
+        this.avgSpeedEnc = encoder.getAverageSpeedEnc();
         this.nodeAccess = nodeAccess;
 
         EdgeIteratorState tmpEdge;
@@ -142,8 +144,15 @@ class InstructionsOutgoingEdges {
         return maxSurroundingSpeed * factor < pathSpeed;
     }
 
+    /**
+     * Will return the tagged maxspeed, if available, if not, we use the average speed
+     * TODO: Should we rely only on the tagged maxspeed?
+     */
     private double getSpeed(EdgeIteratorState edge) {
-        return edge.get(speedEnc);
+       double maxSpeed = edge.get(maxSpeedEnc);
+       if(Double.isInfinite(maxSpeed))
+           return edge.get(avgSpeedEnc);
+       return maxSpeed;
     }
 
     /**


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/PathTest.java b/core/src/test/java/com/graphhopper/routing/PathTest.java
index 7f4c19c21..c417c1806 100644
--- a/core/src/test/java/com/graphhopper/routing/PathTest.java
+++ b/core/src/test/java/com/graphhopper/routing/PathTest.java
@@ -18,9 +18,7 @@
 package com.graphhopper.routing;
 
 import com.graphhopper.reader.ReaderWay;
-import com.graphhopper.routing.profiles.BooleanEncodedValue;
-import com.graphhopper.routing.profiles.DecimalEncodedValue;
-import com.graphhopper.routing.profiles.Roundabout;
+import com.graphhopper.routing.profiles.*;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
@@ -587,6 +585,38 @@ public class PathTest {
         assertEquals(-7, wayList.get(1).getSign());
     }
 
+    @Test
+    public void testCalcInstructionForMotorwayFork() {
+        final Graph g = new GraphBuilder(carManager).create();
+        final NodeAccess na = g.getNodeAccess();
+
+        // Actual example: point=48.909071%2C8.647136&point=48.908789%2C8.649244
+        // 1-2 & 2-4 is a motorway, 2-3 is a motorway_link
+        // We should skip the instruction here
+        //      1 ---- 2 ---- 4
+        //              \
+        //               3
+        na.setNode(1, 48.909071,8.647136);
+        na.setNode(2, 48.908962,8.647978);
+        na.setNode(3, 48.908867,8.648155);
+        na.setNode(4, 48.908789, 8.649244);
+
+        EnumEncodedValue<RoadClass> roadClassEnc = carManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class);
+        BooleanEncodedValue roadClassLinkEnc = carManager.getBooleanEncodedValue(RoadClassLink.KEY);
+
+        g.edge(1, 2, 5, true).setName("A 8").set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false);
+        g.edge(2, 4, 5, true).setName("A 8").set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false);
+        g.edge(2, 3, 5, true).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, true);
+
+        ShortestWeighting weighting = new ShortestWeighting(encoder);
+        Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED)
+                .calcPath(1, 4);
+        assertTrue(p.isFound());
+        InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, carManager, tr);
+
+        assertEquals(2, wayList.size());
+    }
+
     @Test
     public void testCalcInstructionsEnterMotoway() {
         final Graph g = new GraphBuilder(carManager).create();
diff --git a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
index d7af64e9d..7e8034df3 100644
--- a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
@@ -99,13 +99,13 @@ public class GraphHopperIT {
 
         PathWrapper arsp = rsp.getBest();
         assertEquals(3437.6, arsp.getDistance(), .1);
-        assertEquals(87, arsp.getPoints().getSize());
+        assertEquals(86, arsp.getPoints().getSize());
 
         assertEquals(43.7276852, arsp.getWaypoints().getLat(0), 1e-7);
         assertEquals(43.7495432, arsp.getWaypoints().getLat(1), 1e-7);
 
         InstructionList il = arsp.getInstructions();
-        assertEquals(20, il.size());
+        assertEquals(15, il.size());
 
         // TODO roundabout fine tuning -> enter + leave roundabout (+ two roundabouts -> is it necessary if we do not leave the street?)
         Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.US);
@@ -129,7 +129,7 @@ public class GraphHopperIT {
         assertEquals(7, il.get(4).getTime() / 1000);
         assertEquals(30, il.get(5).getTime() / 1000);
 
-        assertEquals(87, arsp.getPoints().size());
+        assertEquals(86, arsp.getPoints().size());
     }
 
     @Test
@@ -145,7 +145,7 @@ public class GraphHopperIT {
         routeRsp = hopper.route(request);
 
         assertTrue("there should not be more points if instructions are disabled due to simplify but was " + withInstructionsPoints + " vs " + routeRsp.getBest().getPoints().size(),
-                withInstructionsPoints > routeRsp.getBest().getPoints().size());
+                withInstructionsPoints >= routeRsp.getBest().getPoints().size());
     }
 
     @Test
@@ -232,7 +232,7 @@ public class GraphHopperIT {
             assertNotEquals(sum, 0);
             assertTrue("Too many nodes visited " + sum, sum < 120);
             assertEquals(3437.6, bestPath.getDistance(), .1);
-            assertEquals(87, bestPath.getPoints().getSize());
+            assertEquals(86, bestPath.getPoints().getSize());
         }
 
         if (lm) {
@@ -249,7 +249,7 @@ public class GraphHopperIT {
             assertNotEquals(sum, 0);
             assertTrue("Too many nodes visited " + sum, sum < 120);
             assertEquals(3437.6, bestPath.getDistance(), .1);
-            assertEquals(87, bestPath.getPoints().getSize());
+            assertEquals(86, bestPath.getPoints().getSize());
         }
 
         // flexible
@@ -265,7 +265,7 @@ public class GraphHopperIT {
         assertNotEquals(sum, 0);
         assertTrue("Too few nodes visited " + sum, sum > 120);
         assertEquals(3437.6, bestPath.getDistance(), .1);
-        assertEquals(87, bestPath.getPoints().getSize());
+        assertEquals(86, bestPath.getPoints().getSize());
 
         tmpHopper.close();
     }
@@ -486,25 +486,25 @@ public class GraphHopperIT {
 
         PathWrapper arsp = rsp.getBest();
         assertEquals(6875.2, arsp.getDistance(), .1);
-        assertEquals(173, arsp.getPoints().getSize());
+        assertEquals(171, arsp.getPoints().getSize());
 
         InstructionList il = arsp.getInstructions();
-        assertEquals(36, il.size());
+        assertEquals(29, il.size());
         assertEquals("continue onto Avenue des Guelfes", il.get(0).getTurnDescription(tr));
         assertEquals("continue onto Avenue des Papalins", il.get(1).getTurnDescription(tr));
         assertEquals("turn sharp right onto Quai Jean-Charles Rey", il.get(4).getTurnDescription(tr));
         assertEquals("turn left", il.get(5).getTurnDescription(tr));
         assertEquals("turn right onto Avenue Albert II", il.get(6).getTurnDescription(tr));
 
-        assertEquals("waypoint 1", il.get(19).getTurnDescription(tr));
-        assertEquals(Instruction.U_TURN_UNKNOWN, il.get(20).getSign());
+        assertEquals("waypoint 1", il.get(14).getTurnDescription(tr));
+        assertEquals(Instruction.U_TURN_UNKNOWN, il.get(15).getSign());
 
-        assertEquals("continue onto Avenue Albert II", il.get(29).getTurnDescription(tr));
-        assertEquals("turn left", il.get(30).getTurnDescription(tr));
-        assertEquals("turn right onto Quai Jean-Charles Rey", il.get(31).getTurnDescription(tr));
-        assertEquals("turn sharp left onto Avenue des Papalins", il.get(32).getTurnDescription(tr));
-        assertEquals("continue onto Avenue des Guelfes", il.get(34).getTurnDescription(tr));
-        assertEquals("arrive at destination", il.get(35).getTurnDescription(tr));
+        assertEquals("continue onto Avenue Albert II", il.get(22).getTurnDescription(tr));
+        assertEquals("turn left", il.get(23).getTurnDescription(tr));
+        assertEquals("turn right onto Quai Jean-Charles Rey", il.get(24).getTurnDescription(tr));
+        assertEquals("turn sharp left onto Avenue des Papalins", il.get(25).getTurnDescription(tr));
+        assertEquals("continue onto Avenue des Guelfes", il.get(27).getTurnDescription(tr));
+        assertEquals("arrive at destination", il.get(28).getTurnDescription(tr));
 
         assertEquals(11, il.get(0).getDistance(), 1);
         assertEquals(97, il.get(1).getDistance(), 1);
@@ -999,7 +999,7 @@ public class GraphHopperIT {
         assertNotEquals(sum, 0);
         assertTrue("Too many nodes visited " + sum, sum < 120);
         assertEquals(3437.6, bestPath.getDistance(), .1);
-        assertEquals(87, bestPath.getPoints().getSize());
+        assertEquals(86, bestPath.getPoints().getSize());
 
         tmpHopper.close();
     }
@@ -1028,7 +1028,7 @@ public class GraphHopperIT {
         PathWrapper pw = rsp.getBest();
         assertEquals(1.49, rsp.getBest().getDistance() / 1000f, .01);
         assertEquals(19, rsp.getBest().getTime() / 1000f / 60, 1);
-        assertEquals(68, pw.getPoints().size());
+        assertEquals(67, pw.getPoints().size());
     }
 
     @Test
@@ -1103,7 +1103,7 @@ public class GraphHopperIT {
         assertTrue("Too many visited nodes for ch mode " + chSum, chSum < 60);
         PathWrapper bestPath = rsp.getBest();
         assertEquals(3587, bestPath.getDistance(), 1);
-        assertEquals(89, bestPath.getPoints().getSize());
+        assertEquals(90, bestPath.getPoints().getSize());
 
         // request flex mode
         req.setAlgorithm(Parameters.Algorithms.ASTAR_BI);
@@ -1115,7 +1115,7 @@ public class GraphHopperIT {
 
         bestPath = rsp.getBest();
         assertEquals(3587, bestPath.getDistance(), 1);
-        assertEquals(89, bestPath.getPoints().getSize());
+        assertEquals(90, bestPath.getPoints().getSize());
 
         // request hybrid mode
         req.getHints().put(Landmark.DISABLE, false);
@@ -1129,7 +1129,7 @@ public class GraphHopperIT {
 
         bestPath = rsp.getBest();
         assertEquals(3587, bestPath.getDistance(), 1);
-        assertEquals(89, bestPath.getPoints().getSize());
+        assertEquals(90, bestPath.getPoints().getSize());
 
         // combining hybrid & speed mode is currently not possible and should be avoided: #1082
     }
@@ -1318,8 +1318,8 @@ public class GraphHopperIT {
         final String adamSeiler = "Adam-Seiler-Straße";
         final String friedhof = "Friedhofsweg";
         assertCurbsidesPath(h, p, q, asList(CURBSIDE_RIGHT, CURBSIDE_RIGHT), 344, asList(itz, rotmain, rotmain));
-        assertCurbsidesPath(h, p, q, asList(CURBSIDE_RIGHT, CURBSIDE_LEFT), 1564, asList(itz, rotmain, rotmain, bayreuth, kulmbach, adamSeiler, adamSeiler, friedhof, kulmbach, rotmain));
-        assertCurbsidesPath(h, p, q, asList(CURBSIDE_LEFT, CURBSIDE_RIGHT), 1199, asList(itz, bayreuth, kulmbach, adamSeiler, adamSeiler, friedhof, kulmbach, itz, rotmain, rotmain));
+        assertCurbsidesPath(h, p, q, asList(CURBSIDE_RIGHT, CURBSIDE_LEFT), 1564, asList(itz, rotmain, rotmain, bayreuth, adamSeiler, adamSeiler, friedhof, kulmbach, rotmain));
+        assertCurbsidesPath(h, p, q, asList(CURBSIDE_LEFT, CURBSIDE_RIGHT), 1199, asList(itz, bayreuth, adamSeiler, adamSeiler, friedhof, kulmbach, itz, rotmain, rotmain));
         assertCurbsidesPath(h, p, q, asList(CURBSIDE_LEFT, CURBSIDE_LEFT), 266, asList(itz, bayreuth, rotmain));
         // without restricting anything we get the shortest path
         assertCurbsidesPath(h, p, q, asList(CURBSIDE_ANY, CURBSIDE_ANY), 266, asList(itz, bayreuth, rotmain));
@@ -1341,8 +1341,8 @@ public class GraphHopperIT {
 
         // when going from p to p and one curbside is right and the other is left, we expect driving a loop back to
         // where we came from
-        assertCurbsidesPath(h, p, p, asList(CURBSIDE_RIGHT, CURBSIDE_LEFT), 1908, asList(itz, rotmain, rotmain, bayreuth, kulmbach, adamSeiler, adamSeiler, friedhof, kulmbach, rotmain, rotmain, itz));
-        assertCurbsidesPath(h, p, p, asList(CURBSIDE_LEFT, CURBSIDE_RIGHT), 855, asList(itz, bayreuth, kulmbach, adamSeiler, adamSeiler, friedhof, kulmbach, itz));
+        assertCurbsidesPath(h, p, p, asList(CURBSIDE_RIGHT, CURBSIDE_LEFT), 1908, asList(itz, rotmain, rotmain, bayreuth, adamSeiler, adamSeiler, friedhof, kulmbach, rotmain, rotmain, itz));
+        assertCurbsidesPath(h, p, p, asList(CURBSIDE_LEFT, CURBSIDE_RIGHT), 855, asList(itz, bayreuth, adamSeiler, adamSeiler, friedhof, kulmbach, itz));
     }
 
     @Test
diff --git a/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
index 7603fcd1c..f297f0214 100644
--- a/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
@@ -190,11 +190,11 @@ public class RouteResourceTest {
         assertTrue("distance wasn't correct:" + arsp.getDistance(), arsp.getDistance() < 21000);
 
         InstructionList instructions = arsp.getInstructions();
-        assertEquals(25, instructions.size());
+        assertEquals(24, instructions.size());
         assertEquals("Continue onto la Callisa", instructions.get(0).getTurnDescription(null));
         assertEquals("At roundabout, take exit 2", instructions.get(4).getTurnDescription(null));
         assertEquals(true, instructions.get(4).getExtraInfoJSON().get("exited"));
-        assertEquals(false, instructions.get(23).getExtraInfoJSON().get("exited"));
+        assertEquals(false, instructions.get(22).getExtraInfoJSON().get("exited"));
     }
 
     @Test