Repository: graphhopper/graphhopper
Pull request #2882 (2023-09-13 16:22:33)

Use boolean turn restriction enc instead of decimal turn cost enc
OSM only contains data about turns being forbidden or not so it is sufficient to store this information using a boolean value instead of the decimal turn costs we used so far. On a lower level it is (and will be) still possible to store decimal turn costs per turn (or other turn cost encoded values), of course.


Patch:
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index 9db86a288..9833c2334 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -694,14 +694,14 @@ public class GraphHopper {
                     if (encodingManager.hasEncodedValue(FootNetwork.KEY) && added.add(FootNetwork.KEY))
                         osmParsers.addRelationTagParser(relConfig -> new OSMFootNetworkTagParser(encodingManager.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class), relConfig));
                 }
-                String turnCostKey = TurnCost.key(new PMap(vehicleStr).getString("name", name));
-                if (encodingManager.hasEncodedValue(turnCostKey)
+                String turnRestrictionKey = TurnRestriction.key(new PMap(vehicleStr).getString("name", name));
+                if (encodingManager.hasEncodedValue(turnRestrictionKey)
                         // need to make sure we do not add the same restriction parsers multiple times
-                        && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnCostEnc().getName().equals(turnCostKey))) {
+                        && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnRestrictionEnc().getName().equals(turnRestrictionKey))) {
                     List<String> restrictions = tagParser instanceof AbstractAccessParser
                             ? ((AbstractAccessParser) tagParser).getRestrictions()
                             : OSMRoadAccessParser.toOSMRestrictions(TransportationMode.valueOf(new PMap(vehicleStr).getString("transportation_mode", "VEHICLE")));
-                    osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getDecimalEncodedValue(turnCostKey)));
+                    osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getBooleanEncodedValue(turnRestrictionKey)));
                 }
             });
             vehicleTagParsers.getTagParsers().forEach(tagParser -> {
@@ -1098,10 +1098,10 @@ public class GraphHopper {
             if (!encodingManager.getVehicles().contains(profile.getVehicle()))
                 throw new IllegalArgumentException("Unknown vehicle '" + profile.getVehicle() + "' in profile: " + profile + ". " +
                         "Available vehicles: " + String.join(",", encodingManager.getVehicles()));
-            DecimalEncodedValue turnCostEnc = encodingManager.hasEncodedValue(TurnCost.key(profile.getVehicle()))
-                    ? encodingManager.getDecimalEncodedValue(TurnCost.key(profile.getVehicle()))
+            BooleanEncodedValue turnRestrictionEnc = encodingManager.hasEncodedValue(TurnRestriction.key(profile.getVehicle()))
+                    ? encodingManager.getBooleanEncodedValue(TurnRestriction.key(profile.getVehicle()))
                     : null;
-            if (profile.isTurnCosts() && turnCostEnc == null) {
+            if (profile.isTurnCosts() && turnRestrictionEnc == null) {
                 throw new IllegalArgumentException("The profile '" + profile.getName() + "' was configured with " +
                         "'turn_costs=true', but the corresponding vehicle '" + profile.getVehicle() + "' does not support turn costs." +
                         "\nYou need to add `|turn_costs=true` to the vehicle in `graph.vehicles`");
diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java
index 91b0951c0..bebfd3491 100644
--- a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java
+++ b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java
@@ -607,7 +607,7 @@ public class OSMReader {
                     r.second = null;
                 }
             }
-            restrictionSetter.setRestrictions(restrictionsWithType, restrictionTagParser.getTurnCostEnc());
+            restrictionSetter.setRestrictions(restrictionsWithType, restrictionTagParser.getTurnRestrictionEnc());
         }
     }
 
diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java
index e6ba31c02..05ac5c490 100644
--- a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java
+++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java
@@ -18,7 +18,7 @@
 
 package com.graphhopper.reader.osm;
 
-import com.graphhopper.routing.ev.DecimalEncodedValue;
+import com.graphhopper.routing.ev.BooleanEncodedValue;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -30,15 +30,15 @@ import static java.util.Collections.emptyList;
  */
 public class RestrictionTagParser {
     private final List<String> vehicleTypes;
-    private final DecimalEncodedValue turnCostEnc;
+    private final BooleanEncodedValue turnRestrictionEnc;
 
-    public RestrictionTagParser(List<String> vehicleTypes, DecimalEncodedValue turnCostEnc) {
+    public RestrictionTagParser(List<String> vehicleTypes, BooleanEncodedValue turnRestrictionEnc) {
         this.vehicleTypes = vehicleTypes;
-        this.turnCostEnc = turnCostEnc;
+        this.turnRestrictionEnc = turnRestrictionEnc;
     }
 
-    public DecimalEncodedValue getTurnCostEnc() {
-        return turnCostEnc;
+    public BooleanEncodedValue getTurnRestrictionEnc() {
+        return turnRestrictionEnc;
     }
 
     public Result parseRestrictionTags(Map<String, Object> tags) throws OSMRestrictionException {
diff --git a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java
index b2fa69e8a..f881810b3 100644
--- a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java
+++ b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java
@@ -58,11 +58,11 @@ public class DefaultWeightingFactory implements WeightingFactory {
         final String vehicle = profile.getVehicle();
         TurnCostProvider turnCostProvider;
         if (profile.isTurnCosts() && !disableTurnCosts) {
-            DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(vehicle));
-            if (turnCostEnc == null)
+            BooleanEncodedValue turnRestrictionEnc = encodingManager.getBooleanEncodedValue(TurnRestriction.key(vehicle));
+            if (turnRestrictionEnc == null)
                 throw new IllegalArgumentException("Vehicle " + vehicle + " does not support turn costs");
             int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS);
-            turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), uTurnCosts);
+            turnCostProvider = new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), uTurnCosts);
         } else {
             turnCostProvider = NO_TURN_COST_PROVIDER;
         }
diff --git a/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java b/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java
new file mode 100644
index 000000000..d7b8e371d
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for
+ *  additional information regarding copyright ownership.
+ *
+ *  GraphHopper GmbH licenses this file to you under the Apache License,
+ *  Version 2.0 (the "License"); you may not use this file except in
+ *  compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.graphhopper.routing.ev;
+
+import static com.graphhopper.routing.util.EncodingManager.getKey;
+
+public class TurnRestriction {
+
+    public static String key(String prefix) {
+        return getKey(prefix, "turn_restriction");
+    }
+
+    public static BooleanEncodedValue create(String name) {
+        return new SimpleBooleanEncodedValue(key(name), false);
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java
index a6d1c602e..3b4ac26ed 100644
--- a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java
+++ b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java
@@ -33,7 +33,7 @@ public class VehicleEncodedValues {
     private final BooleanEncodedValue accessEnc;
     private final DecimalEncodedValue avgSpeedEnc;
     private final DecimalEncodedValue priorityEnc;
-    private final DecimalEncodedValue turnCostEnc;
+    private final BooleanEncodedValue turnRestrictionEnc;
 
     public static VehicleEncodedValues foot(PMap properties) {
         String name = properties.getString("name", "foot");
@@ -44,8 +44,8 @@ public class VehicleEncodedValues {
         BooleanEncodedValue accessEnc = VehicleAccess.create(name);
         DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections);
         DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false);
-        DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null;
-        return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc);
+        BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null;
+        return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc);
     }
 
     public static VehicleEncodedValues wheelchair(PMap properties) {
@@ -66,8 +66,8 @@ public class VehicleEncodedValues {
         BooleanEncodedValue accessEnc = VehicleAccess.create(name);
         DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections);
         DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false);
-        DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null;
-        return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc);
+        BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null;
+        return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc);
     }
 
     public static VehicleEncodedValues racingbike(PMap properties) {
@@ -85,8 +85,8 @@ public class VehicleEncodedValues {
         boolean turnCosts = properties.getBool("turn_costs", false);
         BooleanEncodedValue accessEnc = VehicleAccess.create(name);
         DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, true);
-        DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null;
-        return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc);
+        BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null;
+        return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc);
     }
 
     public static VehicleEncodedValues roads(PMap properties) {
@@ -97,17 +97,17 @@ public class VehicleEncodedValues {
         boolean turnCosts = properties.getBool("turn_costs", false);
         BooleanEncodedValue accessEnc = VehicleAccess.create(name);
         DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections);
-        DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null;
-        return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc);
+        BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null;
+        return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc);
     }
 
     public VehicleEncodedValues(String name, BooleanEncodedValue accessEnc, DecimalEncodedValue avgSpeedEnc,
-                                DecimalEncodedValue priorityEnc, DecimalEncodedValue turnCostEnc) {
+                                DecimalEncodedValue priorityEnc, BooleanEncodedValue turnRestrictionEnc) {
         this.name = name;
         this.accessEnc = accessEnc;
         this.avgSpeedEnc = avgSpeedEnc;
         this.priorityEnc = priorityEnc;
-        this.turnCostEnc = turnCostEnc;
+        this.turnRestrictionEnc = turnRestrictionEnc;
     }
 
     public void createEncodedValues(List<EncodedValue> registerNewEncodedValue) {
@@ -120,8 +120,8 @@ public class VehicleEncodedValues {
     }
 
     public void createTurnCostEncodedValues(List<EncodedValue> registerNewTurnCostEncodedValues) {
-        if (turnCostEnc != null)
-            registerNewTurnCostEncodedValues.add(turnCostEnc);
+        if (turnRestrictionEnc != null)
+            registerNewTurnCostEncodedValues.add(turnRestrictionEnc);
     }
 
     public BooleanEncodedValue getAccessEnc() {
@@ -136,8 +136,8 @@ public class VehicleEncodedValues {
         return priorityEnc;
     }
 
-    public DecimalEncodedValue getTurnCostEnc() {
-        return turnCostEnc;
+    public BooleanEncodedValue getTurnRestrictionEnc() {
+        return turnRestrictionEnc;
     }
 
     public String getName() {
diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java
index 16f22e262..37b00dda7 100644
--- a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java
+++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java
@@ -18,12 +18,15 @@
 
 package com.graphhopper.routing.util.parsers;
 
-import com.carrotsearch.hppc.*;
+import com.carrotsearch.hppc.IntHashSet;
+import com.carrotsearch.hppc.IntIntHashMap;
+import com.carrotsearch.hppc.IntIntMap;
+import com.carrotsearch.hppc.IntSet;
 import com.carrotsearch.hppc.cursors.IntCursor;
 import com.graphhopper.reader.osm.GraphRestriction;
 import com.graphhopper.reader.osm.Pair;
 import com.graphhopper.reader.osm.RestrictionType;
-import com.graphhopper.routing.ev.DecimalEncodedValue;
+import com.graphhopper.routing.ev.BooleanEncodedValue;
 import com.graphhopper.storage.BaseGraph;
 import com.graphhopper.util.EdgeExplorer;
 import com.graphhopper.util.EdgeIterator;
@@ -52,14 +55,14 @@ public class RestrictionSetter {
      * Since we keep track of the added artificial edges here it is important to only use one RestrictionSetter instance
      * for **all** turn restrictions and vehicle types.
      */
-    public void setRestrictions(List<Pair<GraphRestriction, RestrictionType>> restrictions, DecimalEncodedValue turnCostEnc) {
+    public void setRestrictions(List<Pair<GraphRestriction, RestrictionType>> restrictions, BooleanEncodedValue turnRestrictionEnc) {
         // we first need to add all the artificial edges, because we might need to restrict turns between artificial
         // edges created for different restrictions (when restrictions are overlapping)
         addArtificialEdges(restrictions);
         // now we can add all the via-way restrictions
-        addViaWayRestrictions(restrictions, turnCostEnc);
+        addViaWayRestrictions(restrictions, turnRestrictionEnc);
         // ... and finally all the via-node restrictions
-        addViaNodeRestrictions(restrictions, turnCostEnc);
+        addViaNodeRestrictions(restrictions, turnRestrictionEnc);
     }
 
     private void addArtificialEdges(List<Pair<GraphRestriction, RestrictionType>> restrictions) {
@@ -82,7 +85,7 @@ public class RestrictionSetter {
         }
     }
 
-    private void addViaWayRestrictions(List<Pair<GraphRestriction, RestrictionType>> restrictions, DecimalEncodedValue turnCostEnc) {
+    private void addViaWayRestrictions(List<Pair<GraphRestriction, RestrictionType>> restrictions, BooleanEncodedValue turnRestrictionEnc) {
         IntSet viaEdgesUsedByOnlyRestrictions = new IntHashSet();
         for (Pair<GraphRestriction, RestrictionType> p : restrictions) {
             if (!p.first.isViaWayRestriction()) continue;
@@ -101,17 +104,17 @@ public class RestrictionSetter {
             final int viaToToNode = p.first.getViaNodes().get(1);
 
             // never turn between an artificial edge and its corresponding real edge
-            restrictTurn(turnCostEnc, artificialVia, fromToViaNode, viaEdge);
-            restrictTurn(turnCostEnc, viaEdge, fromToViaNode, artificialVia);
-            restrictTurn(turnCostEnc, artificialVia, viaToToNode, viaEdge);
-            restrictTurn(turnCostEnc, viaEdge, viaToToNode, artificialVia);
+            restrictTurn(turnRestrictionEnc, artificialVia, fromToViaNode, viaEdge);
+            restrictTurn(turnRestrictionEnc, viaEdge, fromToViaNode, artificialVia);
+            restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, viaEdge);
+            restrictTurn(turnRestrictionEnc, viaEdge, viaToToNode, artificialVia);
 
             if (p.second == NO) {
                 // This is how we implement via-way NO restrictions: we deny turning from the from-edge onto the via-edge,
                 // but allow turning onto the artificial edge instead. Then we deny turning from the artificial edge onto
                 // the to edge.
-                restrictTurn(turnCostEnc, fromEdge, fromToViaNode, viaEdge);
-                restrictTurn(turnCostEnc, artificialVia, viaToToNode, toEdge);
+                restrictTurn(turnRestrictionEnc, fromEdge, fromToViaNode, viaEdge);
+                restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, toEdge);
             } else if (p.second == ONLY) {
                 // For via-way ONLY restrictions we have to turn from the from-edge onto the via-edge and from the via-edge
                 // onto the to-edge, but only if we actually start at the from-edge. Therefore we enforce turning onto
@@ -120,24 +123,24 @@ public class RestrictionSetter {
                 EdgeIterator iter = edgeExplorer.setBaseNode(fromToViaNode);
                 while (iter.next())
                     if (iter.getEdge() != fromEdge && iter.getEdge() != artificialVia)
-                        restrictTurn(turnCostEnc, fromEdge, fromToViaNode, iter.getEdge());
+                        restrictTurn(turnRestrictionEnc, fromEdge, fromToViaNode, iter.getEdge());
                 iter = edgeExplorer.setBaseNode(viaToToNode);
                 while (iter.next())
                     if (iter.getEdge() != artificialVia && iter.getEdge() != toEdge)
-                        restrictTurn(turnCostEnc, artificialVia, viaToToNode, iter.getEdge());
+                        restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, iter.getEdge());
             } else {
                 throw new IllegalArgumentException("Unexpected restriction type: " + p.second);
             }
 
             // this is important for overlapping restrictions
             if (artificialFrom != fromEdge)
-                restrictTurn(turnCostEnc, artificialFrom, fromToViaNode, artificialVia);
+                restrictTurn(turnRestrictionEnc, artificialFrom, fromToViaNode, artificialVia);
             if (artificialTo != toEdge)
-                restrictTurn(turnCostEnc, artificialVia, viaToToNode, artificialTo);
+                restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, artificialTo);
         }
     }
 
-    private void addViaNodeRestrictions(List<Pair<GraphRestriction, RestrictionType>> restrictions, DecimalEncodedValue turnCostEnc) {
+    private void addViaNodeRestrictions(List<Pair<GraphRestriction, RestrictionType>> restrictions, BooleanEncodedValue turnRestrictionEnc) {
         for (Pair<GraphRestriction, RestrictionType> p : restrictions) {
             if (p.first.isViaWayRestriction()) continue;
             final int viaNode = p.first.getViaNodes().get(0);
@@ -148,14 +151,14 @@ public class RestrictionSetter {
                     final int artificialFrom = artificialEdgesByEdges.getOrDefault(fromEdge, fromEdge);
                     final int artificialTo = artificialEdgesByEdges.getOrDefault(toEdge, toEdge);
                     if (p.second == NO) {
-                        restrictTurn(turnCostEnc, fromEdge, viaNode, toEdge);
+                        restrictTurn(turnRestrictionEnc, fromEdge, viaNode, toEdge);
                         // we also need to restrict this term in case there are artificial edges for the from- and/or to-edge
                         if (artificialFrom != fromEdge)
-                            restrictTurn(turnCostEnc, artificialFrom, viaNode, toEdge);
+                            restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, toEdge);
                         if (artificialTo != toEdge)
-                            restrictTurn(turnCostEnc, fromEdge, viaNode, artificialTo);
+                            restrictTurn(turnRestrictionEnc, fromEdge, viaNode, artificialTo);
                         if (artificialFrom != fromEdge && artificialTo != toEdge)
-                            restrictTurn(turnCostEnc, artificialFrom, viaNode, artificialTo);
+                            restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, artificialTo);
                     } else if (p.second == ONLY) {
                         // we need to restrict all turns except the one, but that also means not restricting the
                         // artificial counterparts of these turns, if they exist.
@@ -163,10 +166,10 @@ public class RestrictionSetter {
                         EdgeIterator iter = edgeExplorer.setBaseNode(viaNode);
                         while (iter.next()) {
                             if (iter.getEdge() != fromEdge && iter.getEdge() != toEdge && iter.getEdge() != artificialTo)
-                                restrictTurn(turnCostEnc, fromEdge, viaNode, iter.getEdge());
+                                restrictTurn(turnRestrictionEnc, fromEdge, viaNode, iter.getEdge());
                             // and the same for the artificial edge belonging to the from-edge if it exists
                             if (fromEdge != artificialFrom && iter.getEdge() != artificialFrom && iter.getEdge() != toEdge && iter.getEdge() != artificialTo)
-                                restrictTurn(turnCostEnc, artificialFrom, viaNode, iter.getEdge());
+                                restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, iter.getEdge());
                         }
                     } else {
                         throw new IllegalArgumentException("Unexpected restriction type: " + p.second);
@@ -180,10 +183,10 @@ public class RestrictionSetter {
         return artificialEdgesByEdges;
     }
 
-    private void restrictTurn(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge) {
+    private void restrictTurn(BooleanEncodedValue turnRestrictionEnc, int fromEdge, int viaNode, int toEdge) {
         if (fromEdge < 0 || toEdge < 0 || viaNode < 0)
             throw new IllegalArgumentException("from/toEdge and viaNode must be >= 0");
-        baseGraph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, toEdge, Double.POSITIVE_INFINITY);
+        baseGraph.getTurnCostStorage().set(turnRestrictionEnc, fromEdge, viaNode, toEdge, true);
     }
 
     private static boolean ignoreViaWayRestriction(Pair<GraphRestriction, RestrictionType> p) {
@@ -197,4 +200,4 @@ public class RestrictionSetter {
         return false;
     }
 
-}
\ No newline at end of file
+}
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java
index 7f996014c..299dd04a6 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java
@@ -18,27 +18,27 @@
 
 package com.graphhopper.routing.weighting;
 
-import com.graphhopper.routing.ev.DecimalEncodedValue;
+import com.graphhopper.routing.ev.BooleanEncodedValue;
 import com.graphhopper.storage.TurnCostStorage;
 import com.graphhopper.util.EdgeIterator;
 
 import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
 
 public class DefaultTurnCostProvider implements TurnCostProvider {
-    private final DecimalEncodedValue turnCostEnc;
+    private final BooleanEncodedValue turnRestrictionEnc;
     private final TurnCostStorage turnCostStorage;
     private final int uTurnCostsInt;
     private final double uTurnCosts;
 
-    public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage) {
-        this(turnCostEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS);
+    public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage) {
+        this(turnRestrictionEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS);
     }
 
     /**
      * @param uTurnCosts the costs of a u-turn in seconds, for {@link Weighting#INFINITE_U_TURN_COSTS} the u-turn costs
      *                   will be infinite
      */
-    public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, int uTurnCosts) {
+    public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage, int uTurnCosts) {
         if (uTurnCosts < 0 && uTurnCosts != INFINITE_U_TURN_COSTS) {
             throw new IllegalArgumentException("u-turn costs must be positive, or equal to " + INFINITE_U_TURN_COSTS + " (=infinite costs)");
         }
@@ -48,12 +48,12 @@ public class DefaultTurnCostProvider implements TurnCostProvider {
             throw new IllegalArgumentException("No storage set to calculate turn weight");
         }
         // if null the TurnCostProvider can be still useful for edge-based routing
-        this.turnCostEnc = turnCostEnc;
+        this.turnRestrictionEnc = turnRestrictionEnc;
         this.turnCostStorage = turnCostStorage;
     }
 
-    public DecimalEncodedValue getTurnCostEnc() {
-        return turnCostEnc;
+    public BooleanEncodedValue getTurnRestrictionEnc() {
+        return turnRestrictionEnc;
     }
 
     @Override
@@ -66,8 +66,8 @@ public class DefaultTurnCostProvider implements TurnCostProvider {
             // note that the u-turn costs overwrite any turn costs set in TurnCostStorage
             tCost = uTurnCosts;
         } else {
-            if (turnCostEnc != null)
-                tCost = turnCostStorage.get(turnCostEnc, edgeFrom, nodeVia, edgeTo);
+            if (turnRestrictionEnc != null)
+                tCost = turnCostStorage.get(turnRestrictionEnc, edgeFrom, nodeVia, edgeTo) ? Double.POSITIVE_INFINITY : 0;
         }
         return tCost;
     }
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java
index 403311ad7..ae12375e9 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java
@@ -25,20 +25,38 @@ import com.graphhopper.util.EdgeIteratorState;
 
 public class SpeedWeighting implements Weighting {
     private final DecimalEncodedValue speedEnc;
-    private final DecimalEncodedValue turnCostEnc;
-    private final TurnCostStorage turnCostStorage;
-    private final double uTurnCosts;
+    private final TurnCostProvider turnCostProvider;
 
     public SpeedWeighting(DecimalEncodedValue speedEnc) {
-        this(speedEnc, null, null, 0);
+        this(speedEnc, TurnCostProvider.NO_TURN_COST_PROVIDER);
     }
 
     public SpeedWeighting(DecimalEncodedValue speedEnc, DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, double uTurnCosts) {
+        if (turnCostStorage == null || turnCostEnc == null)
+            throw new IllegalArgumentException("This SpeedWeighting constructor expects turnCostEnc and turnCostStorage to be != null");
         if (uTurnCosts < 0) throw new IllegalArgumentException("u-turn costs must be positive");
         this.speedEnc = speedEnc;
-        this.turnCostEnc = turnCostEnc;
-        this.turnCostStorage = turnCostStorage;
-        this.uTurnCosts = uTurnCosts;
+        this.turnCostProvider = new TurnCostProvider() {
+            @Override
+            public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
+                if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge))
+                    return 0;
+                if (inEdge == outEdge)
+                    return Math.max(turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge), uTurnCosts);
+                else
+                    return turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge);
+            }
+
+            @Override
+            public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+                return (long) (1000 * calcTurnWeight(inEdge, viaNode, outEdge));
+            }
+        };
+    }
+
+    public SpeedWeighting(DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) {
+        this.speedEnc = speedEnc;
+        this.turnCostProvider = turnCostProvider;
     }
 
     @Override
@@ -60,22 +78,17 @@ public class SpeedWeighting implements Weighting {
 
     @Override
     public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
-        if (turnCostEnc == null) return 0;
-        if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge))
-            return 0;
-        if (inEdge == outEdge)
-            return Math.max(turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge), uTurnCosts);
-        else return turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge);
+        return turnCostProvider.calcTurnWeight(inEdge, viaNode, outEdge);
     }
 
     @Override
     public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
-        return (long) (1000 * calcTurnWeight(inEdge, viaNode, outEdge));
+        return turnCostProvider.calcTurnMillis(inEdge, viaNode, outEdge);
     }
 
     @Override
     public boolean hasTurnCosts() {
-        return turnCostEnc != null;
+        return turnCostProvider != TurnCostProvider.NO_TURN_COST_PROVIDER;
     }
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java b/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java
index da76472af..8d694bcf3 100644
--- a/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java
+++ b/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.storage;
 
+import com.graphhopper.routing.ev.BooleanEncodedValue;
 import com.graphhopper.routing.ev.DecimalEncodedValue;
 import com.graphhopper.routing.ev.EdgeIntAccess;
 import com.graphhopper.routing.ev.IntsRefEdgeIntAccess;
@@ -82,6 +83,13 @@ public class TurnCostStorage {
         return true;
     }
 
+    public void set(BooleanEncodedValue bev, int fromEdge, int viaNode, int toEdge, boolean value) {
+        long pointer = findOrCreateTurnCostEntry(fromEdge, viaNode, toEdge);
+        if (pointer < 0)
+            throw new IllegalStateException("Invalid pointer: " + pointer + " at (" + fromEdge + ", " + viaNode + ", " + toEdge + ")");
+        bev.setBool(false, -1, createIntAccess(pointer), value);
+    }
+
     /**
      * Sets the turn cost at the viaNode when going from "fromEdge" to "toEdge"
      */
@@ -108,11 +116,12 @@ public class TurnCostStorage {
         return pointer;
     }
 
-    /**
-     * @return the turn cost of the viaNode when going from "fromEdge" to "toEdge"
-     */
-    public double get(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge) {
-        return turnCostEnc.getDecimal(false, -1, createIntAccess(findPointer(fromEdge, viaNode, toEdge)));
+    public double get(DecimalEncodedValue dev, int fromEdge, int viaNode, int toEdge) {
+        return dev.getDecimal(false, -1, createIntAccess(findPointer(fromEdge, viaNode, toEdge)));
+    }
+
+    public boolean get(BooleanEncodedValue bev, int fromEdge, int viaNode, int toEdge) {
+        return bev.getBool(false, -1, createIntAccess(findPointer(fromEdge, viaNode, toEdge)));
     }
 
     private EdgeIntAccess createIntAccess(long pointer) {
@@ -181,6 +190,8 @@ public class TurnCostStorage {
 
         int getToEdge();
 
+        boolean get(BooleanEncodedValue booleanEncodedValue);
+
         double getCost(DecimalEncodedValue encodedValue);
 
         boolean next();
@@ -211,6 +222,12 @@ public class TurnCostStorage {
             return turnCosts.getInt(turnCostPtr() + TC_TO);
         }
 
+        @Override
+        public boolean get(BooleanEncodedValue booleanEncodedValue) {
+            intsRef.ints[0] = turnCosts.getInt(turnCostPtr() + TC_FLAGS);
+            return booleanEncodedValue.getBool(false, -1, edgeIntAccess);
+        }
+
         @Override
         public double getCost(DecimalEncodedValue encodedValue) {
             intsRef.ints[0] = turnCosts.getInt(turnCostPtr() + TC_FLAGS);
diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java
index d73054b17..e43ef73a4 100644
--- a/core/src/main/java/com/graphhopper/util/GHUtility.java
+++ b/core/src/main/java/com/graphhopper/util/GHUtility.java
@@ -567,9 +567,10 @@ public class GHUtility {
      */
     public static long calcMillisWithTurnMillis(Weighting weighting, EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
         long edgeMillis = weighting.calcEdgeMillis(edgeState, reverse);
-        if (!EdgeIterator.Edge.isValid(prevOrNextEdgeId)) {
+        if (edgeMillis == Long.MAX_VALUE)
+            return edgeMillis;
+        if (!EdgeIterator.Edge.isValid(prevOrNextEdgeId))
             return edgeMillis;
-        }
         // should we also separate weighting vs. time for turn? E.g. a fast but dangerous turn - is this common?
         // todo: why no first/last orig edge here as in calcWeight ?
 //        final int origEdgeId = reverse ? edgeState.getOrigEdgeLast() : edgeState.getOrigEdgeFirst();
@@ -577,6 +578,8 @@ public class GHUtility {
         long turnMillis = reverse
                 ? weighting.calcTurnMillis(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId)
                 : weighting.calcTurnMillis(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId);
+        if (turnMillis == Long.MAX_VALUE)
+            return turnMillis;
         return edgeMillis + turnMillis;
     }
 
diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java
index 40e35494f..6cfe62778 100644
--- a/tools/src/main/java/com/graphhopper/tools/Measurement.java
+++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java
@@ -27,10 +27,7 @@ import com.graphhopper.config.LMProfile;
 import com.graphhopper.config.Profile;
 import com.graphhopper.jackson.Jackson;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
-import com.graphhopper.routing.ev.BooleanEncodedValue;
-import com.graphhopper.routing.ev.Subnetwork;
-import com.graphhopper.routing.ev.TurnCost;
-import com.graphhopper.routing.ev.VehicleAccess;
+import com.graphhopper.routing.ev.*;
 import com.graphhopper.routing.lm.LMConfig;
 import com.graphhopper.routing.lm.PrepareLandmarks;
 import com.graphhopper.routing.util.*;
@@ -178,7 +175,7 @@ public class Measurement {
         BaseGraph g = hopper.getBaseGraph();
         EncodingManager encodingManager = hopper.getEncodingManager();
         BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key(vehicle));
-        boolean withTurnCosts = encodingManager.hasEncodedValue(TurnCost.key(vehicle));
+        boolean withTurnCosts = encodingManager.hasEncodedValue(TurnRestriction.key(vehicle));
 
         StopWatch sw = new StopWatch().start();
         try {
diff --git a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
index bd4400353..f59725ad3 100644
--- a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
@@ -94,7 +94,7 @@ public class InfoResource {
             Info.ProfileData profileData = new Info.ProfileData(p.getName());
             info.profiles.add(profileData);
             defaultHiddenEVs.addAll(Arrays.asList(VehiclePriority.key(p.getName()), VehicleAccess.key(p.getName()),
-                    VehicleSpeed.key(p.getName()), TurnCost.key(p.getName()), Subnetwork.key(p.getName())));
+                    VehicleSpeed.key(p.getName()), TurnRestriction.key(p.getName()), Subnetwork.key(p.getName())));
         }
         if (config.has("gtfs.file"))
             info.profiles.add(new Info.ProfileData("pt"));


Test patch:
diff --git a/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java b/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java
index 56898db4b..10a448ccb 100644
--- a/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java
+++ b/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java
@@ -545,30 +545,30 @@ public class OSMReaderTest {
         // (2-3)->(3-4) only_straight_on = (2-3)->(3-8) restricted
         // (4-3)->(3-8) no_right_turn = (4-3)->(3-8) restricted
         // (2-3)->(3-8) no_entry = (2-3)->(3-8) restricted
-        DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car"));
-        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0);
-        assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8) > 0);
-        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0);
-        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4) == 0);
-        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2) == 0);
-        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4) == 0);
-        assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2) == 0);
-        assertTrue(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2) == 0);
+        BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car"));
+        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8));
+        assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8));
+        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8));
+        assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4));
+        assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2));
+        assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4));
+        assertFalse(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2));
+        assertFalse(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2));
 
         // u-turn restriction for (6-1)->(1-6) but not for (1-6)->(6-1)
-        assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6) > 0);
-        assertTrue(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6) == 0);
+        assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6));
+        assertFalse(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6));
 
         int edge4_5 = GHUtility.getEdge(graph, n4, n5).getEdge();
         int edge5_6 = GHUtility.getEdge(graph, n5, n6).getEdge();
         int edge5_1 = GHUtility.getEdge(graph, n5, n1).getEdge();
 
         // (4-5)->(5-1) right_turn_only = (4-5)->(5-6) restricted
-        assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6) == 0);
-        assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1) > 0);
+        assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6));
+        assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1));
 
-        DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike"));
-        assertTrue(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6) == 0);
+        BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike"));
+        assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6));
 
         int n10 = AbstractGraphStorageTester.getIdOf(graph, 40, 10);
         int n11 = AbstractGraphStorageTester.getIdOf(graph, 40, 11);
@@ -577,12 +577,12 @@ public class OSMReaderTest {
         int edge10_11 = GHUtility.getEdge(graph, n10, n11).getEdge();
         int edge11_14 = GHUtility.getEdge(graph, n11, n14).getEdge();
 
-        assertTrue(tcStorage.get(carTCEnc, edge11_14, n11, edge10_11) == 0);
-        assertTrue(tcStorage.get(bikeTCEnc, edge11_14, n11, edge10_11) == 0);
+        assertFalse(tcStorage.get(carTCEnc, edge11_14, n11, edge10_11));
+        assertFalse(tcStorage.get(bikeTCEnc, edge11_14, n11, edge10_11));
 
         // the turn is restricted for car even though it turns into a one-way, but we treat this separately now
-        assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14) > 0);
-        assertTrue(tcStorage.get(bikeTCEnc, edge10_11, n11, edge11_14) > 0);
+        assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14));
+        assertTrue(tcStorage.get(bikeTCEnc, edge10_11, n11, edge11_14));
     }
 
     @Test
@@ -603,11 +603,11 @@ public class OSMReaderTest {
         int edge9_3 = GHUtility.getEdge(graph, n9, n3).getEdge();
         int edge3_8 = GHUtility.getEdge(graph, n3, n8).getEdge();
 
-        DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car"));
-        DecimalEncodedValue roadsTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("roads"));
+        BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car"));
+        BooleanEncodedValue roadsTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("roads"));
 
-        assertTrue(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8) == 0);
-        assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8) > 0);
+        assertFalse(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8));
+        assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8));
     }
 
     @Test
@@ -711,9 +711,9 @@ public class OSMReaderTest {
                 ).
                 importOrLoad();
         EncodingManager manager = hopper.getEncodingManager();
-        DecimalEncodedValue carTCEnc = manager.getDecimalEncodedValue(TurnCost.key("car"));
-        DecimalEncodedValue truckTCEnc = manager.getDecimalEncodedValue(TurnCost.key("truck"));
-        DecimalEncodedValue bikeTCEnc = manager.getDecimalEncodedValue(TurnCost.key("bike"));
+        BooleanEncodedValue carTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("car"));
+        BooleanEncodedValue truckTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("truck"));
+        BooleanEncodedValue bikeTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("bike"));
 
         Graph graph = hopper.getBaseGraph();
         TurnCostStorage tcStorage = graph.getTurnCostStorage();
@@ -721,22 +721,22 @@ public class OSMReaderTest {
         int edge1 = GHUtility.getEdge(graph, 1, 0).getEdge();
         int edge2 = GHUtility.getEdge(graph, 0, 2).getEdge();
         // the 2nd entry provides turn flags for bike only
-        assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge1, 0, edge2)));
-        assertTrue(Double.isInfinite(tcStorage.get(truckTCEnc, edge1, 0, edge2)));
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge1, 0, edge2), .1);
+        assertTrue(tcStorage.get(carTCEnc, edge1, 0, edge2));
+        assertTrue(tcStorage.get(truckTCEnc, edge1, 0, edge2));
+        assertFalse(tcStorage.get(bikeTCEnc, edge1, 0, edge2));
 
         edge1 = GHUtility.getEdge(graph, 2, 0).getEdge();
         edge2 = GHUtility.getEdge(graph, 0, 3).getEdge();
         // the first entry provides turn flags for car and foot only
-        assertEquals(0, tcStorage.get(carTCEnc, edge1, 0, edge2), .1);
-        assertEquals(0, tcStorage.get(truckTCEnc, edge1, 0, edge2), .1);
-        assertTrue(Double.isInfinite(tcStorage.get(bikeTCEnc, edge1, 0, edge2)));
+        assertFalse(tcStorage.get(carTCEnc, edge1, 0, edge2));
+        assertFalse(tcStorage.get(truckTCEnc, edge1, 0, edge2));
+        assertTrue(tcStorage.get(bikeTCEnc, edge1, 0, edge2));
 
         edge1 = GHUtility.getEdge(graph, 3, 0).getEdge();
         edge2 = GHUtility.getEdge(graph, 0, 2).getEdge();
-        assertEquals(0, tcStorage.get(carTCEnc, edge1, 0, edge2), .1);
-        assertTrue(Double.isInfinite(tcStorage.get(truckTCEnc, edge1, 0, edge2)));
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge1, 0, edge2), .1);
+        assertFalse(tcStorage.get(carTCEnc, edge1, 0, edge2));
+        assertTrue(tcStorage.get(truckTCEnc, edge1, 0, edge2));
+        assertFalse(tcStorage.get(bikeTCEnc, edge1, 0, edge2));
     }
 
     @Test
@@ -775,37 +775,37 @@ public class OSMReaderTest {
         // (2-3)->(3-4) only_straight_on except bicycle = (2-3)->(3-8) restricted for car
         // (4-3)->(3-8) no_right_turn dedicated to motorcar = (4-3)->(3-8) restricted for car
 
-        DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car"));
-        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0);
-        assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8) > 0);
-        assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_4), .1);
-        assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_2), .1);
-        assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_4), .1);
-        assertEquals(0, tcStorage.get(carTCEnc, edge4_3, n3, edge3_2), .1);
-        assertEquals(0, tcStorage.get(carTCEnc, edge8_3, n3, edge3_2), .1);
-
-        DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike"));
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_8), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_8), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_2), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_2), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge8_3, n3, edge3_2), .1);
+        BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car"));
+        assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8));
+        assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8));
+        assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4));
+        assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2));
+        assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4));
+        assertFalse(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2));
+        assertFalse(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2));
+
+        BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike"));
+        assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_8));
+        assertFalse(tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_8));
+        assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4));
+        assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_2));
+        assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4));
+        assertFalse(tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_2));
+        assertFalse(tcStorage.get(bikeTCEnc, edge8_3, n3, edge3_2));
 
         // u-turn except bus;bicycle restriction for (6-1)->(1-6) but not for (1-6)->(6-1)
-        assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6) > 0);
-        assertEquals(0, tcStorage.get(carTCEnc, edge1_6, n6, edge1_6), .1);
+        assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6));
+        assertFalse(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6));
 
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge1_6, n1, edge1_6), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge1_6, n6, edge1_6), .1);
+        assertFalse(tcStorage.get(bikeTCEnc, edge1_6, n1, edge1_6));
+        assertFalse(tcStorage.get(bikeTCEnc, edge1_6, n6, edge1_6));
 
         // (4-5)->(5-6) right_turn_only dedicated to motorcar = (4-5)->(5-1) restricted
-        assertEquals(0, tcStorage.get(carTCEnc, edge4_5, n5, edge5_6), .1);
-        assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1) > 0);
+        assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6));
+        assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1));
 
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1), .1);
+        assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6));
+        assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1));
     }
 
     @Test
@@ -831,24 +831,24 @@ public class OSMReaderTest {
         int edge4_5 = GHUtility.getEdge(graph, n4, n5).getEdge();
         int edge5_1 = GHUtility.getEdge(graph, n5, n1).getEdge();
 
-        DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car"));
-        DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike"));
+        BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car"));
+        BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike"));
 
         // (1-2)->(2-3) no_right_turn for motorcar and bus
-        assertTrue(tcStorage.get(carTCEnc, edge1_2, n2, edge2_3) > 0);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge1_2, n2, edge2_3), .1);
+        assertTrue(tcStorage.get(carTCEnc, edge1_2, n2, edge2_3));
+        assertFalse(tcStorage.get(bikeTCEnc, edge1_2, n2, edge2_3));
 
         // (3-4)->(4-5) no_right_turn for motorcycle and motorcar
-        assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge3_4, n4, edge4_5)));
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge3_4, n4, edge4_5), .1);
+        assertTrue(tcStorage.get(carTCEnc, edge3_4, n4, edge4_5));
+        assertFalse(tcStorage.get(bikeTCEnc, edge3_4, n4, edge4_5));
 
         // (5-1)->(1-2) no_right_turn for bus and psv except for motorcar and bicycle
-        assertEquals(0, tcStorage.get(carTCEnc, edge4_5, n5, edge5_1), .1);
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1), .1);
+        assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1));
+        assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1));
 
         // (5-1)->(1-2) no_right_turn for motorcar and motorcycle except for bus and bicycle
-        assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge5_1, n1, edge1_2)));
-        assertEquals(0, tcStorage.get(bikeTCEnc, edge5_1, n1, edge1_2), .1);
+        assertTrue(tcStorage.get(carTCEnc, edge5_1, n1, edge1_2));
+        assertFalse(tcStorage.get(bikeTCEnc, edge5_1, n1, edge1_2));
     }
 
     @Test
diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
index cd8df5b96..ed4196b78 100644
--- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
@@ -56,7 +56,9 @@ public class AlternativeRouteTest {
 
             EncodingManager em = EncodingManager.start().add(speedEnc).add(turnCostEnc).build();
             graph = new BaseGraph.Builder(em).withTurnCosts(true).create();
-            weighting = new SpeedWeighting(speedEnc, tMode.isEdgeBased() ? turnCostEnc : null, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY);
+            weighting = tMode.isEdgeBased()
+                    ? new SpeedWeighting(speedEnc, turnCostEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY)
+                    : new SpeedWeighting(speedEnc);
         }
 
         @Override
diff --git a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java
index cce4e66ef..2a8c65a1c 100644
--- a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java
+++ b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java
@@ -249,7 +249,7 @@ public class PrepareRoutingSubnetworksTest {
     }
 
     private static PrepareRoutingSubnetworks.PrepareJob createJob(BooleanEncodedValue subnetworkEnc, DecimalEncodedValue speedEnc) {
-        return createJob(subnetworkEnc, speedEnc, null, null, 0);
+        return new PrepareRoutingSubnetworks.PrepareJob(subnetworkEnc, new SpeedWeighting(speedEnc));
     }
 
     private static PrepareRoutingSubnetworks.PrepareJob createJob(BooleanEncodedValue subnetworkEnc, DecimalEncodedValue speedEnc,
diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java
index 7236c0bd2..6cb932e56 100644
--- a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java
@@ -690,7 +690,6 @@ public class CarTagParserTest {
         EncodingManager lowFactorEm = new EncodingManager.Builder()
                 .add(new SimpleBooleanEncodedValue(VehicleAccess.key("car"), true))
                 .add(lowFactorSpeedEnc)
-                .addTurnCostEncodedValue(TurnCost.create(TurnCost.key("car"), 1))
                 .build();
         edgeIntAccess = new ArrayEdgeIntAccess(lowFactorEm.getIntsForFlags());
         new CarAverageSpeedParser(lowFactorEm, new PMap()).handleWayTags(edgeId, edgeIntAccess, way);
diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java
index 10ef0a2be..2166e9d02 100644
--- a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java
@@ -5,13 +5,11 @@ import com.graphhopper.reader.osm.GraphRestriction;
 import com.graphhopper.reader.osm.Pair;
 import com.graphhopper.reader.osm.RestrictionType;
 import com.graphhopper.routing.Dijkstra;
-import com.graphhopper.routing.ev.DecimalEncodedValue;
-import com.graphhopper.routing.ev.DecimalEncodedValueImpl;
-import com.graphhopper.routing.ev.EncodedValue;
-import com.graphhopper.routing.ev.TurnCost;
+import com.graphhopper.routing.ev.*;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.SpeedWeighting;
+import com.graphhopper.routing.weighting.TurnCostProvider;
 import com.graphhopper.storage.BaseGraph;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -46,9 +44,9 @@ public class RestrictionSetterTest {
         edge(2, 4);
         edge(3, 4);
         GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b);
-        DecimalEncodedValue turnCostEnc = createTurnCostEnc("car");
-        r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.NO)), turnCostEnc);
-        assertEquals(nodes(0, 1, 3, 4, 2), calcPath(0, 2, turnCostEnc));
+        BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car");
+        r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.NO)), turnRestrictionEnc);
+        assertEquals(nodes(0, 1, 3, 4, 2), calcPath(0, 2, turnRestrictionEnc));
     }
 
     @Test
@@ -62,9 +60,9 @@ public class RestrictionSetterTest {
         edge(2, 4);
         edge(3, 4);
         GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b);
-        DecimalEncodedValue turnCostEnc = createTurnCostEnc("car");
-        r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.ONLY)), turnCostEnc);
-        assertEquals(nodes(0, 1, 2, 4, 3), calcPath(0, 3, turnCostEnc));
+        BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car");
+        r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.ONLY)), turnRestrictionEnc);
+        assertEquals(nodes(0, 1, 2, 4, 3), calcPath(0, 3, turnRestrictionEnc));
     }
 
     @Test
@@ -86,15 +84,15 @@ public class RestrictionSetterTest {
         edge(6, 9);
         edge(8, 9);
         GraphRestriction graphRestriction = GraphRestriction.way(a, b, c, nodes(1, 2));
-        DecimalEncodedValue turnCostEnc = createTurnCostEnc("car");
+        BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car");
         r.setRestrictions(Arrays.asList(
                 new Pair<>(graphRestriction, RestrictionType.NO)
-        ), turnCostEnc);
+        ), turnRestrictionEnc);
         // turning from a to b and then to c is not allowed
-        assertEquals(nodes(0, 1, 5, 8, 9, 6, 2, 3), calcPath(0, 3, turnCostEnc));
+        assertEquals(nodes(0, 1, 5, 8, 9, 6, 2, 3), calcPath(0, 3, turnRestrictionEnc));
         // turning from a to b, or b to c is still allowed
-        assertEquals(nodes(0, 1, 2, 4), calcPath(0, 4, turnCostEnc));
-        assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc));
+        assertEquals(nodes(0, 1, 2, 4), calcPath(0, 4, turnRestrictionEnc));
+        assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnRestrictionEnc));
     }
 
     @Test
@@ -111,21 +109,21 @@ public class RestrictionSetterTest {
         int t = edge(2, 6);
         int u = edge(3, 7);
 
-        DecimalEncodedValue turnCostEnc = createTurnCostEnc("car");
+        BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car");
         r.setRestrictions(Arrays.asList(
                 new Pair<>(GraphRestriction.way(a, b, c, nodes(1, 2)), RestrictionType.NO),
                 new Pair<>(GraphRestriction.way(b, c, d, nodes(2, 3)), RestrictionType.NO)
-        ), turnCostEnc);
+        ), turnRestrictionEnc);
 
-        assertEquals(NO_PATH, calcPath(0, 3, turnCostEnc)); // a-b-c
-        assertEquals(nodes(0, 1, 2, 6), calcPath(0, 6, turnCostEnc)); // a-b-t
-        assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc)); // s-b-c
-        assertEquals(nodes(5, 1, 2, 6), calcPath(5, 6, turnCostEnc)); // s-b-t
+        assertEquals(NO_PATH, calcPath(0, 3, turnRestrictionEnc)); // a-b-c
+        assertEquals(nodes(0, 1, 2, 6), calcPath(0, 6, turnRestrictionEnc)); // a-b-t
+        assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnRestrictionEnc)); // s-b-c
+        assertEquals(nodes(5, 1, 2, 6), calcPath(5, 6, turnRestrictionEnc)); // s-b-t
 
-        assertEquals(NO_PATH, calcPath(1, 4, turnCostEnc)); // b-c-d
-        assertEquals(nodes(1, 2, 3, 7), calcPath(1, 7, turnCostEnc)); // b-c-u
-        assertEquals(nodes(6, 2, 3, 4), calcPath(6, 4, turnCostEnc)); // t-c-d
-        assertEquals(nodes(6, 2, 3, 7), calcPath(6, 7, turnCostEnc)); // t-c-u
+        assertEquals(NO_PATH, calcPath(1, 4, turnRestrictionEnc)); // b-c-d
+        assertEquals(nodes(1, 2, 3, 7), calcPath(1, 7, turnRestrictionEnc)); // b-c-u
+        assertEquals(nodes(6, 2, 3, 4), calcPath(6, 4, turnRestrictionEnc)); // t-c-d
+        assertEquals(nodes(6, 2, 3, 7), calcPath(6, 7, turnRestrictionEnc)); // t-c-u
     }
 
     @Test
@@ -150,7 +148,7 @@ public class RestrictionSetterTest {
         edge(7, 10);
         edge(8, 11);
         edge(10, 11);
-        DecimalEncodedValue turnCostEnc = createTurnCostEnc("car");
+        BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car");
         r.setRestrictions(Arrays.asList(
                 new Pair<>(GraphRestriction.node(t, 4, d), RestrictionType.NO),
                 new Pair<>(GraphRestriction.node(s, 3, a), RestrictionType.NO),
@@ -158,13 +156,13 @@ public class RestrictionSetterTest {
                 new Pair<>(GraphRestriction.way(b, c, d, nodes(7, 8)), RestrictionType.NO),
                 new Pair<>(GraphRestriction.way(c, d, a, nodes(8, 4)), RestrictionType.NO),
                 new Pair<>(GraphRestriction.way(d, a, b, nodes(4, 3)), RestrictionType.NO)
-        ), turnCostEnc);
+        ), turnRestrictionEnc);
 
-        assertEquals(nodes(0, 3, 7, 8, 9), calcPath(0, 9, turnCostEnc));
-        assertEquals(nodes(5, 4, 3, 7, 10, 11, 8, 9), calcPath(5, 9, turnCostEnc));
-        assertEquals(nodes(5, 4, 3, 2), calcPath(5, 2, turnCostEnc));
-        assertEquals(nodes(0, 3, 7, 10), calcPath(0, 10, turnCostEnc));
-        assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnCostEnc));
+        assertEquals(nodes(0, 3, 7, 8, 9), calcPath(0, 9, turnRestrictionEnc));
+        assertEquals(nodes(5, 4, 3, 7, 10, 11, 8, 9), calcPath(5, 9, turnRestrictionEnc));
+        assertEquals(nodes(5, 4, 3, 2), calcPath(5, 2, turnRestrictionEnc));
+        assertEquals(nodes(0, 3, 7, 10), calcPath(0, 10, turnRestrictionEnc));
+        assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnRestrictionEnc));
     }
 
     @Test
@@ -183,22 +181,22 @@ public class RestrictionSetterTest {
         int e = edge(4, 5);
         int f = edge(5, 7);
         int g = edge(5, 6);
-        DecimalEncodedValue turnCostEnc = createTurnCostEnc("car");
+        BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car");
         r.setRestrictions(Arrays.asList(
                 new Pair<>(GraphRestriction.way(a, d, f, nodes(2, 5)), RestrictionType.ONLY),
                 // we add a few more restrictions, because that happens a lot in real data
                 new Pair<>(GraphRestriction.way(c, d, g, nodes(2, 5)), RestrictionType.NO),
                 new Pair<>(GraphRestriction.node(e, 5, f), RestrictionType.NO)
-        ), turnCostEnc);
+        ), turnRestrictionEnc);
         // following the restriction is allowed of course
-        assertEquals(nodes(1, 2, 5, 7), calcPath(1, 7, turnCostEnc));
+        assertEquals(nodes(1, 2, 5, 7), calcPath(1, 7, turnRestrictionEnc));
         // taking another turn at the beginning is not allowed
-        assertEquals(nodes(), calcPath(1, 3, turnCostEnc));
+        assertEquals(nodes(), calcPath(1, 3, turnRestrictionEnc));
         // taking another turn after the first turn is not allowed either
-        assertEquals(nodes(), calcPath(1, 4, turnCostEnc));
+        assertEquals(nodes(), calcPath(1, 4, turnRestrictionEnc));
         // coming from somewhere we can go anywhere
-        assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnCostEnc));
-        assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnCostEnc));
+        assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnRestrictionEnc));
+        assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnRestrictionEnc));
     }
 
     @Test
@@ -212,7 +210,7 @@ public class RestrictionSetterTest {
         int c = edge(1, 2);
         int d = edge(2, 3);
         int e = edge(2, 4);
-        DecimalEncodedValue turnCostEnc = createTurnCostEnc("car");
+        BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car");
         assertThrows(IllegalStateException.class, () -> r.setRestrictions(Arrays.asList(
                         // These are two 'only' via-way restrictions that share the same via way. A real-world example can
                         // be found in Rüdesheim am Rhein where vehicles either have to go straight or enter the ferry depending
@@ -220,18 +218,29 @@ public class RestrictionSetterTest {
                         // We have to make sure such cases are ignored already when we parse the OSM data.
                         new Pair<>(GraphRestriction.way(a, c, d, nodes(1, 2)), RestrictionType.ONLY),
                         new Pair<>(GraphRestriction.way(b, c, e, nodes(1, 2)), RestrictionType.ONLY)
-                ), turnCostEnc)
+                ), turnRestrictionEnc)
         );
     }
 
-    private static DecimalEncodedValue createTurnCostEnc(String name) {
-        DecimalEncodedValue turnCostEnc = TurnCost.create(name, 1);
-        turnCostEnc.init(new EncodedValue.InitializerConfig());
-        return turnCostEnc;
+    private static BooleanEncodedValue createTurnRestrictionEnc(String name) {
+        BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create(name);
+        turnRestrictionEnc.init(new EncodedValue.InitializerConfig());
+        return turnRestrictionEnc;
     }
 
-    private IntArrayList calcPath(int from, int to, DecimalEncodedValue turnCostEnc) {
-        return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, turnCostEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes());
+    private IntArrayList calcPath(int from, int to, BooleanEncodedValue turnRestrictionEnc) {
+        return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, new TurnCostProvider() {
+            @Override
+            public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
+                if (inEdge == outEdge) return Double.POSITIVE_INFINITY;
+                return graph.getTurnCostStorage().get(turnRestrictionEnc, inEdge, viaNode, outEdge) ? Double.POSITIVE_INFINITY : 0;
+            }
+
+            @Override
+            public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+                return Double.isInfinite(calcTurnWeight(inEdge, viaNode, outEdge)) ? Long.MAX_VALUE : 0L;
+            }
+        }), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes());
     }
 
     private IntArrayList nodes(int... nodes) {
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java
index 4aeff12c7..5011517e2 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java
@@ -39,8 +39,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 public class FastestWeightingTest {
     private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true);
     private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false);
-    private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10);
-    private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build();
+    private final BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car");
+    private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnRestrictionEnc).build();
     private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create();
 
     @Test
@@ -108,19 +108,18 @@ public class FastestWeightingTest {
     @Test
     public void calcWeightAndTime_withTurnCosts() {
         BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create();
-        Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()));
+        Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()));
         GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100));
         EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100));
-        // turn costs are given in seconds
-        setTurnCost(graph, 0, 1, 2, 5);
-        assertEquals(6 + 5, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6);
-        assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6);
+        setTurnRestriction(graph, 0, 1, 2);
+        assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0)));
+        assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0));
     }
 
     @Test
     public void calcWeightAndTime_uTurnCosts() {
         BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create();
-        Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 40));
+        Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40));
         EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100));
         assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6);
         assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6);
@@ -130,15 +129,12 @@ public class FastestWeightingTest {
     public void calcWeightAndTime_withTurnCosts_shortest() {
         BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create();
         Weighting weighting = new ShortestWeighting(accessEnc, speedEnc,
-                new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()));
+                new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()));
         GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100));
         EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100));
-        // turn costs are given in seconds
-        setTurnCost(graph, 0, 1, 2, 5);
-        // todo: for the shortest weighting turn costs cannot be interpreted as seconds? at least when they are added
-        // to the weight? how much should they contribute ?
-//        assertEquals(105, AbstractWeighting.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6);
-        assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6);
+        setTurnRestriction(graph, 0, 1, 2);
+        assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0)));
+        assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0));
     }
 
     @Test
@@ -204,8 +200,8 @@ public class FastestWeightingTest {
         assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), 1.e-6);
     }
 
-    private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) {
-        graph.getTurnCostStorage().set(turnCostEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost);
+    private void setTurnRestriction(Graph graph, int from, int via, int to) {
+        graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true);
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java
index 655257c1f..942715ac2 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java
@@ -31,7 +31,7 @@ class CustomWeightingTest {
     DecimalEncodedValue maxSpeedEnc;
     EnumEncodedValue<RoadClass> roadClassEnc;
     EncodingManager encodingManager;
-    DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10);
+    BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car");
 
     @BeforeEach
     public void setup() {
@@ -41,7 +41,7 @@ class CustomWeightingTest {
                 .add(Toll.create())
                 .add(Hazmat.create())
                 .add(RouteNetwork.create(BikeNetwork.KEY))
-                .addTurnCostEncodedValue(turnCostEnc)
+                .addTurnCostEncodedValue(turnRestrictionEnc)
                 .build();
         maxSpeedEnc = encodingManager.getDecimalEncodedValue(MaxSpeed.KEY);
         roadClassEnc = encodingManager.getEnumEncodedValue(KEY, RoadClass.class);
@@ -409,20 +409,19 @@ class CustomWeightingTest {
     public void calcWeightAndTime_withTurnCosts() {
         BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create();
         Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager,
-                new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()), new CustomModel());
+                new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()), new CustomModel());
         GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100));
         EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100));
-        // turn costs are given in seconds
-        setTurnCost(graph, 0, 1, 2, 5);
-        assertEquals(6 + 5, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6);
-        assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6);
+        setTurnRestriction(graph, 0, 1, 2);
+        assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0)));
+        assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0));
     }
 
     @Test
     public void calcWeightAndTime_uTurnCosts() {
         BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create();
         Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null,
-                encodingManager, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 40), new CustomModel());
+                encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40), new CustomModel());
         EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100));
         assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6);
         assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6);
@@ -432,15 +431,12 @@ class CustomWeightingTest {
     public void calcWeightAndTime_withTurnCosts_shortest() {
         BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create();
         Weighting weighting = new ShortestWeighting(accessEnc, avSpeedEnc,
-                new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()));
+                new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()));
         GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100));
         EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100));
-        // turn costs are given in seconds
-        setTurnCost(graph, 0, 1, 2, 5);
-        // todo: for the shortest weighting turn costs cannot be interpreted as seconds? at least when they are added
-        // to the weight? how much should they contribute ?
-//        assertEquals(105, AbstractWeighting.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6);
-        assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6);
+        setTurnRestriction(graph, 0, 1, 2);
+        assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0)));
+        assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0));
     }
 
     @Test
@@ -508,7 +504,7 @@ class CustomWeightingTest {
         assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), .01);
     }
 
-    private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) {
-        graph.getTurnCostStorage().set(turnCostEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost);
+    private void setTurnRestriction(Graph graph, int from, int via, int to) {
+        graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true);
     }
 }