Repository: graphhopper/graphhopper
Pull request #1868 (2020-01-26 11:33:57)

Disable rezooming on intermediate point addition
This PR prevents the automatic-rezoom features that takes place when the user adds an intermediate destination (which can be very annoying when fine tuning very-very long tracks).

It is a simplified version of the commit removed from https://github.com/graphhopper/graphhopper/pull/1856


twitwi: There seem to be an issue about that (partly) https://github.com/graphhopper/graphhopper/issues/775


Patch:
diff --git a/api/src/main/java/com/graphhopper/util/Helper.java b/api/src/main/java/com/graphhopper/util/Helper.java
index 48c3aa1d1..60b3b32c6 100644
--- a/api/src/main/java/com/graphhopper/util/Helper.java
+++ b/api/src/main/java/com/graphhopper/util/Helper.java
@@ -294,11 +294,10 @@ public class Helper {
      * Converts into an integer to be compatible with the still limited DataAccess class (accepts
      * only integer values). But this conversion also reduces memory consumption where the precision
      * loss is acceptable. As +- 180° and +-90° are assumed as maximum values.
-     * <p>
      *
      * @return the integer of the specified degree
      */
-    public static final int degreeToInt(double deg) {
+    public static int degreeToInt(double deg) {
         if (deg >= Double.MAX_VALUE)
             return Integer.MAX_VALUE;
         if (deg <= -Double.MAX_VALUE)
@@ -308,11 +307,10 @@ public class Helper {
 
     /**
      * Converts back the integer value.
-     * <p>
      *
      * @return the degree value of the specified integer
      */
-    public static final double intToDegree(int storedInt) {
+    public static double intToDegree(int storedInt) {
         if (storedInt == Integer.MAX_VALUE)
             return Double.MAX_VALUE;
         if (storedInt == -Integer.MAX_VALUE)
@@ -323,7 +321,7 @@ public class Helper {
     /**
      * Converts elevation value (in meters) into integer for storage.
      */
-    public static final int eleToInt(double ele) {
+    public static int eleToInt(double ele) {
         if (ele >= Integer.MAX_VALUE)
             return Integer.MAX_VALUE;
         return (int) (ele * ELE_FACTOR);
@@ -333,7 +331,7 @@ public class Helper {
      * Converts the integer value retrieved from storage into elevation (in meters). Do not expect
      * more precision than meters although it currently is!
      */
-    public static final double intToEle(int integEle) {
+    public static double intToEle(int integEle) {
         if (integEle == Integer.MAX_VALUE)
             return Double.MAX_VALUE;
         return integEle / ELE_FACTOR;
@@ -357,7 +355,7 @@ public class Helper {
     /**
      * This methods returns the value or min if too small or max if too big.
      */
-    public static final double keepIn(double value, double min, double max) {
+    public static double keepIn(double value, double min, double max) {
         return Math.max(min, Math.min(value, max));
     }
 
@@ -369,15 +367,15 @@ public class Helper {
         return Math.round(value * factor) / factor;
     }
 
-    public static final double round6(double value) {
+    public static double round6(double value) {
         return Math.round(value * 1e6) / 1e6;
     }
 
-    public static final double round4(double value) {
+    public static double round4(double value) {
         return Math.round(value * 1e4) / 1e4;
     }
 
-    public static final double round2(double value) {
+    public static double round2(double value) {
         return Math.round(value * 100) / 100d;
     }
 
@@ -402,18 +400,18 @@ public class Helper {
      * This method handles the specified (potentially negative) int as unsigned bit representation
      * and returns the positive converted long.
      */
-    public static final long toUnsignedLong(int x) {
+    public static long toUnsignedLong(int x) {
         return ((long) x) & 0xFFFFffffL;
     }
 
     /**
      * Converts the specified long back into a signed int (reverse method for toUnsignedLong)
      */
-    public static final int toSignedInt(long x) {
+    public static int toSignedInt(long x) {
         return (int) x;
     }
 
-    public static final String camelCaseToUnderScore(String key) {
+    public static String camelCaseToUnderScore(String key) {
         if (key.isEmpty())
             return key;
 
@@ -429,7 +427,7 @@ public class Helper {
         return sb.toString();
     }
 
-    public static final String underScoreToCamelCase(String key) {
+    public static String underScoreToCamelCase(String key) {
         if (key.isEmpty())
             return key;
 
diff --git a/api/src/main/java/com/graphhopper/util/PointList.java b/api/src/main/java/com/graphhopper/util/PointList.java
index 53fed3c0b..69aec762e 100644
--- a/api/src/main/java/com/graphhopper/util/PointList.java
+++ b/api/src/main/java/com/graphhopper/util/PointList.java
@@ -22,11 +22,15 @@ import com.graphhopper.util.shapes.GHPoint3D;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
 
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
+import static com.graphhopper.util.Helper.round2;
+import static com.graphhopper.util.Helper.round6;
+
 /**
  * Slim list to store several points (without the need for a point object). Be aware that the PointList is closely
  * coupled with the {@link ShallowImmutablePointList} both are not designed for inheritance (but final is not possible if we keep it simple).
@@ -34,6 +38,8 @@ import java.util.NoSuchElementException;
  * @author Peter Karich
  */
 public class PointList implements Iterable<GHPoint3D>, PointAccess {
+    // should be thread-safe according to https://github.com/locationtech/jts/issues/512
+    private static final GeometryFactory factory = new GeometryFactory();
     public static final PointList EMPTY = new PointList(0, true) {
         @Override
         public void set(int index, double lat, double lon, double ele) {
@@ -154,6 +160,7 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
     private double[] longitudes;
     private double[] elevations;
     private boolean isImmutable = false;
+    private LineString cachedLineString;
 
     public PointList() {
         this(10, false);
@@ -399,7 +406,6 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
     }
 
     public LineString toLineString(boolean includeElevation) {
-        GeometryFactory gf = new GeometryFactory();
         Coordinate[] coordinates = new Coordinate[getSize() == 1 ? 2 : getSize()];
         for (int i = 0; i < getSize(); i++) {
             coordinates[i] = includeElevation ?
@@ -415,15 +421,15 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
         // special case as just 1 point is not supported in the specification #1412
         if (getSize() == 1)
             coordinates[1] = coordinates[0];
-        return gf.createLineString(coordinates);
-    }
-
-    public static final double round6(double value) {
-        return Math.round(value * 1e6) / 1e6;
+        return factory.createLineString(new PackedCoordinateSequence.Double(coordinates, includeElevation ? 3 : 2));
     }
 
-    public static final double round2(double value) {
-        return Math.round(value * 100) / 100d;
+    public LineString getCachedLineString(boolean includeElevation) {
+        if (cachedLineString != null)
+            return cachedLineString;
+        if (!isImmutable)
+            throw new IllegalArgumentException("Make PointList immutable before calling getCachedLineString");
+        return cachedLineString = toLineString(includeElevation);
     }
 
     @Override
@@ -622,8 +628,9 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
      * Once immutable, there is no way to make this object mutable again. This is done to ensure the consistency of
      * shallow copies. If you need to modify this object again, you have to create a deep copy of it.
      */
-    public void makeImmutable() {
+    public PointList makeImmutable() {
         this.isImmutable = true;
+        return this;
     }
 
     private void ensureMutability() {
diff --git a/api/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java b/api/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java
index 5902e3860..dafd24954 100644
--- a/api/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java
+++ b/api/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java
@@ -87,8 +87,9 @@ public final class ShallowImmutablePointList extends PointList {
         wrappedPointList.setElevation(fromOffset + index, ele);
     }
 
-    public void makeImmutable() {
+    public PointList makeImmutable() {
         this.wrappedPointList.makeImmutable();
+        return this;
     }
 
     @Override
diff --git a/api/src/main/java/com/graphhopper/util/shapes/BBox.java b/api/src/main/java/com/graphhopper/util/shapes/BBox.java
index 9a73055f9..76f4d7b80 100644
--- a/api/src/main/java/com/graphhopper/util/shapes/BBox.java
+++ b/api/src/main/java/com/graphhopper/util/shapes/BBox.java
@@ -19,6 +19,9 @@ package com.graphhopper.util.shapes;
 
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.NumHelper;
+import com.graphhopper.util.PointList;
+import org.locationtech.jts.algorithm.RectangleLineIntersector;
+import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Envelope;
 
 import java.util.ArrayList;
@@ -161,6 +164,29 @@ public class BBox implements Shape, Cloneable {
         throw new UnsupportedOperationException("unsupported shape");
     }
 
+    public static boolean intersects(RectangleLineIntersector intersector, PointList pointList) {
+        int len = pointList.getSize();
+        if (len == 0)
+            throw new IllegalArgumentException("PointList must not be empty");
+
+        Coordinate coords = new Coordinate(pointList.getLongitude(0), pointList.getLatitude(0));
+        if (len == 1)
+            return intersector.intersects(coords, coords);
+
+        for (int pointIndex = 1; pointIndex < len; pointIndex++) {
+            Coordinate nextCoords = new Coordinate(pointList.getLongitude(pointIndex), pointList.getLatitude(pointIndex));
+            if (intersector.intersects(coords, nextCoords))
+                return true;
+            coords = nextCoords;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean intersects(PointList pointList) {
+        return intersects(new RectangleLineIntersector(toEnvelope(this)), pointList);
+    }
+
     public boolean intersects(Circle s) {
         return s.intersects(this);
     }
@@ -287,6 +313,10 @@ public class BBox implements Shape, Cloneable {
         return new BBox(envelope.getMinX(), envelope.getMaxX(), envelope.getMinY(), envelope.getMaxY());
     }
 
+    public static Envelope toEnvelope(BBox bbox) {
+        return new Envelope(bbox.minLon, bbox.maxLon, bbox.minLat, bbox.maxLat);
+    }
+
     /**
      * @return an estimated area in m^2 using the mean value of latitudes for longitude distance
      */
@@ -312,20 +342,23 @@ public class BBox implements Shape, Cloneable {
 
         double maxLat = Double.parseDouble(splittedObject[2]);
         double maxLon = Double.parseDouble(splittedObject[3]);
+        return BBox.fromPoints(minLat, minLon, maxLat, maxLon);
+    }
 
-        if (minLat > maxLat) {
-            double tmp = minLat;
-            minLat = maxLat;
-            maxLat = tmp;
+    public static BBox fromPoints(double lat1, double lon1, double lat2, double lon2) {
+        if (lat1 > lat2) {
+            double tmp = lat1;
+            lat1 = lat2;
+            lat2 = tmp;
         }
 
-        if (minLon > maxLon) {
-            double tmp = minLon;
-            minLon = maxLon;
-            maxLon = tmp;
+        if (lon1 > lon2) {
+            double tmp = lon1;
+            lon1 = lon2;
+            lon2 = tmp;
         }
 
-        return new BBox(minLon, maxLon, minLat, maxLat);
+        return new BBox(lon1, lon2, lat1, lat2);
     }
 
     /**
diff --git a/api/src/main/java/com/graphhopper/util/shapes/Circle.java b/api/src/main/java/com/graphhopper/util/shapes/Circle.java
index bbb38ee33..738d91c12 100644
--- a/api/src/main/java/com/graphhopper/util/shapes/Circle.java
+++ b/api/src/main/java/com/graphhopper/util/shapes/Circle.java
@@ -20,6 +20,7 @@ package com.graphhopper.util.shapes;
 import com.graphhopper.util.DistanceCalc;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.NumHelper;
+import com.graphhopper.util.PointList;
 
 /**
  * @author Peter Karich
@@ -30,7 +31,7 @@ public class Circle implements Shape {
     private final double lon;
     private final double normedDist;
     private final BBox bbox;
-    private DistanceCalc calc = Helper.DIST_EARTH;
+    private DistanceCalc calc;
 
     public Circle(double lat, double lon, double radiusInMeter) {
         this(lat, lon, radiusInMeter, Helper.DIST_EARTH);
@@ -83,6 +84,36 @@ public class Circle implements Shape {
         return o.intersects(this);
     }
 
+    @Override
+    public boolean intersects(PointList pointList) {
+        // similar code to LocationIndexTree.checkAdjacent
+        int len = pointList.getSize();
+        if (len == 0)
+            throw new IllegalArgumentException("PointList must not be empty");
+
+        double tmpLat = pointList.getLatitude(0);
+        double tmpLon = pointList.getLongitude(0);
+        if (len == 1)
+            return calc.calcNormalizedDist(lat, lon, tmpLat, tmpLon) <= normedDist;
+
+        for (int pointIndex = 1; pointIndex < len; pointIndex++) {
+            double wayLat = pointList.getLatitude(pointIndex);
+            double wayLon = pointList.getLongitude(pointIndex);
+
+            if (calc.validEdgeDistance(lat, lon, tmpLat, tmpLon, wayLat, wayLon)) {
+                if (calc.calcNormalizedEdgeDistance(lat, lon, tmpLat, tmpLon, wayLat, wayLon) <= normedDist)
+                    return true;
+            } else {
+                if (calc.calcNormalizedDist(lat, lon, tmpLat, tmpLon) <= normedDist
+                        || pointIndex + 1 == len && calc.calcNormalizedDist(lat, lon, wayLat, wayLon) <= normedDist)
+                    return true;
+            }
+            tmpLat = wayLat;
+            tmpLon = wayLon;
+        }
+        return false;
+    }
+
     @Override
     public boolean contains(Shape o) {
         if (o instanceof Circle) {
diff --git a/api/src/main/java/com/graphhopper/util/shapes/GHPlace.java b/api/src/main/java/com/graphhopper/util/shapes/GHPlace.java
deleted file mode 100644
index 9f776b5de..000000000
--- a/api/src/main/java/com/graphhopper/util/shapes/GHPlace.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  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.util.shapes;
-
-import com.graphhopper.util.Helper;
-
-/**
- * Specifies a place by its coordinates or name
- * <p>
- *
- * @author Peter Karich
- */
-public class GHPlace extends GHPoint {
-    private String name = "";
-
-    public GHPlace() {
-    }
-
-    public GHPlace(String name) {
-        setName(name);
-    }
-
-    public GHPlace(double lat, double lon) {
-        super(lat, lon);
-    }
-
-    public void setValue(String t) {
-        setName(t);
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public GHPlace setName(String name) {
-        this.name = name;
-        return this;
-    }
-
-    public boolean isValidName() {
-        return !Helper.isEmpty(name);
-    }
-
-    @Override
-    public String toString() {
-        String str = "";
-        if (isValidName())
-            str += name;
-
-        if (isValid())
-            str += " " + lat + ", " + lon;
-
-        return str.trim();
-    }
-}
diff --git a/api/src/main/java/com/graphhopper/util/shapes/Polygon.java b/api/src/main/java/com/graphhopper/util/shapes/Polygon.java
index abf7c731c..ff61f1249 100644
--- a/api/src/main/java/com/graphhopper/util/shapes/Polygon.java
+++ b/api/src/main/java/com/graphhopper/util/shapes/Polygon.java
@@ -17,117 +17,72 @@
  */
 package com.graphhopper.util.shapes;
 
+import com.graphhopper.util.PointList;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
+import org.locationtech.jts.geom.prep.PreparedGeometry;
+import org.locationtech.jts.geom.prep.PreparedPolygon;
+
+import java.util.Arrays;
+
 /**
  * This class represents a polygon that is defined by a set of points.
  * Every point i is connected to point i-1 and i+1.
- * <p>
- * TODO: Howto design inner rings in the polygon?
  *
  * @author Robin Boldt
  */
 public class Polygon implements Shape {
 
-    private final double[] lat;
-    private final double[] lon;
+    private final GeometryFactory factory = new GeometryFactory();
+    private final PreparedGeometry prepPolygon;
+    private final Envelope envelope;
 
-    private double minLat;
-    private double minLon;
-    private double maxLat;
-    private double maxLon;
+    public Polygon(PreparedGeometry prepPolygon) {
+        this.prepPolygon = prepPolygon;
+        this.envelope = prepPolygon.getGeometry().getEnvelopeInternal();
+    }
 
-    private final double epsilon;
+    public Polygon(double[] lats, double[] lons) {
+        if (lats.length != lons.length)
+            throw new IllegalArgumentException("Points must be of equal length but was " + lats.length + " vs. " + lons.length);
 
-    public Polygon(double[] lat, double[] lon, double growFactor) {
-        if (lat.length != lon.length) {
-            throw new IllegalArgumentException("Points must be of equal length but was " + lat.length + " vs. " + lon.length);
-        }
-        if (lat.length == 0) {
+        if (lats.length == 0)
             throw new IllegalArgumentException("Points must not be empty");
-        }
-        this.lat = lat;
-        this.lon = lon;
-
-        for (int i = 0; i < lat.length; i++) {
-            if (i == 0) {
-                minLat = lat[i];
-                maxLat = lat[i];
-                minLon = lon[i];
-                maxLon = lon[i];
-            } else {
-                if (lat[i] < minLat) {
-                    minLat = lat[i];
-                } else if (lat[i] > maxLat) {
-                    maxLat = lat[i];
-                }
-                if (lon[i] < minLon) {
-                    minLon = lon[i];
-                } else if (lon[i] > maxLon) {
-                    maxLon = lon[i];
-                }
-            }
-        }
 
-        minLat -= growFactor;
-        minLon -= growFactor;
-        maxLat += growFactor;
-        maxLon += growFactor;
-
-        epsilon = (maxLat - minLat) / 10;
-    }
-
-    public Polygon(double[] lat, double[] lon) {
-        this(lat, lon, 0);
-    }
-
-    /**
-     * Lossy conversion to a GraphHopper Polygon.
-     */
-    public static Polygon create(org.locationtech.jts.geom.Polygon polygon) {
-        double[] lats = new double[polygon.getNumPoints()];
-        double[] lons = new double[polygon.getNumPoints()];
-        for (int i = 0; i < polygon.getNumPoints(); i++) {
-            lats[i] = polygon.getCoordinates()[i].y;
-            lons[i] = polygon.getCoordinates()[i].x;
+        Coordinate[] coordinates = new Coordinate[lats.length + 1];
+        for (int i = 0; i < lats.length; i++) {
+            coordinates[i] = new Coordinate(lons[i], lats[i]);
         }
-        return new Polygon(lats, lons);
+        coordinates[lats.length] = coordinates[0];
+        this.prepPolygon = new PreparedPolygon(factory.createPolygon(new PackedCoordinateSequence.Double(coordinates, 2)));
+        this.envelope = prepPolygon.getGeometry().getEnvelopeInternal();
     }
 
-    /**
-     * Wrapper method for {@link Polygon#contains(double, double)}.
-     */
-    public boolean contains(GHPoint point) {
-        return contains(point.lat, point.lon);
+    public static Polygon create(org.locationtech.jts.geom.Polygon polygon) {
+        return new Polygon(new PreparedPolygon(polygon));
     }
 
     @Override
     public boolean intersects(Shape o) {
-        throw new UnsupportedOperationException("Not supported yet.");
+        throw new IllegalArgumentException("Polygon.intersects: shape not supported");
+    }
+
+    public boolean intersects(PointList pointList) {
+        return prepPolygon.intersects(pointList.getCachedLineString(false));
     }
 
     /**
-     * Implements the ray casting algorithm
-     * Code is inspired from here: http://stackoverflow.com/a/218081/1548788
+     * Does the point in polygon check.
      *
      * @param lat Latitude of the point to be checked
      * @param lon Longitude of the point to be checked
      * @return true if point is inside polygon
      */
     public boolean contains(double lat, double lon) {
-        if (lat < minLat || lat > maxLat || lon < minLon || lon > maxLon) {
-            return false;
-        }
-
-        double rayStartLat = maxLat - (minLat / 2);
-        double rayStartLon = minLon - epsilon;
-
-        boolean inside = false;
-        int len = this.lat.length;
-        for (int i = 0; i < len; i++) {
-            if (edgesAreIntersecting(rayStartLon, rayStartLat, lon, lat, this.lon[i], this.lat[i], this.lon[(i + 1) % len], this.lat[(i + 1) % len]))
-                inside = !inside;
-        }
-        return inside;
-
+        return prepPolygon.contains(factory.createPoint(new Coordinate(lon, lat)));
     }
 
     @Override
@@ -137,115 +92,55 @@ public class Polygon implements Shape {
 
     @Override
     public BBox getBounds() {
-        return new BBox(minLon, maxLon, minLat, maxLat);
+        return new BBox(envelope.getMinX(), envelope.getMaxX(), envelope.getMinY(), envelope.getMaxY());
     }
 
     @Override
     public GHPoint getCenter() {
-        return new GHPoint((maxLat + minLat) / 2, (maxLon + minLon) / 2);
+        Point centroid = prepPolygon.getGeometry().getCentroid();
+        return new GHPoint(centroid.getY(), centroid.getX());
     }
 
     @Override
     public double calculateArea() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    private boolean edgesAreIntersecting(
-            double v1x1, double v1y1, double v1x2, double v1y2,
-            double v2x1, double v2y1, double v2x2, double v2y2
-    ) {
-
-
-        double d1, d2;
-        double a1, a2, b1, b2, c1, c2;
-
-        // Convert vector 1 to a line (line 1) of infinite length.
-        // We want the line in linear equation standard form: A*x + B*y + C = 0
-        // See: http://en.wikipedia.org/wiki/Linear_equation
-        a1 = v1y2 - v1y1;
-        b1 = v1x1 - v1x2;
-        c1 = (v1x2 * v1y1) - (v1x1 * v1y2);
-
-        // Every point (x,y), that solves the equation above, is on the line,
-        // every point that does not solve it, is not. The equation will have a
-        // positive result if it is on one side of the line and a negative one
-        // if is on the other side of it. We insert (x1,y1) and (x2,y2) of vector
-        // 2 into the equation above.
-        d1 = (a1 * v2x1) + (b1 * v2y1) + c1;
-        d2 = (a1 * v2x2) + (b1 * v2y2) + c1;
-
-        // If d1 and d2 both have the same sign, they are both on the same side
-        // of our line 1 and in that case no intersection is possible. Careful,
-        // 0 is a special case, that's why we don't test ">=" and "<=",
-        // but "<" and ">".
-        if (d1 > 0 && d2 > 0) return false;
-        if (d1 < 0 && d2 < 0) return false;
-
-        // The fact that vector 2 intersected the infinite line 1 above doesn't
-        // mean it also intersects the vector 1. Vector 1 is only a subset of that
-        // infinite line 1, so it may have intersected that line before the vector
-        // started or after it ended. To know for sure, we have to repeat the
-        // the same test the other way round. We start by calculating the
-        // infinite line 2 in linear equation standard form.
-        a2 = v2y2 - v2y1;
-        b2 = v2x1 - v2x2;
-        c2 = (v2x2 * v2y1) - (v2x1 * v2y2);
-
-        // Calculate d1 and d2 again, this time using points of vector 1.
-        d1 = (a2 * v1x1) + (b2 * v1y1) + c2;
-        d2 = (a2 * v1x2) + (b2 * v1y2) + c2;
-
-        // Again, if both have the same sign (and neither one is 0),
-        // no intersection is possible.
-        if (d1 > 0 && d2 > 0) return false;
-        if (d1 < 0 && d2 < 0) return false;
-
-        // If we get here, only two possibilities are left. Either the two
-        // vectors intersect in exactly one point or they are collinear, which
-        // means they intersect in any number of points from zero to infinite.
-        if ((a1 * b2) - (a2 * b1) == 0) return false;
-
-        // If they are not collinear, they must intersect in exactly one point.
-        return true;
+        // for estimation use bounding box as reference:
+        return getBounds().calculateArea() * envelope.getArea() / prepPolygon.getGeometry().getArea();
     }
 
     public double getMinLat() {
-        return minLat;
+        return envelope.getMinY();
     }
 
     public double getMinLon() {
-        return minLon;
+        return envelope.getMinX();
     }
 
     public double getMaxLat() {
-        return maxLat;
+        return envelope.getMaxY();
     }
 
     public double getMaxLon() {
-        return maxLon;
+        return envelope.getMaxX();
     }
 
     @Override
     public String toString() {
-        return "polygon (" + lat.length + " points)";
+        return "polygon (" + prepPolygon.getGeometry().getNumPoints() + " points," + prepPolygon.getGeometry().getNumGeometries() + " geometries)";
     }
 
-    public static Polygon parsePoints(String pointsStr, double growFactor) {
+    public static Polygon parsePoints(String pointsStr) {
         String[] arr = pointsStr.split(",");
+        if (arr.length % 2 == 1)
+            throw new IllegalArgumentException("incorrect polygon specified: " + Arrays.asList(arr));
 
-        if (arr.length % 2 == 1) throw new IllegalArgumentException("incorrect polygon specified");
-
-        double[] lats = new double[arr.length /2];
-        double[] lons = new double[arr.length /2];
-
-        for (int j = 0; j < arr.length; j++) {
-            if (j % 2 == 0) {
-                lats[j / 2] = Double.parseDouble(arr[j]);
-            } else {
-                lons[(j - 1) / 2] = Double.parseDouble(arr[j]);
-            }
+        Coordinate[] coordinates = new Coordinate[arr.length / 2 + 1];
+        for (int i = 0; i < coordinates.length - 1; i++) {
+            int arrIndex = i * 2;
+            coordinates[i] = new Coordinate(Double.parseDouble(arr[arrIndex + 1]), Double.parseDouble(arr[arrIndex]));
         }
+        coordinates[coordinates.length - 1] = coordinates[0];
 
-        return new Polygon(lats, lons, growFactor);
+        // store more efficient
+        return new Polygon(new PreparedPolygon(new GeometryFactory().createPolygon(new PackedCoordinateSequence.Double(coordinates, 2))));
     }
 }
diff --git a/api/src/main/java/com/graphhopper/util/shapes/Shape.java b/api/src/main/java/com/graphhopper/util/shapes/Shape.java
index 7962c6761..039a00f40 100644
--- a/api/src/main/java/com/graphhopper/util/shapes/Shape.java
+++ b/api/src/main/java/com/graphhopper/util/shapes/Shape.java
@@ -17,9 +17,10 @@
  */
 package com.graphhopper.util.shapes;
 
+import com.graphhopper.util.PointList;
+
 /**
- * A shape interface to implement circles or rectangles.
- * <p>
+ * A shape interface to implement circles, polygons or rectangles.
  *
  * @author Peter Karich
  */
@@ -29,6 +30,8 @@ public interface Shape {
      */
     boolean intersects(Shape o);
 
+    boolean intersects(PointList pointList);
+
     /**
      * @return true only if lat and lon are inside (or on the edge) of this shape
      */
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index e678d23e0..202c9d202 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -67,6 +67,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import static com.graphhopper.routing.ch.CHAlgoFactoryDecorator.EdgeBasedCHMode.EDGE_OR_NODE;
 import static com.graphhopper.routing.ch.CHAlgoFactoryDecorator.EdgeBasedCHMode.OFF;
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
 import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
 import static com.graphhopper.util.Helper.*;
 import static com.graphhopper.util.Parameters.Algorithms.*;
@@ -827,10 +828,10 @@ public class GraphHopper implements GraphHopperAPI {
 
                     CHAlgoFactoryDecorator.EdgeBasedCHMode edgeBasedCHMode = chFactoryDecorator.getEdgeBasedCHMode();
                     if (!(edgeBasedCHMode == EDGE_OR_NODE && encoder.supportsTurnCosts())) {
-                        chFactoryDecorator.addCHProfile(CHProfile.nodeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null)));
+                        chFactoryDecorator.addCHProfile(CHProfile.nodeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null, NO_TURN_COST_PROVIDER)));
                     }
                     if (edgeBasedCHMode != OFF && encoder.supportsTurnCosts()) {
-                        chFactoryDecorator.addCHProfile(CHProfile.edgeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null), uTurnCosts));
+                        chFactoryDecorator.addCHProfile(CHProfile.edgeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null, new DefaultTurnCostProvider(encoder, ghStorage.getTurnCostStorage(), uTurnCosts))));
                     }
                 }
             }
@@ -847,7 +848,8 @@ public class GraphHopper implements GraphHopperAPI {
 
         for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
             for (String lmWeightingStr : lmFactoryDecorator.getWeightingsAsStrings()) {
-                Weighting weighting = createWeighting(new HintsMap(lmWeightingStr), encoder, null);
+                // note that we do not consider turn costs during LM preparation?
+                Weighting weighting = createWeighting(new HintsMap(lmWeightingStr), encoder, null, NO_TURN_COST_PROVIDER);
                 lmFactoryDecorator.addWeighting(weighting);
             }
         }
@@ -936,23 +938,23 @@ public class GraphHopper implements GraphHopperAPI {
      * @return the weighting to be used for route calculation
      * @see HintsMap
      */
-    public Weighting createWeighting(HintsMap hintsMap, FlagEncoder encoder, Graph graph) {
+    public Weighting createWeighting(HintsMap hintsMap, FlagEncoder encoder, Graph graph, TurnCostProvider turnCostProvider) {
         String weightingStr = toLowerCase(hintsMap.getWeighting());
         Weighting weighting = null;
 
         if ("shortest".equalsIgnoreCase(weightingStr)) {
-            weighting = new ShortestWeighting(encoder);
+            weighting = new ShortestWeighting(encoder, turnCostProvider);
         } else if ("fastest".equalsIgnoreCase(weightingStr) || weightingStr.isEmpty()) {
             if (encoder.supports(PriorityWeighting.class))
-                weighting = new PriorityWeighting(encoder, hintsMap);
+                weighting = new PriorityWeighting(encoder, hintsMap, turnCostProvider);
             else
-                weighting = new FastestWeighting(encoder, hintsMap);
+                weighting = new FastestWeighting(encoder, hintsMap, turnCostProvider);
         } else if ("curvature".equalsIgnoreCase(weightingStr)) {
             if (encoder.supports(CurvatureWeighting.class))
-                weighting = new CurvatureWeighting(encoder, hintsMap);
+                weighting = new CurvatureWeighting(encoder, hintsMap, turnCostProvider);
 
         } else if ("short_fastest".equalsIgnoreCase(weightingStr)) {
-            weighting = new ShortFastestWeighting(encoder, hintsMap);
+            weighting = new ShortFastestWeighting(encoder, hintsMap, turnCostProvider);
         }
 
         if (weighting == null)
@@ -968,16 +970,6 @@ public class GraphHopper implements GraphHopperAPI {
         return weighting;
     }
 
-    /**
-     * Potentially wraps the specified weighting into a TurnWeighting instance.
-     */
-    public Weighting createTurnWeighting(Graph graph, Weighting weighting, TraversalMode tMode, double uTurnCosts) {
-        FlagEncoder encoder = weighting.getFlagEncoder();
-        if (encoder.supportsTurnCosts() && tMode.isEdgeBased())
-            return new TurnWeighting(weighting, graph.getTurnCostStorage(), uTurnCosts);
-        return weighting;
-    }
-
     @Override
     public GHResponse route(GHRequest request) {
         GHResponse response = new GHResponse();
@@ -1050,88 +1042,80 @@ public class GraphHopper implements GraphHopperAPI {
             else
                 routingTemplate = new ViaRoutingTemplate(request, ghRsp, locationIndex, encodingManager);
 
-            List<Path> altPaths = null;
-            int maxRetries = routingTemplate.getMaxRetries();
-            Locale locale = request.getLocale();
-            Translation tr = trMap.getWithFallBack(locale);
-            for (int i = 0; i < maxRetries; i++) {
-                StopWatch sw = new StopWatch().start();
-                List<QueryResult> qResults = routingTemplate.lookup(points, encoder);
-                ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
-                if (ghRsp.hasErrors())
-                    return Collections.emptyList();
-
-                RoutingAlgorithmFactory tmpAlgoFactory = getAlgorithmFactory(hints);
-                Weighting weighting;
-                QueryGraph queryGraph;
-
-                if (chFactoryDecorator.isEnabled() && !disableCH) {
-                    if (request.hasFavoredHeading(0))
-                        throw new IllegalArgumentException("The 'heading' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`. See issue #483");
-
-                    if (request.getHints().getBool(Routing.PASS_THROUGH, false)) {
-                        throw new IllegalArgumentException("The '" + Parameters.Routing.PASS_THROUGH + "' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`. See issue #1765");
-                    }
-                    // if LM is enabled we have the LMFactory with the CH algo!
-                    RoutingAlgorithmFactory chAlgoFactory = tmpAlgoFactory;
-                    if (tmpAlgoFactory instanceof LMAlgoFactoryDecorator.LMRAFactory)
-                        chAlgoFactory = ((LMAlgoFactoryDecorator.LMRAFactory) tmpAlgoFactory).getDefaultAlgoFactory();
-
-                    if (chAlgoFactory instanceof CHRoutingAlgorithmFactory) {
-                        CHProfile chProfile = ((CHRoutingAlgorithmFactory) chAlgoFactory).getCHProfile();
-                        queryGraph = QueryGraph.lookup(ghStorage.getCHGraph(chProfile), qResults);
-                        weighting = chProfile.getWeighting();
-                    } else {
-                        throw new IllegalStateException("Although CH was enabled a non-CH algorithm factory was returned " + tmpAlgoFactory);
-                    }
+            StopWatch sw = new StopWatch().start();
+            List<QueryResult> qResults = routingTemplate.lookup(points, encoder);
+            ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
+            if (ghRsp.hasErrors())
+                return Collections.emptyList();
+
+            final int uTurnCostsInt = request.getHints().getInt(Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS);
+            if (uTurnCostsInt != INFINITE_U_TURN_COSTS && !tMode.isEdgeBased()) {
+                throw new IllegalArgumentException("Finite u-turn costs can only be used for edge-based routing, use `" + Routing.EDGE_BASED + "=true'");
+            }
+            TurnCostProvider turnCostProvider = (encoder.supportsTurnCosts() && tMode.isEdgeBased())
+                    ? new DefaultTurnCostProvider(encoder, ghStorage.getTurnCostStorage(), uTurnCostsInt)
+                    : NO_TURN_COST_PROVIDER;
+
+            RoutingAlgorithmFactory tmpAlgoFactory = getAlgorithmFactory(hints);
+            Weighting weighting;
+            QueryGraph queryGraph;
+
+            if (chFactoryDecorator.isEnabled() && !disableCH) {
+                if (request.hasFavoredHeading(0))
+                    throw new IllegalArgumentException("The 'heading' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`. See issue #483");
+
+                if (request.getHints().getBool(Routing.PASS_THROUGH, false)) {
+                    throw new IllegalArgumentException("The '" + Parameters.Routing.PASS_THROUGH + "' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`. See issue #1765");
+                }
+                // if LM is enabled we have the LMFactory with the CH algo!
+                RoutingAlgorithmFactory chAlgoFactory = tmpAlgoFactory;
+                if (tmpAlgoFactory instanceof LMAlgoFactoryDecorator.LMRAFactory)
+                    chAlgoFactory = ((LMAlgoFactoryDecorator.LMRAFactory) tmpAlgoFactory).getDefaultAlgoFactory();
+
+                if (chAlgoFactory instanceof CHRoutingAlgorithmFactory) {
+                    CHProfile chProfile = ((CHRoutingAlgorithmFactory) chAlgoFactory).getCHProfile();
+                    queryGraph = QueryGraph.lookup(ghStorage.getCHGraph(chProfile), qResults);
+                    weighting = chProfile.getWeighting();
                 } else {
-                    checkNonChMaxWaypointDistance(points);
-                    queryGraph = QueryGraph.lookup(ghStorage, qResults);
-                    weighting = createWeighting(hints, encoder, queryGraph);
+                    throw new IllegalStateException("Although CH was enabled a non-CH algorithm factory was returned " + tmpAlgoFactory);
                 }
-                ghRsp.addDebugInfo("tmode:" + tMode.toString());
+            } else {
+                checkNonChMaxWaypointDistance(points);
+                queryGraph = QueryGraph.lookup(ghStorage, qResults);
+                weighting = createWeighting(hints, encoder, queryGraph, turnCostProvider);
+            }
+            ghRsp.addDebugInfo("tmode:" + tMode.toString());
 
-                int maxVisitedNodesForRequest = hints.getInt(Routing.MAX_VISITED_NODES, maxVisitedNodes);
-                if (maxVisitedNodesForRequest > maxVisitedNodes)
-                    throw new IllegalArgumentException("The max_visited_nodes parameter has to be below or equal to:" + maxVisitedNodes);
+            int maxVisitedNodesForRequest = hints.getInt(Routing.MAX_VISITED_NODES, maxVisitedNodes);
+            if (maxVisitedNodesForRequest > maxVisitedNodes)
+                throw new IllegalArgumentException("The max_visited_nodes parameter has to be below or equal to:" + maxVisitedNodes);
 
-                int uTurnCostInt = request.getHints().getInt(Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS);
-                if (uTurnCostInt != INFINITE_U_TURN_COSTS && !tMode.isEdgeBased()) {
-                    throw new IllegalArgumentException("Finite u-turn costs can only be used for edge-based routing, use `" + Routing.EDGE_BASED + "=true'");
-                }
-                double uTurnCosts = uTurnCostInt == INFINITE_U_TURN_COSTS ? Double.POSITIVE_INFINITY : uTurnCostInt;
-                weighting = createTurnWeighting(queryGraph, weighting, tMode, uTurnCosts);
-
-                AlgorithmOptions algoOpts = AlgorithmOptions.start().
-                        algorithm(algoStr).traversalMode(tMode).weighting(weighting).
-                        maxVisitedNodes(maxVisitedNodesForRequest).
-                        hints(hints).
-                        build();
-
-                // do the actual route calculation !
-                altPaths = routingTemplate.calcPaths(queryGraph, tmpAlgoFactory, algoOpts, encoder);
-
-                boolean tmpEnableInstructions = hints.getBool(Routing.INSTRUCTIONS, getEncodingManager().isEnableInstructions());
-                boolean tmpCalcPoints = hints.getBool(Routing.CALC_POINTS, calcPoints);
-                double wayPointMaxDistance = hints.getDouble(Routing.WAY_POINT_MAX_DISTANCE, 1d);
-
-                DouglasPeucker peucker = new DouglasPeucker().setMaxDistance(wayPointMaxDistance);
-                PathMerger pathMerger = new PathMerger(queryGraph.getBaseGraph(), weighting).
-                        setCalcPoints(tmpCalcPoints).
-                        setDouglasPeucker(peucker).
-                        setEnableInstructions(tmpEnableInstructions).
-                        setPathDetailsBuilders(pathBuilderFactory, request.getPathDetails()).
-                        setSimplifyResponse(simplifyResponse && wayPointMaxDistance > 0);
+            AlgorithmOptions algoOpts = AlgorithmOptions.start().
+                    algorithm(algoStr).traversalMode(tMode).weighting(weighting).
+                    maxVisitedNodes(maxVisitedNodesForRequest).
+                    hints(hints).
+                    build();
 
-                if (request.hasFavoredHeading(0))
-                    pathMerger.setFavoredHeading(request.getFavoredHeading(0));
+            // do the actual route calculation !
+            List<Path> altPaths = routingTemplate.calcPaths(queryGraph, tmpAlgoFactory, algoOpts, encoder);
 
-                if (routingTemplate.isReady(pathMerger, tr))
-                    break;
-            }
+            boolean tmpEnableInstructions = hints.getBool(Routing.INSTRUCTIONS, getEncodingManager().isEnableInstructions());
+            boolean tmpCalcPoints = hints.getBool(Routing.CALC_POINTS, calcPoints);
+            double wayPointMaxDistance = hints.getDouble(Routing.WAY_POINT_MAX_DISTANCE, 1d);
 
-            return altPaths;
+            DouglasPeucker peucker = new DouglasPeucker().setMaxDistance(wayPointMaxDistance);
+            PathMerger pathMerger = new PathMerger(queryGraph.getBaseGraph(), weighting).
+                    setCalcPoints(tmpCalcPoints).
+                    setDouglasPeucker(peucker).
+                    setEnableInstructions(tmpEnableInstructions).
+                    setPathDetailsBuilders(pathBuilderFactory, request.getPathDetails()).
+                    setSimplifyResponse(simplifyResponse && wayPointMaxDistance > 0);
 
+            if (request.hasFavoredHeading(0))
+                pathMerger.setFavoredHeading(request.getFavoredHeading(0));
+
+            routingTemplate.finish(pathMerger, trMap.getWithFallBack(request.getLocale()));
+            return altPaths;
         } catch (IllegalArgumentException ex) {
             ghRsp.addError(ex);
             return Collections.emptyList();
diff --git a/core/src/main/java/com/graphhopper/routing/AStar.java b/core/src/main/java/com/graphhopper/routing/AStar.java
index 275f31e1d..d9c722a19 100644
--- a/core/src/main/java/com/graphhopper/routing/AStar.java
+++ b/core/src/main/java/com/graphhopper/routing/AStar.java
@@ -24,10 +24,7 @@ import com.graphhopper.routing.weighting.WeightApproximator;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.SPTEntry;
-import com.graphhopper.util.EdgeIterator;
-import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.Helper;
-import com.graphhopper.util.Parameters;
+import com.graphhopper.util.*;
 
 import java.util.PriorityQueue;
 
@@ -98,7 +95,9 @@ public class AStar extends AbstractRoutingAlgorithm {
                     continue;
 
                 // todo: for #1776/#1835 move the access check into weighting
-                double tmpWeight = !outEdgeFilter.accept(iter) ? Double.POSITIVE_INFINITY : (weighting.calcWeight(iter, false, currEdge.edge) + currEdge.weightOfVisitedPath);
+                double tmpWeight = !outEdgeFilter.accept(iter)
+                        ? Double.POSITIVE_INFINITY
+                        : (GHUtility.calcWeightWithTurnWeight(weighting, iter, false, currEdge.edge) + currEdge.weightOfVisitedPath);
                 if (Double.isInfinite(tmpWeight)) {
                     continue;
                 }
diff --git a/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java b/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java
index 9d364400d..1686b9fd0 100644
--- a/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java
+++ b/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java
@@ -28,6 +28,7 @@ import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.util.EdgeExplorer;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 
 import java.util.PriorityQueue;
 
@@ -218,7 +219,7 @@ public abstract class AbstractNonCHBidirAlgo extends AbstractBidirAlgo implement
         if (!access) {
             return Double.POSITIVE_INFINITY;
         }
-        return weighting.calcWeight(iter, reverse, getIncomingEdge(currEdge)) + currEdge.getWeightOfVisitedPath();
+        return GHUtility.calcWeightWithTurnWeight(weighting, iter, reverse, getIncomingEdge(currEdge)) + currEdge.getWeightOfVisitedPath();
     }
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/routing/BidirPathExtractor.java b/core/src/main/java/com/graphhopper/routing/BidirPathExtractor.java
index 385e5d1ca..0edbe35e4 100644
--- a/core/src/main/java/com/graphhopper/routing/BidirPathExtractor.java
+++ b/core/src/main/java/com/graphhopper/routing/BidirPathExtractor.java
@@ -18,12 +18,12 @@
 
 package com.graphhopper.routing;
 
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.StopWatch;
 
 /**
@@ -113,7 +113,7 @@ public class BidirPathExtractor {
     protected void onEdge(int edge, int adjNode, boolean reverse, int prevOrNextEdge) {
         EdgeIteratorState edgeState = graph.getEdgeIteratorState(edge, adjNode);
         path.addDistance(edgeState.getDistance());
-        path.addTime(weighting.calcMillis(edgeState, reverse, prevOrNextEdge));
+        path.addTime(GHUtility.calcMillisWithTurnMillis(weighting, edgeState, reverse, prevOrNextEdge));
         path.addEdge(edge);
     }
 
@@ -121,9 +121,7 @@ public class BidirPathExtractor {
         if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge)) {
             return;
         }
-        if (weighting instanceof TurnWeighting) {
-            path.addTime(((TurnWeighting) weighting).calcTurnMillis(inEdge, viaNode, outEdge));
-        }
+        path.addTime(weighting.calcTurnMillis(inEdge, viaNode, outEdge));
     }
 
 }
diff --git a/core/src/main/java/com/graphhopper/routing/Dijkstra.java b/core/src/main/java/com/graphhopper/routing/Dijkstra.java
index 2587d5170..85d34c3bb 100644
--- a/core/src/main/java/com/graphhopper/routing/Dijkstra.java
+++ b/core/src/main/java/com/graphhopper/routing/Dijkstra.java
@@ -25,6 +25,7 @@ import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.Parameters;
 
 import java.util.PriorityQueue;
@@ -79,7 +80,9 @@ public class Dijkstra extends AbstractRoutingAlgorithm {
                     continue;
 
                 // todo: for #1776/#1835 move the access check into weighting
-                double tmpWeight = !outEdgeFilter.accept(iter) ? Double.POSITIVE_INFINITY : (weighting.calcWeight(iter, false, currEdge.edge) + currEdge.weight);
+                double tmpWeight = !outEdgeFilter.accept(iter)
+                        ? Double.POSITIVE_INFINITY
+                        : (GHUtility.calcWeightWithTurnWeight(weighting, iter, false, currEdge.edge) + currEdge.weight);
                 if (Double.isInfinite(tmpWeight)) {
                     continue;
                 }
diff --git a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java
index 7e3bb2342..7814d4fec 100644
--- a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java
+++ b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java
@@ -22,10 +22,7 @@ import com.graphhopper.apache.commons.collections.IntDoubleBinaryHeap;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
-import com.graphhopper.util.EdgeIterator;
-import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.Helper;
-import com.graphhopper.util.Parameters;
+import com.graphhopper.util.*;
 
 import java.util.Arrays;
 
@@ -174,7 +171,9 @@ public class DijkstraOneToMany extends AbstractRoutingAlgorithm {
                 if (!accept(iter, prevEdgeId))
                     continue;
 
-                double tmpWeight = !outEdgeFilter.accept(iter) ? Double.POSITIVE_INFINITY : (weighting.calcWeight(iter, false, prevEdgeId) + weights[currNode]);
+                double tmpWeight = !outEdgeFilter.accept(iter)
+                        ? Double.POSITIVE_INFINITY
+                        : (GHUtility.calcWeightWithTurnWeight(weighting, iter, false, prevEdgeId) + weights[currNode]);
                 if (Double.isInfinite(tmpWeight))
                     continue;
 
diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java
index ec0eac8aa..1d23f95d3 100644
--- a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java
+++ b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java
@@ -508,7 +508,7 @@ public class InstructionsFromEdges implements Path.EdgeVisitor {
         }
         double newDist = edge.getDistance();
         prevInstruction.setDistance(newDist + prevInstruction.getDistance());
-        // todonow: why do we not account for turn times here ?
+        // todo: why do we not account for turn times here ?
         prevInstruction.setTime(weighting.calcEdgeMillis(edge, false) + prevInstruction.getTime());
     }
 
diff --git a/core/src/main/java/com/graphhopper/routing/PathExtractor.java b/core/src/main/java/com/graphhopper/routing/PathExtractor.java
index d07e4a72d..83dda7458 100644
--- a/core/src/main/java/com/graphhopper/routing/PathExtractor.java
+++ b/core/src/main/java/com/graphhopper/routing/PathExtractor.java
@@ -23,6 +23,7 @@ import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.StopWatch;
 
 public class PathExtractor {
@@ -78,7 +79,7 @@ public class PathExtractor {
     protected void onEdge(int edge, int adjNode, int prevEdge) {
         EdgeIteratorState edgeState = graph.getEdgeIteratorState(edge, adjNode);
         path.addDistance(edgeState.getDistance());
-        path.addTime(weighting.calcMillis(edgeState, false, prevEdge));
+        path.addTime(GHUtility.calcMillisWithTurnMillis(weighting, edgeState, false, prevEdge));
         path.addEdge(edge);
     }
 
diff --git a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java
index da676338d..9b21170ba 100644
--- a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java
+++ b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java
@@ -19,6 +19,7 @@ package com.graphhopper.routing;
 
 import com.graphhopper.routing.weighting.BeelineWeightApproximator;
 import com.graphhopper.routing.weighting.WeightApproximator;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.NodeAccess;
 import com.graphhopper.util.Helper;
@@ -37,27 +38,28 @@ public class RoutingAlgorithmFactorySimple implements RoutingAlgorithmFactory {
     public RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts) {
         RoutingAlgorithm ra;
         String algoStr = opts.getAlgorithm();
+        Weighting weighting = g.wrapWeighting(opts.getWeighting());
         if (DIJKSTRA_BI.equalsIgnoreCase(algoStr)) {
-            ra = new DijkstraBidirectionRef(g, opts.getWeighting(), opts.getTraversalMode());
+            ra = new DijkstraBidirectionRef(g, weighting, opts.getTraversalMode());
         } else if (DIJKSTRA.equalsIgnoreCase(algoStr)) {
-            ra = new Dijkstra(g, opts.getWeighting(), opts.getTraversalMode());
+            ra = new Dijkstra(g, weighting, opts.getTraversalMode());
 
         } else if (ASTAR_BI.equalsIgnoreCase(algoStr)) {
-            AStarBidirection aStarBi = new AStarBidirection(g, opts.getWeighting(),
+            AStarBidirection aStarBi = new AStarBidirection(g, weighting,
                     opts.getTraversalMode());
             aStarBi.setApproximation(getApproximation(ASTAR_BI, opts, g.getNodeAccess()));
             ra = aStarBi;
 
         } else if (DIJKSTRA_ONE_TO_MANY.equalsIgnoreCase(algoStr)) {
-            ra = new DijkstraOneToMany(g, opts.getWeighting(), opts.getTraversalMode());
+            ra = new DijkstraOneToMany(g, weighting, opts.getTraversalMode());
 
         } else if (ASTAR.equalsIgnoreCase(algoStr)) {
-            AStar aStar = new AStar(g, opts.getWeighting(), opts.getTraversalMode());
+            AStar aStar = new AStar(g, weighting, opts.getTraversalMode());
             aStar.setApproximation(getApproximation(ASTAR, opts, g.getNodeAccess()));
             ra = aStar;
 
         } else if (ALT_ROUTE.equalsIgnoreCase(algoStr)) {
-            AlternativeRoute altRouteAlgo = new AlternativeRoute(g, opts.getWeighting(), opts.getTraversalMode());
+            AlternativeRoute altRouteAlgo = new AlternativeRoute(g, weighting, opts.getTraversalMode());
             altRouteAlgo.setMaxPaths(opts.getHints().getInt(MAX_PATHS, 2));
             altRouteAlgo.setMaxWeightFactor(opts.getHints().getDouble(MAX_WEIGHT, 1.4));
             altRouteAlgo.setMaxShareFactor(opts.getHints().getDouble(MAX_SHARE, 0.6));
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java b/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java
index f2a051e98..33a53512f 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java
@@ -59,9 +59,6 @@ public class CHProfileSelector {
             if (edgeBased != null && p.isEdgeBased() != edgeBased) {
                 continue;
             }
-            if (uTurnCosts != null && p.getUTurnCostsInt() != uTurnCosts) {
-                continue;
-            }
             if (!p.getWeighting().matches(hintsMap)) {
                 continue;
             }
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java b/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java
index b10b3899f..6fefe278b 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java
@@ -19,7 +19,6 @@
 package com.graphhopper.routing.ch;
 
 import com.graphhopper.routing.*;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 
@@ -53,8 +52,7 @@ public class CHRoutingAlgorithmFactory implements RoutingAlgorithmFactory {
             if (turnCostStorage == null) {
                 throw new IllegalArgumentException("For edge-based CH you need a turn cost extension");
             }
-            TurnWeighting turnWeighting = new TurnWeighting(getWeighting(), turnCostStorage, chProfile.getUTurnCosts());
-            RoutingCHGraph g = new RoutingCHGraphImpl(graph, getWeighting(), turnWeighting);
+            RoutingCHGraph g = new RoutingCHGraphImpl(graph, graph.wrapWeighting(getWeighting()));
             return createAlgoEdgeBased(g, opts);
         } else {
             RoutingCHGraph g = new RoutingCHGraphImpl(graph, chProfile.getWeighting());
diff --git a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedCHBidirPathExtractor.java b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedCHBidirPathExtractor.java
index b3dc09116..315dbeaa0 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedCHBidirPathExtractor.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedCHBidirPathExtractor.java
@@ -19,11 +19,11 @@
 package com.graphhopper.routing.ch;
 
 import com.graphhopper.routing.BidirPathExtractor;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.storage.RoutingCHGraph;
 import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.storage.ShortcutUnpacker;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 
 /**
  * @author easbar
@@ -36,9 +36,6 @@ public class EdgeBasedCHBidirPathExtractor extends BidirPathExtractor {
         super(routingGraph.getGraph(), routingGraph.getWeighting());
         this.routingGraph = routingGraph;
         shortcutUnpacker = createShortcutUnpacker();
-        if (!(routingGraph.getWeighting() instanceof TurnWeighting)) {
-            throw new IllegalArgumentException("Need a TurnWeighting for edge-based CH");
-        }
     }
 
     @Override
@@ -55,7 +52,7 @@ public class EdgeBasedCHBidirPathExtractor extends BidirPathExtractor {
             @Override
             public void visit(EdgeIteratorState edge, boolean reverse, int prevOrNextEdgeId) {
                 path.addDistance(edge.getDistance());
-                path.addTime(routingGraph.getWeighting().calcMillis(edge, reverse, prevOrNextEdgeId));
+                path.addTime(GHUtility.calcMillisWithTurnMillis(routingGraph.getWeighting(), edge, reverse, prevOrNextEdgeId));
                 path.addEdge(edge.getEdge());
             }
         }, true);
diff --git a/core/src/main/java/com/graphhopper/routing/ch/PrepareCHGraph.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHGraph.java
index 298ab77fb..473bdeb3f 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/PrepareCHGraph.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHGraph.java
@@ -19,7 +19,6 @@
 package com.graphhopper.routing.ch;
 
 import com.graphhopper.routing.util.AllCHEdgesIterator;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.CHGraph;
 import com.graphhopper.storage.NodeAccess;
@@ -31,26 +30,24 @@ import com.graphhopper.util.EdgeIterator;
 public class PrepareCHGraph {
     private final CHGraph chGraph;
     private final Weighting weighting;
-    private final TurnWeighting turnWeighting;
 
     public static PrepareCHGraph nodeBased(CHGraph chGraph, Weighting weighting) {
         if (chGraph.getCHProfile().isEdgeBased()) {
             throw new IllegalArgumentException("Expected node-based CHGraph, but was edge-based");
         }
-        return new PrepareCHGraph(chGraph, weighting, null);
+        return new PrepareCHGraph(chGraph, weighting);
     }
 
-    public static PrepareCHGraph edgeBased(CHGraph chGraph, Weighting weighting, TurnWeighting turnWeighting) {
+    public static PrepareCHGraph edgeBased(CHGraph chGraph, Weighting weighting) {
         if (!chGraph.getCHProfile().isEdgeBased()) {
             throw new IllegalArgumentException("Expected edge-based CHGraph, but was node-based");
         }
-        return new PrepareCHGraph(chGraph, weighting, turnWeighting);
+        return new PrepareCHGraph(chGraph, weighting);
     }
 
-    private PrepareCHGraph(CHGraph chGraph, Weighting weighting, TurnWeighting turnWeighting) {
+    private PrepareCHGraph(CHGraph chGraph, Weighting weighting) {
         this.chGraph = chGraph;
         this.weighting = weighting;
-        this.turnWeighting = turnWeighting;
     }
 
     public PrepareCHEdgeExplorer createInEdgeExplorer() {
@@ -110,10 +107,7 @@ public class PrepareCHGraph {
     }
 
     double getTurnWeight(int inEdge, int viaNode, int outEdge) {
-        if (turnWeighting == null) {
-            throw new IllegalStateException("Calculating turn weights is only allowed when using edge-based traversal mode");
-        }
-        return turnWeighting.calcTurnWeight(inEdge, viaNode, outEdge);
+        return weighting.calcTurnWeight(inEdge, viaNode, outEdge);
     }
 
     public AllCHEdgesIterator getAllEdges() {
diff --git a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
index ad8c2cbe5..537a29524 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
@@ -23,7 +23,6 @@ import com.graphhopper.coll.GHTreeMapComposed;
 import com.graphhopper.routing.RoutingAlgorithmFactory;
 import com.graphhopper.routing.util.AbstractAlgoPreparation;
 import com.graphhopper.routing.util.TraversalMode;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.Helper;
@@ -89,8 +88,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation {
             if (turnCostStorage == null) {
                 throw new IllegalArgumentException("For edge-based CH you need a turn cost storage");
             }
-            TurnWeighting turnWeighting = new TurnWeighting(chProfile.getWeighting(), turnCostStorage, chProfile.getUTurnCosts());
-            prepareGraph = PrepareCHGraph.edgeBased(chGraph, chProfile.getWeighting(), turnWeighting);
+            prepareGraph = PrepareCHGraph.edgeBased(chGraph, chProfile.getWeighting());
             nodeContractor = new EdgeBasedNodeContractor(prepareGraph, pMap);
         } else {
             prepareGraph = PrepareCHGraph.nodeBased(chGraph, chProfile.getWeighting());
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
index 31cb4e3de..5023b1e91 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
@@ -99,9 +99,9 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
         // allowing arbitrary weighting is too dangerous
         this.lmSelectionWeighting = new ShortestWeighting(encoder) {
             @Override
-            public double calcWeight(EdgeIteratorState edge, boolean reverse, int prevOrNextEdgeId) {
+            public double calcEdgeWeight(EdgeIteratorState edge, boolean reverse) {
                 // make accessibility of shortest identical to the provided weighting to avoid problems like shown in testWeightingConsistence
-                double res = weighting.calcWeight(edge, reverse, prevOrNextEdgeId);
+                double res = weighting.calcEdgeWeight(edge, reverse);
                 if (res >= Double.MAX_VALUE)
                     return Double.POSITIVE_INFINITY;
 
diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java b/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java
index 06cafc200..74709f85b 100644
--- a/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java
+++ b/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java
@@ -22,6 +22,8 @@ import com.carrotsearch.hppc.procedures.IntObjectProcedure;
 import com.graphhopper.coll.GHIntObjectHashMap;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
+import com.graphhopper.routing.weighting.QueryGraphWeighting;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.ExtendedNodeAccess;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.NodeAccess;
@@ -81,11 +83,7 @@ public class QueryGraph implements Graph {
 
         graphModification = GraphModificationBuilder.build(graph, queryResults);
         nodeAccess = new ExtendedNodeAccess(graph.getNodeAccess(), graphModification.getVirtualNodes(), mainNodes);
-
-        if (mainGraph.getTurnCostStorage() != null)
-            turnCostStorage = new QueryGraphTurnCostStorage(mainGraph, graphModification.getClosestEdges());
-        else
-            turnCostStorage = null;
+        turnCostStorage = mainGraph.getTurnCostStorage();
 
         // build data structures holding the virtual edges at all real/virtual nodes that are modified compared to the
         // mainGraph.
@@ -365,6 +363,11 @@ public class QueryGraph implements Graph {
         return turnCostStorage;
     }
 
+    @Override
+    public Weighting wrapWeighting(Weighting weighting) {
+        return new QueryGraphWeighting(weighting, mainGraph.getNodes(), mainGraph.getEdges(), graphModification.getClosestEdges());
+    }
+
     @Override
     public int getOtherNode(int edge, int node) {
         if (isVirtualEdge(edge)) {
diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraphTurnCostStorage.java b/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraphTurnCostStorage.java
deleted file mode 100644
index 5cc110834..000000000
--- a/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraphTurnCostStorage.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *  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.querygraph;
-
-import com.carrotsearch.hppc.IntArrayList;
-import com.graphhopper.storage.Graph;
-import com.graphhopper.storage.IntsRef;
-import com.graphhopper.storage.TurnCostStorage;
-
-/**
- * special {@link TurnCostStorage} that handles virtual nodes and edges
- */
-class QueryGraphTurnCostStorage extends TurnCostStorage {
-    private final TurnCostStorage mainTurnCostStorage;
-    private final int firstVirtualNodeId;
-    private final int firstVirtualEdgeId;
-    private final IntArrayList closestEdges;
-
-    QueryGraphTurnCostStorage(Graph mainGraph, IntArrayList closestEdges) {
-        super(mainGraph.getTurnCostStorage());
-        this.mainTurnCostStorage = mainGraph.getTurnCostStorage();
-        this.firstVirtualNodeId = mainGraph.getNodes();
-        this.firstVirtualEdgeId = mainGraph.getEdges();
-        this.closestEdges = closestEdges;
-    }
-
-    @Override
-    public IntsRef readFlags(IntsRef tcFlags, int fromEdge, int viaNode, int toEdge) {
-        if (isVirtualNode(viaNode)) {
-            return tcFlags;
-        } else if (isVirtualEdge(fromEdge) || isVirtualEdge(toEdge)) {
-            if (isVirtualEdge(fromEdge)) {
-                fromEdge = getOriginalEdge(fromEdge);
-            }
-            if (isVirtualEdge(toEdge)) {
-                toEdge = getOriginalEdge(toEdge);
-            }
-            return mainTurnCostStorage.readFlags(tcFlags, fromEdge, viaNode, toEdge);
-        } else {
-            return mainTurnCostStorage.readFlags(tcFlags, fromEdge, viaNode, toEdge);
-        }
-    }
-
-    @Override
-    public boolean isUTurn(int edgeFrom, int edgeTo) {
-        // detecting a u-turn from a virtual to a non-virtual edge requires looking at the original edge of the
-        // virtual edge. however when we are turning between virtual edges we need to compare the virtual edge ids
-        // see #1593
-        if (isVirtualEdge(edgeFrom) && !isVirtualEdge(edgeTo)) {
-            edgeFrom = getOriginalEdge(edgeFrom);
-        } else if (!isVirtualEdge(edgeFrom) && isVirtualEdge(edgeTo)) {
-            edgeTo = getOriginalEdge(edgeTo);
-        }
-        return mainTurnCostStorage.isUTurn(edgeFrom, edgeTo);
-    }
-
-    @Override
-    public boolean isUTurnAllowed(int node) {
-        // do not allow u-turns at virtual nodes, otherwise the route depends on whether or not there are virtual
-        // via nodes, see #1672
-        return !isVirtualNode(node);
-    }
-
-    private int getOriginalEdge(int edge) {
-        return closestEdges.get((edge - firstVirtualEdgeId) / 4);
-    }
-
-    private boolean isVirtualNode(int nodeId) {
-        return nodeId >= firstVirtualNodeId;
-    }
-
-    private boolean isVirtualEdge(int edgeId) {
-        return edgeId >= firstVirtualEdgeId;
-    }
-}
diff --git a/core/src/main/java/com/graphhopper/routing/template/AlternativeRoutingTemplate.java b/core/src/main/java/com/graphhopper/routing/template/AlternativeRoutingTemplate.java
index 59238a54d..51727ac19 100644
--- a/core/src/main/java/com/graphhopper/routing/template/AlternativeRoutingTemplate.java
+++ b/core/src/main/java/com/graphhopper/routing/template/AlternativeRoutingTemplate.java
@@ -67,7 +67,7 @@ final public class AlternativeRoutingTemplate extends ViaRoutingTemplate {
     }
 
     @Override
-    public boolean isReady(PathMerger pathMerger, Translation tr) {
+    public void finish(PathMerger pathMerger, Translation tr) {
         if (pathList.isEmpty())
             throw new RuntimeException("Empty paths for alternative route calculation not expected");
 
@@ -82,6 +82,5 @@ final public class AlternativeRoutingTemplate extends ViaRoutingTemplate {
             ghResponse.add(tmpAltRsp);
             pathMerger.doWork(tmpAltRsp, Collections.singletonList(pathList.get(index)), encodingManager, tr);
         }
-        return true;
     }
 }
diff --git a/core/src/main/java/com/graphhopper/routing/template/RoundTripRoutingTemplate.java b/core/src/main/java/com/graphhopper/routing/template/RoundTripRoutingTemplate.java
index 02e3e58ba..aeeeb98fd 100644
--- a/core/src/main/java/com/graphhopper/routing/template/RoundTripRoutingTemplate.java
+++ b/core/src/main/java/com/graphhopper/routing/template/RoundTripRoutingTemplate.java
@@ -154,13 +154,11 @@ public class RoundTripRoutingTemplate extends AbstractRoutingTemplate implements
     }
 
     @Override
-    public boolean isReady(PathMerger pathMerger, Translation tr) {
+    public void finish(PathMerger pathMerger, Translation tr) {
         altResponse = new PathWrapper();
         altResponse.setWaypoints(getWaypoints());
         ghResponse.add(altResponse);
         pathMerger.doWork(altResponse, pathList, encodingManager, tr);
-        // with potentially retrying, including generating new route points, for now disabled
-        return true;
     }
 
     private QueryResult generateValidPoint(GHPoint from, double distanceInMeters, double heading,
@@ -180,9 +178,4 @@ public class RoundTripRoutingTemplate extends AbstractRoutingTemplate implements
         }
     }
 
-    @Override
-    public int getMaxRetries() {
-        // with potentially retrying, including generating new route points, for now disabled
-        return 1;
-    }
 }
diff --git a/core/src/main/java/com/graphhopper/routing/template/RoutingTemplate.java b/core/src/main/java/com/graphhopper/routing/template/RoutingTemplate.java
index de66726e8..d3a5c0293 100644
--- a/core/src/main/java/com/graphhopper/routing/template/RoutingTemplate.java
+++ b/core/src/main/java/com/graphhopper/routing/template/RoutingTemplate.java
@@ -51,10 +51,6 @@ public interface RoutingTemplate {
      * This method merges the returned paths appropriately e.g. all paths from the list into one
      * PathWrapper of GHResponse or multiple (via / round trip).
      */
-    boolean isReady(PathMerger pathMerger, Translation tr);
+    void finish(PathMerger pathMerger, Translation tr);
 
-    /**
-     * This method returns the maximum number of full retries of these 3 steps
-     */
-    int getMaxRetries();
-}
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/graphhopper/routing/template/ViaRoutingTemplate.java b/core/src/main/java/com/graphhopper/routing/template/ViaRoutingTemplate.java
index d95a09590..37286afb6 100644
--- a/core/src/main/java/com/graphhopper/routing/template/ViaRoutingTemplate.java
+++ b/core/src/main/java/com/graphhopper/routing/template/ViaRoutingTemplate.java
@@ -216,19 +216,13 @@ public class ViaRoutingTemplate extends AbstractRoutingTemplate implements Routi
     }
 
     @Override
-    public boolean isReady(PathMerger pathMerger, Translation tr) {
+    public void finish(PathMerger pathMerger, Translation tr) {
         if (ghRequest.getPoints().size() - 1 != pathList.size())
             throw new RuntimeException("There should be exactly one more points than paths. points:" + ghRequest.getPoints().size() + ", paths:" + pathList.size());
 
         altResponse.setWaypoints(getWaypoints());
         ghResponse.add(altResponse);
         pathMerger.doWork(altResponse, pathList, encodingManager, tr);
-        return true;
-    }
-
-    @Override
-    public int getMaxRetries() {
-        return 1;
     }
 
 }
diff --git a/core/src/main/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupArray.java b/core/src/main/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupArray.java
index 4320767b4..a6d576ff0 100644
--- a/core/src/main/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupArray.java
+++ b/core/src/main/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupArray.java
@@ -182,7 +182,7 @@ public class SpatialRuleLookupArray implements SpatialRuleLookup {
 
                     GHPoint center = getCoordinatesForIndex(xIdx, yIdx);
                     // TODO: Consider creating a new method in Polygon that does the 5 checks - p.partOfTile?
-                    if (polygon.contains(center) ||
+                    if (polygon.contains(center.lat, center.lon) ||
                             polygon.contains(center.getLat() - checkDiff, center.getLon() - checkDiff) ||
                             polygon.contains(center.getLat() - checkDiff, center.getLon() + checkDiff) ||
                             polygon.contains(center.getLat() + checkDiff, center.getLon() - checkDiff) ||
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java
index 7d1dd89c3..7b794cee6 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/AbstractAdjustedWeighting.java
@@ -21,8 +21,6 @@ import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.util.EdgeIteratorState;
 
-import static com.graphhopper.util.EdgeIterator.NO_EDGE;
-
 /**
  * The AdjustedWeighting wraps another Weighting.
  *
@@ -43,18 +41,23 @@ public abstract class AbstractAdjustedWeighting implements Weighting {
     }
 
     @Override
-    public final double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
-        return calcWeight(edgeState, reverse, NO_EDGE);
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
+        return superWeighting.calcEdgeWeight(edgeState, reverse);
+    }
+
+    @Override
+    public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
+        return superWeighting.calcEdgeMillis(edgeState, reverse);
     }
 
     @Override
-    public final long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
-        return calcMillis(edgeState, reverse, NO_EDGE);
+    public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
+        return superWeighting.calcTurnWeight(inEdge, viaNode, outEdge);
     }
 
     @Override
-    public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-        return superWeighting.calcMillis(edgeState, reverse, prevOrNextEdgeId);
+    public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+        return superWeighting.calcTurnMillis(inEdge, viaNode, outEdge);
     }
 
     /**
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
index f764e8a74..9479207f3 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
@@ -22,8 +22,10 @@ import com.graphhopper.routing.profiles.DecimalEncodedValue;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.Parameters;
 
-import static com.graphhopper.util.EdgeIterator.NO_EDGE;
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+import static com.graphhopper.util.Helper.isEmpty;
 import static com.graphhopper.util.Helper.toLowerCase;
 
 /**
@@ -33,8 +35,13 @@ public abstract class AbstractWeighting implements Weighting {
     protected final FlagEncoder flagEncoder;
     protected final DecimalEncodedValue avSpeedEnc;
     protected final BooleanEncodedValue accessEnc;
+    private final TurnCostProvider turnCostProvider;
 
     protected AbstractWeighting(FlagEncoder encoder) {
+        this(encoder, NO_TURN_COST_PROVIDER);
+    }
+
+    protected AbstractWeighting(FlagEncoder encoder, TurnCostProvider turnCostProvider) {
         this.flagEncoder = encoder;
         if (!flagEncoder.isRegistered())
             throw new IllegalStateException("Make sure you add the FlagEncoder " + flagEncoder + " to an EncodingManager before using it elsewhere");
@@ -43,20 +50,18 @@ public abstract class AbstractWeighting implements Weighting {
 
         avSpeedEnc = encoder.getAverageSpeedEnc();
         accessEnc = encoder.getAccessEnc();
+        this.turnCostProvider = turnCostProvider;
     }
 
-    @Override
-    public final double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
-        return calcWeight(edgeState, reverse, NO_EDGE);
-    }
+    /**
+     * In most cases subclasses should only override this method to change the edge-weight. The turn cost handling
+     * should normally be changed by passing another {@link TurnCostProvider} implementation to the constructor instead.
+     */
+    public abstract double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse);
 
-    @Override
-    public final long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
-        return calcMillis(edgeState, reverse, NO_EDGE);
-    }
 
     @Override
-    public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+    public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
         // special case for loop edges: since they do not have a meaningful direction we always need to read them in
         // forward direction
         if (edgeState.getBaseNode() == edgeState.getAdjNode()) {
@@ -78,9 +83,21 @@ public abstract class AbstractWeighting implements Weighting {
         return (long) (edgeState.getDistance() * 3600 / speed);
     }
 
+    @Override
+    public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
+        return turnCostProvider.calcTurnWeight(inEdge, viaNode, outEdge);
+    }
+
+    @Override
+    public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+        return turnCostProvider.calcTurnMillis(inEdge, viaNode, outEdge);
+    }
+
     @Override
     public boolean matches(HintsMap reqMap) {
+        String requestedUTurnCosts = reqMap.get(Parameters.Routing.U_TURN_COSTS, "");
         return (reqMap.getWeighting().isEmpty() || getName().equals(reqMap.getWeighting())) &&
+                (requestedUTurnCosts.isEmpty() || turnCostProvider.getName().equals(requestedUTurnCosts)) &&
                 (reqMap.getVehicle().isEmpty() || flagEncoder.toString().equals(reqMap.getVehicle()));
     }
 
@@ -117,11 +134,18 @@ public abstract class AbstractWeighting implements Weighting {
      * Replaces all characters which are not numbers, characters or underscores with underscores
      */
     public static String weightingToFileName(Weighting w) {
-        return toLowerCase(w.toString()).replaceAll("\\|", "_");
+        String name = w.toString();
+        name = name.replaceAll("u_turn_costs=", "");
+        return toLowerCase(name).replaceAll("\\|", "_");
     }
 
     @Override
     public String toString() {
-        return getName() + "|" + flagEncoder;
+        String turnCostProviderName = turnCostProvider.getName();
+        String result = getName() + "|" + flagEncoder;
+        if (!isEmpty(turnCostProviderName)) {
+            result += "|u_turn_costs=" + turnCostProviderName;
+        }
+        return result;
     }
 }
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java
index b681efbf3..78164036e 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java
@@ -54,8 +54,8 @@ public class AvoidEdgesWeighting extends AbstractAdjustedWeighting {
     }
 
     @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-        double weight = superWeighting.calcWeight(edgeState, reverse, prevOrNextEdgeId);
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
+        double weight = superWeighting.calcEdgeWeight(edgeState, reverse);
         if (visitedEdges.contains(edgeState.getEdge()))
             return weight * edgePenaltyFactor;
 
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/BlockAreaWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/BlockAreaWeighting.java
index 682bb34d9..d912800d1 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/BlockAreaWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/BlockAreaWeighting.java
@@ -16,11 +16,11 @@ public class BlockAreaWeighting extends AbstractAdjustedWeighting {
     }
 
     @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-        if (blockArea.contains(edgeState))
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
+        if (blockArea.intersects(edgeState))
             return Double.POSITIVE_INFINITY;
 
-        return superWeighting.calcWeight(edgeState, reverse, prevOrNextEdgeId);
+        return superWeighting.calcEdgeWeight(edgeState, reverse);
     }
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/CurvatureWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/CurvatureWeighting.java
index 680fa00a1..535c5afd9 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/CurvatureWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/CurvatureWeighting.java
@@ -23,6 +23,8 @@ import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.PMap;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+
 /**
  * This Class uses bendiness parameter to prefer curvy routes.
  */
@@ -33,7 +35,11 @@ public class CurvatureWeighting extends PriorityWeighting {
     private final DecimalEncodedValue avSpeedEnc;
 
     public CurvatureWeighting(FlagEncoder flagEncoder, PMap pMap) {
-        super(flagEncoder, pMap);
+        this(flagEncoder, pMap, NO_TURN_COST_PROVIDER);
+    }
+
+    public CurvatureWeighting(FlagEncoder flagEncoder, PMap pMap, TurnCostProvider turnCostProvider) {
+        super(flagEncoder, pMap, turnCostProvider);
 
         priorityEnc = flagEncoder.getDecimalEncodedValue(EncodingManager.getKey(flagEncoder, "priority"));
         curvatureEnc = flagEncoder.getDecimalEncodedValue(EncodingManager.getKey(flagEncoder, "curvature"));
@@ -49,7 +55,7 @@ public class CurvatureWeighting extends PriorityWeighting {
     }
 
     @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
         double priority = priorityEnc.getDecimal(false, edgeState.getFlags());
         double bendiness = curvatureEnc.getDecimal(false, edgeState.getFlags());
         double speed = getRoadSpeed(edgeState, reverse);
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java
new file mode 100644
index 000000000..41e45f19c
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java
@@ -0,0 +1,92 @@
+/*
+ *  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.weighting;
+
+import com.graphhopper.routing.profiles.DecimalEncodedValue;
+import com.graphhopper.routing.profiles.TurnCost;
+import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.storage.IntsRef;
+import com.graphhopper.storage.TurnCostStorage;
+import com.graphhopper.util.EdgeIterator;
+
+import static com.graphhopper.routing.profiles.TurnCost.EV_SUFFIX;
+import static com.graphhopper.routing.util.EncodingManager.getKey;
+import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
+
+public class DefaultTurnCostProvider implements TurnCostProvider {
+    private final DecimalEncodedValue turnCostEnc;
+    private final TurnCostStorage turnCostStorage;
+    private final int uTurnCostsInt;
+    private final double uTurnCosts;
+    private final IntsRef tcFlags = TurnCost.createFlags();
+
+    public DefaultTurnCostProvider(FlagEncoder encoder, TurnCostStorage turnCostStorage) {
+        this(encoder, 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(FlagEncoder encoder, 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)");
+        }
+        this.uTurnCostsInt = uTurnCosts;
+        this.uTurnCosts = uTurnCosts < 0 ? Double.POSITIVE_INFINITY : uTurnCosts;
+        if (turnCostStorage == null) {
+            throw new IllegalArgumentException("No storage set to calculate turn weight");
+        }
+        String key = getKey(encoder.toString(), EV_SUFFIX);
+        // if null the TurnCostProvider can be still useful for edge-based routing
+        this.turnCostEnc = encoder.hasEncodedValue(key) ? encoder.getDecimalEncodedValue(key) : null;
+        this.turnCostStorage = turnCostStorage;
+    }
+
+    @Override
+    public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) {
+        if (!EdgeIterator.Edge.isValid(edgeFrom) || !EdgeIterator.Edge.isValid(edgeTo)) {
+            return 0;
+        }
+        double tCost = 0;
+        if (turnCostStorage.isUTurn(edgeFrom, edgeTo)) {
+            // note that the u-turn costs overwrite any turn costs set in TurnCostStorage
+            tCost = turnCostStorage.isUTurnAllowed(nodeVia) ? uTurnCosts : Double.POSITIVE_INFINITY;
+        } else {
+            if (turnCostEnc != null)
+                tCost = turnCostStorage.get(turnCostEnc, tcFlags, edgeFrom, nodeVia, edgeTo);
+        }
+        return tCost;
+    }
+
+    @Override
+    public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+        return (long) (1000 * calcTurnWeight(inEdge, viaNode, outEdge));
+    }
+
+    @Override
+    public String getName() {
+        return toString();
+    }
+
+    @Override
+    public String toString() {
+        return "" + uTurnCostsInt;
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java
index 741d6d03a..9e822030d 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java
@@ -25,6 +25,7 @@ import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.PMap;
 
 import static com.graphhopper.routing.util.PriorityCode.BEST;
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
 
 /**
  * Special weighting for (motor)bike
@@ -37,7 +38,11 @@ public class PriorityWeighting extends FastestWeighting {
     private final DecimalEncodedValue priorityEnc;
 
     public PriorityWeighting(FlagEncoder encoder, PMap pMap) {
-        super(encoder, pMap);
+        this(encoder, pMap, NO_TURN_COST_PROVIDER);
+    }
+
+    public PriorityWeighting(FlagEncoder encoder, PMap pMap, TurnCostProvider turnCostProvider) {
+        super(encoder, pMap, turnCostProvider);
         priorityEnc = encoder.getDecimalEncodedValue(EncodingManager.getKey(encoder, "priority"));
         double maxPriority = PriorityCode.getFactor(BEST.getValue());
         minFactor = 1 / (0.5 + maxPriority);
@@ -49,8 +54,8 @@ public class PriorityWeighting extends FastestWeighting {
     }
 
     @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-        double weight = super.calcWeight(edgeState, reverse, prevOrNextEdgeId);
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
+        double weight = super.calcEdgeWeight(edgeState, reverse);
         if (Double.isInfinite(weight))
             return Double.POSITIVE_INFINITY;
         return weight / (0.5 + edgeState.get(priorityEnc));
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/QueryGraphWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/QueryGraphWeighting.java
new file mode 100644
index 000000000..aaf161d4b
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/weighting/QueryGraphWeighting.java
@@ -0,0 +1,124 @@
+/*
+ *  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.weighting;
+
+import com.carrotsearch.hppc.IntArrayList;
+import com.graphhopper.routing.querygraph.QueryGraph;
+import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.routing.util.HintsMap;
+import com.graphhopper.util.EdgeIterator;
+import com.graphhopper.util.EdgeIteratorState;
+
+/**
+ * Whenever a {@link QueryGraph} is used for shortest path calculations including turn costs we need to wrap the
+ * {@link Weighting} we want to use with this class. Otherwise turn costs at virtual nodes and/or including virtual
+ * edges will not be calculated correctly.
+ */
+public class QueryGraphWeighting implements Weighting {
+    private final Weighting weighting;
+    private final int firstVirtualNodeId;
+    private final int firstVirtualEdgeId;
+    private final IntArrayList closestEdges;
+
+    public QueryGraphWeighting(Weighting weighting, int firstVirtualNodeId, int firstVirtualEdgeId, IntArrayList closestEdges) {
+        this.weighting = weighting;
+        this.firstVirtualNodeId = firstVirtualNodeId;
+        this.firstVirtualEdgeId = firstVirtualEdgeId;
+        this.closestEdges = closestEdges;
+    }
+
+    @Override
+    public double getMinWeight(double distance) {
+        return weighting.getMinWeight(distance);
+    }
+
+    @Override
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
+        return weighting.calcEdgeWeight(edgeState, reverse);
+    }
+
+    @Override
+    public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
+        if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge)) {
+            return 0;
+        }
+        if (isVirtualNode(viaNode)) {
+            if (isUTurn(inEdge, outEdge)) {
+                // do not allow u-turns at virtual nodes, otherwise the route depends on whether or not there are
+                // virtual via nodes, see #1672. note since we are turning between virtual edges here we need to compare
+                // the *virtual* edge ids (the orig edge would always be the same for all virtual edges at a virtual
+                // node), see #1593
+                return Double.POSITIVE_INFINITY;
+            } else {
+                return 0;
+            }
+        }
+        // to calculate the actual turn costs or detect u-turns we need to look at the original edge of each virtual
+        // edge, see #1593
+        if (isVirtualEdge(inEdge)) {
+            inEdge = getOriginalEdge(inEdge);
+        }
+        if (isVirtualEdge(outEdge)) {
+            outEdge = getOriginalEdge(outEdge);
+        }
+        return weighting.calcTurnWeight(inEdge, viaNode, outEdge);
+    }
+
+    private boolean isUTurn(int inEdge, int outEdge) {
+        return inEdge == outEdge;
+    }
+
+    @Override
+    public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
+        return weighting.calcEdgeMillis(edgeState, reverse);
+    }
+
+    @Override
+    public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+        // todo: here we do not allow calculating turn weights that aren't turn times, also see #1590
+        return (long) (1000 * calcTurnWeight(inEdge, viaNode, outEdge));
+    }
+
+    @Override
+    public FlagEncoder getFlagEncoder() {
+        return weighting.getFlagEncoder();
+    }
+
+    @Override
+    public String getName() {
+        return weighting.getName();
+    }
+
+    @Override
+    public boolean matches(HintsMap map) {
+        return weighting.matches(map);
+    }
+
+    private int getOriginalEdge(int edge) {
+        return closestEdges.get((edge - firstVirtualEdgeId) / 4);
+    }
+
+    private boolean isVirtualNode(int node) {
+        return node >= firstVirtualNodeId;
+    }
+
+    private boolean isVirtualEdge(int edge) {
+        return edge >= firstVirtualEdgeId;
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/TurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/TurnCostProvider.java
new file mode 100644
index 000000000..f17741de7
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/weighting/TurnCostProvider.java
@@ -0,0 +1,55 @@
+/*
+ *  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.weighting;
+
+/**
+ * Implementations of this interface define how turn costs and turn times are calculated.
+ */
+public interface TurnCostProvider {
+    /**
+     * @return the turn weight of a transitions from the edge with id {@param inEdge} to the edge with id
+     * {@param outEdge} at the node with id {@param viaNode}
+     */
+    double calcTurnWeight(int inEdge, int viaNode, int outEdge);
+
+    /**
+     * @return the time it takes to take a turn in milli-seconds
+     * @see #calcTurnWeight(int, int, int)
+     */
+    long calcTurnMillis(int inEdge, int viaNode, int outEdge);
+
+    String getName();
+
+    TurnCostProvider NO_TURN_COST_PROVIDER = new TurnCostProvider() {
+        @Override
+        public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
+            return 0;
+        }
+
+        @Override
+        public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+            return 0;
+        }
+
+        @Override
+        public String getName() {
+            return "";
+        }
+    };
+}
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/TurnWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/TurnWeighting.java
deleted file mode 100644
index 50aaf7573..000000000
--- a/core/src/main/java/com/graphhopper/routing/weighting/TurnWeighting.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- *  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.weighting;
-
-import com.graphhopper.routing.profiles.DecimalEncodedValue;
-import com.graphhopper.routing.profiles.TurnCost;
-import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.routing.util.HintsMap;
-import com.graphhopper.storage.IntsRef;
-import com.graphhopper.storage.TurnCostStorage;
-import com.graphhopper.util.EdgeIterator;
-import com.graphhopper.util.EdgeIteratorState;
-
-import static com.graphhopper.routing.profiles.TurnCost.EV_SUFFIX;
-import static com.graphhopper.routing.util.EncodingManager.getKey;
-import static com.graphhopper.util.EdgeIterator.NO_EDGE;
-
-/**
- * Provides methods to retrieve turn costs for a specific turn.
- *
- * @author Karl Hübner
- * @author Peter Karich
- */
-public class TurnWeighting implements Weighting {
-    private final DecimalEncodedValue turnCostEnc;
-    private final TurnCostStorage turnCostStorage;
-    private final Weighting superWeighting;
-    private final double uTurnCosts;
-    private final IntsRef tcFlags = TurnCost.createFlags();
-
-    public TurnWeighting(Weighting superWeighting, TurnCostStorage turnCostStorage) {
-        this(superWeighting, turnCostStorage, INFINITE_U_TURN_COSTS);
-    }
-
-    /**
-     * @param superWeighting  the weighting that is wrapped by this {@link TurnWeighting} and used to calculate the
-     *                        edge weights for example
-     * @param turnCostStorage the turn cost storage to be used
-     * @param uTurnCosts      the cost of a u-turn in seconds, this value will be applied to all u-turn costs no matter
-     *                        whether or not turnCostExt contains explicit values for these turns.
-     */
-    public TurnWeighting(Weighting superWeighting, TurnCostStorage turnCostStorage, double uTurnCosts) {
-        if (turnCostStorage == null) {
-            throw new RuntimeException("No storage set to calculate turn weight");
-        }
-        FlagEncoder encoder = superWeighting.getFlagEncoder();
-        String key = getKey(encoder.toString(), EV_SUFFIX);
-        // if null the TurnWeighting can be still useful for edge-based routing
-        this.turnCostEnc = encoder.hasEncodedValue(key) ? encoder.getDecimalEncodedValue(key) : null;
-        this.superWeighting = superWeighting;
-        this.turnCostStorage = turnCostStorage;
-        this.uTurnCosts = uTurnCosts < 0 ? Double.POSITIVE_INFINITY : uTurnCosts;
-    }
-
-    public double getUTurnCosts() {
-        return uTurnCosts;
-    }
-
-    @Override
-    public double getMinWeight(double distance) {
-        return superWeighting.getMinWeight(distance);
-    }
-
-    @Override
-    public final double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
-        return calcWeight(edgeState, reverse, NO_EDGE);
-    }
-
-    @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-        double weight = superWeighting.calcWeight(edgeState, reverse, prevOrNextEdgeId);
-        if (!EdgeIterator.Edge.isValid(prevOrNextEdgeId))
-            return weight;
-
-        final int origEdgeId = reverse ? edgeState.getOrigEdgeLast() : edgeState.getOrigEdgeFirst();
-        double turnCosts = reverse
-                ? calcTurnWeight(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId)
-                : calcTurnWeight(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId);
-        return weight + turnCosts;
-    }
-
-    @Override
-    public final long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
-        return calcMillis(edgeState, reverse, NO_EDGE);
-    }
-
-    @Override
-    public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-        long millis = superWeighting.calcMillis(edgeState, reverse, prevOrNextEdgeId);
-        if (!EdgeIterator.Edge.isValid(prevOrNextEdgeId))
-            return millis;
-
-        // 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 = edgeState.getEdge();
-        long turnCostMillis = reverse
-                ? calcTurnMillis(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId)
-                : calcTurnMillis(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId);
-        return millis + turnCostMillis;
-    }
-
-    /**
-     * This method calculates the turn weight separately.
-     */
-    public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) {
-        if (!EdgeIterator.Edge.isValid(edgeFrom) || !EdgeIterator.Edge.isValid(edgeTo)) {
-            return 0;
-        }
-        double tCost = 0;
-        if (turnCostStorage.isUTurn(edgeFrom, edgeTo)) {
-            // note that the u-turn costs overwrite any turn costs set in TurnCostStorage
-            tCost = turnCostStorage.isUTurnAllowed(nodeVia) ? uTurnCosts : Double.POSITIVE_INFINITY;
-        } else {
-            if (turnCostEnc != null)
-                tCost = turnCostStorage.get(turnCostEnc, tcFlags, edgeFrom, nodeVia, edgeTo);
-        }
-        return tCost;
-    }
-
-    public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
-        return (long) (1000 * calcTurnWeight(inEdge, viaNode, outEdge));
-    }
-
-    @Override
-    public FlagEncoder getFlagEncoder() {
-        return superWeighting.getFlagEncoder();
-    }
-
-    @Override
-    public boolean matches(HintsMap weightingMap) {
-        // TODO without 'turn' in comparison
-        return superWeighting.matches(weightingMap);
-    }
-
-    @Override
-    public String toString() {
-        return "turn|" + superWeighting.toString();
-    }
-
-    @Override
-    public String getName() {
-        return "turn|" + superWeighting.getName();
-    }
-}
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java b/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java
index 7b7fedede..cd84eb8fc 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/Weighting.java
@@ -41,8 +41,7 @@ public interface Weighting {
     /**
      * This method calculates the weight of a given {@link EdgeIteratorState}. E.g. a high value indicates that the edge
      * should be avoided during shortest path search. Make sure that this method is very fast and optimized as this is
-     * called potentially millions of times for one route or a lot more for nearly any preprocessing phase. This
-     * method does not include any turn costs like {@link #calcWeight}.
+     * called potentially millions of times for one route or a lot more for nearly any preprocessing phase.
      *
      * @param edgeState the edge for which the weight should be calculated
      * @param reverse   if the specified edge is specified in reverse direction e.g. from the reverse
@@ -52,28 +51,15 @@ public interface Weighting {
      */
     double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse);
 
-    /**
-     * Calculates the weight of a given edge like {@link #calcEdgeWeight}, but potentially also adds the transition
-     * cost (the turn weight) associated with transitioning from/to the edge with ID prevOrNextEdgeId.
-     *
-     * @param prevOrNextEdgeId if reverse is false this has to be the previous edgeId, if true it
-     *                         has to be the next edgeId in the direction from start to end.
-     */
-    double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId);
-
     /**
      * This method calculates the time taken (in milli seconds) to travel along the specified edgeState.
      * It is typically used for post-processing and on only a few thousand edges.
      */
     long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse);
 
-    /**
-     * Like {@link #calcEdgeMillis}, but potentially also includes the turn costs (in seconds) needed for the transition
-     * between the previous (or next) edgeId and this edge via prevOrNextEdgeId.
-     *
-     * @see #calcWeight
-     */
-    long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId);
+    double calcTurnWeight(int inEdge, int viaNode, int outEdge);
+
+    long calcTurnMillis(int inEdge, int viaNode, int outEdge);
 
     FlagEncoder getFlagEncoder();
 
diff --git a/core/src/main/java/com/graphhopper/storage/BaseGraph.java b/core/src/main/java/com/graphhopper/storage/BaseGraph.java
index a39d62c66..ba958b1f9 100644
--- a/core/src/main/java/com/graphhopper/storage/BaseGraph.java
+++ b/core/src/main/java/com/graphhopper/storage/BaseGraph.java
@@ -27,6 +27,7 @@ import com.graphhopper.routing.profiles.IntEncodedValue;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.search.StringIndex;
 import com.graphhopper.util.*;
 import com.graphhopper.util.shapes.BBox;
@@ -49,8 +50,9 @@ import static com.graphhopper.util.Helper.nf;
  * loadExisting, (4) usage, (5) flush, (6) close
  */
 class BaseGraph implements Graph {
-    // currently distances are stored as 4 byte integers. using a conversion factor of 1000 the minimum distance
-    // that is not considered zero is 0.0005m (=0.5mm) and the maximum distance per edge is about 2.147.483m=2147km
+    // Currently distances are stored as 4 byte integers. using a conversion factor of 1000 the minimum distance
+    // that is not considered zero is 0.0005m (=0.5mm) and the maximum distance per edge is about 2.147.483m=2147km.
+    // See OSMReader.addEdge and #1871.
     private static final double INT_DIST_FACTOR = 1000d;
     static double MAX_DIST = Integer.MAX_VALUE / INT_DIST_FACTOR;
 
@@ -841,6 +843,11 @@ class BaseGraph implements Graph {
         return turnCostStorage;
     }
 
+    @Override
+    public Weighting wrapWeighting(Weighting weighting) {
+        return weighting;
+    }
+
     @Override
     public int getOtherNode(int edge, int node) {
         long edgePointer = edgeAccess.toPointer(edge);
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
index 7e046b48a..29f2cd882 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
@@ -25,6 +25,7 @@ import com.graphhopper.routing.profiles.EnumEncodedValue;
 import com.graphhopper.routing.profiles.IntEncodedValue;
 import com.graphhopper.routing.util.AllCHEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.BaseGraph.AllEdgeIterator;
 import com.graphhopper.storage.BaseGraph.EdgeIterable;
 import com.graphhopper.util.*;
@@ -298,6 +299,11 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         return baseGraph.getTurnCostStorage();
     }
 
+    @Override
+    public Weighting wrapWeighting(Weighting weighting) {
+        return baseGraph.wrapWeighting(weighting);
+    }
+
     @Override
     public Graph getBaseGraph() {
         return baseGraph;
diff --git a/core/src/main/java/com/graphhopper/storage/CHProfile.java b/core/src/main/java/com/graphhopper/storage/CHProfile.java
index bd7e4b808..5e8a35924 100644
--- a/core/src/main/java/com/graphhopper/storage/CHProfile.java
+++ b/core/src/main/java/com/graphhopper/storage/CHProfile.java
@@ -4,12 +4,9 @@ import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.AbstractWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
 import java.util.Objects;
-
-import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Specifies all properties of a CH routing profile. Generally these properties cannot be changed after the CH
@@ -20,42 +17,22 @@ import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
 public class CHProfile {
     private final Weighting weighting;
     private final boolean edgeBased;
-    private final int uTurnCosts;
 
     public static CHProfile nodeBased(Weighting weighting) {
-        return new CHProfile(weighting, TraversalMode.NODE_BASED, INFINITE_U_TURN_COSTS);
-    }
-
-    public static CHProfile edgeBased(Weighting weighting, int uTurnCosts) {
-        return new CHProfile(weighting, TraversalMode.EDGE_BASED, uTurnCosts);
+        return new CHProfile(weighting, TraversalMode.NODE_BASED);
     }
 
-    public static List<CHProfile> createProfilesForWeightings(Collection<? extends Weighting> weightings) {
-        List<CHProfile> result = new ArrayList<>(weightings.size());
-        for (Weighting weighting : weightings) {
-            result.add(nodeBased(weighting));
-        }
-        return result;
+    public static CHProfile edgeBased(Weighting weighting) {
+        return new CHProfile(weighting, TraversalMode.EDGE_BASED);
     }
 
-    public CHProfile(Weighting weighting, TraversalMode traversalMode, int uTurnCosts) {
-        this(weighting, traversalMode.isEdgeBased(), uTurnCosts);
+    public CHProfile(Weighting weighting, TraversalMode traversalMode) {
+        this(weighting, traversalMode.isEdgeBased());
     }
 
-    /**
-     * @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 CHProfile(Weighting weighting, boolean edgeBased, int uTurnCosts) {
-        if (!edgeBased && uTurnCosts != INFINITE_U_TURN_COSTS) {
-            throw new IllegalArgumentException("Finite u-turn costs are only allowed for edge-based CH");
-        }
+    public CHProfile(Weighting weighting, boolean edgeBased) {
         this.weighting = weighting;
         this.edgeBased = edgeBased;
-        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)");
-        }
-        this.uTurnCosts = uTurnCosts < 0 ? INFINITE_U_TURN_COSTS : uTurnCosts;
     }
 
     public Weighting getWeighting() {
@@ -66,27 +43,39 @@ public class CHProfile {
         return edgeBased;
     }
 
-    public double getUTurnCosts() {
-        return uTurnCosts < 0 ? Double.POSITIVE_INFINITY : uTurnCosts;
-    }
-
-    /**
-     * Use this method when u-turn costs are used to check CHProfile equality
-     */
-    public int getUTurnCostsInt() {
-        return uTurnCosts;
-    }
-
     public TraversalMode getTraversalMode() {
         return edgeBased ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED;
     }
 
     public String toFileName() {
-        return AbstractWeighting.weightingToFileName(weighting) + "_" + (edgeBased ? ("edge_utc" + uTurnCosts) : "node");
+        String result = AbstractWeighting.weightingToFileName(weighting);
+        // keeping legacy file names for now, like fastest_edge_utc40 (instead of fastest_40_edge), because we will
+        // most likely use profile names soon: #1708
+        Pattern pattern = Pattern.compile("-?\\d+");
+        Matcher matcher = pattern.matcher(result);
+        if (matcher.find()) {
+            String turnCostPostfix = matcher.group();
+            result = matcher.replaceAll("");
+            result += edgeBased ? "edge" : "node";
+            result += "_utc" + turnCostPostfix;
+        } else {
+            result += edgeBased ? "_edge" : "_node";
+        }
+        return result;
     }
 
     public String toString() {
-        return weighting + "|edge_based=" + edgeBased + "|u_turn_costs=" + uTurnCosts;
+        String result = weighting.toString();
+        Pattern pattern = Pattern.compile("\\|u_turn_costs=-?\\d+");
+        Matcher matcher = pattern.matcher(result);
+        if (matcher.find()) {
+            String uTurnCostPostFix = matcher.group();
+            result = matcher.replaceAll("");
+            result += "|edge_based=" + edgeBased + uTurnCostPostFix;
+        } else {
+            result += "|edge_based=" + edgeBased;
+        }
+        return result;
     }
 
     @Override
@@ -95,12 +84,11 @@ public class CHProfile {
         if (o == null || getClass() != o.getClass()) return false;
         CHProfile chProfile = (CHProfile) o;
         return edgeBased == chProfile.edgeBased &&
-                uTurnCosts == chProfile.uTurnCosts &&
                 Objects.equals(weighting, chProfile.weighting);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(weighting, edgeBased, uTurnCosts);
+        return Objects.hash(weighting, edgeBased);
     }
 }
diff --git a/core/src/main/java/com/graphhopper/storage/Graph.java b/core/src/main/java/com/graphhopper/storage/Graph.java
index e59d854f0..85b47acbd 100644
--- a/core/src/main/java/com/graphhopper/storage/Graph.java
+++ b/core/src/main/java/com/graphhopper/storage/Graph.java
@@ -19,6 +19,7 @@ package com.graphhopper.storage;
 
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.EdgeExplorer;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.shapes.BBox;
@@ -121,4 +122,9 @@ public interface Graph {
      * @return the {@link TurnCostStorage} or null if not supported
      */
     TurnCostStorage getTurnCostStorage();
+
+    /**
+     * Wraps the given weighting into a weighting that can be used by this graph
+     */
+    Weighting wrapWeighting(Weighting weighting);
 }
diff --git a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java b/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
index 79710378d..edfa30ed0 100644
--- a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
+++ b/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
 import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
 
 /**
@@ -53,11 +54,13 @@ public class GraphBuilder {
     }
 
     /**
-     * Convenience method to set the CH profiles using a string representation. This is will be convenient once the
-     * turn cost handling is moved into {@link AbstractWeighting} and {@link TurnWeighting} is gone, see #1820.
-     * attention: Currently this only supports a few weightings with limited extra options. The reason is that here the
+     * Convenience method to set the CH profiles using a string representation. This is convenient if you want to add
+     * edge-based {@link CHProfile}s, because otherwise when using {@link #setCHProfiles} you first have to
+     * {@link #build()} the {@link GraphHopperStorage} to obtain a {@link TurnCostStorage} to be able to create the
+     * {@link Weighting} you need for the {@link CHProfile} to be added...
+     * todo: Currently this only supports a few weightings with limited extra options. The reason is that here the
      * same should happen as in the 'real' GraphHopper graph, reading the real config, but this is likely to change
-     * soon, see #493
+     * soon.
      */
     public GraphBuilder setCHProfileStrings(String... profileStrings) {
         this.chProfileStrings = Arrays.asList(profileStrings);
@@ -121,7 +124,7 @@ public class GraphBuilder {
      */
     public GraphHopperStorage build() {
         GraphHopperStorage ghStorage = new GraphHopperStorage(dir, encodingManager, elevation, turnCosts, segmentSize);
-        addCHProfilesFromStrings();
+        addCHProfilesFromStrings(ghStorage.getTurnCostStorage());
         ghStorage.addCHGraphs(chProfiles);
         return ghStorage;
     }
@@ -133,7 +136,7 @@ public class GraphBuilder {
         return build().create(bytes);
     }
 
-    private void addCHProfilesFromStrings() {
+    private void addCHProfilesFromStrings(TurnCostStorage turnCostStorage) {
         for (String profileString : chProfileStrings) {
             String[] split = profileString.split("\\|");
             if (split.length < 3) {
@@ -146,25 +149,30 @@ public class GraphBuilder {
             if (split.length == 4) {
                 uTurnCostsInt = Integer.parseInt(split[3]);
             }
-            boolean edgeBased;
+            TurnCostProvider turnCostProvider;
+            boolean edgeBased = false;
             if (edgeOrNode.equals("edge")) {
+                if (turnCostStorage == null) {
+                    throw new IllegalArgumentException("For edge-based CH profiles you need a turn cost storage");
+                }
+                turnCostProvider = new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCostsInt);
                 edgeBased = true;
             } else if (edgeOrNode.equals("node")) {
-                edgeBased = false;
+                turnCostProvider = NO_TURN_COST_PROVIDER;
             } else {
                 throw new IllegalArgumentException("Invalid CH profile string: " + profileString);
             }
             Weighting weighting;
             if (weightingStr.equalsIgnoreCase("fastest")) {
-                weighting = new FastestWeighting(encoder);
+                weighting = new FastestWeighting(encoder, turnCostProvider);
             } else if (weightingStr.equalsIgnoreCase("shortest")) {
-                weighting = new ShortestWeighting(encoder);
+                weighting = new ShortestWeighting(encoder, turnCostProvider);
             } else if (weightingStr.equalsIgnoreCase("short_fastest")) {
-                weighting = new ShortFastestWeighting(encoder, 0.1);
+                weighting = new ShortFastestWeighting(encoder, 0.1, turnCostProvider);
             } else {
                 throw new IllegalArgumentException("Weighting not supported using this method, maybe you can use setCHProfile instead: " + weightingStr);
             }
-            chProfiles.add(new CHProfile(weighting, edgeBased, uTurnCostsInt));
+            chProfiles.add(new CHProfile(weighting, edgeBased));
         }
 
     }
diff --git a/core/src/main/java/com/graphhopper/storage/GraphEdgeIdFinder.java b/core/src/main/java/com/graphhopper/storage/GraphEdgeIdFinder.java
index 1eba43e87..c7c476b14 100644
--- a/core/src/main/java/com/graphhopper/storage/GraphEdgeIdFinder.java
+++ b/core/src/main/java/com/graphhopper/storage/GraphEdgeIdFinder.java
@@ -28,11 +28,14 @@ import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.PointList;
 import com.graphhopper.util.shapes.Polygon;
 import com.graphhopper.util.shapes.*;
+import org.locationtech.jts.algorithm.RectangleLineIntersector;
 import org.locationtech.jts.geom.*;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.graphhopper.util.shapes.BBox.toEnvelope;
+
 /**
  * This class allows to find edges (or construct shapes) from shape filter.
  *
@@ -70,9 +73,9 @@ public class GraphEdgeIdFinder {
     public void findEdgesInShape(final GHIntHashSet edgeIds, final Shape shape, EdgeFilter filter) {
         GHPoint center = shape.getCenter();
         QueryResult qr = locationIndex.findClosest(center.getLat(), center.getLon(), filter);
-        // TODO: if there is no street close to the center it'll fail although there are roads covered. Maybe we should check edge points or some random points in the Shape instead?
+        // TODO: this is suboptimal and will be fixed in #1324
         if (!qr.isValid())
-            throw new IllegalArgumentException("Shape " + shape + " does not cover graph");
+            throw new IllegalArgumentException("Shape '" + shape + "' does not cover graph. Center: " + center);
 
         if (shape.contains(qr.getSnappedPoint().lat, qr.getSnappedPoint().lon))
             edgeIds.add(qr.getClosestEdge().getEdge());
@@ -155,25 +158,34 @@ public class GraphEdgeIdFinder {
             for (int i = 0; i < blockedCircularAreasArr.length; i++) {
                 String objectAsString = blockedCircularAreasArr[i];
                 String[] splittedObject = objectAsString.split(innerObjSep);
+
+                // always add the shape as we'll need this for virtual edges and for debugging.
                 if (splittedObject.length > 4) {
-                    final Polygon polygon = Polygon.parsePoints(objectAsString, 0.003);
-                    findEdgesInShape(blockArea.blockedEdges, polygon, filter);
+                    final Polygon polygon = Polygon.parsePoints(objectAsString);
+                    blockArea.add(polygon);
+                    if (polygon.calculateArea() <= useEdgeIdsUntilAreaSize)
+                        findEdgesInShape(blockArea.blockedEdges, polygon, filter);
                 } else if (splittedObject.length == 4) {
                     final BBox bbox = BBox.parseTwoPoints(objectAsString);
-                    if (bbox.calculateArea() > useEdgeIdsUntilAreaSize)
-                        blockArea.add(bbox);
-                    else
-                        findEdgesInShape(blockArea.blockedEdges, bbox, filter);
+                    final RectangleLineIntersector cachedIntersector = new RectangleLineIntersector(toEnvelope(bbox));
+                    BBox preparedBBox = new BBox(bbox.minLon, bbox.maxLon, bbox.minLat, bbox.maxLat) {
+                        @Override
+                        public boolean intersects(PointList pointList) {
+                            return BBox.intersects(cachedIntersector, pointList);
+                        }
+                    };
+                    blockArea.add(preparedBBox);
+                    if (bbox.calculateArea() <= useEdgeIdsUntilAreaSize)
+                        findEdgesInShape(blockArea.blockedEdges, preparedBBox, filter);
                 } else if (splittedObject.length == 3) {
                     double lat = Double.parseDouble(splittedObject[0]);
                     double lon = Double.parseDouble(splittedObject[1]);
                     int radius = Integer.parseInt(splittedObject[2]);
                     Circle circle = new Circle(lat, lon, radius);
-                    if (circle.calculateArea() > useEdgeIdsUntilAreaSize) {
-                        blockArea.add(circle);
-                    } else {
+                    blockArea.add(circle);
+                    if (circle.calculateArea() <= useEdgeIdsUntilAreaSize)
                         findEdgesInShape(blockArea.blockedEdges, circle, filter);
-                    }
+
                 } else if (splittedObject.length == 2) {
                     double lat = Double.parseDouble(splittedObject[0]);
                     double lon = Double.parseDouble(splittedObject[1]);
@@ -210,14 +222,20 @@ public class GraphEdgeIdFinder {
         /**
          * @return true if the specified edgeState is part of this BlockArea
          */
-        public final boolean contains(EdgeIteratorState edgeState) {
+        public final boolean intersects(EdgeIteratorState edgeState) {
             if (!blockedEdges.isEmpty() && blockedEdges.contains(edgeState.getEdge())) {
                 return true;
             }
 
-            if (!blockedShapes.isEmpty() && na != null) {
-                for (Shape shape : blockedShapes) {
-                    if (shape.contains(na.getLatitude(edgeState.getAdjNode()), na.getLongitude(edgeState.getAdjNode())))
+            // compromise: mostly avoid expensive fetchWayGeometry which isn't yet fast for being used in Weighting.calc
+            BBox bbox = BBox.fromPoints(na.getLatitude(edgeState.getBaseNode()), na.getLongitude(edgeState.getBaseNode()),
+                    na.getLatitude(edgeState.getAdjNode()), na.getLongitude(edgeState.getAdjNode()));
+            PointList pointList = null;
+            for (Shape shape : blockedShapes) {
+                if (shape.getBounds().intersects(bbox)) {
+                    if (pointList == null)
+                        pointList = edgeState.fetchWayGeometry(3).makeImmutable();
+                    if (shape.intersects(pointList))
                         return true;
                 }
             }
diff --git a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java b/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
index f0c476a3b..fb1ca951e 100644
--- a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
+++ b/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
@@ -20,6 +20,7 @@ package com.graphhopper.storage;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.EdgeExplorer;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.shapes.BBox;
@@ -451,6 +452,11 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
         return baseGraph.getTurnCostStorage();
     }
 
+    @Override
+    public Weighting wrapWeighting(Weighting weighting) {
+        return weighting;
+    }
+
     @Override
     public int getOtherNode(int edge, int node) {
         return baseGraph.getOtherNode(edge, node);
diff --git a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
index c4036a545..b9381f867 100644
--- a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
+++ b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
@@ -512,7 +512,7 @@ public final class MMapDataAccess extends AbstractDataAccess {
             // windows does not allow changing the length of an open files
             if (!Constants.WINDOWS) {
                 // reduce file size
-                raFile.setLength(HEADER_OFFSET + remainingSegNo * segmentSizeInBytes);
+                raFile.setLength(HEADER_OFFSET + (long) remainingSegNo * segmentSizeInBytes);
             }
         } catch (Exception ex) {
             throw new RuntimeException(ex);
diff --git a/core/src/main/java/com/graphhopper/storage/RoutingCHGraphImpl.java b/core/src/main/java/com/graphhopper/storage/RoutingCHGraphImpl.java
index bb7a3be09..7c3d57fc1 100644
--- a/core/src/main/java/com/graphhopper/storage/RoutingCHGraphImpl.java
+++ b/core/src/main/java/com/graphhopper/storage/RoutingCHGraphImpl.java
@@ -19,7 +19,6 @@
 package com.graphhopper.storage;
 
 import com.graphhopper.routing.querygraph.QueryGraph;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.EdgeIteratorState;
 
@@ -37,13 +36,8 @@ public class RoutingCHGraphImpl implements RoutingCHGraph {
      */
     private final Graph baseGraph;
     private final Weighting weighting;
-    private final TurnWeighting turnWeighting;
 
     public RoutingCHGraphImpl(Graph graph, Weighting weighting) {
-        this(graph, weighting, null);
-    }
-
-    public RoutingCHGraphImpl(Graph graph, Weighting weighting, TurnWeighting turnWeighting) {
         this.graph = graph;
         if (graph instanceof QueryGraph) {
             chGraph = (CHGraph) ((QueryGraph) graph).getMainGraph();
@@ -52,7 +46,6 @@ public class RoutingCHGraphImpl implements RoutingCHGraph {
         }
         baseGraph = chGraph.getBaseGraph();
         this.weighting = weighting;
-        this.turnWeighting = turnWeighting;
     }
 
     @Override
@@ -128,15 +121,12 @@ public class RoutingCHGraphImpl implements RoutingCHGraph {
 
     @Override
     public Weighting getWeighting() {
-        return turnWeighting != null ? turnWeighting : weighting;
+        return weighting;
     }
 
     @Override
     public double getTurnWeight(int edgeFrom, int nodeVia, int edgeTo) {
-        if (turnWeighting == null) {
-            return 0;
-        }
-        return turnWeighting.calcTurnWeight(edgeFrom, nodeVia, edgeTo);
+        return weighting.calcTurnWeight(edgeFrom, nodeVia, edgeTo);
     }
 
 }
diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java
index 5b41e882b..7c4f2de2f 100644
--- a/core/src/main/java/com/graphhopper/util/GHUtility.java
+++ b/core/src/main/java/com/graphhopper/util/GHUtility.java
@@ -24,6 +24,7 @@ import com.graphhopper.coll.GHIntArrayList;
 import com.graphhopper.coll.GHTBitSet;
 import com.graphhopper.routing.profiles.*;
 import com.graphhopper.routing.util.*;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.shapes.BBox;
 import org.slf4j.Logger;
@@ -646,6 +647,42 @@ public class GHUtility {
         }
     }
 
+    /**
+     * Calculates the weight of a given edge like {@link Weighting#calcEdgeWeight} and adds the transition
+     * cost (the turn weight, {@link Weighting#calcTurnWeight}) associated with transitioning from/to the edge with ID prevOrNextEdgeId.
+     *
+     * @param prevOrNextEdgeId if reverse is false this has to be the previous edgeId, if true it
+     *                         has to be the next edgeId in the direction from start to end.
+     */
+    public static double calcWeightWithTurnWeight(Weighting weighting, EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+        final double edgeWeight = weighting.calcEdgeWeight(edgeState, reverse);
+        if (!EdgeIterator.Edge.isValid(prevOrNextEdgeId)) {
+            return edgeWeight;
+        }
+        final int origEdgeId = reverse ? edgeState.getOrigEdgeLast() : edgeState.getOrigEdgeFirst();
+        double turnWeight = reverse
+                ? weighting.calcTurnWeight(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId)
+                : weighting.calcTurnWeight(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId);
+        return edgeWeight + turnWeight;
+    }
+
+    /**
+     * @see #calcWeightWithTurnWeight(Weighting, EdgeIteratorState, boolean, int)
+     */
+    public static long calcMillisWithTurnMillis(Weighting weighting, EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+        long edgeMillis = weighting.calcEdgeMillis(edgeState, reverse);
+        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 = edgeState.getEdge();
+        long turnMillis = reverse
+                ? weighting.calcTurnMillis(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId)
+                : weighting.calcTurnMillis(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId);
+        return edgeMillis + turnMillis;
+    }
+
     /**
      * This edge iterator can be used in tests to mock specific iterator behaviour via overloading
      * certain methods.
diff --git a/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java b/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java
index 49ec49b33..81dbfbd70 100644
--- a/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java
+++ b/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java
@@ -2,7 +2,7 @@ package com.graphhopper.util.details;
 
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.Helper;
+import com.graphhopper.util.GHUtility;
 
 import static com.graphhopper.util.Parameters.Details.AVERAGE_SPEED;
 
@@ -10,7 +10,7 @@ public class AverageSpeedDetails extends AbstractPathDetailsBuilder {
 
     private final Weighting weighting;
     private final double precision;
-    private double decimalValue = -1;
+    private Double decimalValue;
     // will include the turn time penalty
     private int prevEdgeId = -1;
 
@@ -35,13 +35,22 @@ public class AverageSpeedDetails extends AbstractPathDetailsBuilder {
 
     @Override
     public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) {
-        double tmpVal = edge.getDistance() / weighting.calcMillis(edge, false, prevEdgeId) * 3600;
-        if (Double.isInfinite(tmpVal))
-            throw new IllegalStateException("average_speed was infinite for " + edge.fetchWayGeometry(3));
+        // for very short edges we might not be able calculate a proper value for speed. dividing by calcMillis can
+        // even lead to speed=Infinity -> just ignore these cases here, see #1848
+        final double distance = edge.getDistance();
+        if (distance < 0.1) {
+            if (decimalValue != null)
+                return false;
+
+            // in case this is the first edge we have to return some value
+            decimalValue = null;
+            return true;
+        }
 
+        double tmpVal = distance / GHUtility.calcMillisWithTurnMillis(weighting, edge, false, prevEdgeId) * 3600;
         prevEdgeId = edge.getEdge();
-        if (Math.abs(tmpVal - decimalValue) >= precision) {
-            this.decimalValue =  Math.round(tmpVal / precision) * precision;
+        if (decimalValue == null || Math.abs(tmpVal - decimalValue) >= precision) {
+            this.decimalValue = Math.round(tmpVal / precision) * precision;
             return true;
         }
         return false;
diff --git a/core/src/main/java/com/graphhopper/util/details/TimeDetails.java b/core/src/main/java/com/graphhopper/util/details/TimeDetails.java
index f15da3755..1a1a48619 100644
--- a/core/src/main/java/com/graphhopper/util/details/TimeDetails.java
+++ b/core/src/main/java/com/graphhopper/util/details/TimeDetails.java
@@ -19,6 +19,7 @@ package com.graphhopper.util.details;
 
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 
 import static com.graphhopper.util.Parameters.Details.TIME;
 
@@ -42,7 +43,7 @@ public class TimeDetails extends AbstractPathDetailsBuilder {
     @Override
     public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) {
         if (edge.getEdge() != prevEdgeId) {
-            time = weighting.calcMillis(edge, false, prevEdgeId);
+            time = GHUtility.calcMillisWithTurnMillis(weighting, edge, false, prevEdgeId);
             prevEdgeId = edge.getEdge();
             return true;
         }
diff --git a/core/src/main/java/com/graphhopper/util/details/WeightDetails.java b/core/src/main/java/com/graphhopper/util/details/WeightDetails.java
index 92e9b064c..a6d8a82c6 100644
--- a/core/src/main/java/com/graphhopper/util/details/WeightDetails.java
+++ b/core/src/main/java/com/graphhopper/util/details/WeightDetails.java
@@ -20,6 +20,7 @@ package com.graphhopper.util.details;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 
 import static com.graphhopper.util.Parameters.Details.WEIGHT;
 
@@ -43,7 +44,7 @@ public class WeightDetails extends AbstractPathDetailsBuilder {
     public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) {
         if (edge.getEdge() != edgeId) {
             edgeId = edge.getEdge();
-            weight = weighting.calcWeight(edge, false, edgeId);
+            weight = GHUtility.calcWeightWithTurnWeight(weighting, edge, false, edgeId);
             return true;
         }
         return false;
diff --git a/isochrone/src/main/java/com/graphhopper/isochrone/algorithm/Isochrone.java b/isochrone/src/main/java/com/graphhopper/isochrone/algorithm/Isochrone.java
index 3cbba9b6f..9adf15450 100644
--- a/isochrone/src/main/java/com/graphhopper/isochrone/algorithm/Isochrone.java
+++ b/isochrone/src/main/java/com/graphhopper/isochrone/algorithm/Isochrone.java
@@ -30,6 +30,7 @@ import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.NodeAccess;
 import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.util.EdgeIterator;
+import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.shapes.GHPoint;
 import org.locationtech.jts.geom.Coordinate;
 
@@ -241,12 +242,14 @@ public class Isochrone extends AbstractRoutingAlgorithm {
                 }
 
                 // todo: for #1776/#1835 move the access check into weighting
-                double tmpWeight = !filter.accept(iter) ? Double.POSITIVE_INFINITY : (weighting.calcWeight(iter, reverseFlow, currEdge.edge) + currEdge.weight);
+                double tmpWeight = !filter.accept(iter)
+                        ? Double.POSITIVE_INFINITY
+                        : (GHUtility.calcWeightWithTurnWeight(weighting, iter, reverseFlow, currEdge.edge) + currEdge.weight);
                 if (Double.isInfinite(tmpWeight))
                     continue;
 
                 double tmpDistance = iter.getDistance() + currEdge.distance;
-                long tmpTime = weighting.calcMillis(iter, reverseFlow, currEdge.edge) + currEdge.time;
+                long tmpTime = GHUtility.calcMillisWithTurnMillis(weighting, iter, reverseFlow, currEdge.edge) + currEdge.time;
                 int tmpNode = iter.getAdjNode();
                 IsoLabel nEdge = fromMap.get(tmpNode);
                 if (nEdge == null) {
diff --git a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphExplorer.java b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphExplorer.java
index d0cd3f38f..0dd0aaf0c 100644
--- a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphExplorer.java
+++ b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphExplorer.java
@@ -113,7 +113,7 @@ public final class GraphExplorer {
         GtfsStorage.EdgeType edgeType = edge.get(flagEncoder.getTypeEnc());
         switch (edgeType) {
             case HIGHWAY:
-                // todonow: why do we not account for turn times here ?
+                // todo: why do we not account for turn times here ?
                 return (long) (accessEgressWeighting.calcEdgeMillis(edge, reverse) * (5.0 / walkSpeedKmH));
             case ENTER_TIME_EXPANDED_NETWORK:
                 if (reverse) {
diff --git a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphSupport.java b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphSupport.java
index 0724ecbdb..e86f18684 100644
--- a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphSupport.java
+++ b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphSupport.java
@@ -24,6 +24,7 @@ import com.graphhopper.routing.profiles.EnumEncodedValue;
 import com.graphhopper.routing.profiles.IntEncodedValue;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.EdgeExplorer;
 import com.graphhopper.util.EdgeIteratorState;
@@ -297,6 +298,11 @@ class GraphSupport {
                 throw new UnsupportedOperationException();
             }
 
+            @Override
+            public Weighting wrapWeighting(Weighting weighting) {
+                throw new UnsupportedOperationException();
+            }
+
             @Override
             public int getOtherNode(int edge, int node) {
                 throw new UnsupportedOperationException();
diff --git a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/RealtimeFeed.java b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/RealtimeFeed.java
index 4fae07f3b..22b7314d5 100644
--- a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/RealtimeFeed.java
+++ b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/RealtimeFeed.java
@@ -31,6 +31,7 @@ import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.NodeAccess;
@@ -249,6 +250,11 @@ public class RealtimeFeed {
                 throw new RuntimeException();
             }
 
+            @Override
+            public Weighting wrapWeighting(Weighting weighting) {
+                throw new RuntimeException();
+            }
+
             @Override
             public int getOtherNode(int edge, int node) {
                 throw new UnsupportedOperationException();
diff --git a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/WrapperGraph.java b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/WrapperGraph.java
index 84349f01b..8435e16e2 100644
--- a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/WrapperGraph.java
+++ b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/WrapperGraph.java
@@ -28,6 +28,7 @@ import com.graphhopper.routing.profiles.IntEncodedValue;
 import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EdgeFilter;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.IntsRef;
 import com.graphhopper.storage.NodeAccess;
@@ -519,6 +520,11 @@ public class WrapperGraph implements Graph {
         return mainGraph.getTurnCostStorage();
     }
 
+    @Override
+    public Weighting wrapWeighting(Weighting weighting) {
+        return mainGraph.wrapWeighting(weighting);
+    }
+
     @Override
     public int getOtherNode(int edge, int node) {
         return mainGraph.getOtherNode(edge, node);
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 57c11ce34..994e1320d 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
@@ -698,11 +698,11 @@ public class OSMReader implements DataReader, TurnCostParser.ExternalInternalMap
                     pillarNodes.add(lat, lon);
             }
         }
-        if (towerNodeDistance < 0.0001) {
+        if (towerNodeDistance < 0.001) {
             // As investigation shows often two paths should have crossed via one identical point 
             // but end up in two very close points.
             zeroCounter++;
-            towerNodeDistance = 0.0001;
+            towerNodeDistance = 0.001;
         }
 
         double maxDistance = (Integer.MAX_VALUE - 1) / 1000d;
diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java
index 45f8a61d4..b5e8be35b 100644
--- a/tools/src/main/java/com/graphhopper/tools/Measurement.java
+++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java
@@ -51,6 +51,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import static com.graphhopper.util.Helper.*;
 import static com.graphhopper.util.Parameters.Algorithms.ALT_ROUTE;
 import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
+import static com.graphhopper.util.Parameters.Routing.BLOCK_AREA;
 
 /**
  * @author Peter Karich
@@ -168,23 +169,30 @@ public class Measurement {
             GHBitSet allowedEdges = printGraphDetails(g, vehicleStr);
             printMiscUnitPerfTests(g, isCH, encoder, count * 100, allowedEdges);
             printLocationIndexQuery(g, hopper.getLocationIndex(), count);
+            String blockAreaStr = "49.394664,11.144428,49.348388,11.144943,49.355768,11.227169,49.411643,11.227512";
             if (runSlow) {
-                printTimeOfRouteQuery(hopper, isCH, isLM, count / 20, "routing", vehicleStr,
-                        true, false, -1, true, false, false, false);
-                printTimeOfRouteQuery(hopper, isCH, isLM, count / 20, "routing_edge", vehicleStr,
-                        true, false, -1, true, true, false, false);
+                printTimeOfRouteQuery(hopper, new QuerySettings("routing", vehicleStr, count / 20, isCH, isLM).
+                        withInstructions());
+                printTimeOfRouteQuery(hopper, new QuerySettings("routing_edge", vehicleStr, count / 20, isCH, isLM).
+                        withInstructions().edgeBased());
+                printTimeOfRouteQuery(hopper, new QuerySettings("routing_block_area", vehicleStr, count / 20, isCH, isLM).
+                        withInstructions().blockArea(blockAreaStr));
             }
 
             if (hopper.getLMFactoryDecorator().isEnabled()) {
                 System.gc();
                 isLM = true;
-                for (int activeLMCount : Arrays.asList(4, 8, 12, 16)) {
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count / 4, "routingLM" + activeLMCount, vehicleStr,
-                            true, false, activeLMCount, true, false, false, false);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count / 4, "routingLM" + activeLMCount + "_edge", vehicleStr,
-                            true, false, activeLMCount, true, true, false, false);
+                int activeLMCount = 12;
+                for (; activeLMCount > 3; activeLMCount -= 4) {
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingLM" + activeLMCount, vehicleStr, count / 4, isCH, isLM).
+                            withInstructions().activeLandmarks(activeLMCount));
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingLM" + activeLMCount + "_edge", vehicleStr, count / 4, isCH, isLM).
+                            withInstructions().activeLandmarks(activeLMCount).edgeBased());
                 }
 
+                activeLMCount = 8;
+                printTimeOfRouteQuery(hopper, new QuerySettings("routingLM" + activeLMCount + "_block_area", vehicleStr, count / 4, isCH, isLM).
+                        withInstructions().activeLandmarks(activeLMCount).blockArea(blockAreaStr));
                 // compareRouting(hopper, vehicleStr, count / 5);
             }
 
@@ -196,8 +204,8 @@ public class Measurement {
                     System.gc();
                     // try just one constellation, often ~4-6 is best
                     int lmCount = 5;
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCHLM" + lmCount, vehicleStr,
-                            true, false, lmCount, true, false, false, false);
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCHLM" + lmCount, vehicleStr, count, isCH, isLM).
+                            withInstructions().activeLandmarks(lmCount).sod());
                 }
 
                 isLM = false;
@@ -207,26 +215,26 @@ public class Measurement {
                     CHGraph lg = g.getCHGraph(chProfile);
                     fillAllowedEdges(lg.getAllEdges(), allowedEdges);
                     printMiscUnitPerfTests(lg, isCH, encoder, count * 100, allowedEdges);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH", vehicleStr, true,
-                            false, -1, true, false, false, false);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count / 10, "routingCH_alt", vehicleStr, true,
-                            false, -1, true, false, false, true);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_with_hints", vehicleStr, true,
-                            true, -1, true, false, false, false);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_no_sod", vehicleStr, true,
-                            false, -1, false, false, false, false);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_no_instr", vehicleStr, false,
-                            false, -1, true, false, false, false);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_full", vehicleStr, true,
-                            true, -1, true, false, true, false);
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH", vehicleStr, count, isCH, isLM).
+                            withInstructions().sod());
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_alt", vehicleStr, count / 10, isCH, isLM).
+                            withInstructions().sod().alternative());
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_with_hints", vehicleStr, count, isCH, isLM).
+                            withInstructions().sod().withPointHints());
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_no_sod", vehicleStr, count, isCH, isLM).
+                            withInstructions());
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_no_instr", vehicleStr, count, isCH, isLM).
+                            sod());
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_full", vehicleStr, count, isCH, isLM).
+                            withInstructions().withPointHints().sod().simplify());
                 }
                 if (!hopper.getCHFactoryDecorator().getEdgeBasedCHProfiles().isEmpty()) {
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_edge", vehicleStr, true,
-                            false, -1, false, true, false, false);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_edge_no_instr", vehicleStr, false,
-                            false, -1, false, true, false, false);
-                    printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_edge_full", vehicleStr, true,
-                            true, -1, false, true, true, false);
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_edge", vehicleStr, count, isCH, isLM).
+                            edgeBased().withInstructions());
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_edge_no_instr", vehicleStr, count, isCH, isLM).
+                            edgeBased());
+                    printTimeOfRouteQuery(hopper, new QuerySettings("routingCH_edge_full", vehicleStr, count, isCH, isLM).
+                            edgeBased().withInstructions().withPointHints().simplify());
                 }
             }
         } catch (Exception ex) {
@@ -252,6 +260,63 @@ public class Measurement {
         }
     }
 
+    private static class QuerySettings {
+        private final String prefix, vehicle;
+        private final int count;
+        final boolean ch, lm;
+        int activeLandmarks = -1;
+        boolean withInstructions, withPointHints, sod, edgeBased, simplify, alternative;
+        String blockArea;
+
+        QuerySettings(String prefix, String vehicle, int count, boolean isCH, boolean isLM) {
+            this.vehicle = vehicle;
+            this.prefix = prefix;
+            this.count = count;
+            this.ch = isCH;
+            this.lm = isLM;
+        }
+
+        QuerySettings withInstructions() {
+            this.withInstructions = true;
+            return this;
+        }
+
+        QuerySettings withPointHints() {
+            this.withPointHints = true;
+            return this;
+        }
+
+        QuerySettings sod() {
+            sod = true;
+            return this;
+        }
+
+        QuerySettings activeLandmarks(int alm) {
+            this.activeLandmarks = alm;
+            return this;
+        }
+
+        QuerySettings edgeBased() {
+            this.edgeBased = true;
+            return this;
+        }
+
+        QuerySettings simplify() {
+            this.simplify = true;
+            return this;
+        }
+
+        QuerySettings alternative() {
+            alternative = true;
+            return this;
+        }
+
+        QuerySettings blockArea(String str) {
+            blockArea = str;
+            return this;
+        }
+    }
+
     void fillAllowedEdges(AllEdgesIterator iter, GHBitSet bs) {
         bs.clear();
         while (iter.next()) {
@@ -455,11 +520,7 @@ public class Measurement {
         }
     }
 
-    private void printTimeOfRouteQuery(final GraphHopper hopper, final boolean ch, final boolean lm,
-                                       int count, String prefix, final String vehicle,
-                                       final boolean withInstructions, final boolean withPointHints,
-                                       final int activeLandmarks, final boolean sod, final boolean edgeBased,
-                                       final boolean simplify, final boolean alternative) {
+    private void printTimeOfRouteQuery(final GraphHopper hopper, final QuerySettings querySettings) {
         final Graph g = hopper.getGraphHopperStorage();
         final AtomicLong maxDistance = new AtomicLong(0);
         final AtomicLong minDistance = new AtomicLong(Long.MAX_VALUE);
@@ -469,7 +530,7 @@ public class Measurement {
         final AtomicInteger failedCount = new AtomicInteger(0);
         final DistanceCalc distCalc = new DistanceCalcEarth();
 
-        final EdgeExplorer edgeExplorer = g.createEdgeExplorer(DefaultEdgeFilter.allEdges(hopper.getEncodingManager().getEncoder(vehicle)));
+        final EdgeExplorer edgeExplorer = g.createEdgeExplorer(DefaultEdgeFilter.allEdges(hopper.getEncodingManager().getEncoder(querySettings.vehicle)));
         final AtomicLong visitedNodesSum = new AtomicLong(0);
         final AtomicLong maxVisitedNodes = new AtomicLong(0);
 //        final AtomicLong extractTimeSum = new AtomicLong(0);
@@ -490,29 +551,32 @@ public class Measurement {
                 double toLon = na.getLongitude(to);
                 GHRequest req = new GHRequest(fromLat, fromLon, toLat, toLon).
                         setWeighting("fastest").
-                        setVehicle(vehicle);
+                        setVehicle(querySettings.vehicle);
 
-                req.getHints().put(CH.DISABLE, !ch).
-                        put("stall_on_demand", sod).
-                        put(Parameters.Routing.EDGE_BASED, edgeBased).
-                        put(Landmark.DISABLE, !lm).
-                        put(Landmark.ACTIVE_COUNT, activeLandmarks).
-                        put("instructions", withInstructions);
+                req.getHints().put(CH.DISABLE, !querySettings.ch).
+                        put("stall_on_demand", querySettings.sod).
+                        put(Parameters.Routing.EDGE_BASED, querySettings.edgeBased).
+                        put(Landmark.DISABLE, !querySettings.lm).
+                        put(Landmark.ACTIVE_COUNT, querySettings.activeLandmarks).
+                        put("instructions", querySettings.withInstructions);
 
-                if (alternative)
+                if (querySettings.alternative)
                     req.setAlgorithm(ALT_ROUTE);
 
-                if (withInstructions)
+                if (querySettings.withInstructions)
                     req.setPathDetails(Arrays.asList(Parameters.Details.AVERAGE_SPEED));
 
-                if (simplify) {
+                if (querySettings.simplify) {
                     req.setPathDetails(Arrays.asList(Parameters.Details.AVERAGE_SPEED, Parameters.Details.EDGE_ID, Parameters.Details.STREET_NAME));
                 } else {
                     // disable path simplification by setting the distance to zero
                     req.getHints().put(Parameters.Routing.WAY_POINT_MAX_DISTANCE, 0);
                 }
 
-                if (withPointHints) {
+                if (querySettings.blockArea != null)
+                    req.getHints().put(BLOCK_AREA, querySettings.blockArea);
+
+                if (querySettings.withPointHints) {
                     EdgeIterator iter = edgeExplorer.setBaseNode(from);
                     if (!iter.next())
                         throw new IllegalArgumentException("wrong 'from' when adding point hint");
@@ -565,21 +629,22 @@ public class Measurement {
                     if (dist < minDistance.get())
                         minDistance.set(dist);
 
-                    if (alternative)
+                    if (querySettings.alternative)
                         altCount.addAndGet(rsp.getAll().size());
                 }
 
                 return arsp.getPoints().getSize();
             }
-        }.setIterations(count).start();
+        }.setIterations(querySettings.count).start();
 
-        count -= failedCount.get();
+        int count = querySettings.count - failedCount.get();
 
         // if using non-bidirectional algorithm make sure you exclude CH routing
-        String algoStr = (ch && !edgeBased) ? Algorithms.DIJKSTRA_BI : Algorithms.ASTAR_BI;
-        if (ch && !sod) {
+        String algoStr = (querySettings.ch && !querySettings.edgeBased) ? Algorithms.DIJKSTRA_BI : Algorithms.ASTAR_BI;
+        if (querySettings.ch && !querySettings.sod) {
             algoStr += "_no_sod";
         }
+        String prefix = querySettings.prefix;
         put(prefix + ".guessed_algorithm", algoStr);
         put(prefix + ".failed_count", failedCount.get());
         put(prefix + ".distance_min", minDistance.get());
diff --git a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
index db0655574..a2d1a6587 100644
--- a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
+++ b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
@@ -52,6 +52,8 @@ import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
 import java.util.Random;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+
 /**
  * A rough graphical user interface for visualizing the OSM graph. Mainly for debugging algorithms
  * and spatial data structures. See e.g. this blog post:
@@ -143,7 +145,7 @@ public class MiniGraphUI {
             map.put(Parameters.CH.DISABLE, true);
 //            map.put(Parameters.Landmark.DISABLE, true);
             routingGraph = graph;
-            weighting = hopper.createWeighting(map, encoder, graph);
+            weighting = hopper.createWeighting(map, encoder, graph, NO_TURN_COST_PROVIDER);
             final RoutingAlgorithmFactory tmpFactory = hopper.getAlgorithmFactory(map);
             algoFactory = new RoutingAlgorithmFactory() {
 
diff --git a/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java
index 8e7cbc383..5e455dcd8 100644
--- a/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java
@@ -34,6 +34,8 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+
 @Path("isochrone")
 public class IsochroneResource {
 
@@ -89,7 +91,8 @@ public class IsochroneResource {
         HintsMap hintsMap = new HintsMap();
         RouteResource.initHints(hintsMap, uriInfo.getQueryParameters());
 
-        Weighting weighting = graphHopper.createWeighting(hintsMap, encoder, graph);
+        // todo: isochrones with turn costs ?
+        Weighting weighting = graphHopper.createWeighting(hintsMap, encoder, graph, NO_TURN_COST_PROVIDER);
         Isochrone isochrone = new Isochrone(queryGraph, weighting, reverseFlow);
 
         if (distanceInMeter > 0) {
diff --git a/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java b/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java
index 62567f926..371871412 100644
--- a/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java
@@ -29,6 +29,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+
 /**
  * This resource provides the entire shortest path tree as response. In a simple CSV format discussed at #1577.
  */
@@ -77,7 +79,8 @@ public class SPTResource {
         HintsMap hintsMap = new HintsMap();
         RouteResource.initHints(hintsMap, uriInfo.getQueryParameters());
 
-        Weighting weighting = graphHopper.createWeighting(hintsMap, encoder, graph);
+        // todo: /spt with turn costs ?
+        Weighting weighting = graphHopper.createWeighting(hintsMap, encoder, graph, NO_TURN_COST_PROVIDER);
         Isochrone isochrone = new Isochrone(queryGraph, weighting, reverseFlow);
 
         if (distanceInMeter > 0) {
diff --git a/web/src/main/resources/assets/css/style.css b/web/src/main/resources/assets/css/style.css
index 627481893..4ed4da019 100644
--- a/web/src/main/resources/assets/css/style.css
+++ b/web/src/main/resources/assets/css/style.css
@@ -115,10 +115,29 @@ body {
     padding-top: 20px;
     color: #666666
 }
+
 #locationpoints {
     padding-bottom: 10px;
     float: left;
 }
+
+#locationpoints {
+    counter-set: pointcounter -1;
+}
+
+#locationpoints>div {
+    counter-increment: pointcounter;
+}
+
+#locationpoints>div:nth-child(n+2):nth-last-child(n+3)::before {
+    content: counter(pointcounter);
+    position: absolute;
+    padding-top: 0.45em;
+    right: calc(100% - 1.2em);
+    text-align: right;
+    font-size: 60%;
+}
+
 .pointDiv {
     margin: 6px 0;
 }
@@ -461,4 +480,4 @@ td img.pic {
 
 .fullscreen-reverse-btn {
     background-image: url("../img/full-reverse.png");
-}
\ No newline at end of file
+}
diff --git a/web/src/main/resources/assets/js/main-template.js b/web/src/main/resources/assets/js/main-template.js
index d1a270ea1..7b0543fb1 100644
--- a/web/src/main/resources/assets/js/main-template.js
+++ b/web/src/main/resources/assets/js/main-template.js
@@ -397,6 +397,7 @@ function setIntermediateCoord(e) {
     });
     var index = routeManipulation.getIntermediatePointIndex(routeSegments, e.latlng);
     ghRequest.route.add(e.latlng.wrap(), index);
+    ghRequest.do_zoom = false;
     resolveIndex(index);
     routeIfAllResolved();
 }


Test patch:
diff --git a/api/src/test/java/com/graphhopper/util/shapes/BBoxTest.java b/api/src/test/java/com/graphhopper/util/shapes/BBoxTest.java
index 321e473b5..24cf2beef 100644
--- a/api/src/test/java/com/graphhopper/util/shapes/BBoxTest.java
+++ b/api/src/test/java/com/graphhopper/util/shapes/BBoxTest.java
@@ -19,6 +19,7 @@ package com.graphhopper.util.shapes;
 
 import com.graphhopper.util.DistanceCalc;
 import com.graphhopper.util.DistanceCalcEarth;
+import com.graphhopper.util.PointList;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -86,8 +87,39 @@ public class BBoxTest {
     }
 
     @Test
-    public void testCalculateIntersection() {
+    public void testPointListIntersect() {
+        BBox bbox = new BBox(-0.5, 1, 1, 2);
+        PointList pointList = new PointList();
+        pointList.add(5, 5);
+        pointList.add(5, 0);
+        assertFalse(bbox.intersects(pointList));
+
+        pointList.add(-5, 0);
+        assertTrue(bbox.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(5, 1);
+        pointList.add(-1, 0);
+        assertTrue(bbox.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(5, 0);
+        pointList.add(-1, 3);
+        assertFalse(bbox.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(5, 0);
+        pointList.add(-1, 2);
+        assertTrue(bbox.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(1.5, -2);
+        pointList.add(1.5, 2);
+        assertTrue(bbox.intersects(pointList));
+    }
 
+    @Test
+    public void testCalculateIntersection() {
         BBox b1 = new BBox(0, 2, 0, 1);
         BBox b2 = new BBox(-1, 1, -1, 2);
         BBox expected = new BBox(0, 1, 0, 1);
diff --git a/api/src/test/java/com/graphhopper/util/shapes/CircleTest.java b/api/src/test/java/com/graphhopper/util/shapes/CircleTest.java
index eaf6e1809..b5e7b28f6 100644
--- a/api/src/test/java/com/graphhopper/util/shapes/CircleTest.java
+++ b/api/src/test/java/com/graphhopper/util/shapes/CircleTest.java
@@ -17,11 +17,11 @@
  */
 package com.graphhopper.util.shapes;
 
+import com.graphhopper.util.Helper;
+import com.graphhopper.util.PointList;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 /**
  * @author Peter Karich
@@ -42,6 +42,38 @@ public class CircleTest {
         assertFalse(new BBox(9, 11, 8, 9).intersects(new Circle(10, 10, 110000)));
     }
 
+    @Test
+    public void testIntersectPointList() {
+        Circle circle = new Circle(1.5, 0.3, Helper.DIST_EARTH.calcDist(0, 0, 0, 0.7));
+        PointList pointList = new PointList();
+        pointList.add(5, 5);
+        pointList.add(5, 0);
+        assertFalse(circle.intersects(pointList));
+
+        pointList.add(-5, 0);
+        assertTrue(circle.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(5, 1);
+        pointList.add(-1, 0);
+        assertTrue(circle.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(5, 0);
+        pointList.add(-1, 3);
+        assertFalse(circle.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(5, 0);
+        pointList.add(2, 0);
+        assertTrue(circle.intersects(pointList));
+
+        pointList = new PointList();
+        pointList.add(1.5, -2);
+        pointList.add(1.5, 2);
+        assertTrue(circle.intersects(pointList));
+    }
+
     @Test
     public void testContains() {
         Circle c = new Circle(10, 10, 120000);
diff --git a/api/src/test/java/com/graphhopper/util/shapes/PolygonTest.java b/api/src/test/java/com/graphhopper/util/shapes/PolygonTest.java
index 9c1b81370..3e874000d 100644
--- a/api/src/test/java/com/graphhopper/util/shapes/PolygonTest.java
+++ b/api/src/test/java/com/graphhopper/util/shapes/PolygonTest.java
@@ -22,7 +22,7 @@ public class PolygonTest {
         assertTrue(square.contains(10,10));
         assertTrue(square.contains(16,10));
         assertFalse(square.contains(10,-20));
-        assertTrue(square.contains(10,0));
+        assertTrue(square.contains(10,0.1));
         assertFalse(square.contains(10,20));
         assertTrue(square.contains(10,16));
         assertFalse(square.contains(20,20));
diff --git a/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java b/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java
index a3a91973a..26585faa0 100644
--- a/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java
+++ b/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java
@@ -20,7 +20,6 @@ package com.graphhopper.routing.util;
 import com.graphhopper.PathWrapper;
 import com.graphhopper.routing.*;
 import com.graphhopper.routing.querygraph.QueryGraph;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.storage.CHGraph;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.index.LocationIndex;
@@ -49,16 +48,6 @@ public class TestAlgoCollector {
                                             OneRun oneRun) {
         List<Path> altPaths = new ArrayList<>();
         QueryGraph queryGraph = QueryGraph.lookup(algoEntry.getForQueryGraph(), queryList);
-        AlgorithmOptions opts = algoEntry.getAlgorithmOptions();
-        FlagEncoder encoder = opts.getWeighting().getFlagEncoder();
-        if (encoder.supportsTurnCosts()) {
-            if (!opts.getTraversalMode().isEdgeBased()) {
-                errors.add("Cannot use TurnWeighting with node based traversal");
-                return this;
-            }
-            algoEntry.setAlgorithmOptions(AlgorithmOptions.start(opts).weighting(new TurnWeighting(opts.getWeighting(), queryGraph.getTurnCostStorage())).build());
-        }
-
         RoutingAlgorithmFactory factory = algoEntry.createRoutingFactory();
         for (int i = 0; i < queryList.size() - 1; i++) {
             RoutingAlgorithm algo = factory.createAlgo(queryGraph, algoEntry.getAlgorithmOptions());
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 6de128f29..736af023d 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java
@@ -25,6 +25,8 @@ import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.PMap;
 import com.graphhopper.util.Parameters.Routing;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+
 /**
  * Calculates the fastest route with the specified vehicle (VehicleEncoder). Calculates the weight
  * in seconds.
@@ -45,8 +47,20 @@ public class FastestWeighting extends AbstractWeighting {
     // this factor puts a penalty on roads with a "destination"-only access, see #733
     private final double roadAccessPenalty;
 
+    public FastestWeighting(FlagEncoder encoder) {
+        this(encoder, new HintsMap(0));
+    }
+
+    public FastestWeighting(FlagEncoder encoder, TurnCostProvider turnCostProvider) {
+        this(encoder, new HintsMap(0), turnCostProvider);
+    }
+
     public FastestWeighting(FlagEncoder encoder, PMap map) {
-        super(encoder);
+        this(encoder, map, NO_TURN_COST_PROVIDER);
+    }
+
+    public FastestWeighting(FlagEncoder encoder, PMap map, TurnCostProvider turnCostProvider) {
+        super(encoder, turnCostProvider);
         headingPenalty = map.getDouble(Routing.HEADING_PENALTY, Routing.DEFAULT_HEADING_PENALTY);
         headingPenaltyMillis = Math.round(headingPenalty * 1000);
         maxSpeed = encoder.getMaxSpeed() / SPEED_CONV;
@@ -61,17 +75,13 @@ public class FastestWeighting extends AbstractWeighting {
         }
     }
 
-    public FastestWeighting(FlagEncoder encoder) {
-        this(encoder, new HintsMap(0));
-    }
-
     @Override
     public double getMinWeight(double distance) {
         return distance / maxSpeed;
     }
 
     @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
         double speed = reverse ? edgeState.getReverse(avSpeedEnc) : edgeState.get(avSpeedEnc);
         if (speed == 0)
             return Double.POSITIVE_INFINITY;
@@ -90,14 +100,14 @@ public class FastestWeighting extends AbstractWeighting {
     }
 
     @Override
-    public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+    public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
         // TODO move this to AbstractWeighting? see #485
         long time = 0;
         boolean unfavoredEdge = edgeState.get(EdgeIteratorState.UNFAVORED_EDGE);
         if (unfavoredEdge)
             time += headingPenaltyMillis;
 
-        return time + super.calcMillis(edgeState, reverse, prevOrNextEdgeId);
+        return time + super.calcEdgeMillis(edgeState, reverse);
     }
 
     static double checkBounds(String key, double val, double from, double to) {
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 33e689972..eb82cf000 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java
@@ -21,6 +21,8 @@ import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.PMap;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+
 /**
  * Calculates the fastest route with distance influence controlled by a new parameter.
  * <p>
@@ -36,7 +38,11 @@ public class ShortFastestWeighting extends FastestWeighting {
     private final double timeFactor;
 
     public ShortFastestWeighting(FlagEncoder encoder, PMap map) {
-        super(encoder);
+        this(encoder, map, NO_TURN_COST_PROVIDER);
+    }
+
+    public ShortFastestWeighting(FlagEncoder encoder, PMap map, TurnCostProvider turnCostProvider) {
+        super(encoder, turnCostProvider);
         timeFactor = checkBounds(TIME_FACTOR, map.getDouble(TIME_FACTOR, 1), 0, 10);
 
         // default value derived from the cost for time e.g. 25€/hour and for distance 0.5€/km
@@ -47,7 +53,11 @@ public class ShortFastestWeighting extends FastestWeighting {
     }
 
     public ShortFastestWeighting(FlagEncoder encoder, double distanceFactor) {
-        super(encoder);
+        this(encoder, distanceFactor, NO_TURN_COST_PROVIDER);
+    }
+
+    public ShortFastestWeighting(FlagEncoder encoder, double distanceFactor, TurnCostProvider turnCostProvider) {
+        super(encoder, turnCostProvider);
         this.distanceFactor = checkBounds(DISTANCE_FACTOR, distanceFactor, 0, 10);
         this.timeFactor = 1;
     }
@@ -58,8 +68,8 @@ public class ShortFastestWeighting extends FastestWeighting {
     }
 
     @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-        double time = super.calcWeight(edgeState, reverse, prevOrNextEdgeId);
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
+        double time = super.calcEdgeWeight(edgeState, reverse);
         return time * timeFactor + edgeState.getDistance() * distanceFactor;
     }
 
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java
index 400461edb..356050e0a 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java
@@ -20,6 +20,8 @@ package com.graphhopper.routing.weighting;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.util.EdgeIteratorState;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+
 /**
  * Calculates the shortest route - independent of a vehicle as the calculation is based on the
  * distance only.
@@ -29,7 +31,11 @@ import com.graphhopper.util.EdgeIteratorState;
  */
 public class ShortestWeighting extends AbstractWeighting {
     public ShortestWeighting(FlagEncoder flagEncoder) {
-        super(flagEncoder);
+        this(flagEncoder, NO_TURN_COST_PROVIDER);
+    }
+
+    public ShortestWeighting(FlagEncoder flagEncoder, TurnCostProvider turnCostProvider) {
+        super(flagEncoder, turnCostProvider);
     }
 
     @Override
@@ -38,7 +44,7 @@ public class ShortestWeighting extends AbstractWeighting {
     }
 
     @Override
-    public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+    public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
         return edgeState.getDistance();
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
index c5d837e79..8e6c4888d 100644
--- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
@@ -23,12 +23,12 @@ import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.TraversalMode;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
 import com.graphhopper.routing.weighting.FastestWeighting;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
+import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.RAMDirectory;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -42,17 +42,16 @@ import static org.junit.Assert.*;
 
 @RunWith(Parameterized.class)
 public class AlternativeRouteTest {
-    private final FlagEncoder carFE = new CarFlagEncoder();
     private final Weighting weighting;
     private final TraversalMode traversalMode;
     private final GraphHopperStorage graph;
 
     public AlternativeRouteTest(TraversalMode tMode) {
         this.traversalMode = tMode;
+        FlagEncoder carFE = new CarFlagEncoder();
         EncodingManager em = EncodingManager.create(carFE);
-        graph = new GraphHopperStorage(new RAMDirectory(), em, false, true);
-        graph.create(1000);
-        weighting = new TurnWeighting(new FastestWeighting(carFE), graph.getTurnCostStorage());
+        graph = new GraphBuilder(em).withTurnCosts(true).create();
+        weighting = new FastestWeighting(carFE, new DefaultTurnCostProvider(carFE, graph.getTurnCostStorage()));
     }
 
     /**
@@ -173,7 +172,6 @@ public class AlternativeRouteTest {
         // one single disconnected node
         updateDistancesFor(graph, 20, 0.00, -0.01);
 
-        Weighting weighting = new FastestWeighting(carFE);
         AlternativeBidirSearch altDijkstra = new AlternativeBidirSearch(graph, weighting, traversalMode, 1);
         Path path = altDijkstra.calcPath(1, 20);
         assertFalse(path.isFound());
diff --git a/core/src/test/java/com/graphhopper/routing/BidirPathExtractorTest.java b/core/src/test/java/com/graphhopper/routing/BidirPathExtractorTest.java
index 0a83d0c45..06ec0e2a4 100644
--- a/core/src/test/java/com/graphhopper/routing/BidirPathExtractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/BidirPathExtractorTest.java
@@ -23,8 +23,8 @@ import com.graphhopper.routing.profiles.TurnCost;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
 import com.graphhopper.routing.weighting.FastestWeighting;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.EdgeIterator;
 import org.junit.Test;
@@ -77,7 +77,7 @@ public class BidirPathExtractorTest {
         SPTEntry bwdEntry = new SPTEntry(1, 2, 1.2);
         bwdEntry.parent = new SPTEntry(EdgeIterator.NO_EDGE, 3, 0);
 
-        Path p = BidirPathExtractor.extractPath(g, new TurnWeighting(new FastestWeighting(carEncoder), turnCostStorage), fwdEntry, bwdEntry, 0);
+        Path p = BidirPathExtractor.extractPath(g, new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, turnCostStorage)), fwdEntry, bwdEntry, 0);
         p.setWeight(5 + 1.8);
 
         assertEquals(IntArrayList.from(1, 2, 3), p.calcNodes());
diff --git a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
index 45b7d2025..bc456d80d 100644
--- a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
+++ b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
@@ -23,7 +23,6 @@ import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.MotorcycleFlagEncoder;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.CHGraph;
 import com.graphhopper.storage.GraphBuilder;
@@ -712,10 +711,9 @@ public class CHQueryWithTurnCostsTest {
     }
 
     private AbstractBidirectionEdgeCHNoSOD createAlgo() {
-        TurnWeighting turnWeighting = new TurnWeighting(weighting, graph.getTurnCostStorage());
         return "astar".equals(algoString) ?
-                new AStarBidirectionEdgeCHNoSOD(new RoutingCHGraphImpl(chGraph, weighting, turnWeighting)) :
-                new DijkstraBidirectionEdgeCHNoSOD(new RoutingCHGraphImpl(chGraph, weighting, turnWeighting));
+                new AStarBidirectionEdgeCHNoSOD(new RoutingCHGraphImpl(chGraph, weighting)) :
+                new DijkstraBidirectionEdgeCHNoSOD(new RoutingCHGraphImpl(chGraph, weighting));
     }
 
     private void addShortcut(int from, int to, int firstOrigEdge, int lastOrigEdge, int skipped1, int skipped2, double weight) {
diff --git a/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java b/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java
index 548cc9c1e..66f5bb152 100644
--- a/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java
@@ -56,12 +56,12 @@ public class DirectedBidirectionalDijkstraTest {
         encodingManager = EncodingManager.create(encoder);
         graph = new GraphHopperStorage(dir, encodingManager, false, true).create(1000);
         turnCostStorage = graph.getTurnCostStorage();
-        weighting = createWeighting(Double.POSITIVE_INFINITY);
+        weighting = createWeighting(Weighting.INFINITE_U_TURN_COSTS);
         turnCostEnc = encodingManager.getDecimalEncodedValue(getKey(encoder.toString(), EV_SUFFIX));
     }
 
-    private Weighting createWeighting(double defaultUTurnCosts) {
-        return new TurnWeighting(new FastestWeighting(encoder), turnCostStorage, defaultUTurnCosts);
+    private Weighting createWeighting(int uTurnCosts) {
+        return new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCosts));
     }
 
     @Test
diff --git a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
index 1ca0c8734..294fd2624 100644
--- a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
+++ b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
@@ -23,8 +23,8 @@ import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.TraversalMode;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
 import com.graphhopper.routing.weighting.FastestWeighting;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphBuilder;
@@ -142,11 +142,11 @@ public class EdgeBasedRoutingAlgorithmTest {
     }
 
     private Weighting createWeighting() {
-        return createWeighting(carEncoder, Double.POSITIVE_INFINITY);
+        return createWeighting(carEncoder, Weighting.INFINITE_U_TURN_COSTS);
     }
 
-    private Weighting createWeighting(FlagEncoder encoder, double uTurnCosts) {
-        return new TurnWeighting(new FastestWeighting(encoder), tcs, uTurnCosts);
+    private Weighting createWeighting(FlagEncoder encoder, int uTurnCosts) {
+        return new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, tcs, uTurnCosts));
     }
 
     @Test
@@ -397,7 +397,7 @@ public class EdgeBasedRoutingAlgorithmTest {
         setTurnCost(g, 2, 5, 6, 3);
         setTurnCost(g, 1, 6, 7, 4);
 
-        TurnWeighting weighting = new TurnWeighting(new FastestWeighting(carEncoder), tcs) {
+        FastestWeighting weighting = new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, tcs) {
             @Override
             public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) {
                 if (edgeFrom >= 0)
@@ -406,7 +406,7 @@ public class EdgeBasedRoutingAlgorithmTest {
                     assertNotNull("edge " + edgeTo + " to " + nodeVia + " does not exist", g.getEdgeIteratorState(edgeTo, nodeVia));
                 return super.calcTurnWeight(edgeFrom, nodeVia, edgeTo);
             }
-        };
+        });
         Path p = createAlgo(g, weighting, EDGE_BASED).calcPath(5, 1);
         assertEquals(IntArrayList.from(5, 6, 7, 4, 3, 1), p.calcNodes());
         assertEquals(5 * 0.06 + 1, p.getWeight(), 1.e-6);
diff --git a/core/src/test/java/com/graphhopper/routing/PathTest.java b/core/src/test/java/com/graphhopper/routing/PathTest.java
index 422a6f77d..51fb890ae 100644
--- a/core/src/test/java/com/graphhopper/routing/PathTest.java
+++ b/core/src/test/java/com/graphhopper/routing/PathTest.java
@@ -316,6 +316,28 @@ public class PathTest {
         assertEquals(4, averageSpeedDetails.get(3).getLast());
     }
 
+    @Test
+    public void testCalcAverageSpeedDetailsWithShortDistances_issue1848() {
+        ShortestWeighting weighting = new ShortestWeighting(encoder);
+        Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 6);
+        assertTrue(p.isFound());
+        Map<String, List<PathDetail>> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting,
+                Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0);
+        assertTrue(details.size() == 1);
+        List<PathDetail> averageSpeedDetails = details.get(AVERAGE_SPEED);
+        assertEquals(4, averageSpeedDetails.size());
+
+        // reverse path includes 'null' value as first
+        p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(6, 1);
+        assertTrue(p.isFound());
+        details = PathDetailsFromEdges.calcDetails(p, carManager, weighting,
+                Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0);
+        assertTrue(details.size() == 1);
+        averageSpeedDetails = details.get(AVERAGE_SPEED);
+        assertEquals(5, averageSpeedDetails.size());
+        assertNull(averageSpeedDetails.get(0).getValue());
+    }
+
     @Test
     public void testCalcStreetNameDetails() {
         ShortestWeighting weighting = new ShortestWeighting(encoder);
@@ -924,6 +946,7 @@ public class PathTest {
         na.setNode(3, 52.514, 13.350);
         na.setNode(4, 52.515, 13.349);
         na.setNode(5, 52.516, 13.3452);
+        na.setNode(6, 52.516, 13.344);
 
         ReaderWay w = new ReaderWay(1);
         w.setTag("highway", "tertiary");
@@ -946,6 +969,9 @@ public class PathTest {
         tmpEdge = g.edge(3, 4, 10, true).setName("3-4");
         tmpEdge.setFlags(carManager.handleWayTags(w, map, relFlags));
 
+        tmpEdge = g.edge(5, 6, 0.01, true).setName("3-4");
+        tmpEdge.setFlags(carManager.handleWayTags(w, map, relFlags));
+
         return g;
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
index 9bc2d53a7..26c2c9a2a 100644
--- a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
@@ -7,7 +7,6 @@ import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.TraversalMode;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndexTree;
@@ -157,9 +156,7 @@ public class RandomCHRoutingTest {
                 assertEquals("queryGraph and chQueryGraph should have equal number of nodes", queryGraph.getNodes(), chQueryGraph.getNodes());
                 int from = rnd.nextInt(queryGraph.getNodes());
                 int to = rnd.nextInt(queryGraph.getNodes());
-                Weighting w = traversalMode.isEdgeBased()
-                        ? new TurnWeighting(weighting, queryGraph.getTurnCostStorage(), uTurnCosts == INFINITE_U_TURN_COSTS ? Double.POSITIVE_INFINITY : uTurnCosts)
-                        : weighting;
+                Weighting w = queryGraph.wrapWeighting(weighting);
                 // using plain dijkstra instead of bidirectional, because of #1592
                 RoutingAlgorithm refAlgo = new Dijkstra(queryGraph, w, traversalMode);
                 Path refPath = refAlgo.calcPath(from, to);
diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
index 24dce0d94..cbe65bebd 100644
--- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
+++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
@@ -24,8 +24,13 @@ import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.routing.util.TestAlgoCollector.AlgoHelperEntry;
 import com.graphhopper.routing.util.TraversalMode;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
+import com.graphhopper.routing.weighting.TurnCostProvider;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.*;
+import com.graphhopper.storage.CHProfile;
+import com.graphhopper.storage.Directory;
+import com.graphhopper.storage.GraphBuilder;
+import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.util.Parameters;
@@ -39,6 +44,7 @@ import java.util.List;
 import java.util.Random;
 import java.util.zip.GZIPInputStream;
 
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
 import static com.graphhopper.util.Parameters.Algorithms.*;
 import static org.junit.Assert.assertTrue;
 
@@ -57,7 +63,8 @@ public class RoutingAlgorithmIT {
             addStr = "turn|";
 
         FlagEncoder encoder = hopper.getEncodingManager().getEncoder(hints.getVehicle());
-        Weighting weighting = hopper.createWeighting(hints, encoder, hopper.getGraphHopperStorage());
+        TurnCostProvider turnCostProvider = tMode.isEdgeBased() ? new DefaultTurnCostProvider(encoder, ghStorage.getTurnCostStorage()) : NO_TURN_COST_PROVIDER;
+        Weighting weighting = hopper.createWeighting(hints, encoder, hopper.getGraphHopperStorage(), turnCostProvider);
 
         HintsMap defaultHints = new HintsMap().put(Parameters.CH.DISABLE, true).put(Parameters.Landmark.DISABLE, true)
                 .setVehicle(hints.getVehicle()).setWeighting(hints.getWeighting());
diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java
index ed84b0422..39b27e942 100644
--- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java
+++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java
@@ -26,7 +26,7 @@ import com.graphhopper.routing.querygraph.QueryGraph;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
-import com.graphhopper.routing.weighting.TurnWeighting;
+import com.graphhopper.routing.weighting.TurnCostProvider;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndex;
@@ -50,7 +50,6 @@ import java.util.Random;
 
 import static com.graphhopper.routing.util.TraversalMode.EDGE_BASED;
 import static com.graphhopper.routing.util.TraversalMode.NODE_BASED;
-import static com.graphhopper.util.EdgeIterator.NO_EDGE;
 import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static com.graphhopper.util.Helper.DIST_EARTH;
 import static com.graphhopper.util.Parameters.Algorithms.ASTAR_BI;
@@ -60,8 +59,9 @@ import static org.junit.Assert.*;
 /**
  * This test tests the different routing algorithms on small user-defined sample graphs. It tests node- and edge-based
  * algorithms, but does *not* use turn costs, because otherwise node- and edge-based implementations would not be
- * comparable. For edge-based traversal u-turns cannot happen even when we are not using a {@link TurnWeighting}, because
- * as long as we do not apply turn restrictions we will never take a u-turn.. All tests should follow the same pattern:
+ * comparable. For edge-based traversal u-turns cannot happen, even with {@link TurnCostProvider#NO_TURN_COST_PROVIDER},
+ * because as long as we do not apply turn restrictions we will never take a u-turn.
+ * All tests should follow the same pattern:
  * <p>
  * - create a GH storage, you need to pass all the weightings used in this test to {@link #createGHStorage}, such that
  * the according CHGraphs can be created
@@ -819,11 +819,6 @@ public class RoutingAlgorithmTest {
 
             @Override
             public final double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
-                return calcWeight(edgeState, reverse, NO_EDGE);
-            }
-
-            @Override
-            public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
                 int adj = edgeState.getAdjNode();
                 int base = edgeState.getBaseNode();
                 if (reverse) {
@@ -845,12 +840,17 @@ public class RoutingAlgorithmTest {
 
             @Override
             public final long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) {
-                return calcMillis(edgeState, reverse, NO_EDGE);
+                return tmpW.calcEdgeMillis(edgeState, reverse);
             }
 
             @Override
-            public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-                return tmpW.calcMillis(edgeState, reverse, prevOrNextEdgeId);
+            public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
+                return tmpW.calcTurnWeight(inEdge, viaNode, outEdge);
+            }
+
+            @Override
+            public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
+                return tmpW.calcTurnMillis(inEdge, viaNode, outEdge);
             }
 
             @Override
@@ -862,6 +862,11 @@ public class RoutingAlgorithmTest {
             public String getName() {
                 return "custom";
             }
+
+            @Override
+            public String toString() {
+                return tmpW.getFlagEncoder().toString() + "_" + getName();
+            }
         };
 
         GraphHopperStorage graph = createGHStorage(true, defaultWeighting);
@@ -998,7 +1003,7 @@ public class RoutingAlgorithmTest {
     private GraphHopperStorage createGHStorage(boolean is3D, Weighting... weightings) {
         CHProfile[] chProfiles = new CHProfile[weightings.length];
         for (int i = 0; i < weightings.length; i++) {
-            chProfiles[i] = new CHProfile(weightings[i], traversalMode, Weighting.INFINITE_U_TURN_COSTS);
+            chProfiles[i] = new CHProfile(weightings[i], traversalMode);
         }
         return new GraphBuilder(encodingManager).set3D(is3D)
                 .setCHProfiles(chProfiles)
@@ -1151,7 +1156,7 @@ public class RoutingAlgorithmTest {
     private static abstract class CHCalculator implements PathCalculator {
         @Override
         public Path calcPath(GraphHopperStorage graph, Weighting weighting, TraversalMode traversalMode, int maxVisitedNodes, int from, int to) {
-            CHProfile chProfile = new CHProfile(weighting, traversalMode, Weighting.INFINITE_U_TURN_COSTS);
+            CHProfile chProfile = new CHProfile(weighting, traversalMode);
             PrepareContractionHierarchies pch = PrepareContractionHierarchies.fromGraphHopperStorage(graph, chProfile);
             CHGraph chGraph = graph.getCHGraph(chProfile);
             if (chGraph.getEdges() == chGraph.getOriginalEdges()) {
@@ -1177,7 +1182,7 @@ public class RoutingAlgorithmTest {
 
         @Override
         public Path calcPath(GraphHopperStorage graph, Weighting weighting, TraversalMode traversalMode, int maxVisitedNodes, QueryResult from, QueryResult to) {
-            CHProfile chProfile = new CHProfile(weighting, traversalMode, Weighting.INFINITE_U_TURN_COSTS);
+            CHProfile chProfile = new CHProfile(weighting, traversalMode);
             PrepareContractionHierarchies pch = PrepareContractionHierarchies.fromGraphHopperStorage(graph, chProfile);
             CHGraph chGraph = graph.getCHGraph(chProfile);
             if (chGraph.getEdges() == chGraph.getOriginalEdges()) {
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java
index 1a3a4f492..484ed028e 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java
@@ -19,11 +19,9 @@
 package com.graphhopper.routing.ch;
 
 import com.graphhopper.routing.util.*;
-import com.graphhopper.routing.weighting.FastestWeighting;
-import com.graphhopper.routing.weighting.ShortFastestWeighting;
-import com.graphhopper.routing.weighting.ShortestWeighting;
-import com.graphhopper.routing.weighting.Weighting;
+import com.graphhopper.routing.weighting.*;
 import com.graphhopper.storage.CHProfile;
+import com.graphhopper.storage.TurnCostStorage;
 import com.graphhopper.util.Parameters;
 import org.junit.Before;
 import org.junit.Test;
@@ -33,7 +31,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
 import static org.junit.Assert.*;
 
 public class CHProfileSelectorTest {
@@ -42,7 +39,12 @@ public class CHProfileSelectorTest {
     private static final String NO_MATCH_ERROR = "Cannot find matching CH profile for your request";
 
     private Weighting weightingFastestCar;
+    private Weighting weightingFastestCarEdge;
+    private Weighting weightingFastestCarEdge10;
+    private Weighting weightingFastestCarEdge30;
+    private Weighting weightingFastestCarEdge50;
     private Weighting weightingFastestBike;
+    private Weighting weightingFastestBikeEdge40;
     private Weighting weightingShortestCar;
     private Weighting weightingShortestBike;
 
@@ -51,8 +53,14 @@ public class CHProfileSelectorTest {
         FlagEncoder carEncoder = new CarFlagEncoder();
         FlagEncoder bikeEncoder = new BikeFlagEncoder();
         EncodingManager.create(carEncoder, bikeEncoder);
+        TurnCostStorage turnCostStorage = new TurnCostStorage(null, null);
         weightingFastestCar = new FastestWeighting(carEncoder);
+        weightingFastestCarEdge = new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, turnCostStorage));
+        weightingFastestCarEdge10 = new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, turnCostStorage, 10));
+        weightingFastestCarEdge30 = new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, turnCostStorage, 30));
+        weightingFastestCarEdge50 = new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, turnCostStorage, 50));
         weightingFastestBike = new FastestWeighting(bikeEncoder);
+        weightingFastestBikeEdge40 = new FastestWeighting(bikeEncoder, new DefaultTurnCostProvider(bikeEncoder, turnCostStorage, 40));
         weightingShortestCar = new ShortestWeighting(carEncoder);
         weightingShortestBike = new ShortestWeighting(bikeEncoder);
     }
@@ -74,7 +82,7 @@ public class CHProfileSelectorTest {
     @Test
     public void onlyEdgeBasedPresent() {
         List<CHProfile> chProfiles = Collections.singletonList(
-                CHProfile.edgeBased(weightingFastestCar, INFINITE_U_TURN_COSTS)
+                CHProfile.edgeBased(weightingFastestCarEdge)
         );
         assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, false, null);
         assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, false, 20);
@@ -86,7 +94,7 @@ public class CHProfileSelectorTest {
     public void edgeAndNodePresent() {
         List<CHProfile> chProfiles = Arrays.asList(
                 CHProfile.nodeBased(weightingFastestCar),
-                CHProfile.edgeBased(weightingFastestCar, INFINITE_U_TURN_COSTS)
+                CHProfile.edgeBased(weightingFastestCarEdge)
         );
         // in case edge-based is not specified we prefer the edge-based profile over the node-based one
         assertProfileFound(chProfiles.get(1), chProfiles, null, null);
@@ -98,8 +106,8 @@ public class CHProfileSelectorTest {
     public void multipleEdgeBased() {
         List<CHProfile> chProfiles = Arrays.asList(
                 CHProfile.nodeBased(weightingFastestCar),
-                CHProfile.edgeBased(weightingFastestCar, 30),
-                CHProfile.edgeBased(weightingFastestCar, 50)
+                CHProfile.edgeBased(weightingFastestCarEdge30),
+                CHProfile.edgeBased(weightingFastestCarEdge50)
         );
         // when no u-turns are specified we throw
         assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, true, null);
@@ -139,7 +147,7 @@ public class CHProfileSelectorTest {
         List<CHProfile> chProfiles = Arrays.asList(
                 CHProfile.nodeBased(weightingFastestBike),
                 CHProfile.nodeBased(weightingShortestBike),
-                CHProfile.edgeBased(weightingFastestBike, 40)
+                CHProfile.edgeBased(weightingFastestBikeEdge40)
         );
         // the vehicle is not given but only bike is used so its fine. note that we prefer edge-based because no edge_based parameter is specified
         assertProfileFound(chProfiles.get(2), chProfiles, "", "fastest", null, null);
@@ -155,8 +163,8 @@ public class CHProfileSelectorTest {
     public void missingWeightingMultipleProfiles() {
         List<CHProfile> chProfiles = Arrays.asList(
                 CHProfile.nodeBased(weightingFastestBike),
-                CHProfile.edgeBased(weightingFastestCar, 10),
-                CHProfile.edgeBased(weightingFastestBike, 40)
+                CHProfile.edgeBased(weightingFastestCarEdge10),
+                CHProfile.edgeBased(weightingFastestBikeEdge40)
         );
         // the weighting is not given but only fastest is used so its fine. note that we prefer edge-based because no edge_based parameter is specified
         assertProfileFound(chProfiles.get(2), chProfiles, "bike", "", null, null);
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
index 977f9a9af..21f7ccf88 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
@@ -23,8 +23,8 @@ import com.graphhopper.RepeatRule;
 import com.graphhopper.routing.*;
 import com.graphhopper.routing.querygraph.QueryGraph;
 import com.graphhopper.routing.util.*;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
 import com.graphhopper.routing.weighting.ShortestWeighting;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndexTree;
@@ -59,7 +59,6 @@ public class CHTurnCostTest {
     private int maxCost;
     private CarFlagEncoder encoder;
     private EncodingManager encodingManager;
-    private Weighting weighting;
     private GraphHopperStorage graph;
     private TurnCostStorage turnCostStorage;
     private List<CHProfile> chProfiles;
@@ -76,13 +75,13 @@ public class CHTurnCostTest {
         maxCost = 10;
         encoder = new CarFlagEncoder(5, 5, maxCost);
         encodingManager = EncodingManager.create(encoder);
-        weighting = new ShortestWeighting(encoder);
+        graph = new GraphBuilder(encodingManager).build();
+        turnCostStorage = graph.getTurnCostStorage();
         chProfiles = createCHProfiles();
-        graph = new GraphBuilder(encodingManager).setCHProfiles(chProfiles).create();
+        graph.addCHGraphs(chProfiles).create(1000);
         // the default CH profile with infinite u-turn costs, can be reset in tests that should run with finite u-turn
         // costs
-        chProfile = CHProfile.edgeBased(weighting, INFINITE_U_TURN_COSTS);
-        turnCostStorage = graph.getTurnCostStorage();
+        chProfile = chProfiles.get(0);
         checkStrict = true;
     }
 
@@ -93,14 +92,15 @@ public class CHTurnCostTest {
     private List<CHProfile> createCHProfiles() {
         Set<CHProfile> profileSet = new LinkedHashSet<>(5);
         // the first one is always the one with infinite u-turn costs
-        profileSet.add(CHProfile.edgeBased(weighting, INFINITE_U_TURN_COSTS));
+        profileSet.add(CHProfile.edgeBased(new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, INFINITE_U_TURN_COSTS))));
         // this one we also always add
-        profileSet.add(CHProfile.edgeBased(weighting, 50));
+        profileSet.add(CHProfile.edgeBased(new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, 50))));
         // add more (distinct) profiles
         long seed = System.nanoTime();
         Random rnd = new Random(seed);
         while (profileSet.size() < 5) {
-            profileSet.add(CHProfile.edgeBased(weighting, 10 + rnd.nextInt(90)));
+            int uTurnCosts = 10 + rnd.nextInt(90);
+            profileSet.add(CHProfile.edgeBased(new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCosts))));
         }
         return new ArrayList<>(profileSet);
     }
@@ -664,7 +664,7 @@ public class CHTurnCostTest {
         graph.edge(3, 1, 100, false);
         setRestriction(0, 3, 1);
         graph.freeze();
-        chProfile = CHProfile.edgeBased(weighting, 50);
+        chProfile = chProfiles.get(1);
         RoutingAlgorithmFactory pch = prepareCH(Arrays.asList(4, 0, 2, 3, 1));
         Path path = pch.createAlgo(chGraph, AlgorithmOptions.start().build()).calcPath(0, 1);
         assertEquals(IntArrayList.from(0, 3, 4, 3, 1), path.calcNodes());
@@ -971,7 +971,7 @@ public class CHTurnCostTest {
         // not allowed to turn right at node 1 -> we have to take a u-turn at node 0 (or at the virtual node...)
         setRestriction(2, 1, 5);
         graph.freeze();
-        chProfile = CHProfile.edgeBased(weighting, 50);
+        chProfile = chProfiles.get(1);
         RoutingAlgorithmFactory pch = prepareCH(Arrays.asList(0, 1, 2, 3, 4, 5, 6));
         LocationIndexTree index = new LocationIndexTree(graph, new RAMDirectory());
         index.prepareIndex();
@@ -989,7 +989,8 @@ public class CHTurnCostTest {
         QueryResult qr2 = index.findClosest(virtualPoint.lat, virtualPoint.lon, EdgeFilter.ALL_EDGES);
         QueryGraph queryGraph = QueryGraph.lookup(graph, qr2);
         assertEquals(3, qr2.getClosestEdge().getEdge());
-        Dijkstra dijkstra = new Dijkstra(queryGraph, new TurnWeighting(weighting, queryGraph.getTurnCostStorage(), chGraph.getCHProfile().getUTurnCosts()), TraversalMode.EDGE_BASED);
+        Weighting w = queryGraph.wrapWeighting(chProfile.getWeighting());
+        Dijkstra dijkstra = new Dijkstra(queryGraph, w, TraversalMode.EDGE_BASED);
         Path dijkstraPath = dijkstra.calcPath(4, 6);
         assertEquals(IntArrayList.from(4, 3, 2, 1, 7, 0, 7, 1, 5, 6), dijkstraPath.calcNodes());
         assertEquals(dijkstraPath.getWeight(), path.getWeight(), 1.e-3);
@@ -1014,9 +1015,8 @@ public class CHTurnCostTest {
     @Test
     public void testFindPath_random_compareWithDijkstra_finiteUTurnCost() {
         long seed = System.nanoTime();
-        LOGGER.info("Seed for testFindPath_random_compareWithDijkstra_finiteUTurnCost: {}", seed);
+        LOGGER.info("Seed for testFindPath_random_compareWithDijkstra_finiteUTurnCost: {}, using weighting: {}", seed, chProfile.getWeighting());
         chProfile = chProfiles.get(1 + new Random(seed).nextInt(chProfiles.size() - 1));
-        LOGGER.info("U-turn-costs: " + chProfile.getUTurnCostsInt());
         compareWithDijkstraOnRandomGraph(seed);
     }
 
@@ -1046,9 +1046,8 @@ public class CHTurnCostTest {
     @Test
     public void testFindPath_heuristic_compareWithDijkstra_finiteUTurnCost() {
         long seed = System.nanoTime();
-        LOGGER.info("Seed for testFindPath_heuristic_compareWithDijkstra_finiteUTurnCost: {}", seed);
+        LOGGER.info("Seed for testFindPath_heuristic_compareWithDijkstra_finiteUTurnCost: {}, using weighting: {}", seed, chProfile.getWeighting());
         chProfile = chProfiles.get(1 + new Random(seed).nextInt(chProfiles.size() - 1));
-        LOGGER.info("U-turn-costs: " + chProfile.getUTurnCostsInt());
         compareWithDijkstraOnRandomGraph_heuristic(seed);
     }
 
@@ -1102,7 +1101,8 @@ public class CHTurnCostTest {
     }
 
     private Path findPathUsingDijkstra(int from, int to) {
-        Dijkstra dijkstra = new Dijkstra(graph, new TurnWeighting(weighting, turnCostStorage, chProfile.getUTurnCosts()), TraversalMode.EDGE_BASED);
+        Weighting w = graph.wrapWeighting(chProfile.getWeighting());
+        Dijkstra dijkstra = new Dijkstra(graph, w, TraversalMode.EDGE_BASED);
         return dijkstra.calcPath(from, to);
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
index eff907335..715bfb682 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
@@ -24,9 +24,8 @@ import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.util.AllCHEdgesIterator;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.weighting.TurnWeighting;
-import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.util.EdgeIteratorState;
@@ -36,12 +35,8 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
 
-import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
 import static org.junit.Assert.*;
 
 /**
@@ -55,11 +50,10 @@ public class EdgeBasedNodeContractorTest {
     private CHGraph chGraph;
     private CarFlagEncoder encoder;
     private GraphHopperStorage graph;
-    private int uTurnCosts;
-    private Weighting weighting;
 
     @Rule
     public RepeatRule repeatRule = new RepeatRule();
+    private List<CHProfile> chProfiles;
 
     @Before
     public void setup() {
@@ -70,14 +64,14 @@ public class EdgeBasedNodeContractorTest {
     private void initialize() {
         encoder = new CarFlagEncoder(5, 5, maxCost);
         EncodingManager encodingManager = EncodingManager.create(encoder);
-        uTurnCosts = INFINITE_U_TURN_COSTS;
         graph = new GraphBuilder(encodingManager)
                 .setCHProfileStrings(
-                        "car|shortest|edge|" + uTurnCosts
+                        "car|shortest|edge",
+                        "car|shortest|edge|60"
                 )
                 .create();
-        chGraph = graph.getCHGraph();
-        weighting = chGraph.getCHProfile().getWeighting();
+        chProfiles = graph.getCHProfiles();
+        chGraph = graph.getCHGraph(chProfiles.get(0));
     }
 
     @Test
@@ -1123,6 +1117,7 @@ public class EdgeBasedNodeContractorTest {
 
     @Test
     public void testFindPath_finiteUTurnCost() {
+        chGraph = graph.getCHGraph(chProfiles.get(1));
         // turning to 1 at node 3 when coming from 0 is forbidden, but taking the full loop 3-4-2-3 is very
         // expensive, so the best solution is to go straight to 4 and take a u-turn there
         //   1
@@ -1138,7 +1133,6 @@ public class EdgeBasedNodeContractorTest {
         graph.freeze();
         setMaxLevelOnAllNodes();
         setRestriction(0, 3, 1);
-        uTurnCosts = 60;
         contractNodes(4);
         checkShortcuts(
                 createShortcut(3, 2, 1, 2, 1, 2, 600),
@@ -1385,8 +1379,7 @@ public class EdgeBasedNodeContractorTest {
     }
 
     private EdgeBasedNodeContractor createNodeContractor() {
-        TurnWeighting turnWeighting = new TurnWeighting(weighting, graph.getTurnCostStorage(), uTurnCosts);
-        PrepareCHGraph prepareGraph = PrepareCHGraph.edgeBased(chGraph, weighting, turnWeighting);
+        PrepareCHGraph prepareGraph = PrepareCHGraph.edgeBased(chGraph, chGraph.getCHProfile().getWeighting());
         EdgeBasedNodeContractor nodeContractor = new EdgeBasedNodeContractor(prepareGraph, new PMap());
         nodeContractor.initFromGraph();
         return nodeContractor;
diff --git a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java
index f181fe944..1332a3a2c 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java
@@ -20,12 +20,10 @@ package com.graphhopper.routing.ch;
 
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.CHGraph;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.TurnCostStorage;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.PMap;
 import org.junit.Before;
@@ -39,7 +37,6 @@ public class EdgeBasedWitnessPathSearcherTest {
     private GraphHopperStorage graph;
     private CHGraph chGraph;
     private Weighting weighting;
-    private TurnWeighting chTurnWeighting;
 
     @Before
     public void setup() {
@@ -50,8 +47,6 @@ public class EdgeBasedWitnessPathSearcherTest {
                 .create();
         chGraph = graph.getCHGraph();
         weighting = chGraph.getCHProfile().getWeighting();
-        TurnCostStorage turnCostStorage = graph.getTurnCostStorage();
-        chTurnWeighting = new TurnWeighting(weighting, turnCostStorage);
     }
 
     @Test
@@ -129,7 +124,7 @@ public class EdgeBasedWitnessPathSearcherTest {
     }
 
     private EdgeBasedWitnessPathSearcher createFinder() {
-        PrepareCHGraph prepareGraph = PrepareCHGraph.edgeBased(chGraph, weighting, chTurnWeighting);
+        PrepareCHGraph prepareGraph = PrepareCHGraph.edgeBased(chGraph, weighting);
         return new EdgeBasedWitnessPathSearcher(prepareGraph, new PMap());
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
index d7e830f51..12e9d9fa5 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
@@ -70,7 +70,7 @@ public class LandmarkStorageTest {
         EdgeIteratorState edge = ghStorage.edge(0, 1);
         int res = new LandmarkStorage(ghStorage, dir, new FastestWeighting(encoder) {
             @Override
-            public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+            public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
                 return Integer.MAX_VALUE * 2L;
             }
         }, 8).setMaximumWeight(LandmarkStorage.PRECISION).calcWeight(edge, false);
@@ -79,7 +79,7 @@ public class LandmarkStorageTest {
         dir = new RAMDirectory();
         res = new LandmarkStorage(ghStorage, dir, new FastestWeighting(encoder) {
             @Override
-            public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
+            public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
                 return Double.POSITIVE_INFINITY;
             }
         }, 8).setMaximumWeight(LandmarkStorage.PRECISION).calcWeight(edge, false);
diff --git a/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java b/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java
index a66efe91d..a8e309732 100644
--- a/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java
+++ b/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java
@@ -22,8 +22,9 @@ import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
 import com.graphhopper.routing.profiles.TurnCost;
 import com.graphhopper.routing.util.*;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
 import com.graphhopper.routing.weighting.FastestWeighting;
-import com.graphhopper.routing.weighting.TurnWeighting;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.LocationIndexTree;
@@ -506,27 +507,26 @@ public class QueryGraphTest {
         EdgeIteratorState edge0 = graphWithTurnCosts.edge(0, 1, 10, true);
         EdgeIteratorState edge1 = graphWithTurnCosts.edge(2, 1, 10, true);
 
-        FastestWeighting weighting = new FastestWeighting(encoder);
-        TurnWeighting turnWeighting = new TurnWeighting(weighting, graphWithTurnCosts.getTurnCostStorage());
+        Weighting weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graphWithTurnCosts.getTurnCostStorage()));
 
         // no turn costs initially
-        assertEquals(0, turnWeighting.calcTurnWeight(edge0.getEdge(), 1, edge1.getEdge()), .1);
+        assertEquals(0, weighting.calcTurnWeight(edge0.getEdge(), 1, edge1.getEdge()), .1);
 
         // now use turn costs
         turnCostEnc.setDecimal(false, tcFlags, 10);
         turnExt.setTurnCost(tcFlags, edge0.getEdge(), 1, edge1.getEdge());
-        assertEquals(10, turnWeighting.calcTurnWeight(edge0.getEdge(), 1, edge1.getEdge()), .1);
+        assertEquals(10, weighting.calcTurnWeight(edge0.getEdge(), 1, edge1.getEdge()), .1);
 
         // now use turn costs with query graph
         QueryResult res1 = createLocationResult(0.000, 0.005, edge0, 0, QueryResult.Position.EDGE);
         QueryResult res2 = createLocationResult(0.005, 0.010, edge1, 0, QueryResult.Position.EDGE);
         QueryGraph qGraph = QueryGraph.lookup(graphWithTurnCosts, res1, res2);
-        turnWeighting = new TurnWeighting(weighting, qGraph.getTurnCostStorage());
+        weighting = qGraph.wrapWeighting(weighting);
 
         int fromQueryEdge = GHUtility.getEdge(qGraph, res1.getClosestNode(), 1).getEdge();
         int toQueryEdge = GHUtility.getEdge(qGraph, res2.getClosestNode(), 1).getEdge();
 
-        assertEquals(10, turnWeighting.calcTurnWeight(fromQueryEdge, 1, toQueryEdge), .1);
+        assertEquals(10, weighting.calcTurnWeight(fromQueryEdge, 1, toQueryEdge), .1);
 
         graphWithTurnCosts.close();
     }
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
index 503e2b91e..d585cda51 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
@@ -100,10 +100,12 @@ public class DirectedRoutingTest {
         // todonow: make this work with speed_both_directions=true!
         encoder = new CarFlagEncoder(5, 5, maxTurnCosts);
         encodingManager = EncodingManager.create(encoder);
-        weighting = new FastestWeighting(encoder);
-        chProfile = CHProfile.edgeBased(weighting, uTurnCosts);
-        graph = createGraph();
+        graph = new GraphBuilder(encodingManager).setDir(dir).withTurnCosts(true).build();
         turnCostStorage = graph.getTurnCostStorage();
+        weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCosts));
+        chProfile = CHProfile.edgeBased(weighting);
+        graph.addCHGraph(chProfile);
+        graph.create(1000);
     }
 
     private void preProcessGraph() {
@@ -130,23 +132,19 @@ public class DirectedRoutingTest {
     private BidirRoutingAlgorithm createAlgo(Graph graph) {
         switch (algo) {
             case ASTAR:
-                return new AStarBidirection(graph, createTurnWeighting(graph), TraversalMode.EDGE_BASED);
+                return new AStarBidirection(graph, graph.wrapWeighting(weighting), TraversalMode.EDGE_BASED);
             case CH_DIJKSTRA:
                 return (BidirRoutingAlgorithm) pch.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(DIJKSTRA_BI).build());
             case CH_ASTAR:
                 return (BidirRoutingAlgorithm) pch.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).build());
             case LM:
-                AStarBidirection astarbi = new AStarBidirection(graph, createTurnWeighting(graph), TraversalMode.EDGE_BASED);
+                AStarBidirection astarbi = new AStarBidirection(graph, graph.wrapWeighting(weighting), TraversalMode.EDGE_BASED);
                 return (BidirRoutingAlgorithm) lm.getDecoratedAlgorithm(graph, astarbi, AlgorithmOptions.start().build());
             default:
                 throw new IllegalArgumentException("unknown algo " + algo);
         }
     }
 
-    private TurnWeighting createTurnWeighting(Graph g) {
-        return new TurnWeighting(weighting, g.getTurnCostStorage(), uTurnCosts);
-    }
-
     @Test
     @Repeat(times = 10)
     public void randomGraph() {
@@ -165,7 +163,7 @@ public class DirectedRoutingTest {
             int sourceOutEdge = getSourceOutEdge(rnd, source, graph);
             int targetInEdge = getTargetInEdge(rnd, target, graph);
 //            System.out.println("source: " + source + ", target: " + target + ", sourceOutEdge: " + sourceOutEdge + ", targetInEdge: " + targetInEdge);
-            Path refPath = new DijkstraBidirectionRef(graph, createTurnWeighting(graph), TraversalMode.EDGE_BASED)
+            Path refPath = new DijkstraBidirectionRef(graph, ((Graph) graph).wrapWeighting(weighting), TraversalMode.EDGE_BASED)
                     .calcPath(source, target, sourceOutEdge, targetInEdge);
             Path path = createAlgo()
                     .calcPath(source, target, sourceOutEdge, targetInEdge);
@@ -221,8 +219,7 @@ public class DirectedRoutingTest {
             int chSourceOutEdge = getSourceOutEdge(tmpRnd2, source, chQueryGraph);
             int chTargetInEdge = getTargetInEdge(tmpRnd2, target, chQueryGraph);
 
-            final TurnWeighting tw = createTurnWeighting(queryGraph);
-            Path refPath = new DijkstraBidirectionRef(queryGraph, tw, TraversalMode.EDGE_BASED)
+            Path refPath = new DijkstraBidirectionRef(queryGraph, ((Graph) queryGraph).wrapWeighting(weighting), TraversalMode.EDGE_BASED)
                     .calcPath(source, target, sourceOutEdge, targetInEdge);
             Path path = createAlgo(chQueryGraph)
                     .calcPath(source, target, chSourceOutEdge, chTargetInEdge);
@@ -284,10 +281,6 @@ public class DirectedRoutingTest {
         return strictViolations;
     }
 
-    private GraphHopperStorage createGraph() {
-        return new GraphBuilder(encodingManager).setDir(dir).setCHProfiles(chProfile).withTurnCosts(true).create();
-    }
-
     private int getTargetInEdge(Random rnd, int node, Graph graph) {
         return getAdjEdge(rnd, node, graph);
     }
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 bb37d2ead..b85ecd90f 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java
@@ -19,8 +19,10 @@ package com.graphhopper.routing.weighting;
 
 import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState;
 import com.graphhopper.routing.util.Bike2WeightFlagEncoder;
+import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.IntsRef;
@@ -29,14 +31,15 @@ import com.graphhopper.util.Parameters.Routing;
 import org.junit.Test;
 
 import static com.graphhopper.util.GHUtility.createMockedEdgeIteratorState;
+import static com.graphhopper.util.GHUtility.getEdge;
 import static org.junit.Assert.assertEquals;
 
 /**
  * @author Peter Karich
  */
 public class FastestWeightingTest {
-    EncodingManager encodingManager = EncodingManager.create("car");
-    private final FlagEncoder encoder = encodingManager.getEncoder("car");
+    private final FlagEncoder encoder = new CarFlagEncoder(5, 5, 10);
+    private final EncodingManager encodingManager = EncodingManager.create(encoder);
 
     @Test
     public void testMinWeightHasSameUnitAs_getWeight() {
@@ -97,4 +100,44 @@ public class FastestWeightingTest {
 
         g.close();
     }
+
+    @Test
+    public void calcWeightAndTime_withTurnCosts() {
+        Graph graph = new GraphBuilder(encodingManager).create();
+        Weighting weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage()));
+        graph.edge(0, 1, 100, true);
+        EdgeIteratorState edge = graph.edge(1, 2, 100, true);
+        // 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);
+    }
+
+    @Test
+    public void calcWeightAndTime_uTurnCosts() {
+        Graph graph = new GraphBuilder(encodingManager).create();
+        Weighting weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage(), 40));
+        EdgeIteratorState edge = graph.edge(0, 1, 100, true);
+        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);
+    }
+
+    @Test
+    public void calcWeightAndTime_withTurnCosts_shortest() {
+        Graph graph = new GraphBuilder(encodingManager).create();
+        Weighting weighting = new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage()));
+        graph.edge(0, 1, 100, true);
+        EdgeIteratorState edge = graph.edge(1, 2, 100, true);
+        // 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);
+    }
+
+    private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) {
+        graph.getTurnCostStorage().setExpensive(encoder.toString(), encodingManager, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost);
+    }
+
 }
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/TurnWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/TurnWeightingTest.java
deleted file mode 100644
index f8f2146fc..000000000
--- a/core/src/test/java/com/graphhopper/routing/weighting/TurnWeightingTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.graphhopper.routing.weighting;
-
-import com.graphhopper.routing.util.CarFlagEncoder;
-import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.storage.Graph;
-import com.graphhopper.storage.GraphBuilder;
-import com.graphhopper.storage.TurnCostStorage;
-import com.graphhopper.util.EdgeIteratorState;
-import org.junit.Before;
-import org.junit.Test;
-
-import static com.graphhopper.util.GHUtility.getEdge;
-import static org.junit.Assert.assertEquals;
-
-public class TurnWeightingTest {
-
-    private Graph graph;
-    private FlagEncoder encoder;
-    private EncodingManager encodingManager;
-    private Weighting weighting;
-    private TurnCostStorage turnCostExt;
-
-    @Before
-    public void setup() {
-        encoder = new CarFlagEncoder(5, 5, 10);
-        encodingManager = EncodingManager.create(encoder);
-        graph = new GraphBuilder(encodingManager).create();
-        weighting = new FastestWeighting(encoder);
-        turnCostExt = graph.getTurnCostStorage();
-    }
-
-    @Test
-    public void calcWeightAndTime_withTurnCosts() {
-        graph.edge(0, 1, 100, true);
-        EdgeIteratorState edge = graph.edge(1, 2, 100, true);
-        // turn costs are given in seconds
-        setTurnCost(0, 1, 2, 5);
-        TurnWeighting turnWeighting = new TurnWeighting(weighting, turnCostExt);
-        assertEquals(6 + 5, turnWeighting.calcWeight(edge, false, 0), 1.e-6);
-        assertEquals(6000 + 5000, turnWeighting.calcMillis(edge, false, 0), 1.e-6);
-    }
-
-    @Test
-    public void calcWeightAndTime_uTurnCosts() {
-        EdgeIteratorState edge = graph.edge(0, 1, 100, true);
-        TurnWeighting turnWeighting = new TurnWeighting(weighting, turnCostExt, 40);
-        assertEquals(6 + 40, turnWeighting.calcWeight(edge, false, 0), 1.e-6);
-        assertEquals((6 + 40) * 1000, turnWeighting.calcMillis(edge, false, 0), 1.e-6);
-    }
-
-    @Test
-    public void calcWeightAndTime_withTurnCosts_shortest() {
-        graph.edge(0, 1, 100, true);
-        EdgeIteratorState edge = graph.edge(1, 2, 100, true);
-        // turn costs are given in seconds
-        setTurnCost(0, 1, 2, 5);
-        TurnWeighting turnWeighting = new TurnWeighting(new ShortestWeighting(encoder), turnCostExt);
-        // 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, turnWeighting.calcWeight(edge, false, 0), 1.e-6);
-        assertEquals(6000 + 5000, turnWeighting.calcMillis(edge, false, 0), 1.e-6);
-    }
-
-    private void setTurnCost(int from, int via, int to, double turnCost) {
-        turnCostExt.setExpensive(encoder.toString(), encodingManager, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost);
-    }
-
-}
\ No newline at end of file
diff --git a/core/src/test/java/com/graphhopper/storage/CHProfileTest.java b/core/src/test/java/com/graphhopper/storage/CHProfileTest.java
index ab9d29da0..3e32fb9f0 100644
--- a/core/src/test/java/com/graphhopper/storage/CHProfileTest.java
+++ b/core/src/test/java/com/graphhopper/storage/CHProfileTest.java
@@ -2,11 +2,15 @@ package com.graphhopper.storage;
 
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
 import com.graphhopper.routing.weighting.FastestWeighting;
+import com.graphhopper.routing.weighting.ShortFastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;
+import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
+import static org.junit.Assert.assertEquals;
 
 public class CHProfileTest {
 
@@ -14,8 +18,12 @@ public class CHProfileTest {
     public void filename() {
         CarFlagEncoder encoder = new CarFlagEncoder();
         EncodingManager.create(encoder);
-        assertEquals("fastest_car_edge_utc30", CHProfile.edgeBased(new FastestWeighting(encoder), 30).toFileName());
-        assertEquals("shortest_car_node", CHProfile.nodeBased(new ShortestWeighting(encoder)).toFileName());
+        TurnCostStorage tcs = new TurnCostStorage(null, null);
+        assertEquals("fastest_car_edge_utc30", CHProfile.edgeBased(new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, tcs, 30))).toFileName());
+        assertEquals("shortest_car_edge_utc-1", CHProfile.edgeBased(new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, tcs, INFINITE_U_TURN_COSTS))).toFileName());
+        assertEquals("shortest_car_edge", CHProfile.edgeBased(new ShortestWeighting(encoder, NO_TURN_COST_PROVIDER)).toFileName());
+        assertEquals("fastest_car_node", CHProfile.nodeBased(new FastestWeighting(encoder)).toFileName());
+        assertEquals("short_fastest_car_node", CHProfile.nodeBased(new ShortFastestWeighting(encoder, 0.1)).toFileName());
     }
 
 }
\ No newline at end of file
diff --git a/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java b/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java
index fe38b067b..e943535d9 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java
@@ -18,21 +18,17 @@
 package com.graphhopper.storage;
 
 import com.graphhopper.coll.GHIntHashSet;
-import com.graphhopper.routing.util.CarFlagEncoder;
-import com.graphhopper.routing.util.DefaultEdgeFilter;
-import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.routing.util.*;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.LocationIndexTree;
-import com.graphhopper.util.shapes.Circle;
-import com.graphhopper.util.shapes.Shape;
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
 
 import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 /**
  * @author Peter Karich
@@ -63,20 +59,14 @@ public class GraphEdgeIdFinderTest {
 
         GraphEdgeIdFinder graphFinder = new GraphEdgeIdFinder(graph, locationIndex);
         GraphEdgeIdFinder.BlockArea blockArea = graphFinder.parseBlockArea("0.01,0.005,1", DefaultEdgeFilter.allEdges(encoder), 1000 * 1000);
+        assertEquals("[0]", blockArea.blockedEdges.toString());
+        assertFalse(blockArea.blockedShapes.isEmpty());
 
-        GHIntHashSet blockedEdges = new GHIntHashSet();
-        blockedEdges.add(0);
-        assertEquals(blockedEdges, blockArea.blockedEdges);
-        List<Shape> blockedShapes = new ArrayList<>();
-        assertEquals(blockedShapes, blockArea.blockedShapes);
-
-        // big area converts into shapes
+        // big area => no edgeIds are collected up-front
         graphFinder = new GraphEdgeIdFinder(graph, locationIndex);
         blockArea = graphFinder.parseBlockArea("0,0,1000", DefaultEdgeFilter.allEdges(encoder), 1000 * 1000);
-        blockedEdges.clear();
-        assertEquals(blockedEdges, blockArea.blockedEdges);
-        blockedShapes.add(new Circle(0, 0, 1000));
-        assertEquals(blockedShapes, blockArea.blockedShapes);
+        assertEquals("[]", blockArea.blockedEdges.toString());
+        assertFalse(blockArea.blockedShapes.isEmpty());
     }
 
     @Test
@@ -121,16 +111,27 @@ public class GraphEdgeIdFinderTest {
                 .prepareIndex();
 
         GraphEdgeIdFinder graphFinder = new GraphEdgeIdFinder(graph, locationIndex);
-        GraphEdgeIdFinder.BlockArea blockArea = graphFinder.parseBlockArea("2,1, 0,2, 2,3", DefaultEdgeFilter.allEdges(encoder), 1000 * 1000);
-
+        // big value => the polygon is small => force edgeId optimization
+        double area = 500_000L * 500_000L;
+        GraphEdgeIdFinder.BlockArea blockArea = graphFinder.parseBlockArea("2.1,1, -1.1,2, 2,3", DefaultEdgeFilter.allEdges(encoder), area);
         GHIntHashSet blockedEdges = new GHIntHashSet();
-        blockedEdges.addAll(1, 2, 6, 7);
+        blockedEdges.addAll(1, 2, 6, 7, 11, 12);
         assertEquals(blockedEdges, blockArea.blockedEdges);
+        assertEdges(graph, "[1, 2, 6, 7, 11, 12]", blockArea);
 
-        blockArea = graphFinder.parseBlockArea("2,1, 1,3, 1,2, 0,1", DefaultEdgeFilter.allEdges(encoder), 1000 * 1000);
+        // small value => same polygon is now "large" => do not pre-calculate edgeId set => check only geometries
+        blockArea = graphFinder.parseBlockArea("2.1,1, 0.9,3, 0.9,2, -0.3,0", DefaultEdgeFilter.allEdges(encoder), 1000 * 1000);
+        assertEquals("[]", blockArea.blockedEdges.toString());
+        assertEdges(graph, "[0, 1, 4, 5, 6, 7, 9, 10]", blockArea);
+    }
 
-        blockedEdges = new GHIntHashSet();
-        blockedEdges.addAll(4, 9, 6, 7);
-        assertEquals(blockedEdges, blockArea.blockedEdges);
+    private void assertEdges(Graph g, String assertSetContent, GraphEdgeIdFinder.BlockArea blockArea) {
+        Set<Integer> blockedEdges = new TreeSet<>();
+        AllEdgesIterator edgeIterator = g.getAllEdges();
+        while (edgeIterator.next()) {
+            if (blockArea.intersects(edgeIterator))
+                blockedEdges.add(edgeIterator.getEdge());
+        }
+        assertEquals(assertSetContent, blockedEdges.toString());
     }
 }
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
index f2b8a64f0..a25d53550 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
@@ -21,6 +21,7 @@ import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.querygraph.QueryGraph;
 import com.graphhopper.routing.util.*;
+import com.graphhopper.routing.weighting.DefaultTurnCostProvider;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.storage.index.QueryResult;
 import com.graphhopper.util.*;
@@ -32,7 +33,6 @@ import java.util.Arrays;
 import java.util.List;
 
 import static com.graphhopper.routing.ch.NodeBasedNodeContractorTest.SC_ACCESS;
-import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS;
 import static com.graphhopper.util.EdgeIterator.NO_EDGE;
 import static org.junit.Assert.*;
 
@@ -59,8 +59,25 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
     }
 
     private GraphHopperStorage newGHStorage(Directory dir, boolean is3D, boolean forEdgeBasedTraversal, int segmentSize) {
-        CHProfile chProfile = new CHProfile(new FastestWeighting(carEncoder), forEdgeBasedTraversal, INFINITE_U_TURN_COSTS);
-        return new GraphBuilder(encodingManager).setCHProfiles(chProfile).setDir(dir).set3D(is3D).setSegmentSize(segmentSize).build();
+        GraphHopperStorage graph = new GraphBuilder(encodingManager)
+                .setDir(dir).set3D(is3D).withTurnCosts(true).setSegmentSize(segmentSize).build();
+        FastestWeighting weighting = new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, graph.getTurnCostStorage()));
+        graph.addCHGraph(new CHProfile(weighting, forEdgeBasedTraversal));
+        return graph;
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @Override
+    public void testClone() {
+        // todo: implement graph copying in the presence of turn costs
+        super.testClone();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @Override
+    public void testCopyTo() {
+        // todo: implement graph copying in the presence of turn costs
+        super.testCopyTo();
     }
 
     @Test
diff --git a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
index cd75a1417..5f08f8193 100644
--- a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
+++ b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
@@ -8,9 +8,8 @@ import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
 import com.graphhopper.routing.util.MotorcycleFlagEncoder;
 import com.graphhopper.routing.util.TraversalMode;
-import com.graphhopper.routing.weighting.TurnWeighting;
-import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
@@ -26,9 +25,9 @@ public class ShortcutUnpackerTest {
     private final boolean edgeBased;
     private EncodingManager encodingManager;
     private FlagEncoder encoder;
-    private Weighting weighting;
     private GraphHopperStorage graph;
     private CHGraph chGraph;
+    private RoutingCHGraph routingCHGraph;
 
     @Parameterized.Parameters(name = "{0}")
     public static Object[] params() {
@@ -51,7 +50,7 @@ public class ShortcutUnpackerTest {
                 .setCHProfileStrings("motorcycle|fastest|" + (edgeBased ? "edge" : "node"))
                 .create();
         chGraph = graph.getCHGraph();
-        weighting = chGraph.getCHProfile().getWeighting();
+        routingCHGraph = new RoutingCHGraphImpl(chGraph, chGraph.getCHProfile().getWeighting());
     }
 
     @Test
@@ -295,7 +294,7 @@ public class ShortcutUnpackerTest {
     }
 
     private ShortcutUnpacker createShortcutUnpacker(ShortcutUnpacker.Visitor visitor) {
-        return new ShortcutUnpacker(new RoutingCHGraphImpl(chGraph, weighting), visitor, edgeBased);
+        return new ShortcutUnpacker(routingCHGraph, visitor, edgeBased);
     }
 
     private void setTurnCost(int fromEdge, int viaNode, int toEdge, double cost) {
@@ -326,22 +325,21 @@ public class ShortcutUnpackerTest {
             edgeIds.add(edge.getEdge());
             baseNodes.add(edge.getBaseNode());
             adjNodes.add(edge.getAdjNode());
-            weights.add(weighting.calcWeight(edge, reverse, prevOrNextEdgeId));
+            weights.add(GHUtility.calcWeightWithTurnWeight(routingCHGraph.getWeighting(), edge, reverse, prevOrNextEdgeId));
             distances.add(edge.getDistance());
-            times.add(weighting.calcMillis(edge, reverse, prevOrNextEdgeId));
+            times.add(GHUtility.calcMillisWithTurnMillis(routingCHGraph.getWeighting(), edge, reverse, prevOrNextEdgeId));
             prevOrNextEdgeIds.add(prevOrNextEdgeId);
         }
     }
 
     private class TurnWeightingVisitor implements ShortcutUnpacker.Visitor {
-        private final TurnWeighting turnWeighting = new TurnWeighting(weighting, graph.getTurnCostStorage());
         private long time = 0;
         private double weight = 0;
 
         @Override
         public void visit(EdgeIteratorState edge, boolean reverse, int prevOrNextEdgeId) {
-            time += turnWeighting.calcMillis(edge, reverse, prevOrNextEdgeId);
-            weight += turnWeighting.calcWeight(edge, reverse, prevOrNextEdgeId);
+            time += GHUtility.calcMillisWithTurnMillis(routingCHGraph.getWeighting(), edge, reverse, prevOrNextEdgeId);
+            weight += GHUtility.calcWeightWithTurnWeight(routingCHGraph.getWeighting(), edge, reverse, prevOrNextEdgeId);
         }
     }
 }
\ No newline at end of file
diff --git a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
index 7e8034df3..afd38ef78 100644
--- a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
@@ -441,25 +441,32 @@ public class GraphHopperIT {
         req.getHints().put(Routing.BLOCK_AREA, someArea);
         rsp = tmpHopper.route(req);
         assertFalse(rsp.getErrors().toString(), rsp.hasErrors());
-        assertEquals(12173, rsp.getBest().getDistance(), 1);
+        assertEquals(13988, rsp.getBest().getDistance(), 1);
 
         // Add blocked point to above area, to increase detour        
         req.getHints().put(Routing.BLOCK_AREA, "50.017578,11.547527;" + someArea);
         rsp = tmpHopper.route(req);
         assertFalse(rsp.getErrors().toString(), rsp.hasErrors());
-        assertEquals(12787, rsp.getBest().getDistance(), 1);
+        assertEquals(14602, rsp.getBest().getDistance(), 1);
 
         // block by edge IDs -> i.e. use small circular area
-        req.getHints().put(Routing.BLOCK_AREA, "49.981599,11.517448,100");
+        req.getHints().put(Routing.BLOCK_AREA, "49.979929,11.520066,200");
+        rsp = tmpHopper.route(req);
+        assertFalse(rsp.getErrors().toString(), rsp.hasErrors());
+        assertEquals(12173, rsp.getBest().getDistance(), 1);
+
+        // TODO after #1324 this will work and should block both roads and return 12173m, currently it still routes
+        //  through one of the roads due to "disconnected" roads
+        req.getHints().put(Routing.BLOCK_AREA, "49.981502,11.51762,80");
         rsp = tmpHopper.route(req);
         assertFalse(rsp.getErrors().toString(), rsp.hasErrors());
-        assertEquals(6879, rsp.getBest().getDistance(), 1);
+        assertEquals(7383, rsp.getBest().getDistance(), 1);
 
         // block by edge IDs -> i.e. use small rectangular area
-        req.getHints().put(Routing.BLOCK_AREA, "49.981875,11.515818,49.981088,11.519423");
+        req.getHints().put(Routing.BLOCK_AREA, "49.981875,11.515818,49.979522,11.521407");
         rsp = tmpHopper.route(req);
         assertFalse(rsp.getErrors().toString(), rsp.hasErrors());
-        assertEquals(6879, rsp.getBest().getDistance(), 1);
+        assertEquals(12173, rsp.getBest().getDistance(), 1);
 
         // blocking works for all weightings
         req = new GHRequest(50.009504, 11.490669, 50.024726, 11.496162).
@@ -1217,8 +1224,9 @@ public class GraphHopperIT {
         assertMoscowNodeBased(tmpHopper, "false", true);
         GHResponse rsp = runMoscow(tmpHopper, "true", true);
         assertEquals(1, rsp.getErrors().size());
-        assertTrue("unexpected error: " + rsp.getErrors().toString(), rsp.getErrors().toString().contains(
-                "Cannot find matching CH profile for your request.\nrequested:  *|car|edge_based=true|u_turn_costs=*\navailable: [fastest|car|edge_based=false|u_turn_costs=-1]"));
+        String expected = "Cannot find matching CH profile for your request.\nrequested:  *|car|edge_based=true|u_turn_costs=*\navailable: [fastest|car|edge_based=false]";
+        assertTrue("unexpected error:\n" + rsp.getErrors().toString() + "\nwhen expecting an error containing:\n" + expected,
+                rsp.getErrors().toString().contains(expected));
     }
 
     @Test
diff --git a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
index 17ff1bfa6..3c2a10b09 100644
--- a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
@@ -989,8 +989,7 @@ public class GraphHopperOSMTest {
         CHAlgoFactoryDecorator decorator = new CHAlgoFactoryDecorator();
         Weighting fwSimpleTruck = new FastestWeighting(simpleTruck);
         Weighting fwTruck = new FastestWeighting(truck);
-        RAMDirectory ramDir = new RAMDirectory();
-        GraphHopperStorage storage = new GraphBuilder(em).setCHProfiles(CHProfile.createProfilesForWeightings(Arrays.asList(fwSimpleTruck, fwTruck))).setDir(ramDir).build();
+        GraphHopperStorage storage = new GraphBuilder(em).setCHProfiles(Arrays.asList(CHProfile.nodeBased(fwSimpleTruck), CHProfile.nodeBased(fwTruck))).build();
         decorator.addCHProfile(CHProfile.nodeBased(fwSimpleTruck));
         decorator.addCHProfile(CHProfile.nodeBased(fwTruck));
         decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, CHProfile.nodeBased(fwSimpleTruck)));
diff --git a/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java b/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
index 58c1a285f..efbc26849 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
@@ -187,8 +187,8 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         }
 
         @Override
-        public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) {
-            double baseWeight = baseWeighting.calcWeight(edgeState, reverse, prevOrNextEdgeId);
+        public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
+            double baseWeight = baseWeighting.calcEdgeWeight(edgeState, reverse);
             if (edgeState instanceof CHEdgeIteratorState) {
                 // important! we may not change weights of shortcuts (the deviations are already included in their weight)
                 if (((CHEdgeIteratorState) edgeState).isShortcut()) {