Repository: graphhopper/graphhopper
Pull request #1699 (2019-08-27 11:06:48)

removed cleanHack used in MMapDataAccess
see #1678


Patch:
diff --git a/.travis.yml b/.travis.yml
index b58fdd993..4e3dffb9c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,8 @@ env:
 matrix:
   include:
     - jdk: openjdk8
-    - jdk: openjdk12
+    - env: JDK='OpenJDK 12'
+      install: . ./install-jdk.sh -F 12 -C
     - env: JDK='OpenJDK 13' SKIP_EXT_TESTS=true
       install: . ./install-jdk.sh -F 13 -C
     
@@ -65,4 +66,4 @@ notifications:
 
 cache:
   directories:
-    - $HOME/.m2
+  - $HOME/.m2
diff --git a/android/app/pom.xml b/android/app/pom.xml
index 292a75bf0..de30b700e 100644
--- a/android/app/pom.xml
+++ b/android/app/pom.xml
@@ -15,7 +15,7 @@
     <parent>
         <relativePath>../..</relativePath>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>
+        <artifactId>graphhopper-parent</artifactId>    	
         <version>0.13-SNAPSHOT</version>
     </parent>
     <properties>
diff --git a/api/src/main/java/com/graphhopper/util/Helper.java b/api/src/main/java/com/graphhopper/util/Helper.java
index e7ed1df51..45cb8cb22 100644
--- a/api/src/main/java/com/graphhopper/util/Helper.java
+++ b/api/src/main/java/com/graphhopper/util/Helper.java
@@ -339,15 +339,6 @@ public class Helper {
         return integEle / ELE_FACTOR;
     }
 
-
-    /**
-     * Trying to force the release of the mapped ByteBuffer. See
-     * http://stackoverflow.com/q/2972986/194609 and use only if you know what you are doing.
-     */
-    public static void cleanHack() {
-        System.gc();
-    }
-
     public static String nf(long no) {
         // I like french localization the most: 123654 will be 123 654 instead
         // of comma vs. point confusion for English/German people.
diff --git a/core/files/changelog.txt b/core/files/changelog.txt
index 78c5a5d18..76eb529d2 100644
--- a/core/files/changelog.txt
+++ b/core/files/changelog.txt
@@ -14,8 +14,6 @@
     removed TraversalMode.EDGE_BASED_1DIR and TraversalMode.EDGE_BASED_2DIR_UTURN, renamed TraversalMode.EDGE_BASED_2DIR to TraversalMode.EDGE_BASED
     to prevent u-turns when using edge-based algorithms it is now required to use TurnWeighting, #1640
     GraphHopperStorage.getGraph(Class) was replaced by GraphHopperStorage.getBase/CHGraph(), #1669
-    CHGraph.shortcut(int, int) was removed (use .shortcut(int, int, ...) and/or .shortcutEdgeBased(int, int, ...) instead, #1693
-    CH graphs are now identified using CHProfile instead of Weighting, #1670
 
 0.12
     renamed VirtualEdgeIteratorState.getOriginalEdgeKey to more precise getOriginalEdgeKey #1549
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index 692639422..e570036eb 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -747,7 +747,8 @@ public class GraphHopper implements GraphHopperAPI {
 
         if (chFactoryDecorator.isEnabled()) {
             initCHAlgoFactoryDecorator();
-            ghStorage = new GraphHopperStorage(chFactoryDecorator.getCHProfiles(), dir, encodingManager, hasElevation(), ext);
+            ghStorage = new GraphHopperStorage(chFactoryDecorator.getNodeBasedWeightings(), chFactoryDecorator.getEdgeBasedWeightings(),
+                    dir, encodingManager, hasElevation(), ext);
         } else {
             ghStorage = new GraphHopperStorage(dir, encodingManager, hasElevation(), ext);
         }
@@ -802,16 +803,16 @@ public class GraphHopper implements GraphHopperAPI {
     }
 
     private void initCHAlgoFactoryDecorator() {
-        if (!chFactoryDecorator.hasCHProfiles()) {
+        if (!chFactoryDecorator.hasWeightings()) {
             for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
-                for (String chWeightingStr : chFactoryDecorator.getCHProfileStrings()) {
+                for (String chWeightingStr : chFactoryDecorator.getWeightingsAsStrings()) {
                     // ghStorage is null at this point
                     CHAlgoFactoryDecorator.EdgeBasedCHMode edgeBasedCHMode = chFactoryDecorator.getEdgeBasedCHMode();
                     if (!(edgeBasedCHMode == EDGE_OR_NODE && encoder.supports(TurnWeighting.class))) {
-                        chFactoryDecorator.addCHProfile(CHProfile.nodeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null)));
+                        chFactoryDecorator.addNodeBasedWeighting(createWeighting(new HintsMap(chWeightingStr), encoder, null));
                     }
                     if (edgeBasedCHMode != OFF && encoder.supports(TurnWeighting.class)) {
-                        chFactoryDecorator.addCHProfile(CHProfile.edgeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null)));
+                        chFactoryDecorator.addEdgeBasedWeighting(createWeighting(new HintsMap(chWeightingStr), encoder, null));
                     }
                 }
             }
@@ -980,10 +981,6 @@ public class GraphHopper implements GraphHopperAPI {
 
             FlagEncoder encoder = encodingManager.getEncoder(vehicle);
 
-            if (tMode.isEdgeBased() && !encoder.supports(TurnWeighting.class)) {
-                throw new IllegalArgumentException("You need a turn cost extension to make use of edge_based=true, e.g. use car|turn_costs=true");
-            }
-
             boolean disableCH = hints.getBool(CH.DISABLE, false);
             if (!chFactoryDecorator.isDisablingAllowed() && disableCH)
                 throw new IllegalArgumentException("Disabling CH not allowed on the server-side");
@@ -1034,14 +1031,13 @@ public class GraphHopper implements GraphHopperAPI {
                     if (tmpAlgoFactory instanceof LMAlgoFactoryDecorator.LMRAFactory)
                         chAlgoFactory = ((LMAlgoFactoryDecorator.LMRAFactory) tmpAlgoFactory).getDefaultAlgoFactory();
 
-                    if (chAlgoFactory instanceof PrepareContractionHierarchies) {
-                        CHProfile chProfile = ((PrepareContractionHierarchies) chAlgoFactory).getCHProfile();
-                        queryGraph = new QueryGraph(ghStorage.getCHGraph(chProfile));
-                        queryGraph.lookup(qResults);
-                        weighting = chProfile.getWeighting();
-                    } else {
+                    if (chAlgoFactory instanceof PrepareContractionHierarchies)
+                        weighting = ((PrepareContractionHierarchies) chAlgoFactory).getWeighting();
+                    else
                         throw new IllegalStateException("Although CH was enabled a non-CH algorithm factory was returned " + tmpAlgoFactory);
-                    }
+
+                    queryGraph = new QueryGraph(ghStorage.getCHGraph(weighting));
+                    queryGraph.lookup(qResults);
                 } else {
                     checkNonChMaxWaypointDistance(points);
                     queryGraph = new QueryGraph(ghStorage);
diff --git a/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java b/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java
index 13ebfb958..d97852661 100644
--- a/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java
+++ b/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java
@@ -19,7 +19,6 @@ package com.graphhopper.routing;
 
 import com.carrotsearch.hppc.IntObjectMap;
 import com.graphhopper.coll.GHIntObjectHashMap;
-import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
@@ -31,20 +30,15 @@ import com.graphhopper.util.GHUtility;
 
 import java.util.PriorityQueue;
 
-import static com.graphhopper.util.EdgeIterator.ANY_EDGE;
-
 /**
  * Common subclass for bidirectional algorithms.
  * <p>
  *
  * @author Peter Karich
- * @author easbar
  */
 public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
     protected int from;
     protected int to;
-    protected int fromOutEdge;
-    protected int toInEdge;
     protected IntObjectMap<SPTEntry> bestWeightMapFrom;
     protected IntObjectMap<SPTEntry> bestWeightMapTo;
     protected IntObjectMap<SPTEntry> bestWeightMapOther;
@@ -61,8 +55,6 @@ public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
 
     public AbstractBidirAlgo(Graph graph, Weighting weighting, TraversalMode tMode) {
         super(graph, weighting, tMode);
-        fromOutEdge = ANY_EDGE;
-        toInEdge = ANY_EDGE;
         int size = Math.min(Math.max(200, graph.getNodes() / 10), 150_000);
         initCollections(size);
     }
@@ -96,24 +88,6 @@ public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
 
     @Override
     public Path calcPath(int from, int to) {
-        return calcPath(from, to, ANY_EDGE, ANY_EDGE);
-    }
-
-    /**
-     * like {@link #calcPath(int, int)}, but this method also allows to strictly restrict the edge the
-     * path will begin with and the edge it will end with.
-     *
-     * @param fromOutEdge the edge id of the first edge of the path. using {@link EdgeIterator#ANY_EDGE} means
-     *                    not enforcing the first edge of the path
-     * @param toInEdge    the edge id of the last edge of the path. using {@link EdgeIterator#ANY_EDGE} means
-     *                    not enforcing the last edge of the path
-     */
-    public Path calcPath(int from, int to, int fromOutEdge, int toInEdge) {
-        if ((fromOutEdge != ANY_EDGE || toInEdge != ANY_EDGE) && !traversalMode.isEdgeBased()) {
-            throw new IllegalArgumentException("Restricting the start/target edges is only possible for edge-based graph traversal");
-        }
-        this.fromOutEdge = fromOutEdge;
-        this.toInEdge = toInEdge;
         checkAlreadyRun();
         createAndInitPath();
         init(from, 0, to, 0);
@@ -156,9 +130,8 @@ public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
                 bestWeightMapOther = bestWeightMapFrom;
                 updateBestPath(GHUtility.getEdge(graph, currFrom.adjNode, to), currFrom, to, true);
             }
-        } else if (from == to && fromOutEdge == ANY_EDGE && toInEdge == ANY_EDGE) {
-            // special handling if start and end are the same and no directions are restricted
-            // the resulting weight should be zero
+        } else if (from == to) {
+            // special case of identical start and end
             if (currFrom.weight != 0 || currTo.weight != 0) {
                 throw new IllegalStateException("If from=to, the starting weight must be zero for from and to");
             }
@@ -174,55 +147,9 @@ public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
     }
 
     protected void postInitFrom() {
-        if (fromOutEdge == ANY_EDGE) {
-            fillEdgesFromUsingFilter(additionalEdgeFilter);
-        } else {
-            // need to use a local reference here, because additionalEdgeFilter is modified when calling fillEdgesFromUsingFilter
-            final EdgeFilter tmpFilter = additionalEdgeFilter;
-            fillEdgesFromUsingFilter(new EdgeFilter() {
-                @Override
-                public boolean accept(EdgeIteratorState edgeState) {
-                    return (tmpFilter == null || tmpFilter.accept(edgeState)) && edgeState.getOrigEdgeFirst() == fromOutEdge;
-                }
-            });
-        }
     }
 
     protected void postInitTo() {
-        if (toInEdge == ANY_EDGE) {
-            fillEdgesToUsingFilter(additionalEdgeFilter);
-        } else {
-            final EdgeFilter tmpFilter = additionalEdgeFilter;
-            fillEdgesToUsingFilter(new EdgeFilter() {
-                @Override
-                public boolean accept(EdgeIteratorState edgeState) {
-                    return (tmpFilter == null || tmpFilter.accept(edgeState)) && edgeState.getOrigEdgeLast() == toInEdge;
-                }
-            });
-        }
-    }
-
-    /**
-     * @param edgeFilter edge filter used to fill edges. the {@link #additionalEdgeFilter} reference will be set to
-     *                   edgeFilter by this method, so make sure edgeFilter does not use it directly.
-     */
-    protected void fillEdgesFromUsingFilter(EdgeFilter edgeFilter) {
-        // we temporarily ignore the additionalEdgeFilter
-        EdgeFilter tmpFilter = additionalEdgeFilter;
-        additionalEdgeFilter = edgeFilter;
-        finishedFrom = !fillEdgesFrom();
-        additionalEdgeFilter = tmpFilter;
-    }
-
-    /**
-     * @see #fillEdgesFromUsingFilter(EdgeFilter)
-     */
-    protected void fillEdgesToUsingFilter(EdgeFilter edgeFilter) {
-        // we temporarily ignore the additionalEdgeFilter
-        EdgeFilter tmpFilter = additionalEdgeFilter;
-        additionalEdgeFilter = edgeFilter;
-        finishedTo = !fillEdgesTo();
-        additionalEdgeFilter = tmpFilter;
     }
 
     protected void runAlgo() {
@@ -420,7 +347,6 @@ public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
 
     void setFromDataStructures(AbstractBidirAlgo other) {
         from = other.from;
-        fromOutEdge = other.fromOutEdge;
         pqOpenSetFrom = other.pqOpenSetFrom;
         bestWeightMapFrom = other.bestWeightMapFrom;
         finishedFrom = other.finishedFrom;
@@ -431,7 +357,6 @@ public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
 
     void setToDataStructures(AbstractBidirAlgo other) {
         to = other.to;
-        toInEdge = other.toInEdge;
         pqOpenSetTo = other.pqOpenSetTo;
         bestWeightMapTo = other.bestWeightMapTo;
         finishedTo = other.finishedTo;
diff --git a/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java b/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java
index 5b1d1df95..6e98c7cee 100644
--- a/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java
+++ b/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java
@@ -30,8 +30,6 @@ import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.GHUtility;
 
-import static com.graphhopper.util.EdgeIterator.ANY_EDGE;
-
 /**
  * @author easbar
  */
@@ -55,35 +53,18 @@ public abstract class AbstractBidirectionEdgeCHNoSOD extends AbstractBidirAlgo {
 
     @Override
     protected void postInitFrom() {
-        // With CH the additionalEdgeFilter is the one that filters out edges leading or coming from higher rank nodes,
-        // i.e. LevelEdgeFilter, For the first step though we need all edges, so we need to ignore this filter.
-        // Using an arbitrary filter is not supported for CH anyway.
-        if (fromOutEdge == ANY_EDGE) {
-            fillEdgesFromUsingFilter(EdgeFilter.ALL_EDGES);
-        } else {
-            fillEdgesFromUsingFilter(new EdgeFilter() {
-                @Override
-                public boolean accept(EdgeIteratorState edgeState) {
-                    return edgeState.getOrigEdgeFirst() == fromOutEdge;
-                }
-            });
-        }
-        finishedFrom = bestPath.isFound();
+        EdgeFilter filter = additionalEdgeFilter;
+        setEdgeFilter(EdgeFilter.ALL_EDGES);
+        fillEdgesFrom();
+        setEdgeFilter(filter);
     }
 
     @Override
     protected void postInitTo() {
-        if (toInEdge == ANY_EDGE) {
-            fillEdgesToUsingFilter(EdgeFilter.ALL_EDGES);
-        } else {
-            fillEdgesToUsingFilter(new EdgeFilter() {
-                @Override
-                public boolean accept(EdgeIteratorState edgeState) {
-                    return edgeState.getOrigEdgeLast() == toInEdge;
-                }
-            });
-        }
-        finishedTo = bestPath.isFound();
+        EdgeFilter filter = additionalEdgeFilter;
+        setEdgeFilter(EdgeFilter.ALL_EDGES);
+        fillEdgesTo();
+        setEdgeFilter(filter);
     }
 
     @Override
@@ -107,9 +88,7 @@ public abstract class AbstractBidirectionEdgeCHNoSOD extends AbstractBidirAlgo {
         // node of the shortest path matches the source or target. in this case one of the searches does not contribute
         // anything to the shortest path.
         int oppositeNode = reverse ? from : to;
-        int oppositeEdge = reverse ? fromOutEdge : toInEdge;
-        boolean oppositeEdgeRestricted = reverse ? (fromOutEdge != ANY_EDGE) : (toInEdge != ANY_EDGE);
-        if (edgeState.getAdjNode() == oppositeNode && (!oppositeEdgeRestricted || getOrigEdgeId(edgeState, reverse) == oppositeEdge)) {
+        if (edgeState.getAdjNode() == oppositeNode) {
             if (entry.getWeightOfVisitedPath() < bestPath.getWeight()) {
                 bestPath.setSwitchToFrom(reverse);
                 bestPath.setSPTEntry(entry);
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
index 015d17e15..719d50deb 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
@@ -20,7 +20,9 @@ package com.graphhopper.routing.ch;
 import com.graphhopper.routing.RoutingAlgorithmFactory;
 import com.graphhopper.routing.RoutingAlgorithmFactoryDecorator;
 import com.graphhopper.routing.util.HintsMap;
-import com.graphhopper.storage.CHProfile;
+import com.graphhopper.routing.util.TraversalMode;
+import com.graphhopper.routing.weighting.AbstractWeighting;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.StorableProperties;
 import com.graphhopper.util.CmdArgs;
@@ -46,11 +48,11 @@ import static com.graphhopper.util.Parameters.CH.DISABLE;
 public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator {
     private final Logger LOGGER = LoggerFactory.getLogger(getClass());
     private final List<PrepareContractionHierarchies> preparations = new ArrayList<>();
-    // we need to decouple the CH profile objects from the list of CH profile strings
-    // as we need the strings to create the GraphHopperStorage and the GraphHopperStorage to create the preparations
-    // from the CHProfile objects currently requiring the encoders
-    private final List<CHProfile> chProfiles = new ArrayList<>();
-    private final Set<String> chProfileStrings = new LinkedHashSet<>();
+    // we need to decouple weighting objects from the weighting list of strings 
+    // as we need the strings to create the GraphHopperStorage and the GraphHopperStorage to create the preparations from the Weighting objects currently requiring the encoders
+    private final List<Weighting> nodeBasedWeightings = new ArrayList<>();
+    private final List<Weighting> edgeBasedWeightings = new ArrayList<>();
+    private final Set<String> weightingsAsStrings = new LinkedHashSet<>();
     private boolean disablingAllowed = false;
     // for backward compatibility enable CH by default.
     private boolean enabled = true;
@@ -61,7 +63,7 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
 
     public CHAlgoFactoryDecorator() {
         setPreparationThreads(1);
-        setCHProfilesAsStrings(Arrays.asList(getDefaultProfile()));
+        setWeightingsAsStrings(Arrays.asList(getDefaultWeighting()));
     }
 
     @Override
@@ -76,18 +78,16 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
 
         // default is enabled & fastest
         String chWeightingsStr = args.get(CH.PREPARE + "weightings", "");
-        if (chWeightingsStr.contains("edge_based")) {
-            throw new IllegalArgumentException("Adding 'edge_based` to " + (CH.PREPARE + "weightings") + " is not allowed, to enable edge-based CH use " + (CH.PREPARE + "edge_based") + " instead.");
-        }
 
         if ("no".equals(chWeightingsStr) || "false".equals(chWeightingsStr)) {
             // default is fastest and we need to clear this explicitly
-            chProfileStrings.clear();
+            weightingsAsStrings.clear();
         } else if (!chWeightingsStr.isEmpty()) {
-            setCHProfilesAsStrings(Arrays.asList(chWeightingsStr.split(",")));
+            List<String> tmpCHWeightingList = Arrays.asList(chWeightingsStr.split(","));
+            setWeightingsAsStrings(tmpCHWeightingList);
         }
 
-        boolean enableThis = !chProfileStrings.isEmpty();
+        boolean enableThis = !weightingsAsStrings.isEmpty();
         setEnabled(enableThis);
         if (enableThis)
             setDisablingAllowed(args.getBool(CH.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
@@ -135,100 +135,95 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     }
 
     /**
-     * Decouple CH profiles from PrepareContractionHierarchies as we need CH profiles for the
+     * Decouple weightings from PrepareContractionHierarchies as we need weightings for the
      * graphstorage and the graphstorage for the preparation.
      */
-    public CHAlgoFactoryDecorator addCHProfile(CHProfile chProfile) {
-        chProfiles.add(chProfile);
+    public CHAlgoFactoryDecorator addNodeBasedWeighting(Weighting weighting) {
+        nodeBasedWeightings.add(weighting);
+        return this;
+    }
+
+    public CHAlgoFactoryDecorator addEdgeBasedWeighting(Weighting weighting) {
+        edgeBasedWeightings.add(weighting);
+        return this;
+    }
+
+    public CHAlgoFactoryDecorator addWeighting(String weighting) {
+        weightingsAsStrings.add(weighting);
         return this;
     }
 
     public CHAlgoFactoryDecorator addPreparation(PrepareContractionHierarchies pch) {
-        // we want to make sure that CH preparations are added in the same order as their corresponding profiles
-        if (preparations.size() >= chProfiles.size()) {
-            throw new IllegalStateException("You need to add the corresponding CH profiles before adding preparations.");
-        }
-        CHProfile expectedProfile = chProfiles.get(preparations.size());
-        if (!pch.getCHProfile().equals(expectedProfile)) {
-            throw new IllegalArgumentException("CH profile of preparation: " + pch + " needs to be identical to previously added CH profile: " + expectedProfile);
+        // we want to make sure that edge- and node-based preparations are added in the same order as their corresponding
+        // weightings, but changing the order between edge- and node-based preparations is accepted
+        int index = 0;
+        for (PrepareContractionHierarchies p : preparations) {
+            if (p.isEdgeBased() == pch.isEdgeBased()) {
+                index++;
+            }
         }
+        List<Weighting> weightings = pch.isEdgeBased() ? edgeBasedWeightings : nodeBasedWeightings;
+        if (index >= weightings.size())
+            throw new IllegalStateException("Cannot access weighting for PrepareContractionHierarchies with " + pch.getWeighting()
+                    + ". Call add(Weighting) before");
+
+        Weighting expectedWeighting = weightings.get(index);
+        if (pch.getWeighting() != expectedWeighting)
+            throw new IllegalArgumentException("Weighting of PrepareContractionHierarchies " + pch
+                    + " needs to be identical to previously added " + expectedWeighting);
+
         preparations.add(pch);
         return this;
     }
 
-    public final boolean hasCHProfiles() {
-        return !chProfiles.isEmpty();
-    }
-
-    public List<CHProfile> getCHProfiles() {
-        return chProfiles;
+    public final boolean hasWeightings() {
+        return !nodeBasedWeightings.isEmpty() || !edgeBasedWeightings.isEmpty();
     }
 
-    public List<CHProfile> getNodeBasedCHProfiles() {
-        List<CHProfile> result = new ArrayList<>();
-        for (CHProfile chProfile : chProfiles) {
-            if (!chProfile.getTraversalMode().isEdgeBased()) {
-                result.add(chProfile);
-            }
-        }
-        return result;
+    public final List<Weighting> getNodeBasedWeightings() {
+        return nodeBasedWeightings;
     }
 
-    public List<CHProfile> getEdgeBasedCHProfiles() {
-        List<CHProfile> result = new ArrayList<>();
-        for (CHProfile chProfile : chProfiles) {
-            if (chProfile.getTraversalMode().isEdgeBased()) {
-                result.add(chProfile);
-            }
-        }
-        return result;
+    public final List<Weighting> getEdgeBasedWeightings() {
+        return edgeBasedWeightings;
     }
 
     public EdgeBasedCHMode getEdgeBasedCHMode() {
         return edgeBasedCHMode;
     }
 
-    public List<String> getCHProfileStrings() {
-        if (chProfileStrings.isEmpty())
-            throw new IllegalStateException("Potential bug: chProfileStrings is empty");
-
-        return new ArrayList<>(chProfileStrings);
+    public CHAlgoFactoryDecorator setWeightingsAsStrings(String... weightingNames) {
+        return setWeightingsAsStrings(Arrays.asList(weightingNames));
     }
 
-    public CHAlgoFactoryDecorator setCHProfileStrings(String... profileStrings) {
-        return setCHProfilesAsStrings(Arrays.asList(profileStrings));
-    }
+    public List<String> getWeightingsAsStrings() {
+        if (this.weightingsAsStrings.isEmpty())
+            throw new IllegalStateException("Potential bug: weightingsAsStrings is empty");
 
-    /**
-     * @param profileStrings A list of multiple CH profile strings
-     * @see #addCHProfileAsString(String)
-     */
-    public CHAlgoFactoryDecorator setCHProfilesAsStrings(List<String> profileStrings) {
-        if (profileStrings.isEmpty())
-            throw new IllegalArgumentException("It is not allowed to pass an empty list of CH profile strings");
-
-        chProfileStrings.clear();
-        for (String profileString : profileStrings) {
-            profileString = toLowerCase(profileString);
-            profileString = profileString.trim();
-            addCHProfileAsString(profileString);
-        }
-        return this;
+        return new ArrayList<>(this.weightingsAsStrings);
     }
 
     /**
      * Enables the use of contraction hierarchies to reduce query times. Enabled by default.
      *
-     * @param profileString String representation of a CH profile like: "fastest", "shortest|edge_based=true",
-     *                       "fastest|u_turn_costs=30 or your own weight-calculation type.
+     * @param weightingList A list containing multiple weightings like: "fastest", "shortest" or
+     *                      your own weight-calculation type.
      */
-    public CHAlgoFactoryDecorator addCHProfileAsString(String profileString) {
-        chProfileStrings.add(profileString);
+    public CHAlgoFactoryDecorator setWeightingsAsStrings(List<String> weightingList) {
+        if (weightingList.isEmpty())
+            throw new IllegalArgumentException("It is not allowed to pass an emtpy weightingList");
+
+        weightingsAsStrings.clear();
+        for (String strWeighting : weightingList) {
+            strWeighting = toLowerCase(strWeighting);
+            strWeighting = strWeighting.trim();
+            addWeighting(strWeighting);
+        }
         return this;
     }
 
-    private String getDefaultProfile() {
-        return chProfileStrings.isEmpty() ? "fastest" : chProfileStrings.iterator().next();
+    private String getDefaultWeighting() {
+        return weightingsAsStrings.isEmpty() ? "fastest" : weightingsAsStrings.iterator().next();
     }
 
     public List<PrepareContractionHierarchies> getPreparations() {
@@ -241,11 +236,12 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         if (!isEnabled() || disablingAllowed && disableCH)
             return defaultAlgoFactory;
 
-        if (preparations.isEmpty())
+        List<PrepareContractionHierarchies> allPreparations = getPreparations();
+        if (allPreparations.isEmpty())
             throw new IllegalStateException("No preparations added to this decorator");
 
         if (map.getWeighting().isEmpty())
-            map.setWeighting(getDefaultProfile());
+            map.setWeighting(getDefaultWeighting());
 
         return getPreparation(map);
     }
@@ -255,13 +251,13 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         List<String> entriesStrs = new ArrayList<>();
         boolean weightingMatchesButNotEdgeBased = false;
         for (PrepareContractionHierarchies p : getPreparations()) {
-            boolean weightingMatches = p.getCHProfile().getWeighting().matches(map);
+            boolean weightingMatches = p.getWeighting().matches(map);
             if (p.isEdgeBased() == edgeBased && weightingMatches)
                 return p;
             else if (weightingMatches)
                 weightingMatchesButNotEdgeBased = true;
 
-            entriesStrs.add(p.getCHProfile().getWeighting() + "|" + (p.getCHProfile().isEdgeBased() ? "edge" : "node"));
+            entriesStrs.add(p.getWeighting() + "|" + (p.isEdgeBased() ? "edge" : "node"));
         }
 
         String hint = weightingMatchesButNotEdgeBased
@@ -284,12 +280,12 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     }
 
     public void prepare(final StorableProperties properties) {
-        ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(threadPool);
+        ExecutorCompletionService completionService = new ExecutorCompletionService<>(threadPool);
         int counter = 0;
         for (final PrepareContractionHierarchies prepare : getPreparations()) {
             LOGGER.info((++counter) + "/" + getPreparations().size() + " calling " +
-                    "CH prepare.doWork for " + prepare.getCHProfile() + " ... (" + getMemInfo() + ")");
-            final String name = prepare.getCHProfile().toFileName();
+                    (prepare.isEdgeBased() ? "edge" : "node") + "-based CH prepare.doWork for " + prepare.getWeighting() + " ... (" + getMemInfo() + ")");
+            final String name = AbstractWeighting.weightingToFileName(prepare.getWeighting(), prepare.isEdgeBased());
             completionService.submit(new Runnable() {
                 @Override
                 public void run() {
@@ -299,6 +295,7 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
                     properties.put(CH.PREPARE + "date." + name, createFormatter().format(new Date()));
                 }
             }, name);
+
         }
 
         threadPool.shutdown();
@@ -316,16 +313,21 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     public void createPreparations(GraphHopperStorage ghStorage) {
         if (!isEnabled() || !getPreparations().isEmpty())
             return;
-        if (!hasCHProfiles())
-            throw new IllegalStateException("No CH profiles found");
+        if (!hasWeightings())
+            throw new IllegalStateException("No CH weightings found");
 
-        for (CHProfile chProfile : chProfiles) {
-            addPreparation(createCHPreparation(ghStorage, chProfile));
+        for (Weighting weighting : nodeBasedWeightings) {
+            addPreparation(createCHPreparation(ghStorage, weighting, TraversalMode.NODE_BASED));
+        }
+        for (Weighting weighting : edgeBasedWeightings) {
+            addPreparation(createCHPreparation(ghStorage, weighting, TraversalMode.EDGE_BASED));
         }
     }
 
-    private PrepareContractionHierarchies createCHPreparation(GraphHopperStorage ghStorage, CHProfile chProfile) {
-        PrepareContractionHierarchies tmpPrepareCH = PrepareContractionHierarchies.fromGraphHopperStorage(ghStorage, chProfile);
+    private PrepareContractionHierarchies createCHPreparation(GraphHopperStorage ghStorage, Weighting weighting,
+                                                              TraversalMode traversalMode) {
+        PrepareContractionHierarchies tmpPrepareCH = PrepareContractionHierarchies.fromGraphHopperStorage(
+                ghStorage, weighting, traversalMode);
         tmpPrepareCH.setParams(pMap);
         return tmpPrepareCH;
     }
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 d36c70816..edaa39feb 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
@@ -52,8 +52,9 @@ import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
  */
 public class PrepareContractionHierarchies extends AbstractAlgoPreparation implements RoutingAlgorithmFactory {
     private final Logger logger = LoggerFactory.getLogger(getClass());
-    private final CHProfile chProfile;
     private final PreparationWeighting prepareWeighting;
+    private final Weighting weighting;
+    private final TraversalMode traversalMode;
     private final CHGraph prepareGraph;
     private final Random rand = new Random(123);
     private final StopWatch allSW = new StopWatch();
@@ -73,15 +74,16 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     private PMap pMap = new PMap();
     private int checkCounter;
 
-    public PrepareContractionHierarchies(CHGraph chGraph) {
+    public PrepareContractionHierarchies(CHGraph chGraph, Weighting weighting, TraversalMode traversalMode) {
         this.prepareGraph = chGraph;
-        this.chProfile = chGraph.getCHProfile();
-        prepareWeighting = new PreparationWeighting(chProfile.getWeighting());
-        this.params = Params.forTraversalMode(chProfile.getTraversalMode());
+        this.traversalMode = traversalMode;
+        this.weighting = weighting;
+        prepareWeighting = new PreparationWeighting(weighting);
+        this.params = Params.forTraversalMode(traversalMode);
     }
 
-    public static PrepareContractionHierarchies fromGraphHopperStorage(GraphHopperStorage ghStorage, CHProfile chProfile) {
-        return new PrepareContractionHierarchies(ghStorage.getCHGraph(chProfile));
+    public static PrepareContractionHierarchies fromGraphHopperStorage(GraphHopperStorage ghStorage, Weighting weighting, TraversalMode traversalMode) {
+        return new PrepareContractionHierarchies(ghStorage.getCHGraph(weighting), weighting, traversalMode);
     }
 
     public PrepareContractionHierarchies setParams(PMap pMap) {
@@ -148,7 +150,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     private AbstractBidirAlgo doCreateAlgo(Graph graph, AlgorithmOptions opts) {
-        if (chProfile.isEdgeBased()) {
+        if (traversalMode.isEdgeBased()) {
             return createAlgoEdgeBased(graph, opts);
         } else {
             return createAlgoNodeBased(graph, opts);
@@ -182,7 +184,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     public boolean isEdgeBased() {
-        return chProfile.isEdgeBased();
+        return traversalMode.isEdgeBased();
     }
 
     private void initFromGraph() {
@@ -199,7 +201,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         //   but we need the additional oldPriorities array to keep the old value which is necessary for the update method
         sortedNodes = new GHTreeMapComposed();
         oldPriorities = new float[prepareGraph.getNodes()];
-        nodeContractor = createNodeContractor(prepareGraph, chProfile.getTraversalMode());
+        nodeContractor = createNodeContractor(prepareGraph, traversalMode);
         nodeContractor.initFromGraph();
     }
 
@@ -424,11 +426,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     public Weighting getWeighting() {
-        return chProfile.getWeighting();
-    }
-
-    public CHProfile getCHProfile() {
-        return chProfile;
+        return weighting;
     }
 
     private String getTimesAsString() {
@@ -451,7 +449,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
 
     @Override
     public String toString() {
-        return chProfile.isEdgeBased() ? "prepare|dijkstrabi|edge|ch" : "prepare|dijkstrabi|ch";
+        return traversalMode.isEdgeBased() ? "prepare|dijkstrabi|edge|ch" : "prepare|dijkstrabi|ch";
     }
 
     private NodeContractor createNodeContractor(Graph graph, TraversalMode traversalMode) {
@@ -459,7 +457,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
             TurnWeighting chTurnWeighting = createTurnWeightingForEdgeBased(graph);
             return new EdgeBasedNodeContractor(prepareGraph, chTurnWeighting, pMap);
         } else {
-            return new NodeBasedNodeContractor(prepareGraph, chProfile.getWeighting(), pMap);
+            return new NodeBasedNodeContractor(prepareGraph, weighting, pMap);
         }
     }
 
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
index 12a2499c3..b93eae331 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
@@ -270,13 +270,13 @@ public class LMAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
      * @see com.graphhopper.routing.ch.CHAlgoFactoryDecorator#prepare(StorableProperties) for a very similar method
      */
     public boolean loadOrDoWork(final StorableProperties properties) {
-        ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(threadPool);
+        ExecutorCompletionService completionService = new ExecutorCompletionService<>(threadPool);
         int counter = 0;
         final AtomicBoolean prepared = new AtomicBoolean(false);
         for (final PrepareLandmarks plm : preparations) {
             counter++;
             final int tmpCounter = counter;
-            final String name = AbstractWeighting.weightingToFileName(plm.getWeighting());
+            final String name = AbstractWeighting.weightingToFileName(plm.getWeighting(), false);
             completionService.submit(new Runnable() {
                 @Override
                 public void run() {
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 793db907e..2ca516593 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
@@ -57,7 +57,7 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
     private static final Logger LOGGER = LoggerFactory.getLogger(LandmarkStorage.class);
     // This value is used to identify nodes where no subnetwork is associated
     private static final int UNSET_SUBNETWORK = -1;
-    // This value should only be used if subnetwork is too small to be explicitly stored
+    // This value should only be used if subnetwork is too small to be explicitely stored
     private static final int UNCLEAR_SUBNETWORK = 0;
     // one node has an associated landmark information ('one landmark row'): the forward and backward weight
     private long LM_ROW_LENGTH;
@@ -106,7 +106,7 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
         // Edge based is not really necessary because when adding turn costs while routing we can still
         // use the node based traversal as this is a smaller weight approximation and will still produce correct results
         this.traversalMode = TraversalMode.NODE_BASED;
-        final String name = AbstractWeighting.weightingToFileName(weighting);
+        final String name = AbstractWeighting.weightingToFileName(weighting, false);
         this.landmarkWeightDA = dir.find("landmarks_" + name);
 
         this.landmarks = landmarks;
diff --git a/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
index 6fa3f8745..27ac7ebac 100644
--- a/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
@@ -64,7 +64,7 @@ public abstract class AbstractFlagEncoder implements FlagEncoder {
     protected BooleanEncodedValue roundaboutEnc;
     protected DecimalEncodedValue speedEncoder;
     protected PMap properties;
-    // This value determines the maximal possible speed of any road regardless of the maxspeed value
+    // This value determines the maximal possible speed of any road regardless the maxspeed value
     // lower values allow more compact representation of the routing graph
     protected int maxPossibleSpeed;
     /* Edge Flag Encoder fields */
diff --git a/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java
index d2319b57d..cd4760a06 100644
--- a/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java
@@ -45,7 +45,7 @@ abstract public class BikeCommonFlagEncoder extends AbstractFlagEncoder {
      */
     public static final int K_UNPAVED = 100;
     protected static final int PUSHING_SECTION_SPEED = 4;
-    // Pushing section highways are parts where you need to get off your bike and push it (German: Schiebestrecke)
+    // Pushing section heighways are parts where you need to get off your bike and push it (German: Schiebestrecke)
     protected final HashSet<String> pushingSectionsHighways = new HashSet<>();
     protected final HashSet<String> oppositeLanes = new HashSet<>();
     protected final Set<String> preferHighwayTags = new HashSet<>();
diff --git a/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java
index 489a9f863..9e4ed568b 100644
--- a/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java
@@ -36,8 +36,8 @@ public class BikeFlagEncoder extends BikeCommonFlagEncoder {
     }
 
     public BikeFlagEncoder(PMap properties) {
-        this(properties.getInt("speed_bits", 4),
-                properties.getInt("speed_factor", 2),
+        this((int) properties.getLong("speed_bits", 4),
+                properties.getLong("speed_factor", 2),
                 properties.getBool("turn_costs", false) ? 1 : 0);
         this.properties = properties;
         this.setBlockFords(properties.getBool("block_fords", true));
diff --git a/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java
index da691baaa..d8f8a9463 100644
--- a/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java
@@ -50,7 +50,7 @@ public class MotorcycleFlagEncoder extends CarFlagEncoder {
     }
 
     public MotorcycleFlagEncoder(PMap properties) {
-        this(properties.getInt("speed_bits", 5),
+        this((int) properties.getLong("speed_bits", 5),
                 properties.getDouble("speed_factor", 5),
                 properties.getBool("turn_costs", false) ? 1 : 0
         );
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 087fac389..160921d85 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
@@ -104,8 +104,8 @@ 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("\\|", "_");
+    public static String weightingToFileName(Weighting w, boolean edgeBased) {
+        return toLowerCase(w.toString()).replaceAll("\\|", "_") + (edgeBased ? "_edge" : "_node");
     }
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraph.java b/core/src/main/java/com/graphhopper/storage/CHGraph.java
index 5d492d0cb..6ef50906c 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraph.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraph.java
@@ -45,22 +45,16 @@ public interface CHGraph extends Graph {
      */
     int getLevel(int nodeId);
 
-    /**
-     * Returns the profile of this CH graph. This is used to identify the CH graph.
-     */
-    CHProfile getCHProfile();
-
     boolean isShortcut(int edgeId);
 
     /**
      * This method creates a shortcut between a to b which is nearly identical to creating an edge
      * except that it can be excluded or included for certain traversals or algorithms.
      */
+    CHEdgeIteratorState shortcut(int a, int b);
+
     int shortcut(int a, int b, int accessFlags, double weight, double distance, int skippedEdge1, int skippedEdge2);
 
-    /**
-     * like shortcut(), but for edge-based CH
-     */
     int shortcutEdgeBased(int a, int b, int accessFlags, double weight, double distance, int skippedEdge1, int skippedEdge2, int origFirst, int origLast);
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
index e2bcb57c3..a8bbe1c3a 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
@@ -52,9 +52,10 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     final DataAccess shortcuts;
     final DataAccess nodesCH;
     final int scDirMask = PrepareEncoder.getScDirMask();
-    private final CHProfile chProfile;
+    private final boolean edgeBased;
     private final BaseGraph baseGraph;
     private final CHEdgeAccess chEdgeAccess;
+    private final Weighting weighting;
     int N_CH_REF;
     int shortcutEntryBytes;
     // the nodesCH storage is limited via baseGraph.nodeCount too
@@ -65,20 +66,21 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     private int shortcutCount = 0;
     private boolean isReadyForContraction;
 
-    CHGraphImpl(CHProfile chProfile, Directory dir, final BaseGraph baseGraph) {
-        if (chProfile.getWeighting() == null)
+    CHGraphImpl(Weighting w, Directory dir, final BaseGraph baseGraph, boolean edgeBased) {
+        if (w == null)
             throw new IllegalStateException("Weighting for CHGraph cannot be null");
-        this.chProfile = chProfile;
+
+        this.weighting = w;
         this.baseGraph = baseGraph;
-        final String name = chProfile.toFileName();
+        final String name = AbstractWeighting.weightingToFileName(w, edgeBased);
+        this.edgeBased = edgeBased;
         this.nodesCH = dir.find("nodes_ch_" + name, DAType.getPreferredInt(dir.getDefaultType()));
         this.shortcuts = dir.find("shortcuts_" + name, DAType.getPreferredInt(dir.getDefaultType()));
         this.chEdgeAccess = new CHEdgeAccess(name);
     }
 
-    @Override
-    public CHProfile getCHProfile() {
-        return chProfile;
+    public final Weighting getWeighting() {
+        return weighting;
     }
 
     @Override
@@ -103,6 +105,22 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         assert nodeId < baseGraph.getNodes() : "node " + nodeId + " is invalid. Not in [0," + baseGraph.getNodes() + ")";
     }
 
+    @Override
+    public CHEdgeIteratorState shortcut(int a, int b) {
+        if (!baseGraph.isFrozen())
+            throw new IllegalStateException("Cannot create shortcut if graph is not yet frozen");
+
+        checkNodeId(a);
+        checkNodeId(b);
+
+        int scId = chEdgeAccess.internalEdgeAdd(nextShortcutId(), a, b);
+        CHEdgeIteratorImpl iter = new CHEdgeIteratorImpl(baseGraph, chEdgeAccess, EdgeFilter.ALL_EDGES);
+        boolean ret = iter.init(scId, b);
+        assert ret;
+        iter.setSkippedEdges(EdgeIterator.NO_EDGE, EdgeIterator.NO_EDGE);
+        return iter;
+    }
+
     @Override
     public int shortcut(int a, int b, int accessFlags, double weight, double distance, int skippedEdge1, int skippedEdge2) {
         if (!baseGraph.isFrozen())
@@ -122,9 +140,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
     @Override
     public int shortcutEdgeBased(int a, int b, int accessFlags, double weight, double distance, int skippedEdge1, int skippedEdge2, int origFirst, int origLast) {
-        if (!chProfile.isEdgeBased()) {
-            throw new IllegalStateException("Edge-based shortcuts should only be added when CHGraph is edge-based");
-        }
+        assert edgeBased : "Edge-based shortcuts should only be added when CHGraph is edge-based";
         int scId = shortcut(a, b, accessFlags, weight, distance, skippedEdge1, skippedEdge2);
         chEdgeAccess.setFirstAndLastOrigEdges(chEdgeAccess.toPointer(scId), origFirst, origLast);
         return scId;
@@ -331,7 +347,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         // shortcuts
         S_SKIP_EDGE1 = ea.E_FLAGS + 4;
         S_SKIP_EDGE2 = S_SKIP_EDGE1 + 4;
-        if (chProfile.isEdgeBased()) {
+        if (edgeBased) {
             S_ORIG_FIRST = S_SKIP_EDGE2 + 4;
             S_ORIG_LAST = S_ORIG_FIRST + 4;
             shortcutEntryBytes = S_ORIG_LAST + 4;
@@ -391,7 +407,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
     @Override
     public String toString() {
-        return "CHGraph|" + chProfile.toString();
+        return "CHGraph|" + getWeighting().toString();
     }
 
     public void debugPrint() {
@@ -409,7 +425,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         String formatShortcutsBase = "%12s | %12s | %12s | %12s | %12s | %12s | %12s | %12s | %12s";
         String formatShortcutExt = " | %12s | %12s";
         String header = String.format(Locale.ROOT, formatShortcutsBase, "#", "E_NODEA", "E_NODEB", "E_LINKA", "E_LINKB", "E_DIST", "E_FLAGS", "S_SKIP_EDGE1", "S_SKIP_EDGE2");
-        if (chProfile.isEdgeBased()) {
+        if (edgeBased) {
             header += String.format(Locale.ROOT, formatShortcutExt, "S_ORIG_FIRST", "S_ORIG_LAST");
         }
         System.out.println(header);
@@ -425,7 +441,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
                     chEdgeAccess.getShortcutFlags(edgePointer),
                     shortcuts.getInt(edgePointer + S_SKIP_EDGE1),
                     shortcuts.getInt(edgePointer + S_SKIP_EDGE2));
-            if (chProfile.isEdgeBased()) {
+            if (edgeBased) {
                 edgeString += String.format(Locale.ROOT, formatShortcutExt,
                         shortcuts.getInt(edgePointer + S_ORIG_FIRST),
                         shortcuts.getInt(edgePointer + S_ORIG_LAST));
@@ -517,7 +533,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         @Override
         public int getOrigEdgeFirst() {
-            if (!isShortcut() || !chProfile.isEdgeBased()) {
+            if (!isShortcut() || !edgeBased) {
                 return getEdge();
             }
             return shortcuts.getInt(edgePointer + S_ORIG_FIRST);
@@ -525,7 +541,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         @Override
         public int getOrigEdgeLast() {
-            if (!isShortcut() || !chProfile.isEdgeBased()) {
+            if (!isShortcut() || !edgeBased) {
                 return getEdge();
             }
             return shortcuts.getInt(edgePointer + S_ORIG_LAST);
@@ -591,7 +607,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         private void checkShortcutAndEdgeBased(String method) {
             checkShortcut(true, method);
-            if (!chProfile.isEdgeBased()) {
+            if (!edgeBased) {
                 throw new IllegalStateException("Method " + method + " only allowed when CH graph is configured for edge based traversal");
             }
         }
@@ -771,7 +787,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         private void checkShortcutAndEdgeBased(String method) {
             checkShortcut(true, method);
-            if (!chProfile.isEdgeBased()) {
+            if (!edgeBased) {
                 throw new IllegalStateException("Method " + method + " not supported when turn costs are disabled");
             }
         }
@@ -866,9 +882,6 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         }
 
         public void setFirstAndLastOrigEdges(long edgePointer, int origFirst, int origLast) {
-            if (!chProfile.isEdgeBased()) {
-                throw new IllegalStateException("Edge-based shortcuts should only be added when CHGraph is edge-based");
-            }
             shortcuts.setInt(edgePointer + S_ORIG_FIRST, origFirst);
             shortcuts.setInt(edgePointer + S_ORIG_LAST, origLast);
         }
diff --git a/core/src/main/java/com/graphhopper/storage/CHProfile.java b/core/src/main/java/com/graphhopper/storage/CHProfile.java
deleted file mode 100644
index 3ade3bd71..000000000
--- a/core/src/main/java/com/graphhopper/storage/CHProfile.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.graphhopper.storage;
-
-import com.graphhopper.routing.util.TraversalMode;
-import com.graphhopper.routing.weighting.AbstractWeighting;
-import com.graphhopper.routing.weighting.Weighting;
-
-import java.util.Objects;
-
-/**
- * Specifies all properties of a CH routing profile. Generally these properties cannot be changed after the CH
- * pre-processing is finished and are stored on disk along with the prepared graph data.
- *
- * @author easbar
- */
-public class CHProfile {
-    private final Weighting weighting;
-    private final boolean edgeBased;
-
-    public static CHProfile nodeBased(Weighting weighting) {
-        return new CHProfile(weighting, TraversalMode.NODE_BASED);
-    }
-
-    public static CHProfile edgeBased(Weighting weighting) {
-        return new CHProfile(weighting, TraversalMode.EDGE_BASED);
-    }
-
-    public CHProfile(Weighting weighting, TraversalMode traversalMode) {
-        this(weighting, traversalMode.isEdgeBased());
-    }
-
-    public CHProfile(Weighting weighting, boolean edgeBased) {
-        this.weighting = weighting;
-        this.edgeBased = edgeBased;
-    }
-
-    public Weighting getWeighting() {
-        return weighting;
-    }
-
-    public boolean isEdgeBased() {
-        return edgeBased;
-    }
-
-    public TraversalMode getTraversalMode() {
-        return edgeBased ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED;
-    }
-
-    public String toFileName() {
-        return AbstractWeighting.weightingToFileName(weighting) + "_" + (edgeBased ? "edge" : "node");
-    }
-
-    public String toString() {
-        return weighting + "|edge_based=" + edgeBased;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        CHProfile chProfile = (CHProfile) o;
-        return edgeBased == chProfile.edgeBased &&
-                Objects.equals(weighting, chProfile.weighting);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(weighting, edgeBased);
-    }
-}
diff --git a/core/src/main/java/com/graphhopper/storage/GHDirectory.java b/core/src/main/java/com/graphhopper/storage/GHDirectory.java
index 4aeb1879f..76f9dd6a0 100644
--- a/core/src/main/java/com/graphhopper/storage/GHDirectory.java
+++ b/core/src/main/java/com/graphhopper/storage/GHDirectory.java
@@ -27,7 +27,6 @@ import static com.graphhopper.util.Helper.*;
 
 /**
  * Implements some common methods for the subclasses.
- * <p>
  *
  * @author Peter Karich
  */
@@ -109,34 +108,20 @@ public class GHDirectory implements Directory {
 
     @Override
     public void clear() {
-        // If there is at least one MMap DA then do not apply the cleanHack 
-        // for every single mmap DA as this is very slow if lots of DataAccess objects were collected 
-        // => forceClean == false
-
-        MMapDataAccess mmapDA = null;
         for (DataAccess da : map.values()) {
-            if (da instanceof MMapDataAccess)
-                mmapDA = (MMapDataAccess) da;
-
-            removeDA(da, da.getName(), false);
+            removeDA(da, da.getName());
         }
-        if (mmapDA != null)
-            cleanHack();
         map.clear();
     }
 
     @Override
     public void remove(DataAccess da) {
         removeFromMap(da.getName());
-        removeDA(da, da.getName(), true);
+        removeDA(da, da.getName());
     }
 
-    void removeDA(DataAccess da, String name, boolean forceClean) {
-        if (da instanceof MMapDataAccess)
-            ((MMapDataAccess) da).close(forceClean);
-        else
-            da.close();
-
+    void removeDA(DataAccess da, String name) {
+        da.close();
         if (da.getType().isStoring())
             removeDir(new File(location + name));
     }
diff --git a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java b/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
index 245baec86..0d2358995 100644
--- a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
+++ b/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
@@ -18,11 +18,10 @@
 package com.graphhopper.storage;
 
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.weighting.Weighting;
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
 
 /**
  * For now this is just a helper class to quickly create a {@link GraphHopperStorage}
@@ -37,28 +36,22 @@ public class GraphBuilder {
     private boolean store;
     private boolean elevation;
     private boolean turnCosts;
+    private boolean edgeBasedCH;
     private long byteCapacity = 100;
-    private List<CHProfile> chProfiles = Collections.emptyList();
+    private Weighting singleCHWeighting;
 
     public GraphBuilder(EncodingManager encodingManager) {
         this.encodingManager = encodingManager;
     }
 
     /**
-     * This method enables creating CHGraphs with the specified CHProfiles
+     * This method enables creating a CHGraph with the specified weighting.
      */
-    public GraphBuilder setCHProfiles(List<CHProfile> chProfiles) {
-        if (chProfiles.size() != new HashSet<>(chProfiles).size()) {
-            throw new IllegalArgumentException("Given CH profiles contain duplicates, given: " + chProfiles);
-        }
-        this.chProfiles = chProfiles;
+    public GraphBuilder setCHGraph(Weighting singleCHWeighting) {
+        this.singleCHWeighting = singleCHWeighting;
         return this;
     }
 
-    public GraphBuilder setCHProfiles(CHProfile... chProfiles) {
-        return setCHProfiles(Arrays.asList(chProfiles));
-    }
-
     public GraphBuilder setLocation(String location) {
         this.location = location;
         return this;
@@ -89,6 +82,11 @@ public class GraphBuilder {
         return this;
     }
 
+    public GraphBuilder setEdgeBasedCH(boolean edgeBasedCH) {
+        this.edgeBasedCH = edgeBasedCH;
+        return this;
+    }
+
     public boolean hasElevation() {
         return elevation;
     }
@@ -96,8 +94,8 @@ public class GraphBuilder {
     /**
      * Creates a CHGraph
      */
-    public CHGraph chGraphCreate(CHProfile chProfile) {
-        return setCHProfiles(chProfile).create().getCHGraph();
+    public CHGraph chGraphCreate(Weighting singleCHWeighting) {
+        return setCHGraph(singleCHWeighting).create().getCHGraph(singleCHWeighting);
     }
 
     /**
@@ -114,9 +112,11 @@ public class GraphBuilder {
                 new TurnCostExtension() :
                 new TurnCostExtension.NoOpExtension();
 
-        return chProfiles.isEmpty() ?
+        return singleCHWeighting == null ?
                 new GraphHopperStorage(dir, encodingManager, elevation, graphExtension) :
-                new GraphHopperStorage(chProfiles, dir, encodingManager, elevation, graphExtension);
+                edgeBasedCH ?
+                        new GraphHopperStorage(Collections.<Weighting>emptyList(), Arrays.asList(singleCHWeighting), dir, encodingManager, elevation, graphExtension) :
+                        new GraphHopperStorage(Arrays.asList(singleCHWeighting), Collections.<Weighting>emptyList(), dir, encodingManager, elevation, graphExtension);
     }
 
     /**
diff --git a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java b/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
index fc9cffd47..4a69f1800 100644
--- a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
+++ b/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
@@ -46,17 +46,20 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
     private final StorableProperties properties;
     private final BaseGraph baseGraph;
     // same flush order etc
-    private final Collection<CHGraphImpl> chGraphs;
+    private final Collection<CHGraphImpl> nodeBasedCHGraphs = new ArrayList<>(5);
+    private final Collection<CHGraphImpl> edgeBasedCHGraphs = new ArrayList<>(5);
 
     public GraphHopperStorage(Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
-        this(Collections.<CHProfile>emptyList(), dir, encodingManager, withElevation, extendedStorage);
+        this(Collections.<Weighting>emptyList(), Collections.<Weighting>emptyList(), dir, encodingManager, withElevation, extendedStorage);
     }
 
-    public GraphHopperStorage(Collection<? extends Weighting> nodeBasedCHWeightings, Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
-        this(createProfilesForWeightings(nodeBasedCHWeightings), dir, encodingManager, withElevation, extendedStorage);
+    public GraphHopperStorage(List<? extends Weighting> nodeBasedCHWeightings, Directory dir, EncodingManager encodingManager,
+                              boolean withElevation, GraphExtension extendedStorage) {
+        this(nodeBasedCHWeightings, Collections.<Weighting>emptyList(), dir, encodingManager, withElevation, extendedStorage);
     }
 
-    public GraphHopperStorage(List<CHProfile> chProfiles, Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
+    public GraphHopperStorage(List<? extends Weighting> nodeBasedCHWeightings, List<? extends Weighting> edgeBasedCHWeightings,
+                              Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
         if (extendedStorage == null)
             throw new IllegalArgumentException("GraphExtension cannot be null, use NoOpExtension");
 
@@ -83,9 +86,11 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
         };
 
         baseGraph = new BaseGraph(dir, encodingManager, withElevation, listener, extendedStorage);
-        this.chGraphs = new ArrayList<>(chProfiles.size());
-        for (CHProfile chProfile : chProfiles) {
-            chGraphs.add(new CHGraphImpl(chProfile, dir, baseGraph));
+        for (Weighting w : nodeBasedCHWeightings) {
+            nodeBasedCHGraphs.add(new CHGraphImpl(w, dir, baseGraph, false));
+        }
+        for (Weighting w : edgeBasedCHWeightings) {
+            edgeBasedCHGraphs.add(new CHGraphImpl(w, dir, baseGraph, true));
         }
     }
 
@@ -94,54 +99,52 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
         if (chGraphs.isEmpty()) {
             throw new IllegalStateException("There is no CHGraph");
         } else if (chGraphs.size() > 1) {
-            throw new IllegalStateException("There are multiple CHGraphs, use getCHGraph(CHProfile) to retrieve a specific one");
+            throw new IllegalStateException("There are multiple CHGraphs, use getCHGraph(Weighting) to retrieve a specific one");
         } else {
             return chGraphs.iterator().next();
         }
     }
 
     /**
-     * @return the {@link CHGraph} for the specified {@link CHProfile}
+     * @return the {@link CHGraph} for the specified {@link Weighting}. The graph is identified by the weighting using
+     * reference identity (==)!
      */
-    public CHGraph getCHGraph(CHProfile profile) {
+    public CHGraph getCHGraph(Weighting weighting) {
         Collection<CHGraphImpl> chGraphs = getAllCHGraphs();
         if (chGraphs.isEmpty())
             throw new IllegalStateException("There is no CHGraph");
 
-        if (profile == null)
-            throw new IllegalStateException("Cannot find CHGraph with null CHProfile");
+        if (weighting == null)
+            throw new IllegalStateException("Cannot find CHGraph with null weighting");
 
-        List<CHProfile> existing = new ArrayList<>();
+        List<Weighting> existing = new ArrayList<>();
         for (CHGraphImpl cg : chGraphs) {
-            if (cg.getCHProfile().equals(profile))
+            if (cg.getWeighting() == weighting)
                 return cg;
-            existing.add(cg.getCHProfile());
+            existing.add(cg.getWeighting());
         }
 
-        throw new IllegalStateException("Cannot find CHGraph for the specified profile: " + profile + ", existing:" + existing);
+        throw new IllegalStateException("Cannot find CHGraph for the specified weighting: " + weighting + ", existing:" + existing);
     }
 
     public boolean isCHPossible() {
         return !getAllCHGraphs().isEmpty();
     }
 
-    public List<CHProfile> getCHProfiles() {
-        List<CHProfile> result = new ArrayList<>(chGraphs.size());
-        for (CHGraphImpl chGraph : chGraphs) {
-            result.add(chGraph.getCHProfile());
-        }
-        return result;
+    public List<Weighting> getNodeBasedCHWeightings() {
+        return getWeightingsFromGraphs(nodeBasedCHGraphs);
     }
 
-    public List<CHProfile> getCHProfiles(boolean edgeBased) {
-        List<CHProfile> result = new ArrayList<>();
-        List<CHProfile> chProfiles = getCHProfiles();
-        for (CHProfile profile : chProfiles) {
-            if (edgeBased == profile.isEdgeBased()) {
-                result.add(profile);
-            }
+    public List<Weighting> getEdgeBasedCHWeightings() {
+        return getWeightingsFromGraphs(edgeBasedCHGraphs);
+    }
+
+    private List<Weighting> getWeightingsFromGraphs(Collection<CHGraphImpl> graphs) {
+        List<Weighting> list = new ArrayList<>(graphs.size());
+        for (CHGraphImpl cg : graphs) {
+            list.add(cg.getWeighting());
         }
-        return result;
+        return list;
     }
 
     /**
@@ -188,7 +191,8 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
             cg.create(byteCount);
         }
 
-        properties.put("graph.ch.profiles", getCHProfiles().toString());
+        properties.put("graph.ch.weightings", getNodeBasedCHWeightings().toString());
+        properties.put("graph.ch.edge.weightings", getEdgeBasedCHWeightings().toString());
         return this;
     }
 
@@ -280,19 +284,28 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
     }
 
     private void checkIfConfiguredAndLoadedWeightingsCompatible() {
-        String loadedStr = properties.get("graph.ch.profiles");
-        List<String> loaded = parseList(loadedStr);
-        List<CHProfile> configured = getCHProfiles();
+        String loadedStrNode = properties.get("graph.ch.weightings");
+        List<String> loadedNode = parseList(loadedStrNode);
+        String loadedStrEdge = properties.get("graph.ch.edge.weightings");
+        List<String> loadedEdge = parseList(loadedStrEdge);
+        List<Weighting> configuredNode = getNodeBasedCHWeightings();
+        List<Weighting> configuredEdge = getEdgeBasedCHWeightings();
         // todo: not entirely sure here. when no ch is configured at all (neither edge nor node), but there are any
         // ch graphs (edge or node) we throw an error ? previously we threw an error when no ch weighting was configured
         // even though there was a ch graph.
-        if (configured.isEmpty() && !loaded.isEmpty()) {
-            throw new IllegalStateException("You loaded a CH graph, but you did not specify any CH weightings in prepare.ch.weightings");
+        if ((configuredNode.isEmpty() && configuredEdge.isEmpty()) && (!loadedNode.isEmpty() || !loadedEdge.isEmpty())) {
+            throw new IllegalStateException("You loaded a CH graph, but you did not specify graph.ch.weightings");
+        }
+        for (Weighting w : configuredNode) {
+            if (!loadedNode.contains(w.toString())) {
+                throw new IllegalStateException("Configured weighting: " + w.toString() + " is not contained in loaded weightings for CH" + loadedStrNode + ".\n" +
+                        "You configured graph.ch.weightings: " + configuredNode);
+            }
         }
-        for (CHProfile chProfile : configured) {
-            if (!loaded.contains(chProfile.toString())) {
-                throw new IllegalStateException("Configured CH profile: " + chProfile.toString() + " is not contained in loaded weightings for CH" + loadedStr + ".\n" +
-                        "You configured: " + configured);
+        for (Weighting w : configuredEdge) {
+            if (!loadedEdge.contains(w.toString())) {
+                throw new IllegalStateException("Configured weighting: " + w.toString() + " is not contained in loaded weightings for edge-based CH" + loadedStrEdge + ".\n" +
+                        "You configured graph.ch.edge.weightings: " + configuredEdge);
             }
         }
     }
@@ -463,14 +476,12 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
     }
 
     private Collection<CHGraphImpl> getAllCHGraphs() {
-        return chGraphs;
-    }
-
-    private static List<CHProfile> createProfilesForWeightings(Collection<? extends Weighting> weightings) {
-        List<CHProfile> result = new ArrayList<>(weightings.size());
-        for (Weighting weighting : weightings) {
-            result.add(CHProfile.nodeBased(weighting));
-        }
+        // todo: this method is only used to have a 'view' on the two collections. we could also create this only once
+        // as long as the graph collections are only modified in the constructor (otherwise we would have to make sure
+        // the lists are in sync). another option would be something like guava concat.
+        List<CHGraphImpl> result = new ArrayList<>(nodeBasedCHGraphs.size() + edgeBasedCHGraphs.size());
+        result.addAll(nodeBasedCHGraphs);
+        result.addAll(edgeBasedCHGraphs);
         return result;
     }
 }
diff --git a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
index ec52bcf00..d2bcd5368 100644
--- a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
+++ b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
@@ -52,8 +52,6 @@ import java.util.StringTokenizer;
  *
  * The exact behavior of memory-mapping is reported to be wildly platform-dependent.
  *
- * <p>
- *
  * @author Peter Karich
  * @author Michael Zilske
  */
@@ -258,7 +256,6 @@ public final class MMapDataAccess extends AbstractDataAccess {
             } catch (IOException tmpex) {
                 ioex = tmpex;
                 trial++;
-                Helper.cleanHack();
                 try {
                     // mini sleep to let JVM do unmapping
                     Thread.sleep(5);
@@ -328,19 +325,9 @@ public final class MMapDataAccess extends AbstractDataAccess {
     @Override
     public void close() {
         super.close();
-        close(true);
-    }
-
-    /**
-     * @param forceClean if true the clean hack (system.gc) will be executed and forces the system
-     *                   to cleanup the mmap resources. Set false if you need to close many MMapDataAccess objects.
-     */
-    void close(boolean forceClean) {
         clean(0, segments.size());
         segments.clear();
         Helper.close(raFile);
-        if (forceClean)
-            Helper.cleanHack();
     }
 
     @Override
@@ -476,7 +463,6 @@ public final class MMapDataAccess extends AbstractDataAccess {
         }
 
         clean(remainingSegNo, segments.size());
-        Helper.cleanHack();
         segments = new ArrayList<>(segments.subList(0, remainingSegNo));
 
         try {
diff --git a/core/src/main/java/com/graphhopper/util/EdgeIterator.java b/core/src/main/java/com/graphhopper/util/EdgeIterator.java
index 0359dedba..0f4d77c43 100644
--- a/core/src/main/java/com/graphhopper/util/EdgeIterator.java
+++ b/core/src/main/java/com/graphhopper/util/EdgeIterator.java
@@ -46,11 +46,6 @@ public interface EdgeIterator extends EdgeIteratorState {
      */
     int NO_EDGE = -1;
 
-    /**
-     * Integer value used in places where normally an edge would be expected, but no specific edge shall be specified.
-     */
-    int ANY_EDGE = -2;
-
     /**
      * To be called to go to the next edge state.
      * <p>
diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java
index fd0338096..6fff92e30 100644
--- a/core/src/main/java/com/graphhopper/util/GHUtility.java
+++ b/core/src/main/java/com/graphhopper/util/GHUtility.java
@@ -36,8 +36,6 @@ import org.slf4j.LoggerFactory;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static com.graphhopper.util.Helper.DIST_EARTH;
-
 /**
  * A helper class to avoid cluttering the Graph interface with all the common methods. Most of the
  * methods are useful for unit tests or debugging only.
@@ -408,7 +406,7 @@ public class GHUtility {
         Directory outdir = guessDirectory(store);
         boolean is3D = store.getNodeAccess().is3D();
 
-        return new GraphHopperStorage(store.getCHProfiles(), outdir, store.getEncodingManager(),
+        return new GraphHopperStorage(store.getNodeBasedCHWeightings(), store.getEdgeBasedCHWeightings(), outdir, store.getEncodingManager(),
                 is3D, store.getExtension()).
                 create(store.getNodes());
     }
@@ -601,22 +599,12 @@ public class GHUtility {
         return edge;
     }
 
-    public static EncodingManager.Builder addDefaultEncodedValues(EncodingManager.Builder builder) {
+    public static final EncodingManager.Builder addDefaultEncodedValues(EncodingManager.Builder builder) {
         return builder.add(new OSMRoadClassParser()).add(new OSMRoadClassLinkParser()).
                 add(new OSMRoadEnvironmentParser()).add(new OSMMaxSpeedParser()).add(new OSMRoadAccessParser()).
                 add(new OSMSurfaceParser());
     }
 
-    public static void updateDistancesFor(Graph g, int node, double lat, double lon) {
-        NodeAccess na = g.getNodeAccess();
-        na.setNode(node, lat, lon);
-        EdgeIterator iter = g.createEdgeExplorer().setBaseNode(node);
-        while (iter.next()) {
-            iter.setDistance(iter.fetchWayGeometry(3).calcDistance(DIST_EARTH));
-            // System.out.println(node + "->" + adj + ": " + iter.getDistance());
-        }
-    }
-
     /**
      * This edge iterator can be used in tests to mock specific iterator behaviour via overloading
      * certain methods.
diff --git a/docs/core/create-new-flagencoder.md b/docs/core/create-new-flagencoder.md
index 3193d39b6..cfea45828 100644
--- a/docs/core/create-new-flagencoder.md
+++ b/docs/core/create-new-flagencoder.md
@@ -15,16 +15,17 @@ Use it e.g. just via `graphHopper.setEncodingManager(new EncodingManager(myEncod
 
 ## Different forward and backward weights?
 
-With 0.12 this is now simple. Specify speedTwoDirections = true in the constructor and overwrite handleSpeed:
-
-```java
-protected void handleSpeed(IntsRef edgeFlags, ReaderWay way, double speed) {
-        speedEncoder.setDecimal(true, edgeFlags, speed);
-        super.handleSpeed(edgeFlags, way, speed);
-}
-```
-
-See Bike2WeightFlagEncoder for an example that uses different weights: slower speeds uphill than downhill.
+If you need to support two different speed values for one street (one edge) you need to create
+a separate EncodedDoubleValue instance (reverseSpeedEncoder) managing the reverse speed, 
+see Bike2WeightFlagEncoder for an example. You'll have to overwrite the following methods:
+
+ * setReverseSpeed, getReverseSpeed to use the reverseSpeedEncoder
+ * handleSpeed, to handle oneway tags correctly
+ * flagsDefault 
+ * setProperties
+ * reverseFlags
+ * setLowSpeed
+ * always set reverse speed explicitely, see #665
 
 ## Elevation
 
@@ -33,5 +34,6 @@ and call edge.fetchWayGeometry(3) or again, see Bike2WeightFlagEncoder.
 
 ## Add to the core
 
-If you want to include your FlagEncoder in GraphHopper and e.g. still want to use the config.yml
-you can use a subclass of DefaultFlagEncoderFactory and use the configuration object to change different properties.
+If you want to include your FlagEncoder in GraphHopper you have to add the creation in
+EncodingManager.parseEncoderString to let the EncodingManager pick the correct class when faced
+with the string. The convention is that encoder.toString is identical to the string.
diff --git a/reader-gtfs/pom.xml b/reader-gtfs/pom.xml
index 369b8d6d8..bc3435462 100644
--- a/reader-gtfs/pom.xml
+++ b/reader-gtfs/pom.xml
@@ -11,7 +11,7 @@
 
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>
+        <artifactId>graphhopper-parent</artifactId>    	
         <version>0.13-SNAPSHOT</version>
     </parent>
 
diff --git a/reader-osm/pom.xml b/reader-osm/pom.xml
index 608aebf94..03d0a48b3 100644
--- a/reader-osm/pom.xml
+++ b/reader-osm/pom.xml
@@ -11,7 +11,7 @@
 
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>
+        <artifactId>graphhopper-parent</artifactId>    	
         <version>0.13-SNAPSHOT</version>
     </parent>
 
diff --git a/tools/pom.xml b/tools/pom.xml
index 76f38ce80..bc70bbb8b 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -11,16 +11,11 @@
 
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>
+        <artifactId>graphhopper-parent</artifactId>    	
         <version>0.13-SNAPSHOT</version>
     </parent>
 
     <dependencies>
-        <dependency>
-            <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-core</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
         <dependency>
             <groupId>com.graphhopper</groupId>
             <artifactId>graphhopper-reader-osm</artifactId>
diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java
index c10e355fa..76932bbd5 100644
--- a/tools/src/main/java/com/graphhopper/tools/Measurement.java
+++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java
@@ -26,7 +26,11 @@ import com.graphhopper.coll.GHBitSetImpl;
 import com.graphhopper.reader.DataReader;
 import com.graphhopper.reader.osm.GraphHopperOSM;
 import com.graphhopper.routing.util.*;
-import com.graphhopper.storage.*;
+import com.graphhopper.routing.weighting.Weighting;
+import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.Graph;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.NodeAccess;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.util.*;
 import com.graphhopper.util.Parameters.Algorithms;
@@ -89,14 +93,14 @@ public class Measurement {
                 // note that we measure the total time of all (possibly edge&node) CH preparations
                 put(Parameters.CH.PREPARE + "time", sw.stop().getMillis());
                 int edges = getGraphHopperStorage().getEdges();
-                if (!getCHFactoryDecorator().getNodeBasedCHProfiles().isEmpty()) {
-                    CHProfile chProfile = getCHFactoryDecorator().getNodeBasedCHProfiles().get(0);
-                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(chProfile).getEdges();
+                if (!getCHFactoryDecorator().getNodeBasedWeightings().isEmpty()) {
+                    Weighting weighting = getCHFactoryDecorator().getNodeBasedWeightings().get(0);
+                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(weighting).getEdges();
                     put(Parameters.CH.PREPARE + "node.shortcuts", edgesAndShortcuts - edges);
                 }
-                if (!getCHFactoryDecorator().getEdgeBasedCHProfiles().isEmpty()) {
-                    CHProfile chProfile = getCHFactoryDecorator().getEdgeBasedCHProfiles().get(0);
-                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(chProfile).getEdges();
+                if (!getCHFactoryDecorator().getEdgeBasedWeightings().isEmpty()) {
+                    Weighting weighting = getCHFactoryDecorator().getEdgeBasedWeightings().get(0);
+                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(weighting).getEdges();
                     put(Parameters.CH.PREPARE + "edge.shortcuts", edgesAndShortcuts - edges);
                 }
             }
@@ -169,16 +173,16 @@ public class Measurement {
 
                 isLM = false;
                 System.gc();
-                if (!hopper.getCHFactoryDecorator().getNodeBasedCHProfiles().isEmpty()) {
-                    CHProfile chProfile = hopper.getCHFactoryDecorator().getNodeBasedCHProfiles().get(0);
-                    CHGraph lg = g.getCHGraph(chProfile);
+                if (!hopper.getCHFactoryDecorator().getNodeBasedWeightings().isEmpty()) {
+                    Weighting weighting = hopper.getCHFactoryDecorator().getNodeBasedWeightings().get(0);
+                    CHGraph lg = g.getCHGraph(weighting);
                     fillAllowedEdges(lg.getAllEdges(), allowedEdges);
                     printMiscUnitPerfTests(lg, isCH, encoder, count * 100, allowedEdges);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH", vehicleStr, true, -1, true, false);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_no_sod", vehicleStr, true, -1, false, false);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_no_instr", vehicleStr, false, -1, true, false);
                 }
-                if (!hopper.getCHFactoryDecorator().getEdgeBasedCHProfiles().isEmpty()) {
+                if (!hopper.getCHFactoryDecorator().getEdgeBasedWeightings().isEmpty()) {
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_edge", vehicleStr, true, -1, false, true);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_edge_no_instr", vehicleStr, false, -1, false, true);
                 }
diff --git a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
index 53876812b..55a98fcd3 100644
--- a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
+++ b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
@@ -29,7 +29,10 @@ import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.*;
+import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.Graph;
+import com.graphhopper.storage.NodeAccess;
+import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.storage.index.QueryResult;
 import com.graphhopper.util.*;
@@ -94,9 +97,8 @@ public class MiniGraphUI {
         boolean ch = true;
         if (ch) {
             map.put(Parameters.Landmark.DISABLE, true);
-            CHProfile chProfile = hopper.getCHFactoryDecorator().getNodeBasedCHProfiles().get(0);
-            weighting = chProfile.getWeighting();
-            routingGraph = hopper.getGraphHopperStorage().getCHGraph(chProfile);
+            weighting = hopper.getCHFactoryDecorator().getNodeBasedWeightings().get(0);
+            routingGraph = hopper.getGraphHopperStorage().getCHGraph(weighting);
 
             final RoutingAlgorithmFactory tmpFactory = hopper.getAlgorithmFactory(map);
             algoFactory = new RoutingAlgorithmFactory() {
diff --git a/web-api/src/main/java/com/graphhopper/jackson/Jackson.java b/web-api/src/main/java/com/graphhopper/jackson/Jackson.java
index 117ec00c8..15f8532eb 100644
--- a/web-api/src/main/java/com/graphhopper/jackson/Jackson.java
+++ b/web-api/src/main/java/com/graphhopper/jackson/Jackson.java
@@ -19,15 +19,11 @@ package com.graphhopper.jackson;
 
 import com.bedatadriven.jackson.datatype.jts.JtsModule;
 import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.*;
 
 public class Jackson {
-
     public static ObjectMapper newObjectMapper() {
-        return initObjectMapper(new ObjectMapper());
-    }
-
-    public static ObjectMapper initObjectMapper(ObjectMapper objectMapper) {
+        ObjectMapper objectMapper = new ObjectMapper();
         objectMapper.registerModule(new GraphHopperModule());
         objectMapper.registerModule(new JtsModule());
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
diff --git a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
index e18907c63..1d0a0b8d9 100644
--- a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
+++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
@@ -18,6 +18,8 @@
 
 package com.graphhopper.http;
 
+import com.bedatadriven.jackson.datatype.jts.JtsModule;
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.BeanDescription;
 import com.fasterxml.jackson.databind.SerializationConfig;
@@ -26,13 +28,12 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
 import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
 import com.fasterxml.jackson.databind.util.StdDateFormat;
-import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 import com.graphhopper.GraphHopper;
 import com.graphhopper.GraphHopperAPI;
 import com.graphhopper.http.health.GraphHopperHealthCheck;
 import com.graphhopper.http.health.GraphHopperStorageHealthCheck;
 import com.graphhopper.isochrone.algorithm.DelaunayTriangulationIsolineBuilder;
-import com.graphhopper.jackson.Jackson;
+import com.graphhopper.jackson.GraphHopperModule;
 import com.graphhopper.reader.gtfs.GraphHopperGtfs;
 import com.graphhopper.reader.gtfs.GtfsStorage;
 import com.graphhopper.reader.gtfs.PtFlagEncoder;
@@ -157,13 +158,10 @@ public class GraphHopperBundle implements ConfiguredBundle<GraphHopperBundleConf
 
     @Override
     public void initialize(Bootstrap<?> bootstrap) {
-        // See #1440: avoids warning regarding com.fasterxml.jackson.module.afterburner.util.MyClassLoader
-        bootstrap.setObjectMapper(io.dropwizard.jackson.Jackson.newMinimalObjectMapper());
-        // avoids warning regarding com.fasterxml.jackson.databind.util.ClassUtil
-        bootstrap.getObjectMapper().registerModule(new Jdk8Module());
-
-        Jackson.initObjectMapper(bootstrap.getObjectMapper());
         bootstrap.getObjectMapper().setDateFormat(new StdDateFormat());
+        bootstrap.getObjectMapper().registerModule(new JtsModule());
+        bootstrap.getObjectMapper().registerModule(new GraphHopperModule());
+        bootstrap.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
         // Because VirtualEdgeIteratorState has getters which throw Exceptions.
         // http://stackoverflow.com/questions/35359430/how-to-make-jackson-ignore-properties-if-the-getters-throw-exceptions
         bootstrap.getObjectMapper().registerModule(new SimpleModule().setSerializerModifier(new BeanSerializerModifier() {
diff --git a/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java b/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java
index 29db6d5e1..b98b46a7a 100644
--- a/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java
+++ b/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java
@@ -22,8 +22,10 @@ import com.graphhopper.http.cli.ImportCommand;
 import com.graphhopper.http.resources.RootResource;
 import io.dropwizard.Application;
 import io.dropwizard.bundles.assets.ConfiguredAssetsBundle;
+import io.dropwizard.client.HttpClientBuilder;
 import io.dropwizard.setup.Bootstrap;
 import io.dropwizard.setup.Environment;
+import org.apache.http.client.HttpClient;
 
 import javax.servlet.DispatcherType;
 import java.util.EnumSet;


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java b/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
index c066e9df9..b578638ca 100644
--- a/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
@@ -19,12 +19,16 @@ package com.graphhopper.routing;
 
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.*;
+import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.RAMDirectory;
+import com.graphhopper.storage.TurnCostExtension;
 import com.graphhopper.util.Parameters;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 public class AStarBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTester {
@@ -37,24 +41,20 @@ public class AStarBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTester {
 
     @Override
     protected CHGraph getGraph(GraphHopperStorage ghStorage, Weighting weighting) {
-        return ghStorage.getCHGraph(CHProfile.edgeBased(weighting));
+        return ghStorage.getCHGraph(weighting);
     }
 
     @Override
     protected GraphHopperStorage createGHStorage(
             EncodingManager em, List<? extends Weighting> weightings, boolean is3D) {
-        List<CHProfile> chProfiles = new ArrayList<>(weightings.size());
-        for (Weighting w : weightings) {
-            chProfiles.add(CHProfile.edgeBased(w));
-        }
-        return new GraphHopperStorage(chProfiles, new RAMDirectory(), em, is3D, new TurnCostExtension()).create(1000);
+        return new GraphHopperStorage(Collections.<Weighting>emptyList(), weightings, new RAMDirectory(), em, is3D, new TurnCostExtension()).create(1000);
     }
 
     @Override
     public RoutingAlgorithmFactory createFactory(GraphHopperStorage ghStorage, AlgorithmOptions opts) {
         ghStorage.freeze();
         PrepareContractionHierarchies ch = PrepareContractionHierarchies.fromGraphHopperStorage(
-                ghStorage, CHProfile.edgeBased(opts.getWeighting()));
+                ghStorage, opts.getWeighting(), TraversalMode.EDGE_BASED);
         ch.doWork();
         return ch;
     }
diff --git a/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java b/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java
index 7e79aa6dd..1c1b63a3a 100644
--- a/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java
@@ -34,7 +34,6 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -94,7 +93,6 @@ public class AStarBidirectionTest extends AbstractRoutingAlgorithmTester {
                 toRef.set(currTo);
             }
         };
-        astar.createAndInitPath();
         astar.init(0, 1, 1, 0.5);
 
         assertEquals(1, ((AStar.AStarEntry) fromRef.get()).weightOfVisitedPath, .1);
diff --git a/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java b/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
index 7985c2f80..584dcbdf7 100644
--- a/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
+++ b/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
@@ -37,8 +37,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
-import static com.graphhopper.util.Helper.DIST_EARTH;
 import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
 import static org.junit.Assert.*;
 
@@ -47,6 +45,7 @@ import static org.junit.Assert.*;
  */
 public abstract class AbstractRoutingAlgorithmTester {
     protected static final EncodingManager encodingManager = EncodingManager.create("car,foot");
+    private static final DistanceCalc distCalc = new DistanceCalcEarth();
     protected FlagEncoder carEncoder;
     protected DecimalEncodedValue carAvSpeedEnc;
     protected BooleanEncodedValue carAccessEnc;
@@ -87,6 +86,16 @@ public abstract class AbstractRoutingAlgorithmTester {
         return graph;
     }
 
+    public static void updateDistancesFor(Graph g, int node, double lat, double lon) {
+        NodeAccess na = g.getNodeAccess();
+        na.setNode(node, lat, lon);
+        EdgeIterator iter = g.createEdgeExplorer().setBaseNode(node);
+        while (iter.next()) {
+            iter.setDistance(iter.fetchWayGeometry(3).calcDistance(distCalc));
+            // System.out.println(node + "->" + adj + ": " + iter.getDistance());
+        }
+    }
+
     protected static GraphHopperStorage createMatrixAlikeGraph(GraphHopperStorage tmpGraph) {
         int WIDTH = 10;
         int HEIGHT = 15;
@@ -229,7 +238,8 @@ public abstract class AbstractRoutingAlgorithmTester {
     public void testCalcFastestPath() {
         GraphHopperStorage graphShortest = createGHStorage(false);
         initDirectedAndDiffSpeed(graphShortest, carEncoder);
-        Path p1 = createAlgo(graphShortest, defaultOpts).calcPath(0, 3);
+        Path p1 = createAlgo(graphShortest, defaultOpts).
+                calcPath(0, 3);
         assertEquals(IntArrayList.from(0, 1, 5, 2, 3), p1.calcNodes());
         assertEquals(p1.toString(), 402.3, p1.getDistance(), .1);
         assertEquals(p1.toString(), 144823, p1.getTime());
@@ -757,7 +767,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         res.setClosestEdge(edge);
         res.setWayIndex(0);
         res.setSnappedPosition(QueryResult.Position.EDGE);
-        res.calcSnappedPoint(DIST_EARTH);
+        res.calcSnappedPoint(distCalc);
         return res;
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
index fcb632b54..3da0516de 100644
--- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
@@ -35,7 +35,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
+import static com.graphhopper.routing.AbstractRoutingAlgorithmTester.updateDistancesFor;
 import static org.junit.Assert.*;
 
 @RunWith(Parameterized.class)
diff --git a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
index 73c408c10..af69fd51c 100644
--- a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
+++ b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
@@ -54,7 +54,7 @@ public class CHQueryWithTurnCostsTest {
     private final FlagEncoder encoder = new MotorcycleFlagEncoder(5, 5, maxCost);
     private final EncodingManager encodingManager = EncodingManager.create(encoder);
     private final Weighting weighting = new ShortestWeighting(encoder);
-    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
+    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
     private final TurnCostExtension turnCostExtension = (TurnCostExtension) graph.getExtension();
     private final CHGraph chGraph = graph.getCHGraph();
     private String algoString;
@@ -718,9 +718,10 @@ public class CHQueryWithTurnCostsTest {
     }
 
     private void addShortcut(int from, int to, int firstOrigEdge, int lastOrigEdge, int skipped1, int skipped2, double weight) {
-        // todo: not using distance atm
-        double distance = 0;
-        chGraph.shortcutEdgeBased(from, to, PrepareEncoder.getScFwdDir(), weight, distance, skipped1, skipped2, firstOrigEdge, lastOrigEdge);
+        CHEdgeIteratorState shortcut = chGraph.shortcut(from, to);
+        // we need to set flags first because they overwrite weight etc
+        shortcut.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), weight);
+        shortcut.setFirstAndLastOrigEdges(firstOrigEdge, lastOrigEdge).setSkippedEdges(skipped1, skipped2);
     }
 
     private void setLevelEqualToNodeIdForAllNodes() {
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
index 73b486abb..51b639a74 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
@@ -18,6 +18,7 @@
 package com.graphhopper.routing;
 
 import com.carrotsearch.hppc.IntArrayList;
+import com.graphhopper.routing.ch.NodeBasedNodeContractorTest;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
@@ -46,7 +47,7 @@ import static org.junit.Assert.assertEquals;
 public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
     @Override
     protected CHGraph getGraph(GraphHopperStorage ghStorage, Weighting weighting) {
-        return ghStorage.getCHGraph(CHProfile.nodeBased(weighting));
+        return ghStorage.getCHGraph(weighting);
     }
 
     @Override
@@ -60,7 +61,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
     public RoutingAlgorithmFactory createFactory(GraphHopperStorage ghStorage, AlgorithmOptions opts) {
         ghStorage.freeze();
         PrepareContractionHierarchies ch = PrepareContractionHierarchies.fromGraphHopperStorage(
-                ghStorage, CHProfile.nodeBased(opts.getWeighting()));
+                ghStorage, opts.getWeighting(), TraversalMode.NODE_BASED);
         ch.doWork();
         return ch;
     }
@@ -72,7 +73,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         EncodingManager em = EncodingManager.create(encoder);
         ShortestWeighting weighting = new ShortestWeighting(encoder);
         GraphHopperStorage ghStorage = createGHStorage(em, Arrays.asList(weighting), false);
-        CHGraph g2 = ghStorage.getCHGraph();
+        CHGraph g2 = ghStorage.getCHGraph(weighting);
         g2.edge(0, 1, 1, true);
         EdgeIteratorState iter1_1 = g2.edge(0, 2, 1.4, false);
         EdgeIteratorState iter1_2 = g2.edge(2, 5, 1.4, false);
@@ -90,8 +91,14 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
 
         ghStorage.freeze();
         // simulate preparation
-        int sc2_1 = g2.shortcut(0, 5, PrepareEncoder.getScFwdDir(), 1, 2.8, iter1_1.getEdge(), iter1_2.getEdge());
-        g2.shortcut(0, 7, PrepareEncoder.getScFwdDir(), 1, 4.2, sc2_1, iter2_2.getEdge());
+        CHEdgeIteratorState iter2_1 = g2.shortcut(0, 5);
+        iter2_1.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 1);
+        iter2_1.setDistance(2.8);
+        iter2_1.setSkippedEdges(iter1_1.getEdge(), iter1_2.getEdge());
+        CHEdgeIteratorState tmp = g2.shortcut(0, 7);
+        tmp.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 1);
+        tmp.setDistance(4.2);
+        tmp.setSkippedEdges(iter2_1.getEdge(), iter2_2.getEdge());
         g2.setLevel(1, 0);
         g2.setLevel(3, 1);
         g2.setLevel(4, 2);
@@ -102,7 +109,9 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         g2.setLevel(0, 7);
 
         AlgorithmOptions opts = new AlgorithmOptions(Parameters.Algorithms.DIJKSTRA_BI, weighting);
-        Path p = new PrepareContractionHierarchies(g2).createAlgo(g2, opts).calcPath(0, 7);
+        Path p = new PrepareContractionHierarchies(
+                g2, weighting, TraversalMode.NODE_BASED).
+                createAlgo(g2, opts).calcPath(0, 7);
 
         assertEquals(IntArrayList.from(0, 2, 5, 7), p.calcNodes());
         assertEquals(1064, p.getTime());
@@ -194,7 +203,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
             chGraph.setLevel(i, i);
         }
         graph.freeze();
-        RoutingAlgorithm algo = createCHAlgo(chGraph, true, defaultOpts);
+        RoutingAlgorithm algo = createCHAlgo(graph, chGraph, true, defaultOpts);
         Path p = algo.calcPath(1, 0);
         // node 3 will be stalled and nodes 4-7 won't be explored --> we visit 7 nodes
         // note that node 9 will be visited by both forward and backward searches
@@ -203,7 +212,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         assertEquals(p.toString(), IntArrayList.from(1, 8, 9, 0), p.calcNodes());
 
         // without stalling we visit 11 nodes
-        RoutingAlgorithm algoNoSod = createCHAlgo(chGraph, false, defaultOpts);
+        RoutingAlgorithm algoNoSod = createCHAlgo(graph, chGraph, false, defaultOpts);
         Path pNoSod = algoNoSod.calcPath(1, 0);
         assertEquals(11, algoNoSod.getVisitedNodes());
         assertEquals(102, pNoSod.getDistance(), 1.e-3);
@@ -247,14 +256,15 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         }
         graph.freeze();
 
-        RoutingAlgorithm algo = createCHAlgo(chGraph, true, algoOpts);
+        RoutingAlgorithm algo = createCHAlgo(graph, chGraph, true, algoOpts);
         Path p = algo.calcPath(from, to);
         assertEquals(3, p.getDistance(), 1.e-3);
         assertEquals(p.toString(), expectedPath, p.calcNodes());
     }
 
-    private RoutingAlgorithm createCHAlgo(CHGraph chGraph, boolean withSOD, AlgorithmOptions algorithmOptions) {
-        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph);
+    private RoutingAlgorithm createCHAlgo(GraphHopperStorage graph, CHGraph chGraph, boolean withSOD, AlgorithmOptions algorithmOptions) {
+        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(
+                chGraph, algorithmOptions.getWeighting(), TraversalMode.NODE_BASED);
         if (!withSOD) {
             algorithmOptions.getHints().put("stall_on_demand", false);
         }
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
index c9e46f14a..5dc139b0e 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
@@ -19,33 +19,31 @@ package com.graphhopper.routing;
 
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 public class DijkstraBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTester {
     @Override
     protected CHGraph getGraph(GraphHopperStorage ghStorage, Weighting weighting) {
-        return ghStorage.getCHGraph(CHProfile.edgeBased(weighting));
+        return ghStorage.getCHGraph(weighting);
     }
 
     @Override
     protected GraphHopperStorage createGHStorage(
             EncodingManager em, List<? extends Weighting> weightings, boolean is3D) {
-        List<CHProfile> chProfiles = new ArrayList<>(weightings.size());
-        for (Weighting w : weightings) {
-            chProfiles.add(CHProfile.edgeBased(w));
-        }
-        return new GraphHopperStorage(chProfiles, new RAMDirectory(), em, is3D, new TurnCostExtension()).create(1000);
+        return new GraphHopperStorage(Collections.<Weighting>emptyList(), weightings, new RAMDirectory(),
+                em, is3D, new TurnCostExtension()).create(1000);
     }
 
     @Override
     public RoutingAlgorithmFactory createFactory(GraphHopperStorage ghStorage, AlgorithmOptions opts) {
         ghStorage.freeze();
         PrepareContractionHierarchies ch = PrepareContractionHierarchies.fromGraphHopperStorage(
-                ghStorage, CHProfile.edgeBased(opts.getWeighting()));
+                ghStorage, opts.getWeighting(), TraversalMode.EDGE_BASED);
         ch.doWork();
         return ch;
     }
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
index 01545fe46..158867463 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
@@ -21,7 +21,6 @@ import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.FastestWeighting;
-import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
@@ -173,7 +172,7 @@ public class DijkstraOneToManyTest extends AbstractRoutingAlgorithmTester {
 
     @Test
     public void testDifferentEdgeFilter() {
-        GraphHopperStorage g = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(new FastestWeighting(carEncoder))).create();
+        GraphHopperStorage g = new GraphBuilder(encodingManager).setCHGraph(new FastestWeighting(carEncoder)).create();
         g.edge(4, 3, 10, true);
         g.edge(3, 6, 10, true);
 
diff --git a/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java b/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java
deleted file mode 100644
index 0a8e28613..000000000
--- a/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java
+++ /dev/null
@@ -1,564 +0,0 @@
-package com.graphhopper.routing;
-
-import com.carrotsearch.hppc.IntArrayList;
-import com.graphhopper.Repeat;
-import com.graphhopper.RepeatRule;
-import com.graphhopper.routing.util.*;
-import com.graphhopper.routing.weighting.*;
-import com.graphhopper.storage.*;
-import com.graphhopper.storage.index.LocationIndex;
-import com.graphhopper.storage.index.LocationIndexTree;
-import com.graphhopper.storage.index.QueryResult;
-import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.GHUtility;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Random;
-
-import static com.graphhopper.util.EdgeIterator.ANY_EDGE;
-import static com.graphhopper.util.EdgeIterator.NO_EDGE;
-import static org.junit.Assert.*;
-import static org.junit.Assert.assertEquals;
-
-/**
- * This test makes sure that {@link DijkstraBidirectionRef#calcPath(int from, int to, int fromOutEdge, int toInEdge)}, i.e.
- * calculating a path with restricted/enforced first/last edges works as expected.
- * <p>
- * For other bidirectional algorithms we simply compare with {@link DijkstraBidirectionRef} in {@link DirectedRoutingTest}
- */
-public class DirectedBidirectionalDijkstraTest {
-    private Directory dir;
-    private TurnCostExtension turnCostExtension;
-    private int maxTurnCosts;
-    private GraphHopperStorage graph;
-    private FlagEncoder encoder;
-    private EncodingManager encodingManager;
-    private Weighting weighting;
-    private LocationIndex locationIndex;
-    private QueryGraph queryGraph;
-
-    @Rule
-    public RepeatRule repeatRule = new RepeatRule();
-
-    @Before
-    public void setup() {
-        dir = new RAMDirectory();
-        maxTurnCosts = 10;
-        encoder = new CarFlagEncoder(5, 5, maxTurnCosts);
-        encodingManager = EncodingManager.create(encoder);
-        turnCostExtension = new TurnCostExtension();
-        graph = new GraphHopperStorage(dir, encodingManager, false, turnCostExtension).create(1000);
-        weighting = createWeighting(Double.POSITIVE_INFINITY);
-    }
-
-    private Weighting createWeighting(double defaultUTurnCosts) {
-        return new TurnWeighting(new FastestWeighting(encoder), turnCostExtension, defaultUTurnCosts);
-    }
-
-    @Test
-    public void connectionNotFound() {
-        // nodes 0 and 2 are not connected
-        // 0 -> 1     2 -> 3
-        graph.edge(0, 1, 1, false);
-        graph.edge(2, 3, 1, false);
-
-        Path path = calcPath(0, 3, 0, 1);
-        assertNotFound(path);
-    }
-
-    @Test
-    public void singleEdge() {
-        graph.edge(0, 1, 1, true);
-
-        // source edge does not exist -> no path
-        assertNotFound(calcPath(0, 1, 5, 0));
-        // target edge does not exist -> no path
-        assertNotFound(calcPath(0, 1, 0, 5));
-        // using NO_EDGE -> no path
-        assertNotFound(calcPath(0, 1, NO_EDGE, 0));
-        assertNotFound(calcPath(0, 1, 0, NO_EDGE));
-        // using ANY_EDGE -> no restriction
-        assertPath(calcPath(0, 1, ANY_EDGE, 0), 0.06, 1, 60, nodes(0, 1));
-        assertPath(calcPath(0, 1, 0, ANY_EDGE), 0.06, 1, 60, nodes(0, 1));
-        // edges exist -> they are used as restrictions
-        assertPath(calcPath(0, 1, 0, 0), 0.06, 1, 60, nodes(0, 1));
-    }
-
-    @Test
-    public void simpleGraph() {
-        // 0 -> 1 -> 2
-        graph.edge(0, 1, 1, true);
-        graph.edge(1, 2, 1, true);
-
-        // source edge does not exist -> no path
-        assertNotFound(calcPath(0, 2, 5, 0));
-        // target edge does not exist -> no path
-        assertNotFound(calcPath(0, 2, 0, 5));
-        // using NO_EDGE -> no  path
-        assertNotFound(calcPath(0, 2, NO_EDGE, 0));
-        assertNotFound(calcPath(0, 2, 0, NO_EDGE));
-        // using ANY_EDGE -> no restriction
-        assertPath(calcPath(0, 2, ANY_EDGE, 1), 0.12, 2, 120, nodes(0, 1, 2));
-        assertPath(calcPath(0, 2, 0, ANY_EDGE), 0.12, 2, 120, nodes(0, 1, 2));
-        // edges exist -> they are used as restrictions
-        assertPath(calcPath(0, 2, 0, 1), 0.12, 2, 120, nodes(0, 1, 2));
-    }
-
-    @Test
-    public void sourceEqualsTarget() {
-        // Since we are enforcing source/target edges, starting and arriving at the same node normally does not yield
-        // zero weight. Either the weight is finite (using a real path back to the source node) or the
-        // weight is infinite (when no such path exists). The exception of course would involve zero weight edges.
-        // 0 - 1
-        //  \  |
-        //   - 2
-        graph.edge(0, 1, 1, true);
-        graph.edge(0, 2, 1, true);
-        graph.edge(1, 2, 1, true);
-        assertPath(calcPath(0, 0, 0, 1), 0.18, 3, 180, nodes(0, 1, 2, 0));
-        assertPath(calcPath(0, 0, 1, 0), 0.18, 3, 180, nodes(0, 2, 1, 0));
-        // without restrictions the weight should be zero
-        assertPath(calcPath(0, 0, ANY_EDGE, ANY_EDGE), 0, 0, 0, nodes(0));
-        // in some cases no path is possible
-        assertNotFound(calcPath(0, 0, 1, 1));
-        assertNotFound(calcPath(0, 0, 5, 1));
-    }
-
-    @Test
-    public void restrictedEdges() {
-        // =: costly edge
-        // 0 = 1 - 2 - 3 = 4
-        //  \      |      /
-        //   - 5 - 6 - 7 -
-        int costlySource = graph.edge(0, 1, 5, true).getEdge();
-        graph.edge(1, 2, 1, true);
-        graph.edge(2, 3, 1, true);
-        int costlyTarget = graph.edge(3, 4, 5, true).getEdge();
-        int cheapSource = graph.edge(0, 5, 1, true).getEdge();
-        graph.edge(5, 6, 1, true);
-        graph.edge(6, 7, 1, true);
-        int cheapTarget = graph.edge(7, 4, 1, true).getEdge();
-        graph.edge(2, 6, 1, true);
-
-        assertPath(calcPath(0, 4, cheapSource, cheapTarget), 0.24, 4, 240, nodes(0, 5, 6, 7, 4));
-        assertPath(calcPath(0, 4, cheapSource, costlyTarget), 0.54, 9, 540, nodes(0, 5, 6, 2, 3, 4));
-        assertPath(calcPath(0, 4, costlySource, cheapTarget), 0.54, 9, 540, nodes(0, 1, 2, 6, 7, 4));
-        assertPath(calcPath(0, 4, costlySource, costlyTarget), 0.72, 12, 720, nodes(0, 1, 2, 3, 4));
-    }
-
-    @Test
-    public void notConnectedDueToRestrictions() {
-        //   - 1 -
-        //  /     \
-        // 0       2
-        //  \     /
-        //   - 3 -
-        // we cannot go from 0 to 2 if we enforce north-south or south-north
-        int sourceNorth = graph.edge(0, 1, 1, true).getEdge();
-        int sourceSouth = graph.edge(0, 3, 2, true).getEdge();
-        int targetNorth = graph.edge(1, 2, 3, true).getEdge();
-        int targetSouth = graph.edge(3, 2, 4, true).getEdge();
-
-        assertPath(calcPath(0, 2, sourceNorth, targetNorth), 0.24, 4, 240, nodes(0, 1, 2));
-        assertNotFound(calcPath(0, 2, sourceNorth, targetSouth));
-        assertNotFound(calcPath(0, 2, sourceSouth, targetNorth));
-        assertPath(calcPath(0, 2, sourceSouth, targetSouth), 0.36, 6, 360, nodes(0, 3, 2));
-    }
-
-    @Test
-    public void restrictions_one_ways() {
-        // 0 <- 1 <- 2
-        //  \   |   /
-        //   >--3-->
-        graph.edge(0, 3, 1, false);
-        graph.edge(1, 0, 1, false);
-        graph.edge(3, 2, 1, false);
-        graph.edge(2, 1, 1, false);
-        graph.edge(1, 3, 1, true);
-
-        assertPath(calcPath(0, 2, 0, 2), 0.12, 2, 120, nodes(0, 3, 2));
-        assertNotFound(calcPath(0, 2, 1, 2));
-        assertNotFound(calcPath(0, 2, 0, 3));
-        assertNotFound(calcPath(0, 2, 1, 3));
-    }
-
-    @Test
-    public void forcingDirectionDoesNotMeanWeCannotUseEdgeAtAll() {
-        // 0 - 6 - 7
-        // |
-        // 1
-        // |
-        // 2 - 3
-        // |   |
-        // 5 - 4
-        int north = graph.edge(1, 0, 1, true).getEdge();
-        int south = graph.edge(1, 2, 1, true).getEdge();
-        graph.edge(2, 5, 1, false);
-        graph.edge(5, 4, 1, false);
-        graph.edge(4, 3, 1, false);
-        graph.edge(3, 2, 1, false);
-        graph.edge(1, 0, 1, false);
-        graph.edge(0, 6, 1, false);
-        int targetEdge = graph.edge(6, 7, 1, false).getEdge();
-        assertPath(calcPath(1, 7, north, targetEdge), 0.18, 3, 180, nodes(1, 0, 6, 7));
-        assertPath(calcPath(1, 7, south, targetEdge), 0.54, 9, 540, nodes(1, 2, 5, 4, 3, 2, 1, 0, 6, 7));
-    }
-
-    @Test
-    public void directedCircle() {
-        // 0---6--1 -> 2
-        // |          /
-        // 5 <- 4 <- 3
-        graph.edge(0, 6, 1, true);
-        graph.edge(6, 1, 1, true);
-        graph.edge(1, 2, 1, false);
-        graph.edge(2, 3, 1, false);
-        graph.edge(3, 4, 1, false);
-        graph.edge(4, 5, 1, false);
-        graph.edge(5, 0, 1, false);
-        assertPath(calcPath(6, 0, 1, 6), 0.36, 6, 360, nodes(6, 1, 2, 3, 4, 5, 0));
-    }
-
-    @Test
-    public void directedRouting() {
-        // =: costly edge
-        //   0 - 1   3 = 4
-        //   |    \ /    |
-        //   9     2     10
-        //   |    / \    |
-        //   8 = 7   6 = 5
-        graph.edge(0, 1, 1, true);
-        graph.edge(1, 2, 1, true);
-        graph.edge(2, 3, 1, true);
-        graph.edge(3, 4, 3, true);
-        int rightNorth = graph.edge(4, 10, 1, true).getEdge();
-        int rightSouth = graph.edge(10, 5, 1, true).getEdge();
-        graph.edge(5, 6, 2, true);
-        graph.edge(6, 2, 1, true);
-        graph.edge(2, 7, 1, true);
-        graph.edge(7, 8, 9, true);
-        int leftSouth = graph.edge(8, 9, 1, true).getEdge();
-        int leftNorth = graph.edge(9, 0, 1, true).getEdge();
-
-        // make paths fully deterministic by applying some turn costs at junction node 2
-        addTurnCost(7, 2, 3, 1);
-        addTurnCost(7, 2, 6, 3);
-        addTurnCost(1, 2, 3, 5);
-        addTurnCost(1, 2, 6, 7);
-        addTurnCost(1, 2, 7, 9);
-
-        final double unitEdgeWeight = 0.06;
-        assertPath(calcPath(9, 9, leftNorth, leftSouth),
-                23 * unitEdgeWeight + 5, 23, (long) ((23 * unitEdgeWeight + 5) * 1000),
-                nodes(9, 0, 1, 2, 3, 4, 10, 5, 6, 2, 7, 8, 9));
-        assertPath(calcPath(9, 9, leftSouth, leftNorth),
-                14 * unitEdgeWeight, 14, (long) ((14 * unitEdgeWeight) * 1000),
-                nodes(9, 8, 7, 2, 1, 0, 9));
-        assertPath(calcPath(9, 10, leftSouth, rightSouth),
-                15 * unitEdgeWeight + 3, 15, (long) ((15 * unitEdgeWeight + 3) * 1000),
-                nodes(9, 8, 7, 2, 6, 5, 10));
-        assertPath(calcPath(9, 10, leftSouth, rightNorth),
-                16 * unitEdgeWeight + 1, 16, (long) ((16 * unitEdgeWeight + 1) * 1000),
-                nodes(9, 8, 7, 2, 3, 4, 10));
-    }
-
-    @Test
-    public void enforceLoopEdge() {
-        //  o       o
-        //  0 - 1 - 2
-        graph.edge(0, 0, 1, true);
-        graph.edge(0, 1, 1, true);
-        graph.edge(1, 2, 1, true);
-        graph.edge(2, 2, 1, true);
-
-        assertPath(calcPath(0, 2, ANY_EDGE, ANY_EDGE), 0.12, 2, 120, nodes(0, 1, 2));
-        assertPath(calcPath(0, 2, 1, 2), 0.12, 2, 120, nodes(0, 1, 2));
-        // we can enforce taking the loop at start/target
-        assertPath(calcPath(0, 2, 0, 2), 0.18, 3, 180, nodes(0, 0, 1, 2));
-        assertPath(calcPath(0, 2, 1, 3), 0.18, 3, 180, nodes(0, 1, 2, 2));
-        assertPath(calcPath(0, 2, 0, 3), 0.24, 4, 240, nodes(0, 0, 1, 2, 2));
-    }
-
-    @Test
-    public void sourceAndTargetAreNeighbors() {
-        // 0-1-2-3
-        graph.edge(0, 1, 100, true);
-        graph.edge(1, 2, 100, true);
-        graph.edge(2, 3, 100, true);
-        assertPath(calcPath(1, 2, ANY_EDGE, ANY_EDGE), 6, 100, 6000, nodes(1, 2));
-        assertPath(calcPath(1, 2, 1, ANY_EDGE), 6, 100, 6000, nodes(1, 2));
-        assertPath(calcPath(1, 2, ANY_EDGE, 1), 6, 100, 6000, nodes(1, 2));
-        assertPath(calcPath(1, 2, 1, 1), 6, 100, 6000, nodes(1, 2));
-        // this case is a bit sketchy: we may not find a valid path just because the from/to
-        // node initialization hits the target/source node, but we have to also consider the
-        // edge restriction at the source/target nodes
-        assertNotFound(calcPath(1, 2, 1, 2));
-        assertNotFound(calcPath(1, 2, 0, 1));
-        assertNotFound(calcPath(1, 2, 0, 2));
-
-        // if we allow u-turns it is of course different again
-        assertPath(calcPath(1, 2, 1, 2, createWeighting(100)), 118, 300, 118000, nodes(1, 2, 3, 2));
-        assertPath(calcPath(1, 2, 0, 1, createWeighting(100)), 118, 300, 118000, nodes(1, 0, 1, 2));
-        assertPath(calcPath(1, 2, 0, 2, createWeighting(100)), 230, 500, 230000, nodes(1, 0, 1, 2, 3, 2));
-    }
-
-    @Test
-    public void worksWithTurnCosts() {
-        // 0 - 1 - 2
-        // |   |   |
-        // 3 - 4 - 5
-        graph.edge(0, 1, 1, true);
-        graph.edge(1, 2, 1, true);
-        graph.edge(1, 4, 1, true);
-        graph.edge(0, 3, 1, true);
-        graph.edge(3, 4, 1, true);
-        graph.edge(4, 5, 1, true);
-        graph.edge(5, 2, 1, true);
-
-        addRestriction(0, 3, 4);
-        addTurnCost(4, 5, 2, 6);
-
-        // due to the restrictions we have to take the expensive path with turn costs
-        assertPath(calcPath(0, 2, 0, 6), 6.24, 4, 6240, nodes(0, 1, 4, 5, 2));
-        // enforcing going south from node 0 yields no path, because of the restricted turn 0->3->4
-        assertNotFound(calcPath(0, 2, 3, ANY_EDGE));
-        // without the restriction its possible
-        assertPath(calcPath(0, 2, ANY_EDGE, ANY_EDGE), 0.12, 2, 120, nodes(0, 1, 2));
-    }
-
-    @Test
-    public void finiteUTurnCosts() {
-        // = expensive edge
-        //      3 - 4
-        //      |   |
-        //      2 = 5
-        //      |
-        // 0 -- 1 -- 6
-        // |         |
-        // 7 -- 8 -- 9
-        int right0 = graph.edge(0, 1, 10, true).getEdge();
-        graph.edge(1, 2, 10, true);
-        graph.edge(2, 3, 10, true);
-        graph.edge(3, 4, 10, true);
-        graph.edge(4, 5, 10, true);
-        graph.edge(5, 2, 1000, true);
-        int left6 = graph.edge(1, 6, 10, true).getEdge();
-        int left0 = graph.edge(0, 7, 10, true).getEdge();
-        graph.edge(7, 8, 10, true);
-        graph.edge(8, 9, 10, true);
-        int right6 = graph.edge(9, 6, 10, true).getEdge();
-
-        // enforce p-turn (using the loop in clockwise direction)
-        addRestriction(0, 1, 6);
-        addRestriction(5, 4, 3);
-
-        assertPath(calcPath(0, 6, right0, left6), 64.2, 1070, 64200, nodes(0, 1, 2, 3, 4, 5, 2, 1, 6));
-        // if the u-turn cost is finite it depends on its value if we rather do the p-turn or do an immediate u-turn at node 2
-        assertPath(calcPath(0, 6, right0, left6, createWeighting(65)), 64.2, 1070, 64200, nodes(0, 1, 2, 3, 4, 5, 2, 1, 6));
-        assertPath(calcPath(0, 6, right0, left6, createWeighting(40)), 42.4, 40, 42400, nodes(0, 1, 2, 1, 6));
-
-        assertPath(calcPath(0, 6, left0, right6), 2.4, 40, 2400, nodes(0, 7, 8, 9, 6));
-        assertPath(calcPath(0, 6, left0, left6), 66.6, 1110, 66600, nodes(0, 7, 8, 9, 6, 1, 2, 3, 4, 5, 2, 1, 6));
-        // if the u-turn cost is finite we do a u-turn at node 1 (not at node 7 at the beginning!)
-        assertPath(calcPath(0, 6, left0, left6, createWeighting(40)), 43.6, 60, 43600, nodes(0, 7, 8, 9, 6, 1, 6));
-    }
-
-    @Test
-    @Repeat(times = 10)
-    public void compare_standard_dijkstra() {
-        compare_with_dijkstra(weighting);
-    }
-
-    @Test
-    @Repeat(times = 10)
-    public void compare_standard_dijkstra_finite_uturn_costs() {
-        compare_with_dijkstra(createWeighting(40));
-    }
-
-    private void compare_with_dijkstra(Weighting w) {
-        // if we do not use start/target edge restrictions we should get the same result as with Dijkstra.
-        // basically this test should cover all kinds of interesting cases except the ones where we restrict the
-        // start/target edges.
-        final long seed = System.nanoTime();
-        final int numQueries = 1000;
-        System.out.println("compare_standard_dijkstra seed: " + seed);
-
-        Random rnd = new Random(seed);
-        int numNodes = 100;
-        GHUtility.buildRandomGraph(graph, rnd, numNodes, 2.2, true, true, encoder.getAverageSpeedEnc(), 0.7, 0.8, 0.8);
-        GHUtility.addRandomTurnCosts(graph, seed, encoder, maxTurnCosts, turnCostExtension);
-
-        long numStrictViolations = 0;
-        for (int i = 0; i < numQueries; i++) {
-            int source = rnd.nextInt(numNodes);
-            int target = rnd.nextInt(numNodes);
-            Path dijkstraPath = new Dijkstra(graph, w, TraversalMode.EDGE_BASED).calcPath(source, target);
-            Path path = calcPath(source, target, ANY_EDGE, ANY_EDGE, w);
-            assertEquals("dijkstra found/did not find a path, from: " + source + ", to: " + target, dijkstraPath.isFound(), path.isFound());
-            assertEquals("weight does not match dijkstra, from: " + source + ", to: " + target, dijkstraPath.getWeight(), path.getWeight(), 1.e-6);
-            // we do not do a strict check because there can be ambiguity, for example when there are zero weight loops.
-            // however, when there are too many deviations we fail
-            if (
-                    Math.abs(dijkstraPath.getDistance() - path.getDistance()) > 1.e-6
-                            || Math.abs(dijkstraPath.getTime() - path.getTime()) > 10
-                            || !dijkstraPath.calcNodes().equals(path.calcNodes())) {
-                numStrictViolations++;
-            }
-        }
-        if (numStrictViolations > Math.max(1, 0.05 * numQueries)) {
-            fail("Too many strict violations: " + numStrictViolations + " / " + numQueries);
-        }
-    }
-
-    @Test
-    public void blockArea() {
-        // 0 - 1 - 2 - 3
-        // |           |
-        // 4 --- 5 --- 6
-        EdgeIteratorState edge1 = graph.edge(0, 1, 10, true);
-        graph.edge(1, 2, 10, true);
-        EdgeIteratorState edge2 = graph.edge(2, 3, 10, true);
-        graph.edge(0, 4, 100, true);
-        graph.edge(4, 5, 100, true);
-        graph.edge(5, 6, 100, true);
-        graph.edge(6, 3, 100, true);
-
-        // usually we would take the direct route
-        assertPath(calcPath(0, 3, ANY_EDGE, ANY_EDGE), 1.8, 30, 1800, nodes(0, 1, 2, 3));
-
-        // with forced edges we might have to go around
-        assertPath(calcPath(0, 3, 3, ANY_EDGE), 24, 400, 24000, nodes(0, 4, 5, 6, 3));
-        assertPath(calcPath(0, 3, ANY_EDGE, 6), 24, 400, 24000, nodes(0, 4, 5, 6, 3));
-
-        // with avoided edges we also have to take a longer route
-        assertPath(calcPath(0, 3, ANY_EDGE, ANY_EDGE, createAvoidEdgeWeighting(edge1)), 24, 400, 24000, nodes(0, 4, 5, 6, 3));
-        assertPath(calcPath(0, 3, ANY_EDGE, ANY_EDGE, createAvoidEdgeWeighting(edge2)), 24, 400, 24000, nodes(0, 4, 5, 6, 3));
-
-        // enforcing forbidden start/target edges still does not allow using them
-        assertNotFound(calcPath(0, 3, edge1.getEdge(), edge2.getEdge(), createAvoidEdgeWeighting(edge1)));
-        assertNotFound(calcPath(0, 3, edge1.getEdge(), edge2.getEdge(), createAvoidEdgeWeighting(edge2)));
-
-        // .. even when the nodes are just next to each other
-        assertNotFound(calcPath(0, 1, edge1.getEdge(), ANY_EDGE, createAvoidEdgeWeighting(edge1)));
-        assertNotFound(calcPath(0, 1, ANY_EDGE, edge2.getEdge(), createAvoidEdgeWeighting(edge2)));
-    }
-
-    private AvoidEdgesWeighting createAvoidEdgeWeighting(EdgeIteratorState edgeOut) {
-        AvoidEdgesWeighting avoidEdgesWeighting = new AvoidEdgesWeighting(weighting);
-        avoidEdgesWeighting.setEdgePenaltyFactor(Double.POSITIVE_INFINITY);
-        avoidEdgesWeighting.addEdges(Collections.singletonList(edgeOut));
-        return avoidEdgesWeighting;
-    }
-
-    @Test
-    public void directedRouting_noUTurnAtVirtualEdge() {
-        // what happens if we force to leave the snapped (virtual) node in eastern direction, even though we would
-        // like to go to node 0 just west from us ? we have to make sure there is no u-turn at node 1 (from the
-        // virtual edge onto edge 1-0). the query graph does this for us!
-
-        //    x
-        // 0 -- 1 -> 2
-        // |         |
-        // 5 <- 4 <- 3
-        graph.edge(0, 1, 1, true);
-        graph.edge(1, 2, 1, false);
-        graph.edge(2, 3, 1, false);
-        graph.edge(3, 4, 1, false);
-        graph.edge(4, 5, 1, false);
-        graph.edge(5, 0, 1, false);
-        NodeAccess na = graph.getNodeAccess();
-        na.setNode(0, 1, 0);
-        na.setNode(1, 1, 1);
-        na.setNode(2, 1, 2);
-        na.setNode(3, 0, 2);
-        na.setNode(4, 0, 1);
-        na.setNode(5, 0, 0);
-        initQueryGraph();
-
-        QueryResult qr = findClosest();
-        assertEquals("wanted to get EDGE", QueryResult.Position.EDGE, qr.getSnappedPosition());
-        assertEquals(6, qr.getClosestNode());
-
-        // check what edges there are on the query graph directly, there should not be a direct connection from 1 to 0
-        // anymore, but only the virtual edge from 1 to 6 (this is how the u-turn is prevented).
-        assertEquals(new HashSet<>(Arrays.asList(0, 2)), GHUtility.getNeighbors(graph.createEdgeExplorer().setBaseNode(1)));
-        assertEquals(new HashSet<>(Arrays.asList(6, 2)), GHUtility.getNeighbors(queryGraph.createEdgeExplorer().setBaseNode(1)));
-
-        EdgeIteratorState virtualEdge = GHUtility.getEdge(queryGraph, 6, 1);
-        int outEdge = virtualEdge.getEdge();
-        AbstractBidirAlgo algo = createAlgo(queryGraph, weighting);
-        Path path = algo.calcPath(6, 0, outEdge, ANY_EDGE);
-        assertEquals(nodes(6, 1, 2, 3, 4, 5, 0), path.calcNodes());
-        assertEquals(5 + virtualEdge.getDistance(), path.getDistance(), 1.e-3);
-    }
-
-    private void initQueryGraph() {
-        queryGraph = new QueryGraph(graph);
-        locationIndex = new LocationIndexTree(graph, dir);
-        locationIndex.prepareIndex();
-    }
-
-    private QueryResult findClosest() {
-        QueryResult qr = locationIndex.findClosest(1.1, 0.5, EdgeFilter.ALL_EDGES);
-        queryGraph.lookup(Collections.singletonList(qr));
-        return qr;
-    }
-
-    private Path calcPath(int source, int target, int sourceOutEdge, int targetInEdge) {
-        return calcPath(source, target, sourceOutEdge, targetInEdge, weighting);
-    }
-
-    private Path calcPath(int source, int target, int sourceOutEdge, int targetInEdge, Weighting w) {
-        AbstractBidirAlgo algo = createAlgo(graph, w);
-        return algo.calcPath(source, target, sourceOutEdge, targetInEdge);
-    }
-
-    private AbstractBidirAlgo createAlgo(Graph graph, Weighting weighting) {
-        return new DijkstraBidirectionRef(graph, weighting, TraversalMode.EDGE_BASED);
-    }
-
-    private void addRestriction(int fromNode, int node, int toNode) {
-        turnCostExtension.addTurnInfo(
-                GHUtility.getEdge(graph, fromNode, node).getEdge(),
-                node,
-                GHUtility.getEdge(graph, node, toNode).getEdge(),
-                encoder.getTurnFlags(true, 0)
-        );
-    }
-
-    private void addTurnCost(int fromNode, int node, int toNode, double turnCost) {
-        turnCostExtension.addTurnInfo(
-                GHUtility.getEdge(graph, fromNode, node).getEdge(),
-                node,
-                GHUtility.getEdge(graph, node, toNode).getEdge(),
-                encoder.getTurnFlags(false, turnCost)
-        );
-    }
-
-    private IntArrayList nodes(int... nodes) {
-        return IntArrayList.from(nodes);
-    }
-
-    private void assertPath(Path path, double weight, double distance, long time, IntArrayList nodes) {
-        assertTrue("expected a path, but no path was found", path.isFound());
-        assertEquals("unexpected weight", weight, path.getWeight(), 1.e-6);
-        assertEquals("unexpected distance", distance, path.getDistance(), 1.e-6);
-        assertEquals("unexpected time", time, path.getTime());
-        assertEquals("unexpected nodes", nodes, path.calcNodes());
-    }
-
-    private void assertNotFound(Path path) {
-        assertFalse("expected no path, but a path was found", path.isFound());
-        assertEquals(Double.MAX_VALUE, path.getWeight(), 1.e-6);
-        // if no path is found dist&time are zero, see core #1566
-        assertEquals(0, path.getDistance(), 1.e-6);
-        assertEquals(0, path.getTime());
-        assertEquals(nodes(), path.calcNodes());
-    }
-}
diff --git a/core/src/test/java/com/graphhopper/routing/QueryGraphTest.java b/core/src/test/java/com/graphhopper/routing/QueryGraphTest.java
index fe6561325..fda2a4854 100644
--- a/core/src/test/java/com/graphhopper/routing/QueryGraphTest.java
+++ b/core/src/test/java/com/graphhopper/routing/QueryGraphTest.java
@@ -36,7 +36,6 @@ import org.junit.Test;
 import java.util.*;
 
 import static com.graphhopper.storage.index.QueryResult.Position.*;
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.*;
 
 /**
@@ -297,10 +296,10 @@ public class QueryGraphTest {
         g.edge(1, 3, 10, true);
         g.edge(3, 4, 10, true);
         EdgeIteratorState edge = g.edge(1, 3, 20, true).setWayGeometry(Helper.createPointList(-0.001, 0.001, -0.001, 0.002));
-        updateDistancesFor(g, 0, 0, 0);
-        updateDistancesFor(g, 1, 0, 0.001);
-        updateDistancesFor(g, 3, 0, 0.002);
-        updateDistancesFor(g, 4, 0, 0.003);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(g, 0, 0, 0);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(g, 1, 0, 0.001);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(g, 3, 0, 0.002);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(g, 4, 0, 0.003);
 
         QueryResult qr = new QueryResult(-0.0005, 0.001);
         qr.setClosestEdge(edge);
diff --git a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
index e21622bbd..affbb9930 100644
--- a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
@@ -39,6 +39,7 @@ public class RandomCHRoutingTest {
     private Weighting weighting;
     private GraphHopperStorage graph;
     private LocationIndexTree locationIndex;
+    private CHGraph chGraph;
 
     @Parameterized.Parameters(name = "{0}")
     public static Object[] params() {
@@ -59,9 +60,10 @@ public class RandomCHRoutingTest {
         encoder = new CarFlagEncoder(5, 5, maxTurnCosts);
         EncodingManager em = EncodingManager.create(encoder);
         weighting = new FastestWeighting(encoder);
-        graph = new GraphBuilder(em)
-                .setCHProfiles(new CHProfile(weighting, traversalMode.isEdgeBased()))
-                .create();
+        GraphBuilder graphBuilder = new GraphBuilder(em);
+        graphBuilder.setEdgeBasedCH(traversalMode.isEdgeBased());
+        graph = graphBuilder.setCHGraph(weighting).create();
+        chGraph = graph.getCHGraph();
     }
 
     /**
@@ -133,9 +135,7 @@ public class RandomCHRoutingTest {
         locationIndex.prepareIndex();
 
         graph.freeze();
-        CHProfile chProfile = new CHProfile(weighting, traversalMode);
-        CHGraph chGraph = graph.getCHGraph(chProfile);
-        PrepareContractionHierarchies pch = new PrepareContractionHierarchies(chGraph);
+        PrepareContractionHierarchies pch = new PrepareContractionHierarchies(chGraph, weighting, traversalMode);
         pch.doWork();
 
         int numQueryGraph = 25;
diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
index 24dce0d94..98c46678d 100644
--- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
+++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
@@ -25,7 +25,10 @@ import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.routing.util.TestAlgoCollector.AlgoHelperEntry;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.*;
+import com.graphhopper.storage.CHGraph;
+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;
@@ -89,17 +92,25 @@ public class RoutingAlgorithmIT {
             final HintsMap chHints = new HintsMap(defaultHints);
             chHints.put(Parameters.CH.DISABLE, false);
             chHints.put(Parameters.Routing.EDGE_BASED, tMode.isEdgeBased());
-            CHProfile pickedProfile = null;
-            for (CHProfile chProfile : hopper.getCHFactoryDecorator().getCHProfiles()) {
-                if (chProfile.getWeighting().equals(weighting) && tMode.isEdgeBased() == chProfile.getTraversalMode().isEdgeBased()) {
-                    pickedProfile = chProfile;
+            Weighting pickedWeighting = null;
+            for (Weighting tmpWeighting : hopper.getCHFactoryDecorator().getNodeBasedWeightings()) {
+                if (tmpWeighting.equals(weighting)) {
+                    pickedWeighting = tmpWeighting;
                     break;
                 }
             }
-            if (pickedProfile == null)
-                throw new IllegalStateException("Didn't find weighting " + hints.getWeighting() + " in " + hopper.getCHFactoryDecorator().getCHProfiles());
+            // todo: not so sure about this, can the edge based weighting entry overwrite the picked weighting found
+            // in the node based weightings ?
+            for (Weighting tmpWeighting : hopper.getCHFactoryDecorator().getEdgeBasedWeightings()) {
+                if (tmpWeighting.equals(weighting)) {
+                    pickedWeighting = tmpWeighting;
+                    break;
+                }
+            }
+            if (pickedWeighting == null)
+                throw new IllegalStateException("Didn't find weighting " + hints.getWeighting() + " in " + hopper.getCHFactoryDecorator().getNodeBasedWeightings());
 
-            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedProfile),
+            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedWeighting),
                     AlgorithmOptions.start(dijkstrabiOpts).hints(chHints).build(), idx, "dijkstrabi|ch|prepare|" + hints.getWeighting()) {
                 @Override
                 public RoutingAlgorithmFactory createRoutingFactory() {
@@ -107,7 +118,7 @@ public class RoutingAlgorithmIT {
                 }
             });
 
-            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedProfile),
+            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedWeighting),
                     AlgorithmOptions.start(astarbiOpts).hints(chHints).build(), idx, "astarbi|ch|prepare|" + hints.getWeighting()) {
                 @Override
                 public RoutingAlgorithmFactory createRoutingFactory() {
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
index 5c5260f8c..b3915467b 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
@@ -20,17 +20,23 @@ package com.graphhopper.routing.ch;
 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.FastestWeighting;
 import com.graphhopper.routing.weighting.ShortFastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.*;
+import com.graphhopper.storage.Directory;
+import com.graphhopper.storage.GraphExtension;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.RAMDirectory;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.List;
 
+import static com.graphhopper.routing.util.TraversalMode.EDGE_BASED;
+import static com.graphhopper.routing.util.TraversalMode.NODE_BASED;
 import static org.junit.Assert.*;
 
 /**
@@ -38,12 +44,9 @@ import static org.junit.Assert.*;
  */
 public class CHAlgoFactoryDecoratorTest {
     private CHAlgoFactoryDecorator instance;
-    private CHProfile profileNode1;
-    private CHProfile profileNode2;
-    private CHProfile profileNode3;
-    private CHProfile profileEdge1;
-    private CHProfile profileEdge2;
-    private CHProfile profileEdge3;
+    private Weighting weighting1;
+    private Weighting weighting2;
+    private Weighting weighting3;
     private GraphHopperStorage ghStorage;
 
     @Before
@@ -52,77 +55,87 @@ public class CHAlgoFactoryDecoratorTest {
         Directory dir = new RAMDirectory();
         FlagEncoder encoder = new CarFlagEncoder();
         EncodingManager encodingManager = EncodingManager.create(encoder);
-        profileNode1 = CHProfile.nodeBased(new FastestWeighting(encoder));
-        profileNode2 = CHProfile.nodeBased(new ShortestWeighting(encoder));
-        profileNode3 = CHProfile.nodeBased(new ShortFastestWeighting(encoder, 0.1));
-        profileEdge1 = CHProfile.edgeBased(new FastestWeighting(encoder));
-        profileEdge2 = CHProfile.edgeBased(new ShortestWeighting(encoder));
-        profileEdge3 = CHProfile.edgeBased(new ShortFastestWeighting(encoder, 0.1));
+        weighting1 = new FastestWeighting(encoder);
+        weighting2 = new ShortestWeighting(encoder);
+        weighting3 = new ShortFastestWeighting(encoder, 0.1);
         ghStorage = new GraphHopperStorage(
-                Arrays.asList(profileNode1, profileNode2, profileNode3, profileEdge1, profileEdge2, profileEdge3),
+                Arrays.asList(weighting1, weighting2, weighting3),
+                Arrays.asList(weighting1, weighting2, weighting3),
                 dir, encodingManager, false, new GraphExtension.NoOpExtension());
     }
 
     @Test
-    public void testDisablingAllowed() {
+    public void testCreatePreparations() {
         assertFalse(instance.isDisablingAllowed());
         instance.setEnabled(false);
         assertTrue(instance.isDisablingAllowed());
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAddingPreparationBeforeProfile_throws() {
-        PrepareContractionHierarchies preparation = createPreparation(profileNode1);
+    public void testAddingPreparationBeforeWeighting_throws() {
+        PrepareContractionHierarchies preparation = createNodeBasedPreparation(weighting1);
         instance.addPreparation(preparation);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void testAddingPreparationWithWrongProfile_throws() {
-        instance.addCHProfile(profileNode1);
-        PrepareContractionHierarchies preparation = createPreparation(profileNode2);
+    public void testAddingPreparationWithWrongWeighting_throws() {
+        instance.addNodeBasedWeighting(weighting1);
+        PrepareContractionHierarchies preparation = createNodeBasedPreparation(weighting2);
         instance.addPreparation(preparation);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testAddingPreparationsInWrongOrder_throws() {
-        instance.addCHProfile(profileNode1);
-        instance.addCHProfile(profileNode2);
-        instance.addPreparation(createPreparation(profileNode2));
-        instance.addPreparation(createPreparation(profileNode1));
+        instance.addNodeBasedWeighting(weighting1);
+        instance.addNodeBasedWeighting(weighting2);
+        instance.addPreparation(createNodeBasedPreparation(weighting2));
+        instance.addPreparation(createNodeBasedPreparation(weighting1));
     }
 
     @Test
     public void testAddingPreparationsWithEdgeAndNodeBasedIntermixed_works() {
-        instance.addCHProfile(profileNode1);
-        instance.addCHProfile(profileEdge1);
-        instance.addCHProfile(profileNode2);
-        instance.addPreparation(createPreparation(profileNode1));
-        instance.addPreparation(createPreparation(profileEdge1));
-        instance.addPreparation(createPreparation(profileNode2));
+        instance.addNodeBasedWeighting(weighting1);
+        instance.addEdgeBasedWeighting(weighting1);
+        instance.addPreparation(createEdgeBasedPreparation(weighting1));
+        instance.addPreparation(createNodeBasedPreparation(weighting1));
     }
 
     @Test
     public void testAddingEdgeAndNodeBased_works() {
-        instance.addCHProfile(profileNode1);
-        instance.addCHProfile(profileNode2);
-        instance.addCHProfile(profileEdge1);
-        instance.addCHProfile(profileEdge2);
-        instance.addCHProfile(profileNode3);
-        instance.addPreparation(createPreparation(profileNode1));
-        instance.addPreparation(createPreparation(profileNode2));
-        instance.addPreparation(createPreparation(profileEdge1));
-        instance.addPreparation(createPreparation(profileEdge2));
-        instance.addPreparation(createPreparation(profileNode3));
-
-        CHProfile[] expectedProfiles = new CHProfile[]{profileNode1, profileNode2, profileEdge1, profileEdge2, profileNode3};
+        instance.addEdgeBasedWeighting(weighting1);
+        instance.addNodeBasedWeighting(weighting2);
+        instance.addNodeBasedWeighting(weighting1);
+        instance.addEdgeBasedWeighting(weighting2);
+        instance.addEdgeBasedWeighting(weighting3);
+        // we can change the order between edge and node based as long as within each group the weightings have the
+        // right order
+        instance.addPreparation(createEdgeBasedPreparation(weighting1));
+        instance.addPreparation(createNodeBasedPreparation(weighting2));
+        instance.addPreparation(createEdgeBasedPreparation(weighting2));
+        instance.addPreparation(createEdgeBasedPreparation(weighting3));
+        instance.addPreparation(createNodeBasedPreparation(weighting1));
+
+        Weighting[] expectedWeightings = new Weighting[]{weighting1, weighting2, weighting2, weighting3, weighting1};
+        boolean[] expectedEdgedBaseds = new boolean[]{true, false, true, true, false};
+
         List<PrepareContractionHierarchies> preparations = instance.getPreparations();
         for (int i = 0; i < preparations.size(); ++i) {
-            assertSame(expectedProfiles[i], preparations.get(i).getCHProfile());
+            assertSame(expectedWeightings[i], preparations.get(i).getWeighting());
+            assertSame(expectedEdgedBaseds[i], preparations.get(i).isEdgeBased());
         }
     }
 
-    private PrepareContractionHierarchies createPreparation(CHProfile chProfile) {
-        return PrepareContractionHierarchies.fromGraphHopperStorage(ghStorage, chProfile);
+    private PrepareContractionHierarchies createNodeBasedPreparation(Weighting weighting) {
+        return createPreparation(weighting, false);
+    }
+
+    private PrepareContractionHierarchies createEdgeBasedPreparation(Weighting weighting) {
+        return createPreparation(weighting, true);
+    }
+
+    private PrepareContractionHierarchies createPreparation(Weighting weighting, boolean edgedBased) {
+        TraversalMode traversalMode = edgedBased ? EDGE_BASED : NODE_BASED;
+        return PrepareContractionHierarchies.fromGraphHopperStorage(ghStorage, weighting, traversalMode);
     }
 
 }
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 0566c531c..5736f074e 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
@@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
+import static com.graphhopper.routing.AbstractRoutingAlgorithmTester.updateDistancesFor;
 import static com.graphhopper.routing.ch.CHParameters.*;
 import static org.junit.Assert.*;
 
@@ -59,6 +59,7 @@ public class CHTurnCostTest {
     private Weighting weighting;
     private GraphHopperStorage graph;
     private TurnCostExtension turnCostExtension;
+    private TurnWeighting turnWeighting;
     private CHGraph chGraph;
     private boolean checkStrict;
 
@@ -72,10 +73,10 @@ public class CHTurnCostTest {
         encoder = new CarFlagEncoder(5, 5, maxCost);
         EncodingManager encodingManager = EncodingManager.create(encoder);
         weighting = new ShortestWeighting(encoder);
-        CHProfile chProfile = CHProfile.edgeBased(weighting);
-        graph = new GraphBuilder(encodingManager).setCHProfiles(chProfile).create();
-        chGraph = graph.getCHGraph();
+        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
         turnCostExtension = (TurnCostExtension) graph.getExtension();
+        turnWeighting = new TurnWeighting(weighting, turnCostExtension);
+        chGraph = graph.getCHGraph();
         checkStrict = true;
     }
 
@@ -1017,7 +1018,7 @@ public class CHTurnCostTest {
     }
 
     private Path findPathUsingDijkstra(int from, int to) {
-        Dijkstra dijkstra = new Dijkstra(graph, new TurnWeighting(weighting, turnCostExtension), TraversalMode.EDGE_BASED);
+        Dijkstra dijkstra = new Dijkstra(graph, turnWeighting, TraversalMode.EDGE_BASED);
         return dijkstra.calcPath(from, to);
     }
 
@@ -1041,7 +1042,7 @@ public class CHTurnCostTest {
                 return contractionOrder.size();
             }
         };
-        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph)
+        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph, weighting, TraversalMode.EDGE_BASED)
                 .useFixedNodeOrdering(nodeOrderingProvider);
         ch.doWork();
         return ch;
@@ -1053,7 +1054,8 @@ public class CHTurnCostTest {
         pMap.put(LAST_LAZY_NODES_UPDATES, 100);
         pMap.put(NEIGHBOR_UPDATES, 4);
         pMap.put(LOG_MESSAGES, 10);
-        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph);
+        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(
+                chGraph, weighting, TraversalMode.EDGE_BASED);
         ch.setParams(pMap);
         ch.doWork();
         return ch;
@@ -1137,7 +1139,7 @@ public class CHTurnCostTest {
         turnCostExtension.addTurnInfo(inEdge.getEdge(), viaNode, outEdge.getEdge(), encoder.getTurnFlags(true, 0));
     }
 
-    private void addTurnCost(int from, int via, int to, double cost) {
+    private void addTurnCost(int from, int via, int to, int cost) {
         addTurnCost(getEdge(from, via), getEdge(via, to), via, cost);
     }
 
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 8fcc23ee3..f2d3a8fad 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
@@ -27,11 +27,16 @@ import com.graphhopper.routing.util.EncodingManager;
 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.CHGraph;
+import com.graphhopper.storage.GraphBuilder;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.TurnCostExtension;
+import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.PMap;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -54,7 +59,8 @@ public class EdgeBasedNodeContractorTest {
     private CarFlagEncoder encoder;
     private GraphHopperStorage graph;
     private TurnCostExtension turnCostExtension;
-    private Weighting weighting;
+    private TurnWeighting turnWeighting;
+    private TurnWeighting chTurnWeighting;
 
     @Rule
     public RepeatRule repeatRule = new RepeatRule();
@@ -68,9 +74,12 @@ public class EdgeBasedNodeContractorTest {
     private void initialize() {
         encoder = new CarFlagEncoder(5, 5, maxCost);
         EncodingManager encodingManager = EncodingManager.create(encoder);
-        weighting = new ShortestWeighting(encoder);
-        graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
+        Weighting weighting = new ShortestWeighting(encoder);
+        PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
+        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
         turnCostExtension = (TurnCostExtension) graph.getExtension();
+        turnWeighting = new TurnWeighting(weighting, turnCostExtension);
+        chTurnWeighting = new TurnWeighting(preparationWeighting, turnCostExtension);
         chGraph = graph.getCHGraph();
     }
 
@@ -381,7 +390,8 @@ public class EdgeBasedNodeContractorTest {
         graph.freeze();
         setMaxLevelOnAllNodes();
         contractNodes(2);
-        checkShortcuts(createShortcut(3, 4, e3to2, e2to4, 12));
+        double weight = calcWeight(e3to2, e2to4);
+        checkShortcuts(createShortcut(3, 4, e3to2, e2to4, weight));
     }
 
     @Test
@@ -1354,13 +1364,16 @@ public class EdgeBasedNodeContractorTest {
     }
 
     private EdgeBasedNodeContractor createNodeContractor() {
-        PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
-        TurnWeighting turnWeighting = new TurnWeighting(preparationWeighting, turnCostExtension);
-        EdgeBasedNodeContractor nodeContractor = new EdgeBasedNodeContractor(chGraph, turnWeighting, new PMap());
+        EdgeBasedNodeContractor nodeContractor = new EdgeBasedNodeContractor(chGraph, chTurnWeighting, new PMap());
         nodeContractor.initFromGraph();
         return nodeContractor;
     }
 
+    private double calcWeight(EdgeIteratorState edge1, EdgeIteratorState edge2) {
+        return turnWeighting.calcWeight(edge1, false, EdgeIterator.NO_EDGE) +
+                turnWeighting.calcWeight(edge2, false, edge1.getEdge());
+    }
+
     private void addRestriction(EdgeIteratorState inEdge, EdgeIteratorState outEdge, int viaNode) {
         turnCostExtension.addTurnInfo(inEdge.getEdge(), viaNode, outEdge.getEdge(), encoder.getTurnFlags(true, 0));
     }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
index ee21e05b6..18ae4a100 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
@@ -26,8 +26,13 @@ import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.*;
+import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.GraphBuilder;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.RAMDirectory;
 import com.graphhopper.storage.index.LocationIndexTree;
+import com.graphhopper.storage.index.QueryResult;
+import com.graphhopper.util.CHEdgeIteratorState;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.PMap;
@@ -35,7 +40,7 @@ import org.junit.Test;
 
 import java.util.*;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
+import static com.graphhopper.routing.AbstractRoutingAlgorithmTester.updateDistancesFor;
 import static com.graphhopper.util.Parameters.Routing.HEADING_PENALTY;
 import static org.junit.Assert.*;
 
@@ -50,7 +55,7 @@ public class NodeBasedNodeContractorTest {
     private final CarFlagEncoder encoder = new CarFlagEncoder();
     private final EncodingManager encodingManager = EncodingManager.create(encoder);
     private final Weighting weighting = new ShortestWeighting(encoder);
-    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
+    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
     private final CHGraph lg = graph.getCHGraph();
     private final TraversalMode traversalMode = TraversalMode.NODE_BASED;
 
@@ -179,9 +184,20 @@ public class NodeBasedNodeContractorTest {
         graph.edge(6, 7, 1, true);
         graph.freeze();
 
-        int sc1to4 = lg.shortcut(1, 4, PrepareEncoder.getScDirMask(), 2, 2, iter1to3.getEdge(), iter3to4.getEdge());
-        int sc4to6 = lg.shortcut(4, 6, PrepareEncoder.getScFwdDir(), 2, 2, iter4to5.getEdge(), iter5to6.getEdge());
-        int sc6to4 = lg.shortcut(6, 4, PrepareEncoder.getScFwdDir(), 3, 3, iter6to8.getEdge(), iter8to4.getEdge());
+        CHEdgeIteratorState sc1to4 = lg.shortcut(1, 4);
+        sc1to4.setFlagsAndWeight(PrepareEncoder.getScDirMask(), 2);
+        sc1to4.setDistance(2);
+        sc1to4.setSkippedEdges(iter1to3.getEdge(), iter3to4.getEdge());
+
+        CHEdgeIteratorState sc4to6 = lg.shortcut(4, 6);
+        sc4to6.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 2);
+        sc4to6.setDistance(2);
+        sc4to6.setSkippedEdges(iter4to5.getEdge(), iter5to6.getEdge());
+
+        CHEdgeIteratorState sc6to4 = lg.shortcut(6, 4);
+        sc6to4.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 3);
+        sc6to4.setDistance(3);
+        sc6to4.setSkippedEdges(iter6to8.getEdge(), iter8to4.getEdge());
 
         setMaxLevelOnAllNodes();
 
@@ -205,8 +221,8 @@ public class NodeBasedNodeContractorTest {
         nodeContractor.contractNode(4);
         checkShortcuts(manualSc1, manualSc2, manualSc3,
                 // there should be two different shortcuts for both directions!
-                expectedShortcut(1, 6, lg.getEdgeIteratorState(sc1to4, 4), lg.getEdgeIteratorState(sc4to6, 6), true, false),
-                expectedShortcut(6, 1, lg.getEdgeIteratorState(sc6to4, 4), lg.getEdgeIteratorState(sc1to4, 1), true, false)
+                expectedShortcut(1, 6, sc1to4, sc4to6, true, false),
+                expectedShortcut(6, 1, sc6to4, sc1to4, true, false)
         );
     }
 
@@ -336,7 +352,7 @@ public class NodeBasedNodeContractorTest {
         CarFlagEncoder encoder = new CarFlagEncoder();
         EncodingManager encodingManager = EncodingManager.create(encoder);
         Weighting weighting = new FastestWeighting(encoder);
-        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
+        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
         CHGraph lg = graph.getCHGraph();
         // 0 ------------> 4
         //  \             /
@@ -374,7 +390,7 @@ public class NodeBasedNodeContractorTest {
         CarFlagEncoder encoder = new CarFlagEncoder();
         EncodingManager encodingManager = EncodingManager.create(encoder);
         Weighting weighting = new FastestWeighting(encoder);
-        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
+        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
         CHGraph lg = graph.getCHGraph();
         // 0 - 1 - 2 - 3
         // o           o
diff --git a/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java b/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
index 6c2ee95ec..4eeb5f876 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
@@ -11,13 +11,15 @@ import com.graphhopper.routing.util.MotorcycleFlagEncoder;
 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.CHGraph;
+import com.graphhopper.storage.GraphBuilder;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.TurnCostExtension;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.GHUtility;
 import org.junit.Before;
 import org.junit.Test;
 
-
 import static org.junit.Assert.assertEquals;
 
 public class Path4CHTest {
@@ -33,7 +35,7 @@ public class Path4CHTest {
         encoder = new MotorcycleFlagEncoder(5, 5, maxTurnCosts);
         EncodingManager em = EncodingManager.create(encoder);
         weighting = new FastestWeighting(encoder);
-        graph = new GraphBuilder(em).setCHProfiles(CHProfile.edgeBased(weighting)).create();
+        graph = new GraphBuilder(em).setEdgeBasedCH(true).setCHGraph(weighting).create();
         chGraph = graph.getCHGraph();
         turnCostExtension = (TurnCostExtension) graph.getExtension();
     }
@@ -155,7 +157,7 @@ public class Path4CHTest {
 
     private AbstractBidirectionEdgeCHNoSOD createAlgo() {
         TurnWeighting chTurnWeighting = new TurnWeighting(new PreparationWeighting(weighting), turnCostExtension);
-        CHGraph lg = graph.getCHGraph();
+        CHGraph lg = graph.getCHGraph(weighting);
         AbstractBidirectionEdgeCHNoSOD algo = new DijkstraBidirectionEdgeCHNoSOD(lg, chTurnWeighting);
         algo.setEdgeFilter(new LevelEdgeFilter(lg));
         return algo;
diff --git a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
index 232696098..8b73762ab 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
@@ -32,7 +32,7 @@ import org.junit.Test;
 
 import java.util.*;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
+import static com.graphhopper.routing.AbstractRoutingAlgorithmTester.updateDistancesFor;
 import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
 import static org.junit.Assert.*;
 
@@ -125,7 +125,7 @@ public class PrepareContractionHierarchiesTest {
     }
 
     GraphHopperStorage createGHStorage(Weighting w) {
-        return new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(w)).create();
+        return new GraphBuilder(encodingManager).setCHGraph(w).create();
     }
 
     GraphHopperStorage createExampleGraph() {
@@ -309,30 +309,34 @@ public class PrepareContractionHierarchiesTest {
         EdgeIteratorState edgeState34 = g.edge(3, 4).setDistance(dist).setFlags(edgeFlags);
         EdgeIteratorState edgeState45 = g.edge(4, 5).setDistance(dist).setFlags(edgeFlags);
         EdgeIteratorState edgeState56 = g.edge(5, 6).setDistance(dist).setFlags(edgeFlags);
-        int oneDirFlags = PrepareEncoder.getScFwdDir();
 
         int tmpEdgeId = edgeState01.getEdge();
         ghStorage.freeze();
+        CHEdgeIteratorState sc0_2 = g.shortcut(0, 2);
         int x = EdgeIterator.NO_EDGE;
-        double weight = w.calcWeight(edgeState01, false, x) + w.calcWeight(edgeState12, false, x);
-        int sc0_2 = g.shortcut(0, 2, oneDirFlags, w.calcWeight(edgeState01, false, x) + w.calcWeight(edgeState12, false, x), 2 * dist, tmpEdgeId, edgeState12.getEdge());
-
-        tmpEdgeId = sc0_2;
-        weight += w.calcWeight(edgeState23, false, x);
-        int sc0_3 = g.shortcut(0, 3, oneDirFlags, weight, 3 * dist, tmpEdgeId, edgeState23.getEdge());
-
-        tmpEdgeId = sc0_3;
-        weight += w.calcWeight(edgeState34, false, x);
-        int sc0_4 = g.shortcut(0, 4, oneDirFlags, weight, 4, tmpEdgeId, edgeState34.getEdge());
-
-        tmpEdgeId = sc0_4;
-        weight += w.calcWeight(edgeState45, false, x);
-        int sc0_5 = g.shortcut(0, 5, oneDirFlags, weight, 5, tmpEdgeId, edgeState45.getEdge());
-
-        tmpEdgeId = sc0_5;
-        weight += w.calcWeight(edgeState56, false, x);
-        int sc0_6 = g.shortcut(0, 6, oneDirFlags, weight, 6, tmpEdgeId, edgeState56.getEdge());
-
+        sc0_2.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), w.calcWeight(edgeState01, false, x) + w.calcWeight(edgeState12, false, x));
+        sc0_2.setDistance(2 * dist);
+        sc0_2.setSkippedEdges(tmpEdgeId, edgeState12.getEdge());
+        tmpEdgeId = sc0_2.getEdge();
+        CHEdgeIteratorState sc0_3 = g.shortcut(0, 3);
+        sc0_3.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), sc0_2.getWeight() + w.calcWeight(edgeState23, false, x));
+        sc0_3.setDistance(3 * dist);
+        sc0_3.setSkippedEdges(tmpEdgeId, edgeState23.getEdge());
+        tmpEdgeId = sc0_3.getEdge();
+        CHEdgeIteratorState sc0_4 = g.shortcut(0, 4);
+        sc0_4.setDistance(4);
+        sc0_4.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), sc0_3.getWeight() + w.calcWeight(edgeState34, false, x));
+        sc0_4.setSkippedEdges(tmpEdgeId, edgeState34.getEdge());
+        tmpEdgeId = sc0_4.getEdge();
+        CHEdgeIteratorState sc0_5 = g.shortcut(0, 5);
+        sc0_5.setDistance(5);
+        sc0_5.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), sc0_4.getWeight() + w.calcWeight(edgeState45, false, x));
+        sc0_5.setSkippedEdges(tmpEdgeId, edgeState45.getEdge());
+        tmpEdgeId = sc0_5.getEdge();
+        CHEdgeIteratorState sc0_6 = g.shortcut(0, 6);
+        sc0_6.setDistance(6);
+        sc0_6.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), sc0_5.getWeight() + w.calcWeight(edgeState56, false, x));
+        sc0_6.setSkippedEdges(tmpEdgeId, edgeState56.getEdge());
         g.setLevel(0, 10);
         g.setLevel(6, 9);
         g.setLevel(5, 8);
@@ -619,17 +623,17 @@ public class PrepareContractionHierarchiesTest {
         EncodingManager tmpEncodingManager = EncodingManager.create(tmpCarEncoder, tmpBikeEncoder);
 
         // FastestWeighting would lead to different shortcuts due to different default speeds for bike and car
-        CHProfile carProfile = CHProfile.nodeBased(new ShortestWeighting(tmpCarEncoder));
-        CHProfile bikeProfile = CHProfile.nodeBased(new ShortestWeighting(tmpBikeEncoder));
+        Weighting carWeighting = new ShortestWeighting(tmpCarEncoder);
+        Weighting bikeWeighting = new ShortestWeighting(tmpBikeEncoder);
 
-        List<CHProfile> profiles = Arrays.asList(carProfile, bikeProfile);
-        GraphHopperStorage ghStorage = new GraphHopperStorage(profiles, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
+        List<Weighting> chWeightings = Arrays.asList(carWeighting, bikeWeighting);
+        GraphHopperStorage ghStorage = new GraphHopperStorage(chWeightings, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
         initShortcutsGraph(ghStorage);
 
         ghStorage.freeze();
 
-        for (CHProfile p : profiles) {
-            checkPath(ghStorage, p, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
+        for (Weighting w : chWeightings) {
+            checkPath(ghStorage, w, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
         }
     }
 
@@ -639,20 +643,20 @@ public class PrepareContractionHierarchiesTest {
         BikeFlagEncoder tmpBikeEncoder = new BikeFlagEncoder();
         EncodingManager tmpEncodingManager = EncodingManager.create(tmpCarEncoder, tmpBikeEncoder);
 
-        CHProfile carProfile = CHProfile.nodeBased(new FastestWeighting(tmpCarEncoder));
-        CHProfile bikeProfile = CHProfile.nodeBased(new FastestWeighting(tmpBikeEncoder));
+        Weighting carWeighting = new FastestWeighting(tmpCarEncoder);
+        Weighting bikeWeighting = new FastestWeighting(tmpBikeEncoder);
 
-        List<CHProfile> profiles = Arrays.asList(carProfile, bikeProfile);
-        GraphHopperStorage ghStorage = new GraphHopperStorage(profiles, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
+        List<Weighting> chWeightings = Arrays.asList(carWeighting, bikeWeighting);
+        GraphHopperStorage ghStorage = new GraphHopperStorage(chWeightings, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
         initShortcutsGraph(ghStorage);
         EdgeIteratorState edge = GHUtility.getEdge(ghStorage, 9, 14).
                 set(tmpBikeEncoder.getAccessEnc(), false).setReverse(tmpBikeEncoder.getAccessEnc(), false);
 
         ghStorage.freeze();
 
-        checkPath(ghStorage, carProfile, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
+        checkPath(ghStorage, carWeighting, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
         // detour around blocked 9,14
-        checkPath(ghStorage, bikeProfile, 9, 5, IntArrayList.from(3, 10, 14, 16, 13, 12));
+        checkPath(ghStorage, bikeWeighting, 9, 5, IntArrayList.from(3, 10, 14, 16, 13, 12));
     }
 
     @Test
@@ -660,10 +664,10 @@ public class PrepareContractionHierarchiesTest {
         CarFlagEncoder carFlagEncoder = new CarFlagEncoder();
         MotorcycleFlagEncoder motorCycleEncoder = new MotorcycleFlagEncoder();
         EncodingManager em = EncodingManager.create(carFlagEncoder, motorCycleEncoder);
-        CHProfile carProfile = CHProfile.nodeBased(new FastestWeighting(carFlagEncoder));
-        CHProfile motorCycleProfile = CHProfile.nodeBased(new FastestWeighting(motorCycleEncoder));
+        Weighting carWeighting = new FastestWeighting(carFlagEncoder);
+        Weighting motorCycleWeighting = new FastestWeighting(motorCycleEncoder);
         Directory dir = new RAMDirectory();
-        GraphHopperStorage ghStorage = new GraphHopperStorage(Arrays.asList(carProfile, motorCycleProfile), dir, em, false, new GraphExtension.NoOpExtension());
+        GraphHopperStorage ghStorage = new GraphHopperStorage(Arrays.asList(carWeighting, motorCycleWeighting), dir, em, false, new GraphExtension.NoOpExtension());
         ghStorage.create(1000);
 
         int numNodes = 5_000;
@@ -675,24 +679,25 @@ public class PrepareContractionHierarchiesTest {
 
         // create CH for cars
         StopWatch sw = new StopWatch().start();
-        CHGraph carCH = ghStorage.getCHGraph(carProfile);
-        PrepareContractionHierarchies carPch = new PrepareContractionHierarchies(carCH);
+        CHGraph carCH = ghStorage.getCHGraph(carWeighting);
+        TraversalMode traversalMode = TraversalMode.NODE_BASED;
+        PrepareContractionHierarchies carPch = new PrepareContractionHierarchies(carCH, carWeighting, traversalMode);
         carPch.doWork();
         long timeCar = sw.stop().getMillis();
 
         // create CH for motorcycles, re-use car contraction order
         // this speeds up contraction significantly, but can lead to slower queries
         sw = new StopWatch().start();
-        CHGraph motorCycleCH = ghStorage.getCHGraph(motorCycleProfile);
+        CHGraph motorCycleCH = ghStorage.getCHGraph(motorCycleWeighting);
         NodeOrderingProvider nodeOrderingProvider = carCH.getNodeOrderingProvider();
-        PrepareContractionHierarchies motorCyclePch = new PrepareContractionHierarchies(motorCycleCH)
+        PrepareContractionHierarchies motorCyclePch = new PrepareContractionHierarchies(motorCycleCH, motorCycleWeighting, traversalMode)
                 .useFixedNodeOrdering(nodeOrderingProvider);
         motorCyclePch.doWork();
 
         // run a few sample queries to check correctness
         for (int i = 0; i < numQueries; ++i) {
-            Dijkstra dijkstra = new Dijkstra(ghStorage, motorCycleProfile.getWeighting(), TraversalMode.NODE_BASED);
-            RoutingAlgorithm chAlgo = motorCyclePch.createAlgo(motorCycleCH, AlgorithmOptions.start().weighting(motorCycleProfile.getWeighting()).build());
+            Dijkstra dijkstra = new Dijkstra(ghStorage, motorCycleWeighting, traversalMode);
+            RoutingAlgorithm chAlgo = motorCyclePch.createAlgo(motorCycleCH, AlgorithmOptions.start().weighting(motorCycleWeighting).build());
 
             int from = rnd.nextInt(numNodes);
             int to = rnd.nextInt(numNodes);
@@ -705,15 +710,15 @@ public class PrepareContractionHierarchiesTest {
         assertTrue("reusing node ordering should speed up ch contraction", timeMotorCycle < 0.5 * timeCar);
     }
 
-    void checkPath(GraphHopperStorage g, CHProfile p, int expShortcuts, double expDistance, IntIndexedContainer expNodes) {
-        CHGraph lg = g.getCHGraph(p);
-        PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g, lg, p);
+    void checkPath(GraphHopperStorage g, Weighting w, int expShortcuts, double expDistance, IntIndexedContainer expNodes) {
+        CHGraph lg = g.getCHGraph(w);
+        PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g, lg, w);
         prepare.doWork();
-        assertEquals(p.toString(), expShortcuts, prepare.getShortcuts());
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, p.getWeighting(), tMode));
-        Path path = algo.calcPath(3, 12);
-        assertEquals(path.toString(), expDistance, path.getDistance(), 1e-5);
-        assertEquals(path.toString(), expNodes, path.calcNodes());
+        assertEquals(w.toString(), expShortcuts, prepare.getShortcuts());
+        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, w, tMode));
+        Path p = algo.calcPath(3, 12);
+        assertEquals(w.toString(), expDistance, p.getDistance(), 1e-5);
+        assertEquals(w.toString(), expNodes, p.calcNodes());
     }
 
     private PrepareContractionHierarchies createPrepareContractionHierarchies(GraphHopperStorage g, CHGraph lg) {
@@ -721,12 +726,8 @@ public class PrepareContractionHierarchiesTest {
     }
 
     private PrepareContractionHierarchies createPrepareContractionHierarchies(GraphHopperStorage g, CHGraph lg, Weighting w) {
-        return createPrepareContractionHierarchies(g, lg, CHProfile.nodeBased(w));
-    }
-
-    private PrepareContractionHierarchies createPrepareContractionHierarchies(GraphHopperStorage g, CHGraph lg, CHProfile p) {
         g.freeze();
-        return new PrepareContractionHierarchies(lg);
+        return new PrepareContractionHierarchies(lg, w, tMode);
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java b/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java
index 5202944d9..897ef6496 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java
@@ -23,7 +23,10 @@ import com.graphhopper.routing.util.EncodingManager;
 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.CHGraph;
+import com.graphhopper.storage.GraphBuilder;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.TurnCostExtension;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.PMap;
 import org.junit.Before;
@@ -44,7 +47,7 @@ public class WitnessPathSearcherTest {
         EncodingManager encodingManager = EncodingManager.create(encoder);
         Weighting weighting = new ShortestWeighting(encoder);
         PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
-        graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
+        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
         TurnCostExtension turnCostExtension = (TurnCostExtension) graph.getExtension();
         chTurnWeighting = new TurnWeighting(preparationWeighting, turnCostExtension);
         chGraph = graph.getCHGraph();
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 4a9fa9c3e..41d97b5e6 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
@@ -87,7 +87,7 @@ public class LandmarkStorageTest {
     public void testSetGetWeight() {
         ghStorage.edge(0, 1, 40, true);
         Directory dir = new RAMDirectory();
-        DataAccess da = dir.find("landmarks_fastest_car");
+        DataAccess da = dir.find("landmarks_fastest_car_node");
         da.create(2000);
 
         LandmarkStorage lms = new LandmarkStorage(ghStorage, dir, new FastestWeighting(encoder), 4).
diff --git a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
index 33c6ee0e3..def9c2eea 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
@@ -40,7 +40,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -88,7 +87,7 @@ public class PrepareLandmarksTest
                 if (hIndex + 1 < height)
                     graph.edge(node, node + width).set(accessEnc, true).setReverse(accessEnc, true).set(avSpeedEnc, speed);
 
-                updateDistancesFor(graph, node, -hIndex / 50.0, wIndex / 50.0);
+                AbstractRoutingAlgorithmTester.updateDistancesFor(graph, node, -hIndex / 50.0, wIndex / 50.0);
             }
         }
         Directory dir = new RAMDirectory();
diff --git a/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java b/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java
index 9b26da4bd..57d62929d 100644
--- a/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java
+++ b/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java
@@ -39,7 +39,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
+import static com.graphhopper.routing.AbstractRoutingAlgorithmTester.updateDistancesFor;
 import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
 import static org.junit.Assert.assertEquals;
 
diff --git a/core/src/test/java/com/graphhopper/routing/util/DataFlagEncoderTest.java b/core/src/test/java/com/graphhopper/routing/util/DataFlagEncoderTest.java
index c81864b6f..7b80efa42 100644
--- a/core/src/test/java/com/graphhopper/routing/util/DataFlagEncoderTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/DataFlagEncoderTest.java
@@ -1,6 +1,7 @@
 package com.graphhopper.routing.util;
 
 import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.profiles.*;
 import com.graphhopper.routing.util.parsers.*;
 import com.graphhopper.routing.util.spatialrules.SpatialRule;
@@ -21,7 +22,6 @@ import org.junit.Test;
 import java.util.Arrays;
 import java.util.Collections;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.*;
 
 /**
@@ -411,11 +411,11 @@ public class DataFlagEncoderTest {
         EdgeIteratorState e2 = graph.edge(0, 2, 1, true);
         EdgeIteratorState e3 = graph.edge(0, 3, 1, true);
         EdgeIteratorState e4 = graph.edge(0, 4, 1, true);
-        updateDistancesFor(graph, 0, 0.00, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
-        updateDistancesFor(graph, 2, -0.01, -0.01);
-        updateDistancesFor(graph, 3, 0.01, 0.01);
-        updateDistancesFor(graph, 4, -0.01, -0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 2, -0.01, -0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 3, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 4, -0.01, -0.01);
 
         ReaderWay way = new ReaderWay(27l);
         way.setTag("highway", "track");
diff --git a/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java b/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java
index fd114b4f4..cc9645584 100644
--- a/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java
@@ -23,11 +23,11 @@ import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 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.CHEdgeExplorer;
 import com.graphhopper.util.CHEdgeIterator;
+import com.graphhopper.util.CHEdgeIteratorState;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -36,7 +36,7 @@ public class DefaultEdgeFilterTest {
     private final CarFlagEncoder encoder = new CarFlagEncoder();
     private final EncodingManager encodingManager = EncodingManager.create(encoder);
     private final Weighting weighting = new ShortestWeighting(encoder);
-    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
+    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
     private final CHGraph chGraph = graph.getCHGraph();
 
     @Test
@@ -68,9 +68,10 @@ public class DefaultEdgeFilterTest {
         assertEquals("Wrong incoming edges", IntHashSet.from(2, 3), inEdges);
     }
 
-    private void addShortcut(CHGraph chGraph, int from, int to, boolean fwd, int skip1, int skip2) {
-        int accessFlags = fwd ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir();
-        chGraph.shortcut(from, to, accessFlags, 5, 5, skip1, skip2);
+    private void addShortcut(CHGraph chGraph, int from, int to, boolean fwd, int firstOrigEdge, int lastOrigEdge) {
+        CHEdgeIteratorState shortcut = chGraph.shortcut(from, to);
+        shortcut.setFlagsAndWeight(fwd ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir(), 0);
+        shortcut.setFirstAndLastOrigEdges(firstOrigEdge, lastOrigEdge);
     }
 
 }
\ No newline at end of file
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/BlockAreaWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/BlockAreaWeightingTest.java
index 2a28ef42f..d9f9cafef 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/BlockAreaWeightingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/BlockAreaWeightingTest.java
@@ -1,5 +1,6 @@
 package com.graphhopper.routing.weighting;
 
+import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
@@ -14,7 +15,6 @@ import org.junit.Test;
 
 import java.util.Arrays;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.assertEquals;
 
 public class BlockAreaWeightingTest {
@@ -30,8 +30,8 @@ public class BlockAreaWeightingTest {
         graph = new GraphBuilder(em).create();
         // 0-1
         graph.edge(0, 1, 1, true);
-        updateDistancesFor(graph, 0, 0.00, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
     }
 
     @Test
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
deleted file mode 100644
index 77c1a3710..000000000
--- a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
+++ /dev/null
@@ -1,325 +0,0 @@
-package com.graphhopper.routing.weighting;
-
-import com.graphhopper.Repeat;
-import com.graphhopper.RepeatRule;
-import com.graphhopper.routing.*;
-import com.graphhopper.routing.ch.PrepareContractionHierarchies;
-import com.graphhopper.routing.lm.PrepareLandmarks;
-import com.graphhopper.routing.util.*;
-import com.graphhopper.storage.*;
-import com.graphhopper.storage.index.LocationIndex;
-import com.graphhopper.storage.index.LocationIndexTree;
-import com.graphhopper.storage.index.QueryResult;
-import com.graphhopper.util.CHEdgeIteratorState;
-import com.graphhopper.util.EdgeExplorer;
-import com.graphhopper.util.EdgeIterator;
-import com.graphhopper.util.GHUtility;
-import com.graphhopper.util.shapes.BBox;
-import com.graphhopper.util.shapes.GHPoint;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.*;
-
-import static com.graphhopper.util.EdgeIterator.ANY_EDGE;
-import static com.graphhopper.util.EdgeIterator.NO_EDGE;
-import static com.graphhopper.util.Parameters.Algorithms.ASTAR_BI;
-import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-/**
- * This test makes sure the different bidirectional routing algorithms correctly implement restrictions of the source/
- * target edges, by comparing with {@link DijkstraBidirectionRef}
- *
- * @author easbar
- * @see DirectedBidirectionalDijkstraTest
- */
-@RunWith(Parameterized.class)
-public class DirectedRoutingTest {
-    private final Algo algo;
-    private final double uTurnCosts;
-    private final boolean prepareCH;
-    private final boolean prepareLM;
-    private Directory dir;
-    private GraphHopperStorage graph;
-    private CHGraph chGraph;
-    private CarFlagEncoder encoder;
-    private TurnCostExtension turnCostExtension;
-    private int maxTurnCosts;
-    private Weighting weighting;
-    private EncodingManager encodingManager;
-    private PrepareContractionHierarchies pch;
-    private PrepareLandmarks lm;
-
-    @Rule
-    public RepeatRule repeatRule = new RepeatRule();
-
-    @Parameterized.Parameters(name = "{0}, u-turn-costs: {1}")
-    public static Collection<Object[]> params() {
-        return Arrays.asList(new Object[][]{
-                {Algo.ASTAR, Double.POSITIVE_INFINITY, false, false},
-                {Algo.CH_ASTAR, Double.POSITIVE_INFINITY, true, false},
-                {Algo.CH_DIJKSTRA, Double.POSITIVE_INFINITY, true, false},
-                // todo: yields warnings and fails, see #1665, #1687
-//                {Algo.LM, Double.POSITIVE_INFINITY, false, true}
-                {Algo.ASTAR, 40, false, false},
-                // todo: CH does not handle finite u-turn costs so far, see #1652
-//                {Algo.CH_ASTAR, 40, true, false},
-//                {Algo.CH_DIJKSTRA, 40, true, false},
-                // todo: yields warnings and fails, see #1665, 1687
-//                {Algo.LM, 40, false, true}
-                // todo: add AlternativeRoute ?
-        });
-    }
-
-    private enum Algo {
-        ASTAR,
-        CH_ASTAR,
-        CH_DIJKSTRA,
-        LM
-    }
-
-    public DirectedRoutingTest(Algo algo, double uTurnCosts, boolean prepareCH, boolean prepareLM) {
-        this.algo = algo;
-        this.uTurnCosts = uTurnCosts;
-        this.prepareCH = prepareCH;
-        this.prepareLM = prepareLM;
-    }
-
-    @Before
-    public void init() {
-        dir = new RAMDirectory();
-        maxTurnCosts = 10;
-        // todonow: make this work with speed_both_directions=true!
-        encoder = new CarFlagEncoder(5, 5, maxTurnCosts);
-        encodingManager = EncodingManager.create(encoder);
-        weighting = new FastestWeighting(encoder);
-        turnCostExtension = new TurnCostExtension();
-        graph = createGraph();
-        chGraph = graph.getCHGraph();
-    }
-
-    private void preProcessGraph() {
-        graph.freeze();
-        if (!prepareCH && !prepareLM) {
-            return;
-        }
-        if (prepareCH) {
-            pch = new PrepareContractionHierarchies(chGraph);
-            pch.doWork();
-        }
-        if (prepareLM) {
-            lm = new PrepareLandmarks(dir, graph, weighting, 16, 8);
-            lm.setMaximumWeight(1000);
-            lm.doWork();
-        }
-    }
-
-    private AbstractBidirAlgo createAlgo() {
-        return createAlgo(prepareCH ? chGraph : graph);
-    }
-
-    private AbstractBidirAlgo createAlgo(Graph graph) {
-        switch (algo) {
-            case ASTAR:
-                return new AStarBidirection(graph, createTurnWeighting(graph), TraversalMode.EDGE_BASED);
-            case CH_DIJKSTRA:
-                return (AbstractBidirAlgo) pch.createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(DIJKSTRA_BI).build());
-            case CH_ASTAR:
-                return (AbstractBidirAlgo) pch.createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).build());
-            case LM:
-                AStarBidirection astarbi = new AStarBidirection(graph, createTurnWeighting(graph), TraversalMode.EDGE_BASED);
-                return (AbstractBidirAlgo) lm.getDecoratedAlgorithm(graph, astarbi, AlgorithmOptions.start().build());
-            default:
-                throw new IllegalArgumentException("unknown algo " + algo);
-        }
-    }
-
-    private TurnWeighting createTurnWeighting(Graph g) {
-        return new TurnWeighting(weighting, (TurnCostExtension) g.getExtension(), uTurnCosts);
-    }
-
-    @Test
-    @Repeat(times = 10)
-    public void randomGraph() {
-        final long seed = System.nanoTime();
-        System.out.println("random Graph seed: " + seed);
-        final int numQueries = 50;
-        Random rnd = new Random(seed);
-        GHUtility.buildRandomGraph(graph, rnd, 100, 2.2, true, true, encoder.getAverageSpeedEnc(), 0.7, 0.8, 0.8);
-        GHUtility.addRandomTurnCosts(graph, seed, encoder, maxTurnCosts, turnCostExtension);
-//        GHUtility.printGraphForUnitTest(graph, encoder);
-        preProcessGraph();
-        List<String> strictViolations = new ArrayList<>();
-        for (int i = 0; i < numQueries; i++) {
-            int source = getRandom(rnd);
-            int target = getRandom(rnd);
-            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)
-                    .calcPath(source, target, sourceOutEdge, targetInEdge);
-            Path path = createAlgo()
-                    .calcPath(source, target, sourceOutEdge, targetInEdge);
-            strictViolations.addAll(comparePaths(refPath, path, source, target));
-        }
-        if (strictViolations.size() > Math.max(1, 0.20 * numQueries)) {
-            for (String strictViolation : strictViolations) {
-                System.out.println("strict violation: " + strictViolation);
-            }
-            fail("Too many strict violations: " + strictViolations.size() + " / " + numQueries);
-        }
-    }
-
-    /**
-     * Similar to {@link #randomGraph()}, but using the {@link QueryGraph} as it is done in real usage.
-     */
-    @Test
-    @Repeat(times = 10)
-    public void randomGraph_withQueryGraph() {
-        final long seed = System.nanoTime();
-        System.out.println("randomGraph_withQueryGraph seed: " + seed);
-        final int numQueries = 50;
-
-        // we may not use an offset when query graph is involved, otherwise traveling via virtual edges will not be
-        // the same as taking the direct edge!
-        double pOffset = 0;
-        Random rnd = new Random(seed);
-        GHUtility.buildRandomGraph(graph, rnd, 50, 2.2, true, true, encoder.getAverageSpeedEnc(), 0.7, 0.8, pOffset);
-        GHUtility.addRandomTurnCosts(graph, seed, encoder, maxTurnCosts, turnCostExtension);
-//        GHUtility.printGraphForUnitTest(graph, encoder);
-        preProcessGraph();
-        LocationIndexTree index = new LocationIndexTree(graph, dir);
-        index.prepareIndex();
-        List<String> strictViolations = new ArrayList<>();
-        for (int i = 0; i < numQueries; i++) {
-            List<GHPoint> points = getRandomPoints(2, index, rnd);
-
-            List<QueryResult> chQueryResults = findQueryResults(index, points);
-            List<QueryResult> queryResults = findQueryResults(index, points);
-
-            QueryGraph chQueryGraph = new QueryGraph(prepareCH ? chGraph : graph);
-            QueryGraph queryGraph = new QueryGraph(graph);
-
-            chQueryGraph.lookup(chQueryResults);
-            queryGraph.lookup(queryResults);
-
-            int source = queryResults.get(0).getClosestNode();
-            int target = queryResults.get(1).getClosestNode();
-            Random tmpRnd1 = new Random(seed);
-            int sourceOutEdge = getSourceOutEdge(tmpRnd1, source, queryGraph);
-            int targetInEdge = getTargetInEdge(tmpRnd1, target, queryGraph);
-            Random tmpRnd2 = new Random(seed);
-            int chSourceOutEdge = getSourceOutEdge(tmpRnd2, source, chQueryGraph);
-            int chTargetInEdge = getTargetInEdge(tmpRnd2, target, chQueryGraph);
-
-            Path refPath = new DijkstraBidirectionRef(queryGraph, createTurnWeighting(queryGraph), TraversalMode.EDGE_BASED)
-                    .calcPath(source, target, sourceOutEdge, targetInEdge);
-            Path path = createAlgo(chQueryGraph)
-                    .calcPath(source, target, chSourceOutEdge, chTargetInEdge);
-            strictViolations.addAll(comparePaths(refPath, path, source, target));
-        }
-        // we do not do a strict check because there can be ambiguity, for example when there are zero weight loops.
-        // however, when there are too many deviations we fail
-        if (strictViolations.size() > Math.max(1, 0.20 * numQueries)) {
-            fail("Too many strict violations: " + strictViolations.size() + " / " + numQueries);
-        }
-    }
-
-    private List<GHPoint> getRandomPoints(int numPoints, LocationIndex index, Random rnd) {
-        List<GHPoint> points = new ArrayList<>(numPoints);
-        BBox bounds = graph.getBounds();
-        final int maxAttempts = 100 * numPoints;
-        int attempts = 0;
-        while (attempts < maxAttempts && points.size() < numPoints) {
-            double lat = rnd.nextDouble() * (bounds.maxLat - bounds.minLat) + bounds.minLat;
-            double lon = rnd.nextDouble() * (bounds.maxLon - bounds.minLon) + bounds.minLon;
-            QueryResult queryResult = index.findClosest(lat, lon, EdgeFilter.ALL_EDGES);
-            if (queryResult.isValid()) {
-                points.add(new GHPoint(lat, lon));
-            }
-            attempts++;
-        }
-        assertEquals("could not find valid random points after " + attempts + " attempts", numPoints, points.size());
-        return points;
-    }
-
-    private List<QueryResult> findQueryResults(LocationIndexTree index, List<GHPoint> ghPoints) {
-        List<QueryResult> result = new ArrayList<>(ghPoints.size());
-        for (GHPoint ghPoint : ghPoints) {
-            result.add(index.findClosest(ghPoint.getLat(), ghPoint.getLon(), DefaultEdgeFilter.ALL_EDGES));
-        }
-        return result;
-    }
-
-    private List<String> comparePaths(Path refPath, Path path, int source, int target) {
-        List<String> strictViolations = new ArrayList<>();
-        double refWeight = refPath.getWeight();
-        double weight = path.getWeight();
-        if (Math.abs(refWeight - weight) > 1.e-2) {
-            System.out.println("expected: " + refPath.calcNodes());
-            System.out.println("given:    " + path.calcNodes());
-            fail("wrong weight: " + source + "->" + target + ", expected: " + refWeight + ", given: " + weight);
-        }
-        if (Math.abs(path.getDistance() - refPath.getDistance()) > 1.e-1) {
-            strictViolations.add("wrong distance " + source + "->" + target + ", expected: " + refPath.getDistance() + ", given: " + path.getDistance());
-        }
-        if (Math.abs(path.getTime() - refPath.getTime()) > 50) {
-            strictViolations.add("wrong time " + source + "->" + target + ", expected: " + refPath.getTime() + ", given: " + path.getTime());
-        }
-        if (!refPath.calcNodes().equals(path.calcNodes())) {
-            strictViolations.add("wrong nodes " + source + "->" + target + "\nexpected: " + refPath.calcNodes() + "\ngiven:    " + path.calcNodes());
-        }
-        return strictViolations;
-    }
-
-    private GraphHopperStorage createGraph() {
-        GraphHopperStorage gh = new GraphHopperStorage(Collections.singletonList(CHProfile.edgeBased(weighting)), dir, encodingManager,
-                false, turnCostExtension);
-        gh.create(1000);
-        return gh;
-    }
-
-    private int getTargetInEdge(Random rnd, int node, Graph graph) {
-        return getAdjEdge(rnd, node, graph);
-    }
-
-    private int getSourceOutEdge(Random rnd, int node, Graph graph) {
-        return getAdjEdge(rnd, node, graph);
-    }
-
-    private int getAdjEdge(Random rnd, int node, Graph graph) {
-        // sometimes do not restrict anything
-        if (rnd.nextDouble() < 0.05) {
-            return ANY_EDGE;
-        }
-        // sometimes use NO_EDGE
-        if (rnd.nextDouble() < 0.05) {
-            return NO_EDGE;
-        }
-        // use all edge explorer, sometimes we will find an edge we can restrict sometimes we do not
-        EdgeExplorer explorer = graph.createEdgeExplorer();
-        EdgeIterator iter = explorer.setBaseNode(node);
-        List<Integer> edgeIds = new ArrayList<>();
-        while (iter.next()) {
-            if (iter instanceof CHEdgeIteratorState && ((CHEdgeIteratorState) iter).isShortcut()) {
-                // skip shortcuts here so we get the same restricted edges for a normal query graph and a
-                // query graph that wraps a CH graph (provided that the rnd number generator is in the same
-                // state)!
-                continue;
-            }
-            edgeIds.add(iter.getOrigEdgeFirst());
-            edgeIds.add(iter.getOrigEdgeLast());
-        }
-        return edgeIds.isEmpty() ? ANY_EDGE : edgeIds.get(rnd.nextInt(edgeIds.size()));
-    }
-
-    private int getRandom(Random rnd) {
-        return rnd.nextInt(graph.getNodes());
-    }
-
-}
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/GenericWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/GenericWeightingTest.java
index 2ae8440fb..2a7404ae8 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/GenericWeightingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/GenericWeightingTest.java
@@ -18,19 +18,21 @@
 package com.graphhopper.routing.weighting;
 
 import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.util.DataFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.routing.util.parsers.OSMMaxHeightParser;
+import com.graphhopper.routing.util.parsers.OSMMaxWeightParser;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.GHUtility;
+import com.graphhopper.util.PMap;
 import org.junit.Before;
 import org.junit.Test;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -59,8 +61,8 @@ public class GenericWeightingTest {
         graph = new GraphBuilder(em).create();
         // 0-1
         graph.edge(0, 1, 1, true);
-        updateDistancesFor(graph, 0, 0.00, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
         EncodingManager.AcceptWay map = new EncodingManager.AcceptWay().put(encoder.toString(), EncodingManager.Access.WAY);
         graph.getEdgeIteratorState(0, 1).setFlags(em.handleWayTags(way, map, 0));
     }
@@ -95,8 +97,8 @@ public class GenericWeightingTest {
 
         // 0-1
         simpleGraph.edge(0, 1, 1, true);
-        updateDistancesFor(simpleGraph, 0, 0.00, 0.00);
-        updateDistancesFor(simpleGraph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(simpleGraph, 0, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(simpleGraph, 1, 0.01, 0.01);
         EncodingManager.AcceptWay map = new EncodingManager.AcceptWay().put(encoder.toString(), EncodingManager.Access.WAY);
         simpleGraph.getEdgeIteratorState(0, 1).setFlags(simpleEncodingManager.handleWayTags(way, map, 0));
 
diff --git a/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java b/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java
index fe38b067b..55982a04e 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java
@@ -18,6 +18,7 @@
 package com.graphhopper.storage;
 
 import com.graphhopper.coll.GHIntHashSet;
+import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.DefaultEdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
@@ -31,7 +32,6 @@ import org.junit.Test;
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -52,11 +52,11 @@ public class GraphEdgeIdFinderTest {
         graph.edge(3, 4, 1, true);
         graph.edge(0, 3, 1, true);
         graph.edge(1, 4, 1, true);
-        updateDistancesFor(graph, 0, 0.01, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
-        updateDistancesFor(graph, 2, 0.01, 0.02);
-        updateDistancesFor(graph, 3, 0.00, 0.00);
-        updateDistancesFor(graph, 4, 0.00, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.01, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 2, 0.01, 0.02);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 3, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 4, 0.00, 0.01);
 
         LocationIndex locationIndex = new LocationIndexTree(graph, new RAMDirectory())
                 .prepareIndex();
@@ -104,18 +104,18 @@ public class GraphEdgeIdFinderTest {
         graph.edge(9, 10, 1, true); // 11
         graph.edge(10, 11, 1, true); // 12
 
-        updateDistancesFor(graph, 0, 2, 0);
-        updateDistancesFor(graph, 1, 2, 1);
-        updateDistancesFor(graph, 2, 2, 2);
-        updateDistancesFor(graph, 3, 2, 3);
-        updateDistancesFor(graph, 4, 1, 0);
-        updateDistancesFor(graph, 5, 1, 1);
-        updateDistancesFor(graph, 6, 1, 2);
-        updateDistancesFor(graph, 7, 1, 3);
-        updateDistancesFor(graph, 8, 0, 0);
-        updateDistancesFor(graph, 9, 0, 1);
-        updateDistancesFor(graph, 10, 0, 2);
-        updateDistancesFor(graph, 11, 0, 3);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 2, 0);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 2, 1);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 2, 2, 2);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 3, 2, 3);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 4, 1, 0);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 5, 1, 1);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 6, 1, 2);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 7, 1, 3);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 8, 0, 0);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 9, 0, 1);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 10, 0, 2);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 11, 0, 3);
 
         LocationIndex locationIndex = new LocationIndexTree(graph, new RAMDirectory())
                 .prepareIndex();
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
index 92f4bd084..f4b4443ce 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
@@ -36,7 +36,6 @@ import java.util.Collections;
 import java.util.List;
 
 import static com.graphhopper.routing.ch.NodeBasedNodeContractorTest.SC_ACCESS;
-import static com.graphhopper.util.EdgeIterator.NO_EDGE;
 import static org.junit.Assert.*;
 
 /**
@@ -57,8 +56,11 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
     }
 
     private GraphHopperStorage newGHStorage(Directory dir, boolean is3D, boolean forEdgeBasedTraversal) {
-        CHProfile chProfile = new CHProfile(new FastestWeighting(carEncoder), forEdgeBasedTraversal);
-        return new GraphHopperStorage(Collections.singletonList(chProfile), dir, encodingManager, is3D, new GraphExtension.NoOpExtension());
+        if (forEdgeBasedTraversal) {
+            return new GraphHopperStorage(Collections.<Weighting>emptyList(), Arrays.asList(new FastestWeighting(carEncoder)), dir, encodingManager, is3D, new GraphExtension.NoOpExtension());
+        } else {
+            return new GraphHopperStorage(Arrays.asList(new FastestWeighting(carEncoder)), Collections.<Weighting>emptyList(), dir, encodingManager, is3D, new GraphExtension.NoOpExtension());
+        }
     }
 
     @Test
@@ -106,8 +108,15 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         g.edge(10, 11, 1, true);
 
         graph.freeze();
-        g.shortcut(3, 4, PrepareEncoder.getScDirMask(), 0, 40, NO_EDGE, NO_EDGE);
-        g.shortcut(0, 4, PrepareEncoder.getScDirMask(), 0, 40, NO_EDGE, NO_EDGE);
+        CHEdgeIteratorState tmpIter = g.shortcut(3, 4);
+        tmpIter.setFlagsAndWeight(PrepareEncoder.getScDirMask(), 0);
+        tmpIter.setDistance(40);
+        assertEquals(EdgeIterator.NO_EDGE, tmpIter.getSkippedEdge1());
+        assertEquals(EdgeIterator.NO_EDGE, tmpIter.getSkippedEdge2());
+
+        tmpIter = g.shortcut(0, 4);
+        tmpIter.setFlagsAndWeight(PrepareEncoder.getScDirMask(), 0);
+        tmpIter.setDistance(40);
         g.setLevel(0, 1);
         g.setLevel(4, 1);
 
@@ -116,8 +125,8 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         iter = g.createEdgeExplorer().setBaseNode(2);
         assertEquals(2, GHUtility.count(iter));
 
-        int sc = g.shortcut(5, 6, PrepareEncoder.getScDirMask(), 0, 40, 1, 2);
-        CHEdgeIteratorState tmpIter = g.getEdgeIteratorState(sc, 6);
+        tmpIter = g.shortcut(5, 6);
+        tmpIter.setSkippedEdges(1, 2);
         assertEquals(1, tmpIter.getSkippedEdge1());
         assertEquals(2, tmpIter.getSkippedEdge2());
     }
@@ -135,9 +144,18 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         // only remove edges
         lg.edge(4, 1, 30, true);
         graph.freeze();
-        lg.shortcut(1, 2, PrepareEncoder.getScDirMask(), 0, 10, 10, 11);
-        lg.shortcut(1, 0, PrepareEncoder.getScFwdDir(), 0, 20, 12, 13);
-        lg.shortcut(3, 1, PrepareEncoder.getScFwdDir(), 0, 30, 14, 15);
+        CHEdgeIteratorState tmp = lg.shortcut(1, 2);
+        tmp.setFlagsAndWeight(PrepareEncoder.getScDirMask(), 0);
+        tmp.setDistance(10);
+        tmp.setSkippedEdges(10, 11);
+        tmp = lg.shortcut(1, 0);
+        tmp.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 0);
+        tmp.setDistance(20);
+        tmp.setSkippedEdges(12, 13);
+        tmp = lg.shortcut(3, 1);
+        tmp.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 0);
+        tmp.setDistance(30);
+        tmp.setSkippedEdges(14, 15);
         // create everytime a new independent iterator for disconnect method
         EdgeIterator iter = lg.createEdgeExplorer().setBaseNode(1);
         iter.next();
@@ -176,8 +194,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
 
         // only remove edges
         int flags = PrepareEncoder.getScDirMask();
-        int sc = g.shortcut(0, 1, flags, 5, 5, NO_EDGE, NO_EDGE);
-        CHEdgeIteratorState sc1 = g.getEdgeIteratorState(sc, 1);
+        CHEdgeIteratorState sc1 = g.shortcut(0, 1);
         assertTrue(sc1.isShortcut());
         sc1.setWeight(2.001);
         assertEquals(2.001, sc1.getWeight(), 1e-3);
@@ -207,21 +224,23 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         FlagEncoder customEncoder = new Bike2WeightFlagEncoder();
         EncodingManager em = EncodingManager.create(customEncoder);
         FastestWeighting weighting = new FastestWeighting(customEncoder);
-        GraphHopperStorage ghStorage = new GraphBuilder(em).setCHProfiles(CHProfile.nodeBased(weighting)).create();
+        GraphHopperStorage ghStorage = new GraphBuilder(em).setCHGraph(weighting).create();
         ghStorage.edge(0, 2);
         ghStorage.freeze();
 
-        CHGraph lg = ghStorage.getCHGraph();
-        int sc1 = lg.shortcut(0, 1, PrepareEncoder.getScFwdDir(), 100.123, 0, NO_EDGE, NO_EDGE);
+        CHGraph lg = ghStorage.getCHGraph(weighting);
+        CHEdgeIteratorState sc1 = lg.shortcut(0, 1);
+        sc1.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 100.123);
 
-        assertEquals(100.123, lg.getEdgeIteratorState(sc1, 1).getWeight(), 1e-3);
-        assertEquals(100.123, lg.getEdgeIteratorState(sc1, 0).getWeight(), 1e-3);
-        assertEquals(100.123, ((CHEdgeIteratorState) GHUtility.getEdge(lg, 0, 1)).getWeight(), 1e-3);
-        assertEquals(100.123, ((CHEdgeIteratorState) GHUtility.getEdge(lg, 1, 0)).getWeight(), 1e-3);
+        assertEquals(100.123, lg.getEdgeIteratorState(sc1.getEdge(), sc1.getAdjNode()).getWeight(), 1e-3);
+        assertEquals(100.123, lg.getEdgeIteratorState(sc1.getEdge(), sc1.getBaseNode()).getWeight(), 1e-3);
+        assertEquals(100.123, ((CHEdgeIteratorState) GHUtility.getEdge(lg, sc1.getBaseNode(), sc1.getAdjNode())).getWeight(), 1e-3);
+        assertEquals(100.123, ((CHEdgeIteratorState) GHUtility.getEdge(lg, sc1.getAdjNode(), sc1.getBaseNode())).getWeight(), 1e-3);
 
-        int sc2 = lg.shortcut(1, 0, PrepareEncoder.getScDirMask(), 1.011011, 0, NO_EDGE, NO_EDGE);
-        assertEquals(1.011011, lg.getEdgeIteratorState(sc2, 0).getWeight(), 1e-3);
-        assertEquals(1.011011, lg.getEdgeIteratorState(sc2, 1).getWeight(), 1e-3);
+        sc1 = lg.shortcut(1, 0);
+        assertTrue(sc1.isShortcut());
+        sc1.setFlagsAndWeight(PrepareEncoder.getScDirMask(), 1.011011);
+        assertEquals(1.011011, sc1.getWeight(), 1e-3);
     }
 
     @Test
@@ -254,7 +273,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         EdgeIteratorState edge1 = chGraph.edge(0, 1);
         chGraph.edge(1, 2);
         graph.freeze();
-        chGraph.shortcut(0, 1, PrepareEncoder.getScDirMask(), 10, 10, NO_EDGE, NO_EDGE);
+        chGraph.shortcut(0, 1);
 
         QueryGraph qGraph = new QueryGraph(chGraph);
         QueryResult fromRes = createQR(1.004, 1.01, 0, edge1);
@@ -302,7 +321,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         assertEquals(1, GHUtility.count(graph.createEdgeExplorer().setBaseNode(1)));
         assertEquals(1, GHUtility.count(chGraph.createEdgeExplorer().setBaseNode(1)));
 
-        chGraph.shortcut(2, 3, PrepareEncoder.getScDirMask(), 10, 10, NO_EDGE, NO_EDGE);
+        chGraph.shortcut(2, 3);
 
         // should be identical to access without shortcut
         assertEquals(1, GHUtility.count(graph.createEdgeExplorer().setBaseNode(1)));
@@ -348,7 +367,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.freeze();
 
         CHGraph lg = graph.getCHGraph();
-        lg.shortcut(1, 4, PrepareEncoder.getScFwdDir(), 3, 3, NO_EDGE, NO_EDGE);
+        lg.shortcut(1, 4).setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 3);
 
         EdgeExplorer vehicleOutExplorer = lg.createEdgeExplorer(DefaultEdgeFilter.outEdges(carEncoder));
         // iteration should result in same nodes even if reusing the iterator
@@ -364,7 +383,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.freeze();
 
         CHGraph lg = graph.getCHGraph();
-        lg.shortcut(1, 4, PrepareEncoder.getScDirMask(), 10, 10, NO_EDGE, NO_EDGE);
+        lg.shortcut(1, 4);
 
         AllCHEdgesIterator iter = lg.getAllEdges();
         iter.next();
@@ -384,7 +403,8 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         graph.freeze();
 
         CHGraph lg = graph.getCHGraph();
-        lg.shortcut(1, 4, PrepareEncoder.getScDirMask(), 10, 10, edge1.getEdge(), edge2.getEdge());
+        CHEdgeIteratorState shortcut = lg.shortcut(1, 4);
+        shortcut.setSkippedEdges(edge1.getEdge(), edge2.getEdge());
 
         AllCHEdgesIterator iter = lg.getAllEdges();
         iter.next();
@@ -455,7 +475,9 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
 
     private void addShortcut(CHGraph chGraph, int from, int to, boolean fwd, int firstOrigEdge, int lastOrigEdge,
                              int skipEdge1, int skipEdge2, int distance) {
-        chGraph.shortcutEdgeBased(from, to, fwd ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir(), 0, distance, skipEdge1, skipEdge2, firstOrigEdge, lastOrigEdge);
+        CHEdgeIteratorState shortcut = chGraph.shortcut(from, to);
+        shortcut.setFlagsAndWeight(fwd ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir(), 0);
+        shortcut.setFirstAndLastOrigEdges(firstOrigEdge, lastOrigEdge).setSkippedEdges(skipEdge1, skipEdge2).setDistance(distance);
     }
 
     @Test
@@ -463,46 +485,50 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         FlagEncoder tmpCar = new CarFlagEncoder();
         FlagEncoder tmpBike = new Bike2WeightFlagEncoder();
         EncodingManager em = EncodingManager.create(tmpCar, tmpBike);
-        List<CHProfile> chProfiles = Arrays.asList(
-                CHProfile.nodeBased(new FastestWeighting(tmpCar)),
-                CHProfile.nodeBased(new FastestWeighting(tmpBike)));
+        List<Weighting> chWeightings = new ArrayList<>();
+        chWeightings.add(new FastestWeighting(tmpCar));
+        chWeightings.add(new FastestWeighting(tmpBike));
         BooleanEncodedValue tmpCarAccessEnc = tmpCar.getAccessEnc();
 
-        graph = new GraphHopperStorage(chProfiles, new RAMDirectory(), em, false, new GraphExtension.NoOpExtension()).create(1000);
+        graph = new GraphHopperStorage(chWeightings, new RAMDirectory(), em, false, new GraphExtension.NoOpExtension()).create(1000);
         IntsRef edgeFlags = GHUtility.setProperties(em.createEdgeFlags(), tmpCar, 100, true, false);
         graph.edge(0, 1).setDistance(10).setFlags(GHUtility.setProperties(edgeFlags, tmpBike, 10, true, true));
         graph.edge(1, 2).setDistance(10).setFlags(edgeFlags);
 
         graph.freeze();
 
-        CHGraph carCHGraph = graph.getCHGraph(chProfiles.get(0));
+        CHGraph carCHGraph = graph.getCHGraph(chWeightings.get(0));
         // enable forward directions for car
-        int carSC02 = carCHGraph.shortcut(0, 2, PrepareEncoder.getScFwdDir(), 10, 20, NO_EDGE, NO_EDGE);
+        CHEdgeIteratorState carSC02 = carCHGraph.shortcut(0, 2);
+        carSC02.setFlagsAndWeight(PrepareEncoder.getScFwdDir(), 10);
+        carSC02.setDistance(20);
 
-        CHGraph bikeCHGraph = graph.getCHGraph(chProfiles.get(1));
+        CHGraph bikeCHGraph = graph.getCHGraph(chWeightings.get(1));
+        CHEdgeIteratorState bikeSC02 = bikeCHGraph.shortcut(0, 2);
         // enable both directions for bike
-        int bikeSC02 = bikeCHGraph.shortcut(0, 2, PrepareEncoder.getScDirMask(), 10, 20, NO_EDGE, NO_EDGE);
+        bikeSC02.setFlagsAndWeight(PrepareEncoder.getScDirMask(), 10);
+        bikeSC02.setDistance(20);
 
         // assert car CH graph
-        assertTrue(carCHGraph.getEdgeIteratorState(carSC02, 2).get(tmpCarAccessEnc));
-        assertFalse(carCHGraph.getEdgeIteratorState(carSC02, 2).getReverse(tmpCarAccessEnc));
+        assertTrue(carCHGraph.getEdgeIteratorState(carSC02.getEdge(), 2).get(tmpCarAccessEnc));
+        assertFalse(carCHGraph.getEdgeIteratorState(carSC02.getEdge(), 2).getReverse(tmpCarAccessEnc));
 
         BooleanEncodedValue tmpBikeAccessEnc = tmpBike.getAccessEnc();
 
         // throw exception for wrong encoder
         try {
-            assertFalse(carCHGraph.getEdgeIteratorState(carSC02, 2).get(tmpBikeAccessEnc));
+            assertFalse(carCHGraph.getEdgeIteratorState(carSC02.getEdge(), 2).get(tmpBikeAccessEnc));
             fail();
         } catch (AssertionError ex) {
         }
 
         // assert bike CH graph
-        assertTrue(bikeCHGraph.getEdgeIteratorState(bikeSC02, 2).get(tmpBikeAccessEnc));
-        assertTrue(bikeCHGraph.getEdgeIteratorState(bikeSC02, 2).getReverse(tmpBikeAccessEnc));
+        assertTrue(bikeCHGraph.getEdgeIteratorState(bikeSC02.getEdge(), 2).get(tmpBikeAccessEnc));
+        assertTrue(bikeCHGraph.getEdgeIteratorState(bikeSC02.getEdge(), 2).getReverse(tmpBikeAccessEnc));
 
         // throw exception for wrong encoder
         try {
-            assertFalse(bikeCHGraph.getEdgeIteratorState(bikeSC02, 2).getReverse(tmpCarAccessEnc));
+            assertFalse(bikeCHGraph.getEdgeIteratorState(bikeSC02.getEdge(), 2).getReverse(tmpCarAccessEnc));
             fail();
         } catch (AssertionError ex) {
         }
@@ -563,15 +589,15 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         // now we create a new storage but only use one of the weightings, which should be ok
         GraphHopperStorage smallStorage = createStorageWithWeightings(edgeBased, weighting1);
         smallStorage.loadExisting();
-        assertEquals(edgeBased ? 0 : 1, smallStorage.getCHProfiles(false).size());
-        assertEquals(edgeBased ? 1 : 0, smallStorage.getCHProfiles(true).size());
+        assertEquals(edgeBased ? 0 : 1, smallStorage.getNodeBasedCHWeightings().size());
+        assertEquals(edgeBased ? 1 : 0, smallStorage.getEdgeBasedCHWeightings().size());
         smallStorage.flush();
 
         // now we create yet another storage that uses both weightings again, which still works
         GraphHopperStorage fullStorage = createStorageWithWeightings(edgeBased, weighting1, weighting2);
         fullStorage.loadExisting();
-        assertEquals(edgeBased ? 0 : 2, fullStorage.getCHProfiles(false).size());
-        assertEquals(edgeBased ? 2 : 0, fullStorage.getCHProfiles(true).size());
+        assertEquals(edgeBased ? 0 : 2, fullStorage.getNodeBasedCHWeightings().size());
+        assertEquals(edgeBased ? 2 : 0, fullStorage.getEdgeBasedCHWeightings().size());
         fullStorage.flush();
     }
 
@@ -589,17 +615,17 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         // now we create a new storage but only use the edge weighting, which should be ok
         GraphHopperStorage edgeStorage = createStorageWithWeightings(true, weighting2);
         edgeStorage.loadExisting();
-        assertEquals(0, edgeStorage.getCHProfiles(false).size());
-        assertEquals(1, edgeStorage.getCHProfiles(true).size());
+        assertEquals(0, edgeStorage.getNodeBasedCHWeightings().size());
+        assertEquals(1, edgeStorage.getEdgeBasedCHWeightings().size());
         edgeStorage.flush();
 
         // now we create yet another storage that uses one of the node and the edge weighting, which still works
         GraphHopperStorage mixedStorage = createStorageWithWeightings(
-                Collections.<Weighting>singletonList(weighting1),
-                Collections.<Weighting>singletonList(weighting2));
+                Arrays.<Weighting>asList(weighting1),
+                Arrays.<Weighting>asList(weighting2));
         mixedStorage.loadExisting();
-        assertEquals(1, mixedStorage.getCHProfiles(false).size());
-        assertEquals(1, mixedStorage.getCHProfiles(true).size());
+        assertEquals(1, mixedStorage.getNodeBasedCHWeightings().size());
+        assertEquals(1, mixedStorage.getNodeBasedCHWeightings().size());
         mixedStorage.flush();
     }
 
@@ -610,14 +636,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
     }
 
     private GraphHopperStorage createStorageWithWeightings(List<Weighting> nodeBasedCHWeightings, List<Weighting> edgeBasedCHWeightings) {
-        List<CHProfile> profiles = new ArrayList<>(nodeBasedCHWeightings.size() + edgeBasedCHWeightings.size());
-        for (Weighting nodeBasedCHWeighting : nodeBasedCHWeightings) {
-            profiles.add(CHProfile.nodeBased(nodeBasedCHWeighting));
-        }
-        for (Weighting edgeBasedCHWeighting : edgeBasedCHWeightings) {
-            profiles.add(CHProfile.edgeBased(edgeBasedCHWeighting));
-        }
-        return new GraphHopperStorage(profiles,
+        return new GraphHopperStorage(nodeBasedCHWeightings, edgeBasedCHWeightings,
                 new GHDirectory(defaultGraphLoc, DAType.RAM_STORE), encodingManager, false, new GraphExtension.NoOpExtension());
     }
 }
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageForDataFlagEncoderTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageForDataFlagEncoderTest.java
index 7aacb8315..901d2fcaf 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageForDataFlagEncoderTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageForDataFlagEncoderTest.java
@@ -2,6 +2,7 @@ package com.graphhopper.storage;
 
 import com.graphhopper.GraphHopper;
 import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.util.DataFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.EncodingManager.Access;
@@ -18,7 +19,6 @@ import org.junit.Test;
 
 import java.io.File;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.*;
 
 /**
@@ -61,8 +61,8 @@ public class GraphHopperStorageForDataFlagEncoderTest {
         way_0_1.setTag("maxheight", "4.4");
 
         graph.edge(0, 1, 1, true);
-        updateDistancesFor(graph, 0, 0.00, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
         EncodingManager.AcceptWay map = new EncodingManager.AcceptWay().put(encoder.toString(), Access.WAY);
         graph.getEdgeIteratorState(0, 1).setFlags(encodingManager.handleWayTags(way_0_1, map, 0));
 
@@ -72,7 +72,7 @@ public class GraphHopperStorageForDataFlagEncoderTest {
         way_1_2.setTag("maxweight", "45");
 
         graph.edge(1, 2, 1, true);
-        updateDistancesFor(graph, 2, 0.02, 0.02);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 2, 0.02, 0.02);
         graph.getEdgeIteratorState(1, 2).setFlags(encodingManager.handleWayTags(way_1_2, map, 0));
 
         // 2-0
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java
index 09030265b..e7fdb3c55 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java
@@ -2,6 +2,7 @@ package com.graphhopper.storage;
 
 import com.graphhopper.GraphHopper;
 import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.EncodingManager.Access;
@@ -11,7 +12,6 @@ import org.junit.Test;
 import java.io.File;
 import java.util.Arrays;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
@@ -31,8 +31,8 @@ public class GraphHopperStorageLMTest {
         way_0_1.setTag("maxheight", "4.4");
 
         graph.edge(0, 1, 1, true);
-        updateDistancesFor(graph, 0, 0.00, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
         graph.getEdgeIteratorState(0, 1).setFlags(carFlagEncoder.handleWayTags(encodingManager.createEdgeFlags(), way_0_1, Access.WAY, 0));
 
         // 1-2
@@ -41,7 +41,7 @@ public class GraphHopperStorageLMTest {
         way_1_2.setTag("maxweight", "45");
 
         graph.edge(1, 2, 1, true);
-        updateDistancesFor(graph, 2, 0.02, 0.02);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 2, 0.02, 0.02);
         graph.getEdgeIteratorState(1, 2).setFlags(carFlagEncoder.handleWayTags(encodingManager.createEdgeFlags(), way_1_2, Access.WAY, 0));
 
         graph.flush();
diff --git a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
index 54670866f..840434c7d 100644
--- a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
+++ b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
@@ -49,8 +49,8 @@ public class ShortcutUnpackerTest {
         encoder = new MotorcycleFlagEncoder(5, 5, 10);
         EncodingManager encodingManager = EncodingManager.create(encoder);
         weighting = new FastestWeighting(encoder);
-        graph = new GraphBuilder(encodingManager).setCHProfiles(new CHProfile(weighting, edgeBased)).create();
-        chGraph = graph.getCHGraph();
+        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(edgeBased).create();
+        chGraph = graph.getCHGraph(weighting);
         if (edgeBased) {
             turnCostExtension = (TurnCostExtension) graph.getExtension();
         }
diff --git a/core/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java b/core/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
index 837d4d896..3feea2b09 100644
--- a/core/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
+++ b/core/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
@@ -2,6 +2,7 @@ package com.graphhopper.storage.change;
 
 import com.graphhopper.jackson.Jackson;
 import com.graphhopper.json.geo.JsonFeatureCollection;
+import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.Dijkstra;
 import com.graphhopper.routing.Path;
 import com.graphhopper.routing.profiles.BooleanEncodedValue;
@@ -25,7 +26,6 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
 
-import static com.graphhopper.util.GHUtility.updateDistancesFor;
 import static org.junit.Assert.*;
 
 /**
@@ -51,11 +51,11 @@ public class ChangeGraphHelperTest {
         graph.edge(3, 4, 1, true);
         graph.edge(0, 3, 1, true);
         graph.edge(1, 4, 1, true);
-        updateDistancesFor(graph, 0, 0.01, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
-        updateDistancesFor(graph, 2, 0.01, 0.02);
-        updateDistancesFor(graph, 3, 0.00, 0.00);
-        updateDistancesFor(graph, 4, 0.00, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.01, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 2, 0.01, 0.02);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 3, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 4, 0.00, 0.01);
         LocationIndex locationIndex = new LocationIndexTree(graph, new RAMDirectory()).prepareIndex();
 
         FlagEncoder encoder = encodingManager.getEncoder("car");
@@ -91,11 +91,11 @@ public class ChangeGraphHelperTest {
         graph.edge(3, 4, 1, true);
         graph.edge(0, 3, 1, true);
         graph.edge(1, 4, 1, true);
-        updateDistancesFor(graph, 0, 0.01, 0.00);
-        updateDistancesFor(graph, 1, 0.01, 0.01);
-        updateDistancesFor(graph, 2, 0.01, 0.02);
-        updateDistancesFor(graph, 3, 0.00, 0.00);
-        updateDistancesFor(graph, 4, 0.00, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 0, 0.01, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 1, 0.01, 0.01);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 2, 0.01, 0.02);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 3, 0.00, 0.00);
+        AbstractRoutingAlgorithmTester.updateDistancesFor(graph, 4, 0.00, 0.01);
         LocationIndex locationIndex = new LocationIndexTree(graph, new RAMDirectory()).prepareIndex();
 
         FlagEncoder encoder = encodingManager.getEncoder("car");
diff --git a/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeCHTest.java b/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeCHTest.java
index e777fe5b8..63a83f116 100644
--- a/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeCHTest.java
+++ b/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeCHTest.java
@@ -83,18 +83,23 @@ public class LocationIndexTreeCHTest extends LocationIndexTreeTest {
         // create shortcuts
         ghStorage.freeze();
         int flags = PrepareEncoder.getScDirMask();
-        int sc1 = addShortcut(lg, 0, 2, 0, 20, iter1.getEdge(), iter2.getEdge(), flags);
-        int sc2 = addShortcut(lg, 2, 4, 0, 28, iter3.getEdge(), iter4.getEdge(), flags);
-        addShortcut(lg, 0, 4, 0, 40, sc1, sc2, flags);
+        CHEdgeIteratorState iter5 = lg.shortcut(0, 2);
+        iter5.setFlagsAndWeight(flags, 0);
+        iter5.setDistance(20);
+        iter5.setSkippedEdges(iter1.getEdge(), iter2.getEdge());
+        CHEdgeIteratorState iter6 = lg.shortcut(2, 4);
+        iter6.setFlagsAndWeight(flags, 0);
+        iter6.setDistance(28);
+        iter6.setSkippedEdges(iter3.getEdge(), iter4.getEdge());
+        CHEdgeIteratorState tmp = lg.shortcut(0, 4);
+        tmp.setFlagsAndWeight(flags, 0);
+        tmp.setDistance(40);
+        tmp.setSkippedEdges(iter5.getEdge(), iter6.getEdge());
 
         LocationIndex index = createIndex(ghStorage, -1);
         assertEquals(2, findID(index, 0, 0.5));
     }
 
-    private int addShortcut(CHGraph lg, int from, int to, double weight, double distance, int skip1, int skip2, int direction) {
-        return lg.shortcut(from, to, direction, weight, distance, skip1, skip2);
-    }
-
     @Test
     public void testSortHighLevelFirst() {
         GraphHopperStorage g = createGHStorage(new RAMDirectory(), encodingManager, false);
diff --git a/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java b/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java
index 5e3d88181..02cfbb8e9 100644
--- a/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java
+++ b/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java
@@ -25,7 +25,6 @@ import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import org.junit.Test;
@@ -44,8 +43,8 @@ public class CHEdgeIteratorTest {
         EncodingManager encodingManager = EncodingManager.create(carFlagEncoder);
         FastestWeighting weighting = new FastestWeighting(carFlagEncoder);
         EdgeFilter carOutFilter = DefaultEdgeFilter.outEdges(carFlagEncoder);
-        GraphHopperStorage ghStorage = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
-        CHGraph g = ghStorage.getCHGraph();
+        GraphHopperStorage ghStorage = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
+        CHGraph g = ghStorage.getCHGraph(weighting);
         BooleanEncodedValue accessEnc = carFlagEncoder.getAccessEnc();
         DecimalEncodedValue avSpeedEnc = carFlagEncoder.getAverageSpeedEnc();
         g.edge(0, 1).setDistance(12).set(accessEnc, true).setReverse(accessEnc, true).set(avSpeedEnc, 10.0);
diff --git a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java
index 557881867..76c74de1f 100644
--- a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java
+++ b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java
@@ -111,7 +111,7 @@ public class GHUtilityTest {
         Graph g = initUnsorted(createGraph());
         g.edge(0, 0, 11, true);
 
-        CHGraph lg = new GraphBuilder(encodingManager).chGraphCreate(CHProfile.nodeBased(new FastestWeighting(carEncoder)));
+        CHGraph lg = new GraphBuilder(encodingManager).chGraphCreate(new FastestWeighting(carEncoder));
         GHUtility.copyTo(g, lg);
 
         assertEquals(g.getAllEdges().length(), lg.getEdges());
@@ -123,7 +123,7 @@ public class GHUtilityTest {
         EdgeIteratorState edgeState = g.edge(6, 5, 11, true);
         edgeState.setWayGeometry(Helper.createPointList(12, 10, -1, 3));
 
-        GraphHopperStorage newStore = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(new FastestWeighting(carEncoder))).create();
+        GraphHopperStorage newStore = new GraphBuilder(encodingManager).setCHGraph(new FastestWeighting(carEncoder)).create();
         CHGraph lg = newStore.getCHGraph();
         GHUtility.copyTo(g, lg);
         newStore.freeze();
diff --git a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
index 8d4066630..336dea3e5 100644
--- a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
@@ -101,7 +101,7 @@ public class GraphHopperIT {
         InstructionList il = arsp.getInstructions();
         assertEquals(21, il.size());
 
-        // TODO roundabout fine tuning -> enter + leave roundabout (+ two roundabouts -> is it necessary if we do not leave the street?)
+        // TODO roundabout fine tuning -> enter + leave roundabout (+ two rounabouts -> is it necessary if we do not leave the street?)
         Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.US);
         assertEquals("continue onto Avenue des Guelfes", il.get(0).getTurnDescription(tr));
         assertEquals("continue onto Avenue des Papalins", il.get(1).getTurnDescription(tr));
@@ -439,13 +439,13 @@ public class GraphHopperIT {
         request.addPoint(new GHPoint(43.74958, 7.436566));
         request.addPoint(new GHPoint(43.727687, 7.418737));
         request.setAlgorithm(ASTAR).setVehicle(vehicle).setWeighting(weightCalcStr);
-        request.setPathDetails(Collections.singletonList(Parameters.Details.AVERAGE_SPEED));
+        request.setPathDetails(Arrays.asList(Parameters.Details.AVERAGE_SPEED));
 
         GHResponse rsp = hopper.route(request);
 
         PathWrapper arsp = rsp.getBest();
         Map<String, List<PathDetail>> details = arsp.getPathDetails();
-        assertEquals(1, details.size());
+        assertTrue(details.size() == 1);
         List<PathDetail> detailList = details.get(Parameters.Details.AVERAGE_SPEED);
         assertEquals(1, detailList.size());
         assertEquals(5.0, detailList.get(0).getValue());
@@ -565,7 +565,7 @@ public class GraphHopperIT {
     }
 
     @Test
-    public void testSRTMWithInstructions() {
+    public void testSRTMWithInstructions() throws Exception {
         GraphHopper tmpHopper = new GraphHopperOSM().
                 setOSMFile(osmFile).
                 setStoreOnFlush(true).
@@ -608,7 +608,7 @@ public class GraphHopperIT {
 
         assertEquals(54, arsp.getPoints().size());
         assertEquals(new GHPoint3D(43.73068455771767, 7.421283689825812, 62.0), arsp.getPoints().get(0));
-        assertEquals(new GHPoint3D(43.727680946587874, 7.4191987684222065, 11.0), arsp.getPoints().get(arsp.getPoints().size() - 1));
+        assertEquals(new GHPoint3D(43.727680946587874, 7.4191987684222065, 11.0), arsp.getPoints().get(arsp.getPoints().size()-1));
 
         assertEquals(62, arsp.getPoints().get(0).getElevation(), 1e-2);
         assertEquals(66, arsp.getPoints().get(1).getElevation(), 1e-2);
@@ -836,7 +836,7 @@ public class GraphHopperIT {
     }
 
     @Test
-    public void testIfCHIsUsed() {
+    public void testIfCHIsUsed() throws Exception {
         // route directly after import
         executeCHFootRoute();
 
@@ -853,7 +853,7 @@ public class GraphHopperIT {
                 setStoreOnFlush(true).
                 setGraphHopperLocation(tmpGraphFile).
                 setEncodingManager(EncodingManager.create(tmpImportVehicles));
-        tmpHopper.getCHFactoryDecorator().setCHProfileStrings(weightCalcStr);
+        tmpHopper.getCHFactoryDecorator().setWeightingsAsStrings(weightCalcStr);
         tmpHopper.importOrLoad();
 
         // same query as in testMonacoWithInstructions
@@ -904,7 +904,7 @@ public class GraphHopperIT {
                         addPoint(new GHPoint(49.984565, 11.499188)).
                         addPoint(new GHPoint(49.9847, 11.499612)).
                         setVehicle("car").setWeighting("fastest").
-                        setPathDetails(Collections.singletonList(Parameters.Details.AVERAGE_SPEED));
+                        setPathDetails(Arrays.asList(Parameters.Details.AVERAGE_SPEED));
 
         GHResponse rsp = tmpHopper.route(req);
 
@@ -924,7 +924,7 @@ public class GraphHopperIT {
                 addPoint(new GHPoint(49.984352, 11.498802)).
                 addPoint(new GHPoint(49.984352, 11.498802)).
                 setVehicle("car").setWeighting("fastest").
-                setPathDetails(Collections.singletonList(Parameters.Details.AVERAGE_SPEED));
+                setPathDetails(Arrays.asList(Parameters.Details.AVERAGE_SPEED));
 
         GHResponse rsp = tmpHopper.route(req);
 
@@ -942,11 +942,11 @@ public class GraphHopperIT {
                 setEncodingManager(EncodingManager.create("car"));
 
         tmpHopper.getCHFactoryDecorator().setEnabled(true).
-                setCHProfilesAsStrings(Collections.singletonList("fastest")).
+                setWeightingsAsStrings(Arrays.asList("fastest")).
                 setDisablingAllowed(true);
 
         tmpHopper.getLMFactoryDecorator().setEnabled(true).
-                setWeightingsAsStrings(Collections.singletonList("fastest|maximum=2000")).
+                setWeightingsAsStrings(Arrays.asList("fastest|maximum=2000")).
                 setDisablingAllowed(true);
 
         tmpHopper.importOrLoad();
@@ -1075,16 +1075,4 @@ public class GraphHopperIT {
         assertNotEquals(rsp1.getHints().get("visited_nodes.sum", "_"), rsp2.getHints().get("visited_nodes.sum", "_"));
     }
 
-    @Test
-    public void testEdgeBasedRequiresTurnCostSupport() {
-        GHPoint p = new GHPoint(43.727687, 7.418737);
-        GHPoint q = new GHPoint(43.74958, 7.436566);
-        GHRequest req = new GHRequest(p, q);
-        req.getHints().put(Routing.EDGE_BASED, true);
-        req.setVehicle("foot");
-        GHResponse rsp = hopper.route(req);
-        assertTrue("using edge-based for encoder without turncost support should be an error, but got:\n" + rsp.getErrors(),
-                rsp.getErrors().toString().contains("You need a turn cost extension to make use of edge_based=true, e.g. use car|turn_costs=true"));
-    }
-
 }
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 087b20de8..214cf585f 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
@@ -225,7 +225,7 @@ public class GraphHopperOSMTest {
             gh.load(ghLoc);
             fail();
         } catch (Exception ex) {
-            assertTrue(ex.getMessage(), ex.getMessage().startsWith("You loaded a CH graph, but you did not specify any CH weightings in prepare.ch.weightings"));
+            assertTrue(ex.getMessage(), ex.getMessage().startsWith("You loaded a CH graph, but you did not specify graph.ch.weightings"));
         }
 
         Helper.removeDir(new File(ghLoc));
@@ -337,7 +337,7 @@ public class GraphHopperOSMTest {
                 setEncodingManager(EncodingManager.create("car")).
                 setGraphHopperLocation(ghLoc).
                 setDataReaderFile(testOsm);
-        instance.getCHFactoryDecorator().setCHProfileStrings("shortest");
+        instance.getCHFactoryDecorator().setWeightingsAsStrings("shortest");
         instance.importOrLoad();
         GHResponse rsp = instance.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).
                 setAlgorithm(DIJKSTRA_BI));
@@ -847,7 +847,7 @@ public class GraphHopperOSMTest {
         GraphHopper tmp = new GraphHopperOSM().
                 setCHEnabled(withCH).
                 setEncodingManager(encodingManager);
-        tmp.getCHFactoryDecorator().setCHProfileStrings("fastest");
+        tmp.getCHFactoryDecorator().setWeightingsAsStrings("fastest");
         tmp.setGraphHopperStorage(g);
         tmp.postProcessing();
 
@@ -934,7 +934,7 @@ public class GraphHopperOSMTest {
                 PrepareContractionHierarchies pch = (PrepareContractionHierarchies) raf;
                 assertTrue("Preparation wasn't run! [" + threadCount + "]", pch.isPrepared());
 
-                String name = pch.getCHProfile().toFileName();
+                String name = AbstractWeighting.weightingToFileName(pch.getWeighting(), pch.isEdgeBased());
                 Long singleThreadShortcutCount = shortcutCountMap.get(name);
                 if (singleThreadShortcutCount == null)
                     shortcutCountMap.put(name, pch.getShortcuts());
@@ -947,7 +947,7 @@ public class GraphHopperOSMTest {
 
                 String key = Parameters.CH.PREPARE + "date." + name;
                 String value = tmpGH.getGraphHopperStorage().getProperties().get(key);
-                assertFalse("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", value.isEmpty());
+                assertTrue("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", !value.isEmpty());
             }
             tmpGH.close();
         }
@@ -978,7 +978,7 @@ public class GraphHopperOSMTest {
             for (PrepareLandmarks prepLM : tmpGH.getLMFactoryDecorator().getPreparations()) {
                 assertTrue("Preparation wasn't run! [" + threadCount + "]", prepLM.isPrepared());
 
-                String name = AbstractWeighting.weightingToFileName(prepLM.getWeighting());
+                String name = AbstractWeighting.weightingToFileName(prepLM.getWeighting(), false);
                 Integer singleThreadShortcutCount = landmarkCount.get(name);
                 if (singleThreadShortcutCount == null)
                     landmarkCount.put(name, prepLM.getSubnetworksWithLandmarks());
@@ -991,7 +991,7 @@ public class GraphHopperOSMTest {
 
                 String key = Parameters.Landmark.PREPARE + "date." + name;
                 String value = tmpGH.getGraphHopperStorage().getProperties().get(key);
-                assertFalse("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", value.isEmpty());
+                assertTrue("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", !value.isEmpty());
             }
             tmpGH.close();
         }
@@ -1009,10 +1009,10 @@ public class GraphHopperOSMTest {
         Weighting fwTruck = new FastestWeighting(truck);
         RAMDirectory ramDir = new RAMDirectory();
         GraphHopperStorage storage = new GraphHopperStorage(Arrays.asList(fwSimpleTruck, fwTruck), ramDir, em, false, new GraphExtension.NoOpExtension());
-        decorator.addCHProfile(CHProfile.nodeBased(fwSimpleTruck));
-        decorator.addCHProfile(CHProfile.nodeBased(fwTruck));
-        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, CHProfile.nodeBased(fwSimpleTruck)));
-        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, CHProfile.nodeBased(fwTruck)));
+        decorator.addNodeBasedWeighting(fwSimpleTruck);
+        decorator.addNodeBasedWeighting(fwTruck);
+        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, fwSimpleTruck, TraversalMode.NODE_BASED));
+        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, fwTruck, TraversalMode.NODE_BASED));
 
         HintsMap wMap = new HintsMap("fastest");
         wMap.put("vehicle", "truck");
@@ -1021,10 +1021,10 @@ public class GraphHopperOSMTest {
         assertEquals("fastest|simple_truck", ((PrepareContractionHierarchies) decorator.getDecoratedAlgorithmFactory(null, wMap)).getWeighting().toString());
 
         // make sure weighting cannot be mixed
-        decorator.addCHProfile(CHProfile.nodeBased(fwTruck));
-        decorator.addCHProfile(CHProfile.nodeBased(fwSimpleTruck));
+        decorator.addNodeBasedWeighting(fwTruck);
+        decorator.addNodeBasedWeighting(fwSimpleTruck);
         try {
-            decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, CHProfile.nodeBased(fwSimpleTruck)));
+            decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, fwSimpleTruck, TraversalMode.NODE_BASED));
             fail();
         } catch (Exception ex) {
         }
@@ -1032,17 +1032,17 @@ public class GraphHopperOSMTest {
 
     @Test
     public void testGetMultipleWeightingsForCH() {
-        EncodingManager em = EncodingManager.create(Collections.singletonList(new CarFlagEncoder()), 8);
+        EncodingManager em = EncodingManager.create(Arrays.asList(new CarFlagEncoder()), 8);
 
         GraphHopper tmpGH = new GraphHopperOSM().
                 setStoreOnFlush(false).
                 setEncodingManager(em);
-        tmpGH.getCHFactoryDecorator().setCHProfileStrings("fastest", "shortest");
+        tmpGH.getCHFactoryDecorator().setWeightingsAsStrings("fastest", "shortest");
 
-        assertEquals(2, tmpGH.getCHFactoryDecorator().getCHProfileStrings().size());
+        assertEquals(2, tmpGH.getCHFactoryDecorator().getWeightingsAsStrings().size());
     }
 
-    private static class TestEncoder extends CarFlagEncoder {
+    class TestEncoder extends CarFlagEncoder {
         private final String name;
 
         public TestEncoder(String name) {
diff --git a/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java b/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
index f3ef03f86..1ea121c3d 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
@@ -569,7 +569,7 @@ public class RoutingAlgorithmWithOSMIT {
 
             if (withCH)
                 hopper.getCHFactoryDecorator().
-                        addCHProfileAsString(weightStr).
+                        addWeighting(weightStr).
                         setEnabled(true).
                         setEdgeBasedCHMode(CHAlgoFactoryDecorator.EdgeBasedCHMode.EDGE_OR_NODE).
                         setDisablingAllowed(true);
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 8021cac9a..d4c9d95dd 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
@@ -41,6 +41,8 @@ public class TrafficChangeWithNodeOrderingReusingTest {
     // make sure to increase xmx/xms for the JVM created by the surefire plugin in parent pom.xml
     private static final String OSM_FILE = "../local/maps/berlin-latest.osm.pbf";
 
+    private final Weighting baseWeighting;
+    private final Weighting trafficWeighting;
     private final GraphHopperStorage ghStorage;
     private final CHGraph baseCHGraph;
     private final CHGraph trafficCHGraph;
@@ -55,12 +57,12 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         this.maxDeviationPercentage = maxDeviationPercentage;
         FlagEncoder encoder = new CarFlagEncoder();
         EncodingManager em = EncodingManager.create(encoder);
-        CHProfile baseProfile = CHProfile.nodeBased(new FastestWeighting(encoder));
-        CHProfile trafficProfile = CHProfile.nodeBased(new RandomDeviationWeighting(baseProfile.getWeighting(), maxDeviationPercentage));
+        baseWeighting = new FastestWeighting(encoder);
+        trafficWeighting = new RandomDeviationWeighting(baseWeighting, maxDeviationPercentage);
         Directory dir = new RAMDirectory("traffic-change-test");
-        ghStorage = new GraphHopperStorage(Arrays.asList(baseProfile, trafficProfile), dir, em, false, new GraphExtension.NoOpExtension());
-        baseCHGraph = ghStorage.getCHGraph(baseProfile);
-        trafficCHGraph = ghStorage.getCHGraph(trafficProfile);
+        ghStorage = new GraphHopperStorage(Arrays.asList(baseWeighting, trafficWeighting), dir, em, false, new GraphExtension.NoOpExtension());
+        baseCHGraph = ghStorage.getCHGraph(baseWeighting);
+        trafficCHGraph = ghStorage.getCHGraph(trafficWeighting);
     }
 
     @Test
@@ -77,7 +79,7 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         ghStorage.freeze();
 
         // create CH
-        PrepareContractionHierarchies basePch = new PrepareContractionHierarchies(baseCHGraph);
+        PrepareContractionHierarchies basePch = new PrepareContractionHierarchies(baseCHGraph, baseWeighting, TraversalMode.NODE_BASED);
         basePch.doWork();
 
         // check correctness & performance
@@ -85,7 +87,7 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         runPerformanceTest(ghStorage, baseCHGraph, basePch, seed, numQueries);
 
         // now we re-use the contraction order from the previous contraction and re-run it with the traffic weighting
-        PrepareContractionHierarchies trafficPch = new PrepareContractionHierarchies(trafficCHGraph)
+        PrepareContractionHierarchies trafficPch = new PrepareContractionHierarchies(trafficCHGraph, trafficWeighting, TraversalMode.NODE_BASED)
                 .useFixedNodeOrdering(baseCHGraph.getNodeOrderingProvider());
         trafficPch.doWork();