Repository: graphhopper/graphhopper
Pull request #2222 (2020-12-21 21:55:52)

Simplify edge geometry before the distance is calculated
During import we calculate `edge.getDistance()` before we simplify the edge geometry. Therefore the edge distance can be different to the distance we would calculate from the edge geometry:

```java
edge.getDistance() != new DistanceCalcEarth().calcDistance(edge.fetchWayGeometry(FetchMode.ALL));
```

This doesn't seem right, so in this PR I moved the edge simplification in front of the distance calculation.

easbar: To me the biggest question is whether the edge simplification should run before or after the long edge sampling. Or even before the elevation smoothing? @msbarry can you comment?
otbutz: Simplification boils down to loss of precision. There _might_ be corner cases where we pick up wrong elevation data because we work with simplified paths.
msbarry: > To me the biggest question is whether the edge simplification should run before or after the long edge sampling. Or even before the elevation smoothing? @msbarry can you comment? Probably it should go before the long edge sampling otherwise we might not sample a long edge, because it only turns out to be a long edge during the simplification?

Edge simplification should run after long edge sampling, since long edge sampling inserts elevation detail on long straight edges that influences simplification (since simplification is now elevation-aware), see the example before/after elevation profile here https://github.com/graphhopper/graphhopper/pull/1953#issue-387694399. 

It seems safe to calculate the edge distance before simplification since we use such a tight threshold is used to simplify before storing edge geometries, especially if the test diffs are so small.
easbar: Thanks for your response!

> Edge simplification should run after long edge sampling, 

Ok you mean first we should add all possible sample points and then simplify afterwards? That sounds reasonable, but actually I do not even expect much of a difference quality-wise(?). When we simplify first this just means some points might get removed, but then we actually add points again during the long edge sampling. Initially I was concerned that if we ran the sampling first we might miss adding some elevation details, because the long edge sampling would not run for all edges. But contrary to what I thought the long edge sampling always runs (and not just on long edges)? So yes let's run the sampling before the simplification (this is the current state of this PR anyway).

> It seems safe to calculate the edge distance before simplification since we use such a tight threshold is used to simplify before storing edge geometries, especially if the test diffs are so small.

Do you mean we should or should not calculate the edge distance before the simplification?

And by the way two more questions:

1) We long-edge-sample with a fixed interval. Would it not make sense to e.g. sample with a higher frequency but only keep the points with significant elevation changes (so we hopefully cover all elevation details)? Hm but as long as the simplification runs afterwards we can probably add as many points we want, because the simplification will remove those that are unnecessary. Maybe this was the idea?

2) Do you remember what the `wayOsmId` parameter here: https://github.com/graphhopper/graphhopper/blob/bde99a8ccd143f3852b3bc87b3bcb8b5328f8f0f/core/src/main/java/com/graphhopper/reader/dem/EdgeSampling.java#L37 was for? It seems unused, can we remove it?
msbarry: 
> Ok you mean first we should add all possible sample points and then simplify afterwards?

Yes, the long edge sampling is just meant to keep a minimum distance between elevation samples along all edges (it just has a shortcut to avoid touching short edges), then 3d-aware simplification throws out any edge samples that really aren't needed to retain elevation details in the profile.  If we don't simplify after the long edge sampling, we'll keep too much extra data around.

> Do you mean we should or should not calculate the edge distance before the simplification?

It seems safe to calculate the edge distance before simplification like you have already in this PR.

> 1. We long-edge-sample with a fixed interval. Would it not make sense to e.g. sample with a higher frequency but only keep the points with significant elevation changes (so we hopefully cover all elevation details)? Hm but as long as the simplification runs afterwards we can probably add as many points we want, because the simplification will remove those that are unnecessary. Maybe this was the idea?

Yes exactly, they need to run together. Long edge sampling adds in detail, then simplification throws out unnecessary detail.

> 2. Do you remember what the `wayOsmId` parameter here: https://github.com/graphhopper/graphhopper/blob/bde99a8ccd143f3852b3bc87b3bcb8b5328f8f0f/core/src/main/java/com/graphhopper/reader/dem/EdgeSampling.java#L37
>     was for? It seems unused, can we remove it?

Oops, I must have had it in there for debug logging. Looks like it's not used anymore, so safe to remove. Sorry about that!

otbutz: > When we simplify first this just means some points might get removed, but then we actually add points again during the long edge sampling.

Removing points changes the path of the edge. In extreme cases the long edge sampling _might_ pick up completely wrong elevation data because the inserted points are not part of the original edge. But i'm not sure if we really run into this kind of problems as we don't simplify that hard and our elevation data usually has low resolution.

![grafik](https://user-images.githubusercontent.com/22315436/102884530-082b6200-4452-11eb-96d4-be56e5266869.png)

easbar: Ok

> It seems safe to calculate the edge distance before simplification like you have already in this PR.

This whole PR is about calculating the edge distance **after** the simplification, but I guess this is just a typo :) 

> Removing points changes the path of the edge.

Yes sure. When we first sample the elevation details the edges will be protected from over-simplification. And yes the simplification threshold is very small anyway (1m) and it cannot really be larger so we keep details like sharp turns of footways or roundabouts etc.

msbarry: 
> This whole PR is about calculating the edge distance **after** the simplification, but I guess this is just a typo :) 

Sorry yes. That's what I meant!

> In extreme cases the long edge sampling _might_ pick up completely wrong elevation data because the inserted points are not part of the original edge. But i'm not sure if we really run into this kind of problems as we don't simplify that hard and our elevation data usually has low resolution.


"Long edge sampling" might be a bad name. We only add intermediate points along long edge segments, but never actually change the shape of a line. "Interpolate" might make more sense here? I think QGis calls it "points along geometry?"
easbar: @karussell any objections? I think this is ready to merge.

Patch:
diff --git a/core/src/main/java/com/graphhopper/reader/dem/EdgeSampling.java b/core/src/main/java/com/graphhopper/reader/dem/EdgeSampling.java
index 29b206b03..47f440659 100644
--- a/core/src/main/java/com/graphhopper/reader/dem/EdgeSampling.java
+++ b/core/src/main/java/com/graphhopper/reader/dem/EdgeSampling.java
@@ -34,7 +34,7 @@ public class EdgeSampling {
 
     private EdgeSampling() {}
 
-    public static PointList sample(long wayOsmId, PointList input, double maxDistance, DistanceCalc distCalc, ElevationProvider elevation) {
+    public static PointList sample(PointList input, double maxDistance, DistanceCalc distCalc, ElevationProvider elevation) {
         PointList output = new PointList(input.getSize() * 2, input.is3D());
         if (input.isEmpty()) return output;
         int nodes = input.getSize();
diff --git a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java
index c1b4c12f8..3f2cf9f79 100644
--- a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java
+++ b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java
@@ -38,7 +38,7 @@ public class RoutingExampleTC {
         GHRequest req = new GHRequest(42.50822, 1.533966, 42.506899, 1.525372).
                 setCurbsides(Arrays.asList(CURBSIDE_ANY, CURBSIDE_RIGHT)).
                 setProfile("car");
-        route(hopper, req, 1730, 112_000);
+        route(hopper, req, 1729, 112_000);
     }
 
     public static void routeWithTurnCostsAndOtherUTurnCosts(GraphHopper hopper) {
diff --git a/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java b/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java
index 8efcf413e..cc9ff3846 100644
--- a/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java
+++ b/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java
@@ -670,7 +670,10 @@ public class OSMReader implements DataReader, TurnCostParser.ExternalInternalMap
 
         // sample points along long edges
         if (this.longEdgeSamplingDistance < Double.MAX_VALUE && pointList.is3D())
-            pointList = EdgeSampling.sample(wayOsmId, pointList, longEdgeSamplingDistance, distCalc, eleProvider);
+            pointList = EdgeSampling.sample(pointList, longEdgeSamplingDistance, distCalc, eleProvider);
+
+        if (doSimplify && pointList.size() > 2)
+            simplifyAlgo.simplify(pointList);
 
         double towerNodeDistance = distCalc.calcDistance(pointList);
 
@@ -696,9 +699,6 @@ public class OSMReader implements DataReader, TurnCostParser.ExternalInternalMap
 
         EdgeIteratorState iter = graph.edge(fromIndex, toIndex).setDistance(towerNodeDistance).setFlags(flags);
 
-        if (doSimplify && pointList.size() > 2)
-            simplifyAlgo.simplify(pointList);
-
         // If the entire way is just the first and last point, do not waste space storing an empty way geometry
         if (pointList.size() > 2)
             iter.setWayGeometry(pointList.shallowCopy(1, pointList.size() - 1, false));


Test patch:
diff --git a/core/src/test/java/com/graphhopper/reader/dem/EdgeSamplingTest.java b/core/src/test/java/com/graphhopper/reader/dem/EdgeSamplingTest.java
index 75c8131ac..a32baa211 100644
--- a/core/src/test/java/com/graphhopper/reader/dem/EdgeSamplingTest.java
+++ b/core/src/test/java/com/graphhopper/reader/dem/EdgeSamplingTest.java
@@ -39,7 +39,6 @@ public class EdgeSamplingTest {
         in.add(1.4, 0, 0);
 
         PointList out = EdgeSampling.sample(
-                1,
                 in,
                 DistanceCalcEarth.METERS_PER_DEGREE,
                 new DistanceCalcEarth(),
@@ -56,7 +55,6 @@ public class EdgeSamplingTest {
         in.add(0.8, 0, 0);
 
         PointList out = EdgeSampling.sample(
-                1,
                 in,
                 DistanceCalcEarth.METERS_PER_DEGREE / 2,
                 new DistanceCalcEarth(),
@@ -73,7 +71,6 @@ public class EdgeSamplingTest {
         in.add(0.8, 0, 0);
 
         PointList out = EdgeSampling.sample(
-                1,
                 in,
                 DistanceCalcEarth.METERS_PER_DEGREE / 3,
                 new DistanceCalcEarth(),
@@ -90,7 +87,6 @@ public class EdgeSamplingTest {
         in.add(0.75, 0, 0);
 
         PointList out = EdgeSampling.sample(
-                1,
                 in,
                 DistanceCalcEarth.METERS_PER_DEGREE / 4,
                 new DistanceCalcEarth(),
@@ -107,7 +103,6 @@ public class EdgeSamplingTest {
         in.add(0.0, 178.5, 0);
 
         PointList out = EdgeSampling.sample(
-                1,
                 in,
                 DistanceCalcEarth.METERS_PER_DEGREE,
                 new DistanceCalcEarth(),
@@ -124,7 +119,6 @@ public class EdgeSamplingTest {
         in.add(88.5, 90, 0);
 
         PointList out = EdgeSampling.sample(
-                1,
                 in,
                 DistanceCalcEarth.METERS_PER_DEGREE,
                 new DistanceCalcEarth(),
diff --git a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java
index 043a8652c..4e1418c50 100644
--- a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java
+++ b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java
@@ -87,7 +87,7 @@ public class GraphHopperMultimodalIT {
         assertThat(firstTransitSolution.getLegs().get(0).getArrivalTime().toInstant())
                 .isEqualTo(firstTransitSolution.getLegs().get(1).getDepartureTime().toInstant());
         assertThat(firstTransitSolution.getLegs().get(2).getArrivalTime().toInstant().atZone(zoneId).toLocalTime())
-                .isEqualTo(LocalTime.parse("06:52:02.729"));
+                .isEqualTo(LocalTime.parse("06:52:02.633"));
     }
 
     @Test
@@ -109,13 +109,13 @@ public class GraphHopperMultimodalIT {
         assertThat(firstTransitSolution.getLegs().get(0).getArrivalTime().toInstant())
                 .isEqualTo(firstTransitSolution.getLegs().get(1).getDepartureTime().toInstant());
         assertThat(firstTransitSolution.getLegs().get(2).getArrivalTime().toInstant().atZone(zoneId).toLocalTime())
-                .isEqualTo(LocalTime.parse("06:52:02.729"));
+                .isEqualTo(LocalTime.parse("06:52:02.633"));
 
         assertThat(firstTransitSolution.getLegs().get(0).distance + firstTransitSolution.getLegs().get(2).distance)
-                .isEqualTo(497.0809678713282); // Total walking distance
+                .isEqualTo(496.9469678713282); // Total walking distance
         List<PathDetail> distances = firstTransitSolution.getPathDetails().get("distance");
         assertThat(distances.stream().mapToDouble(d -> (double) d.getValue()).sum())
-                .isEqualTo(497.0809678713282); // Also total walking distance -- PathDetails only cover access/egress for now
+                .isEqualTo(496.9469678713282); // Also total walking distance -- PathDetails only cover access/egress for now
         assertThat(distances.get(0).getFirst()).isEqualTo(0); // PathDetails start and end with PointList
         assertThat(distances.get(distances.size()-1).getLast()).isEqualTo(firstTransitSolution.getPoints().size()-1);
 
@@ -125,7 +125,7 @@ public class GraphHopperMultimodalIT {
         // In principle, this would dominate the transit solution, since it's faster, but
         // by default, walking gets a slight penalty.
         assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime())
-                .isEqualTo(LocalTime.parse("06:51:10.370"));
+                .isEqualTo(LocalTime.parse("06:51:10.335"));
         assertThat(walkSolution.getLegs().size()).isEqualTo(1);
         assertThat(walkSolution.getNumChanges()).isEqualTo(-1);
 
@@ -165,7 +165,7 @@ public class GraphHopperMultimodalIT {
         assertThat(walkSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime())
                 .isEqualTo(LocalTime.parse("06:40"));
         assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime())
-                .isEqualTo(LocalTime.parse("06:41:07.031"));
+                .isEqualTo(LocalTime.parse("06:41:07.028"));
         assertThat(walkSolution.getLegs().size()).isEqualTo(1);
         assertThat(walkSolution.getNumChanges()).isEqualTo(-1);
     }
@@ -186,7 +186,7 @@ public class GraphHopperMultimodalIT {
         assertThat(walkSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime())
                 .isEqualTo(LocalTime.parse("06:40"));
         assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime())
-                .isEqualTo(LocalTime.parse("06:41:07.031"));
+                .isEqualTo(LocalTime.parse("06:41:07.028"));
         assertThat(walkSolution.getLegs().size()).isEqualTo(1);
         assertThat(walkSolution.getNumChanges()).isEqualTo(-1);
     }
diff --git a/reader-osm/src/test/java/com/graphhopper/GraphHopperTest.java b/reader-osm/src/test/java/com/graphhopper/GraphHopperTest.java
index ef7e7d7e5..688cbb38a 100644
--- a/reader-osm/src/test/java/com/graphhopper/GraphHopperTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/GraphHopperTest.java
@@ -114,8 +114,8 @@ public class GraphHopperTest {
         assertEquals(expectedVisitedNodes, rsp.getHints().getLong("visited_nodes.sum", 0));
 
         ResponsePath res = rsp.getBest();
-        assertEquals(3587.9, res.getDistance(), .1);
-        assertEquals(277173, res.getTime(), 10);
+        assertEquals(3586.9, res.getDistance(), .1);
+        assertEquals(277112, res.getTime(), 10);
         assertEquals(91, res.getPoints().getSize());
 
         assertEquals(43.7276852, res.getWaypoints().getLat(0), 1e-7);
@@ -137,10 +137,10 @@ public class GraphHopperTest {
                 setAlgorithm(ASTAR).setProfile(profile));
 
         // identify the number of counts to compare with CH foot route
-        assertEquals(699, rsp.getHints().getLong("visited_nodes.sum", 0));
+        assertEquals(700, rsp.getHints().getLong("visited_nodes.sum", 0));
 
         ResponsePath res = rsp.getBest();
-        assertEquals(3437.6, res.getDistance(), .1);
+        assertEquals(3437.1, res.getDistance(), .1);
         assertEquals(85, res.getPoints().getSize());
 
         assertEquals(43.7276852, res.getWaypoints().getLat(0), 1e-7);
@@ -280,7 +280,7 @@ public class GraphHopperTest {
             long sum = rsp.getHints().getLong("visited_nodes.sum", 0);
             assertNotEquals(sum, 0);
             assertTrue("Too many nodes visited " + sum, sum < 120);
-            assertEquals(3437.6, bestPath.getDistance(), .1);
+            assertEquals(3437.1, bestPath.getDistance(), .1);
             assertEquals(85, bestPath.getPoints().getSize());
         }
 
@@ -296,7 +296,7 @@ public class GraphHopperTest {
             long sum = rsp.getHints().getLong("visited_nodes.sum", 0);
             assertNotEquals(sum, 0);
             assertTrue("Too many nodes visited " + sum, sum < 120);
-            assertEquals(3437.6, bestPath.getDistance(), .1);
+            assertEquals(3437.1, bestPath.getDistance(), .1);
             assertEquals(85, bestPath.getPoints().getSize());
         }
 
@@ -311,7 +311,7 @@ public class GraphHopperTest {
         long sum = rsp.getHints().getLong("visited_nodes.sum", 0);
         assertNotEquals(sum, 0);
         assertTrue("Too few nodes visited " + sum, sum > 120);
-        assertEquals(3437.6, bestPath.getDistance(), .1);
+        assertEquals(3437.1, bestPath.getDistance(), .1);
         assertEquals(85, bestPath.getPoints().getSize());
 
         hopper.close();
@@ -361,7 +361,7 @@ public class GraphHopperTest {
         assertEquals(2, rsp.getAll().size());
 
         assertEquals(1310, rsp.getAll().get(0).getTime() / 1000);
-        assertEquals(1432, rsp.getAll().get(1).getTime() / 1000);
+        assertEquals(1431, rsp.getAll().get(1).getTime() / 1000);
 
         req.putHint("alternative_route.max_paths", 3);
         req.putHint("alternative_route.min_plateau_factor", 0.1);
@@ -370,7 +370,7 @@ public class GraphHopperTest {
         assertEquals(3, rsp.getAll().size());
 
         assertEquals(1310, rsp.getAll().get(0).getTime() / 1000);
-        assertEquals(1432, rsp.getAll().get(1).getTime() / 1000);
+        assertEquals(1431, rsp.getAll().get(1).getTime() / 1000);
         assertEquals(1492, rsp.getAll().get(2).getTime() / 1000);
     }
 
@@ -393,11 +393,11 @@ public class GraphHopperTest {
 
         assertEquals(3, rsp.getAll().size());
         // via ramsenthal
-        assertEquals(2864, rsp.getAll().get(0).getTime() / 1000);
+        assertEquals(2863, rsp.getAll().get(0).getTime() / 1000);
         // via unterwaiz
         assertEquals(3318, rsp.getAll().get(1).getTime() / 1000);
         // via eselslohe -> theta; BTW: here smaller time as 2nd alternative due to priority influences time order
-        assertEquals(3094, rsp.getAll().get(2).getTime() / 1000);
+        assertEquals(3093, rsp.getAll().get(2).getTime() / 1000);
     }
 
     @Test
@@ -524,7 +524,7 @@ public class GraphHopperTest {
         req.putHint(Routing.BLOCK_AREA, "50.017578,11.547527;" + someArea);
         rsp = hopper.route(req);
         assertFalse(rsp.getErrors().toString(), rsp.hasErrors());
-        assertEquals(14602, rsp.getBest().getDistance(), 1);
+        assertEquals(14601, rsp.getBest().getDistance(), 1);
 
         // block by edge IDs -> i.e. use small circular area
         req.putHint(Routing.BLOCK_AREA, "49.979929,11.520066,200");
@@ -596,7 +596,7 @@ public class GraphHopperTest {
                 setAlgorithm(ASTAR).setProfile(profile));
 
         ResponsePath res = rsp.getBest();
-        assertEquals(6875.2, res.getDistance(), .1);
+        assertEquals(6874.2, res.getDistance(), .1);
         assertEquals(170, res.getPoints().getSize());
 
         InstructionList il = res.getInstructions();
@@ -887,7 +887,7 @@ public class GraphHopperTest {
                 setAlgorithm(ASTAR).setProfile(profile));
 
         ResponsePath res = rsp.getBest();
-        assertEquals(1625.4, res.getDistance(), .1);
+        assertEquals(1614.3, res.getDistance(), .1);
         assertEquals(55, res.getPoints().getSize());
         assertTrue(res.getPoints().is3D());
 
@@ -1015,7 +1015,7 @@ public class GraphHopperTest {
                 setAlgorithm(ASTAR).setProfile(profile));
 
         ResponsePath arsp = rsp.getBest();
-        assertEquals(1570.4, arsp.getDistance(), .1);
+        assertEquals(1569.7, arsp.getDistance(), .1);
         assertEquals(60, arsp.getPoints().getSize());
         assertTrue(arsp.getPoints().is3D());
 
@@ -1086,7 +1086,7 @@ public class GraphHopperTest {
                 setProfile(profile2));
         assertFalse(rsp.hasErrors());
         ResponsePath res = rsp.getBest();
-        assertEquals(6932.2, res.getDistance(), .1);
+        assertEquals(6931.8, res.getDistance(), .1);
         assertEquals(103, res.getPoints().getSize());
 
         InstructionList il = res.getInstructions();
@@ -1218,7 +1218,7 @@ public class GraphHopperTest {
         ResponsePath res = rsp.getBest();
         assertFalse("car routing for " + str + " should not have errors:" + rsp.getErrors(), rsp.hasErrors());
         assertEquals(207, res.getTime() / 1000f, 1);
-        assertEquals(2838, res.getDistance(), 1);
+        assertEquals(2837, res.getDistance(), 1);
 
         rsp = hopper.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826)
                 .setProfile("profile1"));
@@ -1271,7 +1271,7 @@ public class GraphHopperTest {
         long sum = rsp.getHints().getLong("visited_nodes.sum", 0);
         assertNotEquals(sum, 0);
         assertTrue("Too many nodes visited " + sum, sum < 120);
-        assertEquals(3437.6, bestPath.getDistance(), .1);
+        assertEquals(3437.0, bestPath.getDistance(), .1);
         assertEquals(85, bestPath.getPoints().getSize());
 
         hopper.close();
@@ -1451,17 +1451,17 @@ public class GraphHopperTest {
 
         // flex
         testCrossQueryAssert(profile1, hopper, 528.3, 152, true);
-        testCrossQueryAssert(profile2, hopper, 636.0, 150, true);
-        testCrossQueryAssert(profile3, hopper, 815.4, 146, true);
+        testCrossQueryAssert(profile2, hopper, 635.8, 150, true);
+        testCrossQueryAssert(profile3, hopper, 815.2, 146, true);
 
         // LM (should be the same as flex, but with less visited nodes!)
         testCrossQueryAssert(profile1, hopper, 528.3, 74, false);
-        testCrossQueryAssert(profile2, hopper, 636.0, 84, false);
+        testCrossQueryAssert(profile2, hopper, 635.8, 84, false);
         // this is actually interesting: the number of visited nodes *increases* once again (while it strictly decreases
         // with rising distance factor for flex): cross-querying 'works', but performs *worse*, because the landmarks
         // were not customized for the weighting in use. Creating a separate LM preparation for profile3 yields 74
         // instead of 124 visited nodes (not shown here)
-        testCrossQueryAssert(profile3, hopper, 815.4, 128, false);
+        testCrossQueryAssert(profile3, hopper, 815.2, 128, false);
     }
 
     private void testCrossQueryAssert(String profile, GraphHopper hopper, double expectedWeight, int expectedVisitedNodes, boolean disableLM) {
@@ -1679,9 +1679,9 @@ public class GraphHopperTest {
         assertEquals(1995.38, pathLM.getDistance(), 0.1);
         assertEquals(1995.38, path.getDistance(), 0.1);
 
-        assertEquals(149496, pathCH.getTime());
-        assertEquals(149496, pathLM.getTime());
-        assertEquals(149496, path.getTime());
+        assertEquals(149494, pathCH.getTime());
+        assertEquals(149494, pathLM.getTime());
+        assertEquals(149494, path.getTime());
     }
 
     @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 a0f24844b..4e26d53de 100644
--- a/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
@@ -511,7 +511,7 @@ public class RouteResourceTest {
         assertEquals(200, response.getStatus());
         String str = response.readEntity(String.class);
         // For backward compatibility we currently export route and track.
-        assertTrue(str.contains("<gh:distance>1841.8</gh:distance>"));
+        assertTrue(str.contains("<gh:distance>1841.5</gh:distance>"), str);
         assertFalse(str.contains("<wpt lat=\"42.51003\" lon=\"1.548188\"> <name>Finish!</name></wpt>"));
         assertTrue(str.contains("<trkpt lat=\"42.554839\" lon=\"1.536374\"><time>"));
     }