Repository: graphhopper/graphhopper (java)

Issue:
Add support for restriction value 'destination' for car
From: https://discuss.graphhopper.com/t/restrictedvalues-destination/475

The access restriction value destination is not regarded. A high penalty has to be applied when changing from an unrestricted way to a way with destination restriction.

Test case 1: Legal path avoids the hamlet Heitersberg and will make a ~20km detour.
https://graphhopper.com/maps/?point=47.414905%2C8.343494&point=47.415366%2C8.346804&locale=en-US&vehicle=car&weighting=fastest&elevation=true&layer=Omniscale

Test case 2: Fastest path is through Bellikon 
https://graphhopper.com/maps/?point=47.36249%2C8.367419&point=47.415231%2C8.346503&locale=en-US&vehicle=car&weighting=fastest&elevation=true&layer=Omniscale


Pull request description:
destination support for car that doesn't modify the speed
fixes #733

Basically it moves the priorityEncoder to the CarFlagEncoder and fills it with bad priority if e.g. `vehicle` or `motor_vehicle` tag is `destination`

Patch:
diff --git a/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java
index 0d91d03fd..a9e8f7fe3 100644
--- a/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java
@@ -41,8 +41,6 @@ public class CarFlagEncoder extends AbstractFlagEncoder {
     // This value determines the maximal possible on roads with bad surfaces
     protected int badSurfaceSpeed;
 
-    // This value determines the speed for roads with access=destination
-    protected int destinationSpeed;
     protected boolean speedTwoDirections;
     /**
      * A map which associates string to speed. Get some impression:
@@ -143,7 +141,6 @@ public class CarFlagEncoder extends AbstractFlagEncoder {
 
         // limit speed on bad surfaces to 30 km/h
         badSurfaceSpeed = 30;
-        destinationSpeed = 5;
         maxPossibleSpeed = 140;
         speedDefault = defaultSpeedMap.get("secondary");
 
@@ -280,14 +277,6 @@ public class CarFlagEncoder extends AbstractFlagEncoder {
                 setSpeed(true, edgeFlags, ferrySpeed);
         }
 
-        for (String restriction : restrictions) {
-            if (way.hasTag(restriction, "destination")) {
-                // This is problematic as Speed != Time
-                setSpeed(false, edgeFlags, destinationSpeed);
-                if (speedTwoDirections)
-                    setSpeed(true, edgeFlags, destinationSpeed);
-            }
-        }
         return edgeFlags;
     }
 


Test patch:
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java
index 6f29938a6..54568ec9a 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java
@@ -17,6 +17,8 @@
  */
 package com.graphhopper.routing.weighting;
 
+import com.graphhopper.routing.profiles.EnumEncodedValue;
+import com.graphhopper.routing.profiles.RoadAccess;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.util.EdgeIteratorState;
@@ -39,12 +41,21 @@ public class FastestWeighting extends AbstractWeighting {
     private final double headingPenalty;
     private final long headingPenaltyMillis;
     private final double maxSpeed;
+    private EnumEncodedValue<RoadAccess> roadAccessEnc = null;
+    // this factor puts a penalty on roads with a "destination"-only access, see #733
+    private double roadAccessPenalty;
 
     public FastestWeighting(FlagEncoder encoder, PMap map) {
         super(encoder);
         headingPenalty = map.getDouble(Routing.HEADING_PENALTY, Routing.DEFAULT_HEADING_PENALTY);
         headingPenaltyMillis = Math.round(headingPenalty * 1000);
         maxSpeed = encoder.getMaxSpeed() / SPEED_CONV;
+
+        if (encoder.hasEncodedValue(RoadAccess.KEY)) {
+            // ensure that we do not need to change getMinWeight, i.e. road_access_factor >= 1
+            roadAccessPenalty = checkBounds("road_access_factor", map.getDouble("road_access_factor", 10), 1, 10);
+            roadAccessEnc = encoder.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class);
+        }
     }
 
     public FastestWeighting(FlagEncoder encoder) {
@@ -64,6 +75,9 @@ public class FastestWeighting extends AbstractWeighting {
 
         double time = edge.getDistance() / speed * SPEED_CONV;
 
+        if (roadAccessEnc != null && edge.get(roadAccessEnc) == RoadAccess.DESTINATION)
+            time *= roadAccessPenalty;
+
         // add direction penalties at start/stop/via points
         boolean unfavoredEdge = edge.get(EdgeIteratorState.UNFAVORED_EDGE);
         if (unfavoredEdge)
@@ -83,6 +97,13 @@ public class FastestWeighting extends AbstractWeighting {
         return time + super.calcMillis(edgeState, reverse, prevOrNextEdgeId);
     }
 
+    static double checkBounds(String key, double val, double from, double to) {
+        if (val < from || val > to)
+            throw new IllegalArgumentException(key + " has invalid range should be within [" + from + ", " + to + "]");
+
+        return val;
+    }
+
     @Override
     public String getName() {
         return "fastest";
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java
index eb6a8fc1c..03f151e3b 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java
@@ -37,11 +37,10 @@ public class ShortFastestWeighting extends FastestWeighting {
 
     public ShortFastestWeighting(FlagEncoder encoder, PMap map) {
         super(encoder);
-        timeFactor = checkBounds(TIME_FACTOR, map.getDouble(TIME_FACTOR, 1));
+        timeFactor = checkBounds(TIME_FACTOR, map.getDouble(TIME_FACTOR, 1), 0, 10);
 
-        // is it faster to include timeFactor via distanceFactor = tmp / timeFactor?
         // default value derived from the cost for time e.g. 25€/hour and for distance 0.5€/km
-        distanceFactor = checkBounds(DISTANCE_FACTOR, map.getDouble(DISTANCE_FACTOR, 0.07));
+        distanceFactor = checkBounds(DISTANCE_FACTOR, map.getDouble(DISTANCE_FACTOR, 0.07), 0, 10);
 
         if (timeFactor < 1e-5 && distanceFactor < 1e-5)
             throw new IllegalArgumentException("[" + NAME + "] one of distance_factor or time_factor has to be non-zero");
@@ -49,7 +48,7 @@ public class ShortFastestWeighting extends FastestWeighting {
 
     public ShortFastestWeighting(FlagEncoder encoder, double distanceFactor) {
         super(encoder);
-        this.distanceFactor = checkBounds(DISTANCE_FACTOR, distanceFactor);
+        this.distanceFactor = checkBounds(DISTANCE_FACTOR, distanceFactor, 0, 10);
         this.timeFactor = 1;
     }
 
@@ -68,11 +67,4 @@ public class ShortFastestWeighting extends FastestWeighting {
     public String getName() {
         return NAME;
     }
-
-    private double checkBounds(String key, double val) {
-        if (val < 0 || val > 10)
-            throw new IllegalArgumentException(key + " has invalid range should be within [0, 10]");
-
-        return val;
-    }
 }
diff --git a/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java b/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java
index 957260e60..43dd62a4f 100644
--- a/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java
@@ -19,15 +19,20 @@ package com.graphhopper.routing.util;
 
 import com.graphhopper.reader.ReaderNode;
 import com.graphhopper.reader.ReaderWay;
-import com.graphhopper.routing.profiles.*;
+import com.graphhopper.routing.profiles.BooleanEncodedValue;
+import com.graphhopper.routing.profiles.DecimalEncodedValue;
+import com.graphhopper.routing.profiles.EncodedValue;
+import com.graphhopper.routing.profiles.Roundabout;
+import com.graphhopper.routing.util.parsers.OSMRoadAccessParser;
+import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.storage.IntsRef;
+import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.PMap;
 import org.junit.Test;
 
 import java.text.DateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 
@@ -37,8 +42,10 @@ import static org.junit.Assert.*;
  * @author Peter Karich
  */
 public class CarFlagEncoderTest {
-    private final EncodingManager em = EncodingManager.create(Arrays.asList(new CarFlagEncoder(new PMap("speed_two_directions=true")),
-            new BikeFlagEncoder(), new FootFlagEncoder()));
+    private final EncodingManager em = new EncodingManager.Builder().
+            add(new OSMRoadAccessParser()).
+            add(new CarFlagEncoder(new PMap("speed_two_directions=true"))).
+            add(new BikeFlagEncoder()).add(new FootFlagEncoder()).build();
     private final CarFlagEncoder encoder = (CarFlagEncoder) em.getEncoder("car");
     private final BooleanEncodedValue roundaboutEnc = em.getBooleanEncodedValue(Roundabout.KEY);
     private final DecimalEncodedValue avSpeedEnc = encoder.getAverageSpeedEnc();
@@ -208,13 +215,18 @@ public class CarFlagEncoderTest {
 
     @Test
     public void testDestinationTag() {
+        FastestWeighting weighting = new FastestWeighting(encoder);
+
         ReaderWay way = new ReaderWay(1);
         way.setTag("highway", "secondary");
-        assertEquals(60, encoder.getSpeed(way), 1e-1);
+        EncodingManager.AcceptWay acceptWay = new EncodingManager.AcceptWay();
+        assertTrue(em.acceptWay(way, acceptWay));
+        IntsRef edgeFlags = em.handleWayTags(way, acceptWay, 0);
+        assertEquals(60, weighting.calcWeight(GHUtility.createMockedEdgeIteratorState(1000, edgeFlags), false, -1), 0.1);
 
         way.setTag("vehicle", "destination");
-        IntsRef flags = encoder.handleWayTags(em.createEdgeFlags(), way, encoder.getAccess(way), 0);
-        assertEquals(5, avSpeedEnc.getDecimal(false, flags), 1e-1);
+        edgeFlags = em.handleWayTags(way, acceptWay, 0);
+        assertEquals(600, weighting.calcWeight(GHUtility.createMockedEdgeIteratorState(1000, edgeFlags), false, -1), 0.1);
     }
 
     @Test