Repository: graphhopper/graphhopper
Pull request #1818 (2019-12-05 13:37:23)

Use PrepareCHGraph adapter for CH preparation
This PR adds a new class `PrepareCHGraph` (+ corresponding `PrepareCHEdgeExplorer+Iterator`) that basically wraps `CHGraph` to provide a smaller interface used by `PrepareContractionHierarchies` and the `NodeContractor`s. 

These are the most important changes:

- There is `PrepareCHIterator.getWeight()`, which either returns the shortcut weight or calculates the weight of an original edge. Previously we exposed the `EdgeIteratorState` interface of the iterator, and passed it into `PrepareWeighting` again to make this distinction. Now that the `Weighting` is available inside the iterator we can achieve the same without exposing all the `EdgeIteratorState` methods. Maybe this approach has benefits in other places as well like checking for `edgeWeight==infinity` instead of using `accessEnc` inside the edge iterator).

- There is a new class `NodeBasedWitnessPathSearcher`, which at the moment is a copy of `DijkstraOneToMany` minus some methods that are not needed for CH preparation like `calcPaths()` etc. This is partly necessary, because `PrepareCHGraph` is not a `Graph` and partly useful, because we can adjust it to the CH preparation without having to worry about other uses of `DijkstraOneToMany`. It sure means there is some kind of code duplication, but I think this is not so much of a problem here/its better than making the code overly generic in this case (imo, happy to hear other opinions).

-  `PrepareCHGraph` has methods like `createIn/OutEdgeExplorer`, so we no longer need to pass the edge filter/access enc when creating the explorer.

Why is this useful ? Its maybe not so apparent here already, but it takes some 'load' off `CHGraph`, because `CHGraph` is used in multiple ways that can be optimized for their specific use. Currently we use it to:

1) run CH algorithms on it
2) find shortcuts during CH preparation
3) add shortcuts to it (build it)
4) store it on disk, load it from memory etc.

`PrepareCHGraph` does 2) and 3) for now, even though this will probably be split further into two separate parts soon. Most importantly `PrepareCHGraph` is not (compared to the previous `CHGraph`) concerned with 1) and 4) anymore.

So far there is no new functionality, but this PR is part of a bigger refactoring: #1780. Its even very likely that the code changed here will change again very soon. I still thought it might be useful to merge this already to prevent a giant pull request at the end of this refactoring. What do you think ?


easbar: > But isn't it actually just a bit moved around?

Yes, its just moved around. I would argue it is now easier to use the weighting to determine the fwd/bwd access, because we have it right there in the iterator. But this is not very crucial here (it would not yet justify all this refactoring).

> And why are there isForward & isBackward methods directly in the iterator? (in the future I would prefer to rely only on the Weighting for this)

I think its only needed for some heuristic in edge-based CH preparation, but its probably possible to remove it. But implementing this via the weighting would be very easy, because the iterator now has access to the weighting.

> in PrepareCHGraph still the EdgeFilter is used.

Yes. This is because at the moment `PrepareCHGraph` simply relies on `CHGraph` (its just a wrapper around `CHGraph` plus the `Weighting`), but this is more or less temporary. I am almost certain that it is very inefficient to use `CHGraph` during the preparation, because the number of shortcuts increases during the preparation (there are more and more shortcuts, even though we filter them out). This is what I meant with splitting `PrepareCHGraph` into 2) and 3). Sorry its probably hard to follow.

> So I'm a bit unsure about the goal of this refactoring - can you explain? 

Yes I understand, I am sure its hard to see the benefits at the moment :) The main goal is separating the concerns of CHGraph, because it does (at least) the four things I mentioned above and they all can be optimized in a different way:

1) run CH algorithms on it: Here the CH graph is read only and we do not need to add shortcuts anymore, so its most important to use a memory layout that allows for fast edge iteration. Also its important to use little memory to allow loading large graphs / multiple profiles. Also when we are considering flexible CH these algorithms should know very little about the CH graph implementation. This is the main reason for #1780: Right know the `CHGraph` interface is way too broad.

2) find shortcuts during CH preparation: To do this its most beneficial to entirely remove edges at lower levels, because they are no longer considered for witness paths. This requires us to efficiently remove edges from the CH graph (instead of just filtering them out as we currently do)

3) add shortcuts to it (build it): this also happens during the CH preparation, but the CH preparation being built does not necessarily have to be the same graph as the one used in 2).

4) store it on disk, load it from memory etc.: This does not require a specific data structure, but its important to be able to store/load the graph we obtain in 3) while for 1) these methods are entirely irrelevant.

The migration path I see is to change all the code that uses `CHGraph` such that it only uses the parts of it it is interested in. For example the `NodeContractor`s only care about what is `PrepareCHGraph` here. When this is done it should be easier to change the single parts into something that is optimized for the specific use case. At the beginning I would like to simply delegate to the existing `CHGraph` implementation (thats what I did here for the `PrepareCHGraph`).




easbar: > And why are there isForward & isBackward methods directly in the iterator? (in the future I would prefer to rely only on the Weighting for this)

Ok, but how ? I am thinking the easiest way is what I did here:

```java
// the graph is weighted: it knows the weighting. 
MyGraph g = new MyGraph(baseGraph, weighting);
// when the explorer is created it also gets a reference to the weighting internally
MyIter iter = g.createOutEdgeExplorer().setBaseNode();
// in next() the iterator is able to use the weighting and check if the access is blocked/weight is infinite etc.
while (iter.next()) {
   // this is an outgoing edge
}
```

I am not saying we should put the weighting into the base graph, but to me it seems to make a lot of sense to pass a 'weighted graph' (a wrapper of the base graph + weighting) to the code that traverses the graph (at least as long as its code that should run on a weighted graph).

The `isForward()/isBackward()` methods are more or less convenience (and optimization): We can use an all-edge iterator and check the fwd/bwd-ness ourselves (instead of using a separate in/out edge iterator). Internally it can work just the same way: The weighting is used to determine whether the weight is infinite/access is blocked.
karussell: Thanks, I do not understand everything but already more :) ... also the flexible CH is something I have not thought of.

> I am thinking the easiest way is what I did here

For BaseGraph I think we can have it very simple:

```java
while(edgeIter.next()) {
     // in a separate PR we move the accessEnc check into the weighting:
     double weight = weighting.calcWeight(edgeIter, reverse, prevEdge);
     if(Double.isInfinite(weight)) continue; // block access

     // do something with edgeIter
}
```

For PrepareCHGraph we could put this logic into the edgeIter.next call but having two separate calls (e.g. isForward and weighting.calcWeight) will make refactoring #1776 harder IMO. So I would like to say conceptionally goodbye to "access flags" and FlagEncoder and only use Weighting when we need to know if the weight is 0 or 10 or infinity (==access blocked). Think about the required possibilities that the hard constraint "access restrictions" should be able to make it soft (example: "bike profile should be able to traverse oneway roads in reverse direction with 6km/h" or police_car should be able to use car but make access restrictions "soft".
easbar: Ok, so your biggest concern is calling `calcWeight` multiple times ? I think we could even improve (speed) performance here already by storing the `weight` instead of the `weighting` within the preparech edges. This way `calcWeight` would be only called twice per edge instead of the probably millions of times during all these local dijkstra searches during CH preparation. Sure this would increase memory usage, but only during import and only proportional to the number of base graph edges. If that is not an option at the moment I will find a way to call it only once and/or get rid of isForward/Backward, no problem.
easbar: > We need to make the hard constraint "access restrictions" a soft constraint (

Ok I understand. That makes sense and also your 'very' simple method works, because in many cases we do not even need to check for `weight==infinite`, because when the weight is infinite the shortest path tree is simply not being further explored in this direction. If this helps I think it would not be a big problem to get rid of `createIn/OutEdgeExplorer` in `PrepareCHEdgeExplorer` (This reflects the 'hard' constraint as we do it at the moment, but probably makes less sense with the refactoring you have in mind).
karussell: Thanks for the changes - feel free to merge this :)

> Ok, so your biggest concern is calling calcWeight multiple times ?

Yes, but also conceptionally asking for "accessible?" and then again does not seem to be "good". But if it is cached it wouldn't matter much, yes.

> Sure this would increase memory usage, but only during import and only proportional to the number of base graph edges

Yes, interesting idea to do this only for the preparation.

> because when the weight is infinite the shortest path tree is simply not being further explored in this direction

In case of "infinite" it is indeed called just once, but if access=true (should be most of the time) it is called twice (?)
easbar: I think the biggest concern is that with `createIn/OutEdge` we are enforcing the notion of incoming/outgoing edges in code (normally a good thing in my opinion) but for the configurable weighting you/we want to do the opposite and soften this constraint. So thanks for the reminder, I will consider this from now.

> Yes, interesting idea to do this only for the preparation.

I have this in mind, but would rather postpone this a bit. Do you (already) have weightings in mind that might be expensive to evaluate ? Especially when running some user defined scripts etc. to calculate the weight this might become important.

> In case of "infinite" it is indeed called just once, but if access=true (should be most of the time) it is called twice (?)

Ah I thought like this its not called twice:

```java
while(edgeIter.next()) {
     // in a separate PR we move the accessEnc check into the weighting:
     double weight = weighting.calcWeight(edgeIter, reverse, prevEdge);
     if(Double.isInfinite(weight)) continue; // block access

     // use weight here
}
```

karussell: > If this helps I think it would not be a big problem to get rid of createIn/OutEdgeExplorer in PrepareCHEdgeExplorer (

Yes, I think we need to refactor this, but in a later PR and likely with all the other usages of EdgeFilter and EdgeExplorer.

> but for the configurable weighting you/we want to do the opposite and soften this constraint

:+1:  

>  Do you (already) have weightings in mind that might be expensive to evaluate ?

Probably we should indeed benchmark before optimizing :)

> Ah I thought like this its not called twice:

I meant the way it was before with isForward. If isForward is true, then we have called calcWeigh and then again for evaluating the actual weighting.

Patch:
diff --git a/api/src/main/java/com/graphhopper/util/PointList.java b/api/src/main/java/com/graphhopper/util/PointList.java
index 08e5a1fd7..53fed3c0b 100644
--- a/api/src/main/java/com/graphhopper/util/PointList.java
+++ b/api/src/main/java/com/graphhopper/util/PointList.java
@@ -463,7 +463,7 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
 
     /**
      * Clones this PointList. If this PointList was immutable, the cloned will be mutable. If this PointList was a
-     * ShallowImmutablePointList, the cloned PointList will be a regular PointList.
+     * {@link ShallowImmutablePointList}, the cloned PointList will be a regular PointList.
      */
     public PointList clone(boolean reverse) {
         PointList clonePL = new PointList(getSize(), is3D());
diff --git a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java
index 954fffa32..8995f5b60 100644
--- a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java
+++ b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java
@@ -30,8 +30,9 @@ import com.graphhopper.util.Parameters;
 import java.util.Arrays;
 
 /**
- * A simple dijkstra tuned to perform one to many queries more efficient than Dijkstra. Old data
- * structures are cached between requests and potentially reused. Useful for CH preparation.
+ * A simple dijkstra tuned to perform multiple one to many queries with the same source and different target nodes
+ * more efficiently than {@link Dijkstra}. Old data structures are cached between requests and potentially reused and
+ * the shortest path tree is stored in (large as the graph) arrays instead of hash maps.
  * <p>
  *
  * @author Peter Karich
diff --git a/core/src/main/java/com/graphhopper/routing/ch/AbstractNodeContractor.java b/core/src/main/java/com/graphhopper/routing/ch/AbstractNodeContractor.java
index 200f15565..1ce0f9811 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/AbstractNodeContractor.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/AbstractNodeContractor.java
@@ -18,34 +18,28 @@
 
 package com.graphhopper.routing.ch;
 
-import com.graphhopper.routing.util.DefaultEdgeFilter;
-import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.storage.CHGraph;
 import com.graphhopper.storage.DAType;
 import com.graphhopper.storage.DataAccess;
 import com.graphhopper.storage.GHDirectory;
-import com.graphhopper.util.CHEdgeExplorer;
 
 abstract class AbstractNodeContractor implements NodeContractor {
-    final CHGraph prepareGraph;
-    final FlagEncoder encoder;
-    CHEdgeExplorer inEdgeExplorer;
-    CHEdgeExplorer outEdgeExplorer;
+    final PrepareCHGraph prepareGraph;
+    PrepareCHEdgeExplorer inEdgeExplorer;
+    PrepareCHEdgeExplorer outEdgeExplorer;
     private final DataAccess originalEdges;
     int maxLevel;
     private int maxEdgesCount;
 
-    public AbstractNodeContractor(CHGraph prepareGraph, FlagEncoder encoder) {
+    public AbstractNodeContractor(PrepareCHGraph prepareGraph) {
         this.prepareGraph = prepareGraph;
-        this.encoder = encoder;
         originalEdges = new GHDirectory("", DAType.RAM_INT).find("");
         originalEdges.create(1000);
     }
 
     @Override
     public void initFromGraph() {
-        inEdgeExplorer = prepareGraph.createEdgeExplorer(DefaultEdgeFilter.inEdges(encoder));
-        outEdgeExplorer = prepareGraph.createEdgeExplorer(DefaultEdgeFilter.outEdges(encoder));
+        inEdgeExplorer = prepareGraph.createInEdgeExplorer();
+        outEdgeExplorer = prepareGraph.createOutEdgeExplorer();
         maxLevel = prepareGraph.getNodes();
         maxEdgesCount = prepareGraph.getOriginalEdges();
     }
@@ -84,6 +78,4 @@ abstract class AbstractNodeContractor implements NodeContractor {
         originalEdges.ensureCapacity(tmp + 4);
         return originalEdges.getInt(tmp);
     }
-
-    abstract boolean isEdgeBased();
 }
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java b/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java
index 38cea65ee..89dcfadba 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java
@@ -18,7 +18,6 @@
 package com.graphhopper.routing.ch;
 
 import com.graphhopper.storage.SPTEntry;
-import com.graphhopper.util.CHEdgeIteratorState;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
 
@@ -27,7 +26,6 @@ public class CHEntry extends SPTEntry {
      * The id of the incoming original edge at this shortest path tree entry. For original edges this is the same
      * as the edge id, but for shortcuts this is the id of the last original edge of the shortcut.
      *
-     * @see CHEdgeIteratorState#setFirstAndLastOrigEdges(int, int)
      * @see EdgeIteratorState#getOrigEdgeLast()
      */
     public int incEdge;
diff --git a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java
index 4a56bd379..618da815b 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java
@@ -19,12 +19,10 @@ package com.graphhopper.routing.ch;
 
 import com.carrotsearch.hppc.IntHashSet;
 import com.carrotsearch.hppc.IntSet;
-import com.graphhopper.routing.profiles.BooleanEncodedValue;
-import com.graphhopper.routing.util.DefaultEdgeFilter;
-import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.routing.weighting.TurnWeighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.util.*;
+import com.graphhopper.util.EdgeExplorer;
+import com.graphhopper.util.EdgeIterator;
+import com.graphhopper.util.PMap;
+import com.graphhopper.util.StopWatch;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,8 +47,6 @@ import static com.graphhopper.util.Helper.nf;
  */
 class EdgeBasedNodeContractor extends AbstractNodeContractor {
     private static final Logger LOGGER = LoggerFactory.getLogger(EdgeBasedNodeContractor.class);
-    private final TurnWeighting turnWeighting;
-    private final FlagEncoder encoder;
     private final ShortcutHandler addingShortcutHandler = new AddingShortcutHandler();
     private final ShortcutHandler countingShortcutHandler = new CountingShortcutHandler();
     private final Params params = new Params();
@@ -60,8 +56,8 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
     private final SearchStrategy activeStrategy = new AggressiveStrategy();
     private int[] hierarchyDepths;
     private EdgeBasedWitnessPathSearcher witnessPathSearcher;
-    private CHEdgeExplorer existingShortcutExplorer;
-    private CHEdgeExplorer allEdgeExplorer;
+    private PrepareCHEdgeExplorer existingShortcutExplorer;
+    private PrepareCHEdgeExplorer allEdgeExplorer;
     private EdgeExplorer sourceNodeOrigInEdgeExplorer;
     private EdgeExplorer targetNodeOrigOutEdgeExplorer;
     private EdgeExplorer loopAvoidanceInEdgeExplorer;
@@ -79,11 +75,8 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
     // counters used for performance analysis
     private int numPolledEdges;
 
-    public EdgeBasedNodeContractor(CHGraph prepareGraph,
-                                   TurnWeighting turnWeighting, PMap pMap) {
-        super(prepareGraph, turnWeighting.getFlagEncoder());
-        this.turnWeighting = turnWeighting;
-        this.encoder = turnWeighting.getFlagEncoder();
+    public EdgeBasedNodeContractor(PrepareCHGraph prepareGraph, PMap pMap) {
+        super(prepareGraph);
         this.pMap = pMap;
         extractParams(pMap);
     }
@@ -97,18 +90,15 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
     @Override
     public void initFromGraph() {
         super.initFromGraph();
-        witnessPathSearcher = new EdgeBasedWitnessPathSearcher(prepareGraph, turnWeighting, pMap);
-        DefaultEdgeFilter inEdgeFilter = DefaultEdgeFilter.inEdges(encoder);
-        DefaultEdgeFilter outEdgeFilter = DefaultEdgeFilter.outEdges(encoder);
-        DefaultEdgeFilter allEdgeFilter = DefaultEdgeFilter.allEdges(encoder);
-        inEdgeExplorer = prepareGraph.createEdgeExplorer(inEdgeFilter);
-        outEdgeExplorer = prepareGraph.createEdgeExplorer(outEdgeFilter);
-        allEdgeExplorer = prepareGraph.createEdgeExplorer(allEdgeFilter);
-        existingShortcutExplorer = prepareGraph.createEdgeExplorer(outEdgeFilter);
-        sourceNodeOrigInEdgeExplorer = prepareGraph.createOriginalEdgeExplorer(inEdgeFilter);
-        targetNodeOrigOutEdgeExplorer = prepareGraph.createOriginalEdgeExplorer(outEdgeFilter);
-        loopAvoidanceInEdgeExplorer = prepareGraph.createOriginalEdgeExplorer(inEdgeFilter);
-        loopAvoidanceOutEdgeExplorer = prepareGraph.createOriginalEdgeExplorer(outEdgeFilter);
+        witnessPathSearcher = new EdgeBasedWitnessPathSearcher(prepareGraph, pMap);
+        inEdgeExplorer = prepareGraph.createInEdgeExplorer();
+        outEdgeExplorer = prepareGraph.createOutEdgeExplorer();
+        allEdgeExplorer = prepareGraph.createAllEdgeExplorer();
+        existingShortcutExplorer = prepareGraph.createOutEdgeExplorer();
+        sourceNodeOrigInEdgeExplorer = prepareGraph.createOriginalInEdgeExplorer();
+        targetNodeOrigOutEdgeExplorer = prepareGraph.createOriginalOutEdgeExplorer();
+        loopAvoidanceInEdgeExplorer = prepareGraph.createOriginalInEdgeExplorer();
+        loopAvoidanceOutEdgeExplorer = prepareGraph.createOriginalOutEdgeExplorer();
         hierarchyDepths = new int[prepareGraph.getNodes()];
     }
 
@@ -177,43 +167,48 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
         return numPolledEdges;
     }
 
-    @Override
-    boolean isEdgeBased() {
-        return true;
-    }
-
     private void findAndHandleShortcuts(int node) {
         numPolledEdges = 0;
         activeStrategy.findAndHandleShortcuts(node);
     }
 
     private void countPreviousEdges(int node) {
-        BooleanEncodedValue accessEnc = encoder.getAccessEnc();
-        CHEdgeIterator iter = allEdgeExplorer.setBaseNode(node);
-        while (iter.next()) {
-            if (isContracted(iter.getAdjNode()))
+        // todo: this edge counting can probably be simplified, but we might need to re-optimize heuristic parameters then
+        PrepareCHEdgeIterator outIter = outEdgeExplorer.setBaseNode(node);
+        while (outIter.next()) {
+            if (isContracted(outIter.getAdjNode()))
                 continue;
-            if (iter.get(accessEnc)) {
-                numPrevEdges++;
+            numPrevEdges++;
+            if (!outIter.isShortcut()) {
+                numPrevOrigEdges++;
             }
-            if (iter.getReverse(accessEnc)) {
-                numPrevEdges++;
+        }
+
+        PrepareCHEdgeIterator inIter = inEdgeExplorer.setBaseNode(node);
+        while (inIter.next()) {
+            if (isContracted(inIter.getAdjNode()))
+                continue;
+            // do not consider loop edges a second time
+            if (inIter.getBaseNode() == inIter.getAdjNode())
+                continue;
+            numPrevEdges++;
+            if (!inIter.isShortcut()) {
+                numPrevOrigEdges++;
             }
-            if (!iter.isShortcut()) {
-                if (iter.get(accessEnc)) {
-                    numPrevOrigEdges++;
-                }
-                if (iter.getReverse(accessEnc)) {
-                    numPrevOrigEdges++;
-                }
-            } else {
-                numPrevOrigEdges += getOrigEdgeCount(iter.getEdge());
+        }
+
+        PrepareCHEdgeIterator allIter = allEdgeExplorer.setBaseNode(node);
+        while (allIter.next()) {
+            if (isContracted(allIter.getAdjNode()))
+                continue;
+            if (allIter.isShortcut()) {
+                numPrevOrigEdges += getOrigEdgeCount(allIter.getEdge());
             }
         }
     }
 
     private void updateHierarchyDepthsOfNeighbors(int node) {
-        CHEdgeIterator iter = allEdgeExplorer.setBaseNode(node);
+        PrepareCHEdgeIterator iter = allEdgeExplorer.setBaseNode(node);
         while (iter.next()) {
             if (isContracted(iter.getAdjNode()) || iter.getAdjNode() == node)
                 continue;
@@ -269,13 +264,13 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
         int from = edgeFrom.parent.adjNode;
         int adjNode = edgeTo.adjNode;
 
-        final CHEdgeIterator iter = existingShortcutExplorer.setBaseNode(from);
+        final PrepareCHEdgeIterator iter = existingShortcutExplorer.setBaseNode(from);
         while (iter.next()) {
             if (!isSameShortcut(iter, adjNode, edgeFrom.getParent().incEdge, edgeTo.incEdge)) {
                 // this is some other (shortcut) edge, we do not care
                 continue;
             }
-            final double existingWeight = turnWeighting.calcWeight(iter, false, EdgeIterator.NO_EDGE);
+            final double existingWeight = iter.getWeight(false);
             if (existingWeight <= edgeTo.weight) {
                 // our shortcut already exists with lower weight --> do nothing
                 CHEntry entry = new CHEntry(iter.getEdge(), iter.getOrigEdgeLast(), adjNode, existingWeight);
@@ -306,7 +301,7 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
         return entry;
     }
 
-    private boolean isSameShortcut(CHEdgeIteratorState iter, int adjNode, int firstOrigEdge, int lastOrigEdge) {
+    private boolean isSameShortcut(PrepareCHEdgeIterator iter, int adjNode, int firstOrigEdge, int lastOrigEdge) {
         return iter.isShortcut()
                 && (iter.getAdjNode() == adjNode)
                 && (iter.getOrigEdgeFirst() == firstOrigEdge)
@@ -314,7 +309,7 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
     }
 
     private double getTurnCost(int inEdge, int node, int outEdge) {
-        return turnWeighting.calcTurnWeight(inEdge, node, outEdge);
+        return prepareGraph.getTurnWeight(inEdge, node, outEdge);
     }
 
     private void resetEdgeCounters() {
@@ -367,7 +362,7 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
             int lastOrigEdge = edgeTo.incEdge;
 
             // check if this shortcut already exists
-            final CHEdgeIterator iter = existingShortcutExplorer.setBaseNode(fromNode);
+            final PrepareCHEdgeIterator iter = existingShortcutExplorer.setBaseNode(fromNode);
             while (iter.next()) {
                 if (isSameShortcut(iter, toNode, firstOrigEdge, lastOrigEdge)) {
                     // this shortcut exists already, maybe its weight will be updated but we should not count it as
@@ -444,7 +439,7 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
 
             // first we need to identify the possible source nodes from which we can reach the center node
             sourceNodes.clear();
-            EdgeIterator incomingEdges = inEdgeExplorer.setBaseNode(node);
+            PrepareCHEdgeIterator incomingEdges = inEdgeExplorer.setBaseNode(node);
             while (incomingEdges.next()) {
                 int sourceNode = incomingEdges.getAdjNode();
                 if (isContracted(sourceNode) || sourceNode == node) {
@@ -464,7 +459,7 @@ class EdgeBasedNodeContractor extends AbstractNodeContractor {
 
                     // now we need to identify all target nodes that can be reached from the center node
                     toNodes.clear();
-                    EdgeIterator outgoingEdges = outEdgeExplorer.setBaseNode(node);
+                    PrepareCHEdgeIterator outgoingEdges = outEdgeExplorer.setBaseNode(node);
                     while (outgoingEdges.next()) {
                         int targetNode = outgoingEdges.getAdjNode();
                         if (isContracted(targetNode) || targetNode == node) {
diff --git a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java
index dcd87a36b..302513701 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java
@@ -21,10 +21,10 @@ import com.carrotsearch.hppc.IntArrayList;
 import com.carrotsearch.hppc.IntObjectHashMap;
 import com.carrotsearch.hppc.IntObjectMap;
 import com.graphhopper.apache.commons.collections.IntDoubleBinaryHeap;
-import com.graphhopper.routing.util.DefaultEdgeFilter;
-import com.graphhopper.routing.weighting.TurnWeighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.util.*;
+import com.graphhopper.util.EdgeExplorer;
+import com.graphhopper.util.EdgeIterator;
+import com.graphhopper.util.GHUtility;
+import com.graphhopper.util.PMap;
 
 import java.util.Arrays;
 import java.util.Locale;
@@ -66,9 +66,8 @@ public class EdgeBasedWitnessPathSearcher {
     private static final double MAX_ZERO_WEIGHT_LOOP = 1.e-3;
 
     // graph variables
-    private final CHGraph chGraph;
-    private final TurnWeighting turnWeighting;
-    private final EdgeExplorer outEdgeExplorer;
+    private final PrepareCHGraph chGraph;
+    private final PrepareCHEdgeExplorer outEdgeExplorer;
     private final EdgeExplorer origInEdgeExplorer;
     private final int maxLevel;
 
@@ -107,15 +106,12 @@ public class EdgeBasedWitnessPathSearcher {
     private final Stats currentBatchStats = new Stats();
     private final Stats totalStats = new Stats();
 
-    public EdgeBasedWitnessPathSearcher(CHGraph chGraph, TurnWeighting turnWeighting, PMap pMap) {
+    public EdgeBasedWitnessPathSearcher(PrepareCHGraph chGraph, PMap pMap) {
         this.chGraph = chGraph;
-        this.turnWeighting = turnWeighting;
         extractParams(pMap);
 
-        DefaultEdgeFilter inEdgeFilter = DefaultEdgeFilter.inEdges(turnWeighting.getFlagEncoder());
-        DefaultEdgeFilter outEdgeFilter = DefaultEdgeFilter.outEdges(turnWeighting.getFlagEncoder());
-        outEdgeExplorer = chGraph.createEdgeExplorer(outEdgeFilter);
-        origInEdgeExplorer = chGraph.createOriginalEdgeExplorer(inEdgeFilter);
+        outEdgeExplorer = chGraph.createOutEdgeExplorer();
+        origInEdgeExplorer = chGraph.createOriginalInEdgeExplorer();
         maxLevel = chGraph.getNodes();
 
         maxSettledEdges = params.minimumMaxSettledEdges;
@@ -224,12 +220,12 @@ public class EdgeBasedWitnessPathSearcher {
             }
 
             final int fromNode = adjNodes[currKey];
-            EdgeIterator iter = outEdgeExplorer.setBaseNode(fromNode);
+            PrepareCHEdgeIterator iter = outEdgeExplorer.setBaseNode(fromNode);
             while (iter.next()) {
                 if (isContracted(iter.getAdjNode())) {
                     continue;
                 }
-                double edgeWeight = turnWeighting.calcWeight(iter, false, incEdges[currKey]);
+                double edgeWeight = iter.getWeight(false) + calcTurnWeight(incEdges[currKey], iter.getBaseNode(), iter.getOrigEdgeFirst());
                 double weight = edgeWeight + weights[currKey];
                 if (isInfinite(weight)) {
                     continue;
@@ -326,7 +322,7 @@ public class EdgeBasedWitnessPathSearcher {
     }
 
     private void setInitialEntries(int sourceNode, int sourceEdge, int centerNode) {
-        EdgeIterator outIter = outEdgeExplorer.setBaseNode(sourceNode);
+        PrepareCHEdgeIterator outIter = outEdgeExplorer.setBaseNode(sourceNode);
         while (outIter.next()) {
             if (isContracted(outIter.getAdjNode())) {
                 continue;
@@ -335,7 +331,7 @@ public class EdgeBasedWitnessPathSearcher {
             if (isInfinite(turnWeight)) {
                 continue;
             }
-            double edgeWeight = turnWeighting.calcWeight(outIter, false, NO_EDGE);
+            double edgeWeight = outIter.getWeight(false);
             double weight = turnWeight + edgeWeight;
             boolean isPathToCenter = outIter.getAdjNode() == centerNode;
             int incEdge = outIter.getOrigEdgeLast();
@@ -428,7 +424,7 @@ public class EdgeBasedWitnessPathSearcher {
         }
     }
 
-    private void setEntry(int key, EdgeIteratorState edge, double weight, int parent, boolean isPathToCenter) {
+    private void setEntry(int key, PrepareCHEdgeIterator edge, double weight, int parent, boolean isPathToCenter) {
         edges[key] = edge.getEdge();
         incEdges[key] = edge.getOrigEdgeLast();
         adjNodes[key] = edge.getAdjNode();
@@ -440,7 +436,7 @@ public class EdgeBasedWitnessPathSearcher {
         }
     }
 
-    private void updateEntry(int key, EdgeIteratorState edge, double weight, int currKey, boolean isPathToCenter) {
+    private void updateEntry(int key, PrepareCHEdgeIterator edge, double weight, int currKey, boolean isPathToCenter) {
         edges[key] = edge.getEdge();
         weights[key] = weight;
         parents[key] = currKey;
@@ -475,7 +471,7 @@ public class EdgeBasedWitnessPathSearcher {
     }
 
     private double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
-        return turnWeighting.calcTurnWeight(inEdge, viaNode, outEdge);
+        return chGraph.getTurnWeight(inEdge, viaNode, outEdge);
     }
 
     private boolean isContracted(int node) {
diff --git a/core/src/main/java/com/graphhopper/routing/ch/NodeBasedNodeContractor.java b/core/src/main/java/com/graphhopper/routing/ch/NodeBasedNodeContractor.java
index d91228c01..2ea6e62fe 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/NodeBasedNodeContractor.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/NodeBasedNodeContractor.java
@@ -17,15 +17,9 @@
  */
 package com.graphhopper.routing.ch;
 
-import com.graphhopper.routing.DijkstraOneToMany;
-import com.graphhopper.routing.util.DefaultEdgeFilter;
-import com.graphhopper.routing.util.IgnoreNodeFilter;
-import com.graphhopper.routing.util.TraversalMode;
-import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.NodeAccess;
-import com.graphhopper.util.*;
+import com.graphhopper.util.PMap;
+import com.graphhopper.util.StopWatch;
 
 import java.util.Collection;
 import java.util.HashMap;
@@ -36,14 +30,12 @@ import static com.graphhopper.routing.ch.CHParameters.*;
 import static com.graphhopper.util.Helper.nf;
 
 class NodeBasedNodeContractor extends AbstractNodeContractor {
-    private final PreparationWeighting prepareWeighting;
     private final Map<Shortcut, Shortcut> shortcuts = new HashMap<>();
     private final AddShortcutHandler addScHandler = new AddShortcutHandler();
     private final CalcShortcutHandler calcScHandler = new CalcShortcutHandler();
     private final Params params = new Params();
-    private CHEdgeExplorer allEdgeExplorer;
-    private IgnoreNodeFilter ignoreNodeFilter;
-    private DijkstraOneToMany prepareAlgo;
+    private PrepareCHEdgeExplorer allEdgeExplorer;
+    private NodeBasedWitnessPathSearcher prepareAlgo;
     private int addedShortcutsCount;
     private long dijkstraCount;
     private StopWatch dijkstraSW = new StopWatch();
@@ -51,9 +43,8 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
     // each edge can exist in both directions
     private double meanDegree;
 
-    NodeBasedNodeContractor(CHGraph prepareGraph, Weighting weighting, PMap pMap) {
-        super(prepareGraph, weighting.getFlagEncoder());
-        this.prepareWeighting = new PreparationWeighting(weighting);
+    NodeBasedNodeContractor(PrepareCHGraph prepareGraph, PMap pMap) {
+        super(prepareGraph);
         extractParams(pMap);
     }
 
@@ -66,9 +57,8 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
     @Override
     public void initFromGraph() {
         super.initFromGraph();
-        ignoreNodeFilter = new IgnoreNodeFilter(prepareGraph, maxLevel);
-        allEdgeExplorer = prepareGraph.createEdgeExplorer(DefaultEdgeFilter.allEdges(encoder));
-        prepareAlgo = new DijkstraOneToMany(prepareGraph, prepareWeighting, TraversalMode.NODE_BASED);
+        allEdgeExplorer = prepareGraph.createAllEdgeExplorer();
+        prepareAlgo = new NodeBasedWitnessPathSearcher(prepareGraph, maxLevel);
     }
 
     @Override
@@ -113,7 +103,7 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
         // number of already contracted neighbors of v
         int contractedNeighbors = 0;
         int degree = 0;
-        CHEdgeIterator iter = allEdgeExplorer.setBaseNode(node);
+        PrepareCHEdgeIterator iter = allEdgeExplorer.setBaseNode(node);
         while (iter.next()) {
             // only increase the degree for edges going to equal level nodes (the current node is at maxLevel)
             // todo: for historic reasons increase degree also for all shortcuts, even though its wrong, see #1810
@@ -157,11 +147,6 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
                 meanDegree, nf(dijkstraCount), prepareAlgo.getMemoryUsageAsString());
     }
 
-    @Override
-    boolean isEdgeBased() {
-        return false;
-    }
-
     /**
      * Searches for shortcuts and calls the given handler on each shortcut that is found. The graph is not directly
      * changed by this method.
@@ -171,7 +156,7 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
     private long findShortcuts(ShortcutHandler sch) {
         int maxVisitedNodes = getMaxVisitedNodesEstimate();
         long degree = 0;
-        EdgeIterator incomingEdges = inEdgeExplorer.setBaseNode(sch.getNode());
+        PrepareCHEdgeIterator incomingEdges = inEdgeExplorer.setBaseNode(sch.getNode());
         // collect outgoing nodes (goal-nodes) only once
         while (incomingEdges.next()) {
             int fromNode = incomingEdges.getAdjNode();
@@ -179,7 +164,7 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
             if (fromNode == sch.getNode() || isContracted(fromNode))
                 continue;
 
-            final double incomingEdgeWeight = prepareWeighting.calcWeight(incomingEdges, true, EdgeIterator.NO_EDGE);
+            final double incomingEdgeWeight = incomingEdges.getWeight(true);
             // this check is important to prevent calling calcMillis on inaccessible edges and also allows early exit
             if (Double.isInfinite(incomingEdgeWeight)) {
                 continue;
@@ -187,7 +172,7 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
             int incomingEdge = incomingEdges.getEdge();
             int inOrigEdgeCount = getOrigEdgeCount(incomingEdge);
             // collect outgoing nodes (goal-nodes) only once
-            EdgeIterator outgoingEdges = outEdgeExplorer.setBaseNode(sch.getNode());
+            PrepareCHEdgeIterator outgoingEdges = outEdgeExplorer.setBaseNode(sch.getNode());
             // force fresh maps etc as this cannot be determined by from node alone (e.g. same from node but different avoidNode)
             prepareAlgo.clear();
             degree++;
@@ -200,18 +185,17 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
                 // Limit weight as ferries or forbidden edges can increase local search too much.
                 // If we decrease the correct weight we only explore less and introduce more shortcuts.
                 // I.e. no change to accuracy is made.
-                double existingDirectWeight = incomingEdgeWeight + prepareWeighting.calcWeight(outgoingEdges, false, incomingEdges.getEdge());
+                double existingDirectWeight = incomingEdgeWeight + outgoingEdges.getWeight(false);
                 if (Double.isNaN(existingDirectWeight))
                     throw new IllegalStateException("Weighting should never return NaN values"
-                            + ", in:" + getCoords(incomingEdges, prepareGraph) + ", out:" + getCoords(outgoingEdges, prepareGraph)
-                            + ", dist:" + outgoingEdges.getDistance());
+                            + ", in:" + getCoords(incomingEdges, prepareGraph.getNodeAccess()) + ", out:" + getCoords(outgoingEdges, prepareGraph.getNodeAccess()));
 
                 if (Double.isInfinite(existingDirectWeight))
                     continue;
 
                 prepareAlgo.setWeightLimit(existingDirectWeight);
                 prepareAlgo.setMaxVisitedNodes(maxVisitedNodes);
-                prepareAlgo.setEdgeFilter(ignoreNodeFilter.setAvoidNode(sch.getNode()));
+                prepareAlgo.ignoreNode(sch.getNode());
 
                 dijkstraSW.start();
                 dijkstraCount++;
@@ -242,14 +226,14 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
         for (Shortcut sc : shortcuts) {
             boolean updatedInGraph = false;
             // check if we need to update some existing shortcut in the graph
-            CHEdgeIterator iter = outEdgeExplorer.setBaseNode(sc.from);
+            PrepareCHEdgeIterator iter = outEdgeExplorer.setBaseNode(sc.from);
             while (iter.next()) {
                 if (iter.isShortcut() && iter.getAdjNode() == sc.to) {
                     int status = iter.getMergeStatus(sc.flags);
                     if (status == 0)
                         continue;
 
-                    if (sc.weight >= prepareWeighting.calcWeight(iter, false, EdgeIterator.NO_EDGE)) {
+                    if (sc.weight >= iter.getWeight(false)) {
                         // special case if a bidirectional shortcut has worse weight and still has to be added as otherwise the opposite direction would be missing
                         // see testShortcutMergeBug
                         if (status == 2)
@@ -261,11 +245,8 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
                     if (iter.getEdge() == sc.skippedEdge1 || iter.getEdge() == sc.skippedEdge2) {
                         throw new IllegalStateException("Shortcut cannot update itself! " + iter.getEdge()
                                 + ", skipEdge1:" + sc.skippedEdge1 + ", skipEdge2:" + sc.skippedEdge2
-                                + ", edge " + iter + ":" + getCoords(iter, prepareGraph)
-                                + ", sc:" + sc
-                                + ", skippedEdge1: " + getCoords(prepareGraph.getEdgeIteratorState(sc.skippedEdge1, sc.from), prepareGraph)
-                                + ", skippedEdge2: " + getCoords(prepareGraph.getEdgeIteratorState(sc.skippedEdge2, sc.to), prepareGraph)
-                                + ", neighbors:" + GHUtility.getNeighbors(iter));
+                                + ", edge " + iter + ":" + getCoords(iter, prepareGraph.getNodeAccess())
+                                + ", sc:" + sc);
                     }
 
                     iter.setFlagsAndWeight(sc.flags, sc.weight);
@@ -291,8 +272,7 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
         return calcScHandler.calcShortcutsResult;
     }
 
-    private String getCoords(EdgeIteratorState edge, Graph graph) {
-        NodeAccess na = graph.getNodeAccess();
+    private String getCoords(PrepareCHEdgeIterator edge, NodeAccess na) {
         int base = edge.getBaseNode();
         int adj = edge.getAdjNode();
         return base + "->" + adj + " (" + edge.getEdge() + "); "
diff --git a/core/src/main/java/com/graphhopper/routing/ch/NodeBasedWitnessPathSearcher.java b/core/src/main/java/com/graphhopper/routing/ch/NodeBasedWitnessPathSearcher.java
new file mode 100644
index 000000000..ba475b0df
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/ch/NodeBasedWitnessPathSearcher.java
@@ -0,0 +1,229 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for
+ *  additional information regarding copyright ownership.
+ *
+ *  GraphHopper GmbH licenses this file to you under the Apache License,
+ *  Version 2.0 (the "License"); you may not use this file except in
+ *  compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package com.graphhopper.routing.ch;
+
+import com.carrotsearch.hppc.IntArrayList;
+import com.graphhopper.apache.commons.collections.IntDoubleBinaryHeap;
+import com.graphhopper.routing.DijkstraOneToMany;
+import com.graphhopper.util.EdgeIterator;
+import com.graphhopper.util.Helper;
+
+import java.util.Arrays;
+
+/**
+ * Used to find witness paths during node-based CH preparation. Essentially this is like {@link DijkstraOneToMany},
+ * i.e. its a Dijkstra search that allows re-using the shortest path tree for different searches with the same origin
+ * node and uses large int/double arrays instead of hash maps to store the shortest path tree (higher memory consumption,
+ * but faster query times -> better for CH preparation). Main reason we use this instead of {@link DijkstraOneToMany}
+ * is that we can use this implementation with a {@link PrepareCHGraph}.
+ */
+public class NodeBasedWitnessPathSearcher {
+    private static final int EMPTY_PARENT = -1;
+    private static final int NOT_FOUND = -1;
+    private final PrepareCHGraph graph;
+    private final PrepareCHEdgeExplorer outEdgeExplorer;
+    private final IntArrayList changedNodes;
+    private final int maxLevel;
+    private int maxVisitedNodes = Integer.MAX_VALUE;
+    protected double[] weights;
+    private int[] parents;
+    private int[] edgeIds;
+    private IntDoubleBinaryHeap heap;
+    private int ignoreNode = -1;
+    private int visitedNodes;
+    private boolean doClear = true;
+    private int currNode, to;
+    private double weightLimit = Double.MAX_VALUE;
+
+    public NodeBasedWitnessPathSearcher(PrepareCHGraph graph) {
+        this(graph, graph.getNodes());
+    }
+
+    public NodeBasedWitnessPathSearcher(PrepareCHGraph graph, int maxLevel) {
+        this.graph = graph;
+        this.maxLevel = maxLevel;
+        outEdgeExplorer = graph.createOutEdgeExplorer();
+
+        parents = new int[graph.getNodes()];
+        Arrays.fill(parents, EMPTY_PARENT);
+
+        edgeIds = new int[graph.getNodes()];
+        Arrays.fill(edgeIds, EdgeIterator.NO_EDGE);
+
+        weights = new double[graph.getNodes()];
+        Arrays.fill(weights, Double.MAX_VALUE);
+
+        heap = new IntDoubleBinaryHeap(1000);
+        changedNodes = new IntArrayList();
+    }
+
+    /**
+     * Call clear if you have a different start node and need to clear the cache.
+     */
+    public NodeBasedWitnessPathSearcher clear() {
+        doClear = true;
+        return this;
+    }
+
+    public double getWeight(int endNode) {
+        return weights[endNode];
+    }
+
+    public int findEndNode(int from, int to) {
+        if (weights.length < 2)
+            return NOT_FOUND;
+
+        this.to = to;
+        if (doClear) {
+            doClear = false;
+            int vn = changedNodes.size();
+            for (int i = 0; i < vn; i++) {
+                int n = changedNodes.get(i);
+                weights[n] = Double.MAX_VALUE;
+                parents[n] = EMPTY_PARENT;
+                edgeIds[n] = EdgeIterator.NO_EDGE;
+            }
+
+            heap.clear();
+
+            // changedNodes.clear();
+            changedNodes.elementsCount = 0;
+
+            currNode = from;
+            weights[currNode] = 0;
+            changedNodes.add(currNode);
+        } else {
+            // Cached! Re-use existing data structures
+            int parentNode = parents[to];
+            if (parentNode != EMPTY_PARENT && weights[to] <= weights[currNode])
+                return to;
+
+            if (heap.isEmpty() || isMaxVisitedNodesExceeded())
+                return NOT_FOUND;
+
+            currNode = heap.poll_element();
+        }
+
+        visitedNodes = 0;
+
+        // we call 'finished' before heap.peek_element but this would add unnecessary overhead for this special case so we do it outside of the loop
+        if (finished()) {
+            // then we need a small workaround for special cases see #707
+            if (heap.isEmpty())
+                doClear = true;
+            return currNode;
+        }
+
+        while (true) {
+            visitedNodes++;
+            PrepareCHEdgeIterator iter = outEdgeExplorer.setBaseNode(currNode);
+            while (iter.next()) {
+                int adjNode = iter.getAdjNode();
+                int prevEdgeId = edgeIds[adjNode];
+                if (!accept(iter, prevEdgeId))
+                    continue;
+
+                double tmpWeight = iter.getWeight(false) + weights[currNode];
+                if (Double.isInfinite(tmpWeight))
+                    continue;
+
+                double w = weights[adjNode];
+                if (w == Double.MAX_VALUE) {
+                    parents[adjNode] = currNode;
+                    weights[adjNode] = tmpWeight;
+                    heap.insert_(tmpWeight, adjNode);
+                    changedNodes.add(adjNode);
+                    edgeIds[adjNode] = iter.getEdge();
+                } else if (w > tmpWeight) {
+                    parents[adjNode] = currNode;
+                    weights[adjNode] = tmpWeight;
+                    heap.update_(tmpWeight, adjNode);
+                    changedNodes.add(adjNode);
+                    edgeIds[adjNode] = iter.getEdge();
+                }
+            }
+
+            if (heap.isEmpty() || isMaxVisitedNodesExceeded() || isWeightLimitExceeded())
+                return NOT_FOUND;
+
+            // calling just peek and not poll is important if the next query is cached
+            currNode = heap.peek_element();
+            if (finished())
+                return currNode;
+
+            heap.poll_element();
+        }
+    }
+
+    public boolean finished() {
+        return currNode == to;
+    }
+
+    public void setWeightLimit(double weightLimit) {
+        this.weightLimit = weightLimit;
+    }
+
+    protected boolean isWeightLimitExceeded() {
+        return weights[currNode] > weightLimit;
+    }
+
+    public void close() {
+        weights = null;
+        parents = null;
+        edgeIds = null;
+        heap = null;
+    }
+
+    public int getVisitedNodes() {
+        return visitedNodes;
+    }
+
+    /**
+     * List currently used memory in MB (approximately)
+     */
+    public String getMemoryUsageAsString() {
+        long len = weights.length;
+        return ((8L + 4L + 4L) * len
+                + changedNodes.buffer.length * 4L
+                + heap.getCapacity() * (4L + 4L)) / Helper.MB
+                + "MB";
+    }
+
+    public void setMaxVisitedNodes(int numberOfNodes) {
+        this.maxVisitedNodes = numberOfNodes;
+    }
+
+    public void ignoreNode(int node) {
+        ignoreNode = node;
+    }
+
+    private boolean accept(PrepareCHEdgeIterator iter, int prevOrNextEdgeId) {
+        if (iter.getEdge() == prevOrNextEdgeId)
+            return false;
+
+        if (graph.getLevel(iter.getAdjNode()) != maxLevel) {
+            return false;
+        }
+
+        return ignoreNode < 0 || iter.getAdjNode() != ignoreNode;
+    }
+
+    private boolean isMaxVisitedNodesExceeded() {
+        return maxVisitedNodes < getVisitedNodes();
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/routing/util/IgnoreNodeFilter.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHEdgeExplorer.java
similarity index 51%
rename from core/src/main/java/com/graphhopper/routing/util/IgnoreNodeFilter.java
rename to core/src/main/java/com/graphhopper/routing/ch/PrepareCHEdgeExplorer.java
index 9e1bf9fd9..14266bc95 100644
--- a/core/src/main/java/com/graphhopper/routing/util/IgnoreNodeFilter.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHEdgeExplorer.java
@@ -15,30 +15,9 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.routing.util;
 
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.util.EdgeIteratorState;
+package com.graphhopper.routing.ch;
 
-public class IgnoreNodeFilter implements EdgeFilter {
-    private int avoidNode;
-    private CHGraph graph;
-    private int maxLevel;
-
-    public IgnoreNodeFilter(CHGraph chGraph, int maxLevel) {
-        this.graph = chGraph;
-        this.maxLevel = maxLevel;
-    }
-
-    public IgnoreNodeFilter setAvoidNode(int node) {
-        this.avoidNode = node;
-        return this;
-    }
-
-    @Override
-    public final boolean accept(EdgeIteratorState iter) {
-        // ignore if it is skipNode or adjNode is already contracted
-        int node = iter.getAdjNode();
-        return avoidNode != node && graph.getLevel(node) == maxLevel;
-    }
+public interface PrepareCHEdgeExplorer {
+    PrepareCHEdgeIterator setBaseNode(int node);
 }
diff --git a/core/src/main/java/com/graphhopper/routing/ch/PrepareCHEdgeIterator.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHEdgeIterator.java
new file mode 100644
index 000000000..39dc86037
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHEdgeIterator.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for
+ *  additional information regarding copyright ownership.
+ *
+ *  GraphHopper GmbH licenses this file to you under the Apache License,
+ *  Version 2.0 (the "License"); you may not use this file except in
+ *  compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.graphhopper.routing.ch;
+
+import com.graphhopper.routing.weighting.Weighting;
+import com.graphhopper.util.CHEdgeExplorer;
+import com.graphhopper.util.CHEdgeIterator;
+import com.graphhopper.util.EdgeIterator;
+
+public class PrepareCHEdgeIterator implements PrepareCHEdgeExplorer {
+    private final CHEdgeExplorer edgeExplorer;
+    private final Weighting weighting;
+    private CHEdgeIterator chIterator;
+
+    public PrepareCHEdgeIterator(CHEdgeExplorer edgeExplorer, Weighting weighting) {
+        this.edgeExplorer = edgeExplorer;
+        this.weighting = weighting;
+    }
+
+    @Override
+    public PrepareCHEdgeIterator setBaseNode(int node) {
+        chIterator = edgeExplorer.setBaseNode(node);
+        return this;
+    }
+
+    public boolean next() {
+        return iter().next();
+    }
+
+    public int getEdge() {
+        return iter().getEdge();
+    }
+
+    public int getBaseNode() {
+        return iter().getBaseNode();
+    }
+
+    public int getAdjNode() {
+        return iter().getAdjNode();
+    }
+
+    public int getOrigEdgeFirst() {
+        return iter().getOrigEdgeFirst();
+    }
+
+    public int getOrigEdgeLast() {
+        return iter().getOrigEdgeLast();
+    }
+
+    public boolean isShortcut() {
+        return iter().isShortcut();
+    }
+
+    public double getWeight(boolean reverse) {
+        if (isShortcut()) {
+            return iter().getWeight();
+        } else {
+            return weighting.calcWeight(iter(), reverse, EdgeIterator.NO_EDGE);
+        }
+    }
+
+    public void setWeight(double weight) {
+        iter().setWeight(weight);
+    }
+
+    @Override
+    public String toString() {
+        if (chIterator == null) {
+            return "not initialized";
+        } else {
+            return getBaseNode() + "->" + getAdjNode() + " (" + getEdge() + ")";
+        }
+    }
+
+    int getMergeStatus(int flags) {
+        return iter().getMergeStatus(flags);
+    }
+
+    void setFlagsAndWeight(int flags, double weight) {
+        iter().setFlagsAndWeight(flags, weight);
+    }
+
+    void setSkippedEdges(int skippedEdge1, int skippedEdge2) {
+        iter().setSkippedEdges(skippedEdge1, skippedEdge2);
+    }
+
+    private CHEdgeIterator iter() {
+        if (chIterator == null) {
+            throw new IllegalStateException("You need to call setBaseNode() first");
+        }
+        return chIterator;
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/routing/ch/PrepareCHGraph.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHGraph.java
new file mode 100644
index 000000000..0a41db4fa
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareCHGraph.java
@@ -0,0 +1,154 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for
+ *  additional information regarding copyright ownership.
+ *
+ *  GraphHopper GmbH licenses this file to you under the Apache License,
+ *  Version 2.0 (the "License"); you may not use this file except in
+ *  compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.graphhopper.routing.ch;
+
+import com.graphhopper.routing.util.AllCHEdgesIterator;
+import com.graphhopper.routing.util.DefaultEdgeFilter;
+import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.routing.weighting.TurnWeighting;
+import com.graphhopper.routing.weighting.Weighting;
+import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.NodeAccess;
+import com.graphhopper.util.EdgeExplorer;
+import com.graphhopper.util.EdgeIterator;
+
+/**
+ * Helper adapter api over {@link CHGraph} used for CH preparation.
+ */
+public class PrepareCHGraph {
+    private final CHGraph chGraph;
+    private final Weighting weighting;
+    private final TurnWeighting turnWeighting;
+    private final FlagEncoder encoder;
+
+    public static PrepareCHGraph nodeBased(CHGraph chGraph, Weighting weighting) {
+        if (chGraph.getCHProfile().isEdgeBased()) {
+            throw new IllegalArgumentException("Expected node-based CHGraph, but was edge-based");
+        }
+        return new PrepareCHGraph(chGraph, weighting, null);
+    }
+
+    public static PrepareCHGraph edgeBased(CHGraph chGraph, Weighting weighting, TurnWeighting turnWeighting) {
+        if (!chGraph.getCHProfile().isEdgeBased()) {
+            throw new IllegalArgumentException("Expected edge-based CHGraph, but was node-based");
+        }
+        return new PrepareCHGraph(chGraph, weighting, turnWeighting);
+    }
+
+    private PrepareCHGraph(CHGraph chGraph, Weighting weighting, TurnWeighting turnWeighting) {
+        this.chGraph = chGraph;
+        this.encoder = weighting.getFlagEncoder();
+        this.weighting = weighting;
+        this.turnWeighting = turnWeighting;
+    }
+
+    public PrepareCHEdgeExplorer createInEdgeExplorer() {
+        return new PrepareCHEdgeIterator(chGraph.createEdgeExplorer(DefaultEdgeFilter.inEdges(encoder)), weighting);
+    }
+
+    public PrepareCHEdgeExplorer createOutEdgeExplorer() {
+        return new PrepareCHEdgeIterator(chGraph.createEdgeExplorer(DefaultEdgeFilter.outEdges(encoder)), weighting);
+    }
+
+    public PrepareCHEdgeExplorer createAllEdgeExplorer() {
+        return new PrepareCHEdgeIterator(chGraph.createEdgeExplorer(DefaultEdgeFilter.allEdges(encoder)), weighting);
+    }
+
+    public EdgeExplorer createOriginalInEdgeExplorer() {
+        return chGraph.createOriginalEdgeExplorer(DefaultEdgeFilter.inEdges(encoder));
+    }
+
+    public EdgeExplorer createOriginalOutEdgeExplorer() {
+        return chGraph.createOriginalEdgeExplorer(DefaultEdgeFilter.outEdges(encoder));
+    }
+
+    public int getNodes() {
+        return chGraph.getNodes();
+    }
+
+    public int getEdges() {
+        return chGraph.getEdges();
+    }
+
+    public int getOriginalEdges() {
+        return chGraph.getOriginalEdges();
+    }
+
+    public int getLevel(int node) {
+        return chGraph.getLevel(node);
+    }
+
+    public void setLevel(int node, int level) {
+        chGraph.setLevel(node, level);
+    }
+
+    public int shortcut(int a, int b, int accessFlags, double weight, int skippedEdge1, int skippedEdge2) {
+        return chGraph.shortcut(a, b, accessFlags, weight, skippedEdge1, skippedEdge2);
+    }
+
+    public int shortcutEdgeBased(int a, int b, int accessFlags, double weight, int skippedEdge1, int skippedEdge2, int origFirst, int origLast) {
+        return chGraph.shortcutEdgeBased(a, b, accessFlags, weight, skippedEdge1, skippedEdge2, origFirst, origLast);
+    }
+
+    public int getOtherNode(int edge, int adjNode) {
+        return chGraph.getOtherNode(edge, adjNode);
+    }
+
+    public NodeAccess getNodeAccess() {
+        return chGraph.getNodeAccess();
+    }
+
+    double getTurnWeight(int inEdge, int viaNode, int outEdge) {
+        if (turnWeighting == null) {
+            throw new IllegalStateException("Calculating turn weights is only allowed when using edge-based traversal mode");
+        }
+        return turnWeighting.calcTurnWeight(inEdge, viaNode, outEdge);
+    }
+
+    public AllCHEdgesIterator getAllEdges() {
+        return chGraph.getAllEdges();
+    }
+
+    boolean isReadyForContraction() {
+        return chGraph.isReadyForContraction();
+    }
+
+    /**
+     * Disconnects the edges (higher to lower node) via the specified edgeState pointing from lower to
+     * higher node.
+     * <p>
+     *
+     * @param edgeState the edge from lower to higher
+     */
+    public void disconnect(PrepareCHEdgeExplorer explorer, PrepareCHEdgeIterator edgeState) {
+        // search edge with opposite direction but we need to know the previousEdge so we cannot simply do:
+        PrepareCHEdgeIterator tmpIter = explorer.setBaseNode(edgeState.getAdjNode());
+        int prevEdge = EdgeIterator.NO_EDGE;
+        while (tmpIter.next()) {
+            // note that we do not disconnect original edges, because we are re-using the base graph for different profiles,
+            // even though this is not optimal from a speed performance point of view.
+            if (tmpIter.isShortcut() && tmpIter.getEdge() == edgeState.getEdge()) {
+                chGraph.disconnectEdge(edgeState.getEdge(), edgeState.getAdjNode(), prevEdge);
+                break;
+            }
+
+            prevEdge = tmpIter.getEdge();
+        }
+    }
+}
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 af2cf1273..b116e9557 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
@@ -21,11 +21,15 @@ import com.carrotsearch.hppc.IntHashSet;
 import com.carrotsearch.hppc.IntSet;
 import com.graphhopper.coll.GHTreeMapComposed;
 import com.graphhopper.routing.*;
-import com.graphhopper.routing.util.*;
+import com.graphhopper.routing.util.AbstractAlgoPreparation;
+import com.graphhopper.routing.util.LevelEdgeFilter;
+import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
-import com.graphhopper.util.*;
+import com.graphhopper.util.Helper;
+import com.graphhopper.util.PMap;
+import com.graphhopper.util.StopWatch;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,7 +58,8 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     private final Logger logger = LoggerFactory.getLogger(getClass());
     private final CHProfile chProfile;
     private final PreparationWeighting prepareWeighting;
-    private final CHGraph prepareGraph;
+    private final CHGraph chGraph;
+    private final PrepareCHGraph prepareGraph;
     private final Random rand = new Random(123);
     private final IntSet updatedNeighbors;
     private final StopWatch allSW = new StopWatch();
@@ -63,10 +68,10 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     private final StopWatch neighborUpdateSW = new StopWatch();
     private final StopWatch contractionSW = new StopWatch();
     private final Params params;
-    private NodeContractor nodeContractor;
+    private final NodeContractor nodeContractor;
     private NodeOrderingProvider nodeOrderingProvider;
-    private CHEdgeExplorer vehicleAllExplorer;
-    private CHEdgeExplorer vehicleAllTmpExplorer;
+    private PrepareCHEdgeExplorer allEdgeExplorer;
+    private PrepareCHEdgeExplorer disconnectExplorer;
     private int maxLevel;
     // nodes with highest priority come last
     private GHTreeMapComposed sortedNodes;
@@ -75,11 +80,23 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     private int checkCounter;
 
     public PrepareContractionHierarchies(CHGraph chGraph) {
-        this.prepareGraph = chGraph;
-        this.chProfile = chGraph.getCHProfile();
+        this.chGraph = chGraph;
+        chProfile = chGraph.getCHProfile();
         prepareWeighting = new PreparationWeighting(chProfile.getWeighting());
-        this.params = Params.forTraversalMode(chProfile.getTraversalMode());
+        params = Params.forTraversalMode(chProfile.getTraversalMode());
         updatedNeighbors = new IntHashSet(50);
+        if (chProfile.getTraversalMode().isEdgeBased()) {
+            TurnCostStorage turnCostStorage = chGraph.getTurnCostStorage();
+            if (turnCostStorage == null) {
+                throw new IllegalArgumentException("For edge-based CH you need a turn cost storage");
+            }
+            TurnWeighting turnWeighting = new TurnWeighting(chProfile.getWeighting(), turnCostStorage, chProfile.getUTurnCosts());
+            prepareGraph = PrepareCHGraph.edgeBased(chGraph, chProfile.getWeighting(), turnWeighting);
+            nodeContractor = new EdgeBasedNodeContractor(prepareGraph, pMap);
+        } else {
+            prepareGraph = PrepareCHGraph.nodeBased(chGraph, chProfile.getWeighting());
+            nodeContractor = new NodeBasedNodeContractor(prepareGraph, pMap);
+        }
     }
 
     public static PrepareContractionHierarchies fromGraphHopperStorage(GraphHopperStorage ghStorage, CHProfile chProfile) {
@@ -116,7 +133,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         if (!prepareGraph.isReadyForContraction()) {
             throw new IllegalStateException("Given CHGraph has not been frozen yet");
         }
-        if (prepareGraph.getEdges() > prepareGraph.getBaseGraph().getEdges()) {
+        if (prepareGraph.getEdges() > prepareGraph.getOriginalEdges()) {
             throw new IllegalStateException("Given CHGraph has been contracted already");
         }
         allSW.start();
@@ -141,13 +158,12 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         } else {
             contractNodesUsingHeuristicNodeOrdering();
         }
-
     }
 
     @Override
     public RoutingAlgorithm createAlgo(Graph graph, AlgorithmOptions opts) {
         AbstractBidirAlgo algo = doCreateAlgo(graph, opts);
-        algo.setEdgeFilter(new LevelEdgeFilter(prepareGraph));
+        algo.setEdgeFilter(new LevelEdgeFilter(chGraph));
         algo.setMaxVisitedNodes(opts.getMaxVisitedNodes());
         return algo;
     }
@@ -191,11 +207,9 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     private void initFromGraph() {
-        FlagEncoder prepareFlagEncoder = prepareWeighting.getFlagEncoder();
-        final EdgeFilter allFilter = DefaultEdgeFilter.allEdges(prepareFlagEncoder);
         maxLevel = prepareGraph.getNodes();
-        vehicleAllExplorer = prepareGraph.createEdgeExplorer(allFilter);
-        vehicleAllTmpExplorer = prepareGraph.createEdgeExplorer(allFilter);
+        allEdgeExplorer = prepareGraph.createAllEdgeExplorer();
+        disconnectExplorer = prepareGraph.createAllEdgeExplorer();
 
         // Use an alternative to PriorityQueue as it has some advantages:
         //   1. Gets automatically smaller if less entries are stored => less total RAM used.
@@ -204,7 +218,6 @@ 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.initFromGraph();
     }
 
@@ -301,7 +314,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
 
             // there might be multiple edges going to the same neighbor nodes -> only calculate priority once per node
             updatedNeighbors.clear();
-            CHEdgeIterator iter = vehicleAllExplorer.setBaseNode(polledNode);
+            PrepareCHEdgeIterator iter = allEdgeExplorer.setBaseNode(polledNode);
             while (iter.next()) {
                 int nn = iter.getAdjNode();
                 if (prepareGraph.getLevel(nn) != maxLevel)
@@ -318,7 +331,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
                     neighborUpdateSW.stop();
                 }
 
-                prepareGraph.disconnect(vehicleAllTmpExplorer, iter);
+                prepareGraph.disconnect(disconnectExplorer, iter);
             }
         }
 
@@ -327,7 +340,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         logger.info(
                 "new shortcuts: " + nf(nodeContractor.getAddedShortcutsCount())
                         + ", initSize:" + nf(initSize)
-                        + ", " + prepareWeighting
+                        + ", " + chProfile.getWeighting()
                         + ", periodic:" + params.getPeriodicUpdatesPercentage()
                         + ", lazy:" + params.getLastNodesLazyUpdatePercentage()
                         + ", neighbor:" + params.getNeighborUpdatePercentage()
@@ -352,11 +365,11 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
             contractNode(node, i);
 
             // disconnect neighbors
-            CHEdgeIterator iter = vehicleAllExplorer.setBaseNode(node);
+            PrepareCHEdgeIterator iter = allEdgeExplorer.setBaseNode(node);
             while (iter.next()) {
                 if (prepareGraph.getLevel(iter.getAdjNode()) != maxLevel)
                     continue;
-                prepareGraph.disconnect(vehicleAllTmpExplorer, iter);
+                prepareGraph.disconnect(disconnectExplorer, iter);
             }
             if (i % logSize == 0) {
                 stopWatch.stop();
@@ -459,15 +472,6 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         return chProfile.isEdgeBased() ? "prepare|dijkstrabi|edge|ch" : "prepare|dijkstrabi|ch";
     }
 
-    private NodeContractor createNodeContractor(Graph graph, TraversalMode traversalMode) {
-        if (traversalMode.isEdgeBased()) {
-            TurnWeighting chTurnWeighting = createTurnWeightingForEdgeBased(graph);
-            return new EdgeBasedNodeContractor(prepareGraph, chTurnWeighting, pMap);
-        } else {
-            return new NodeBasedNodeContractor(prepareGraph, chProfile.getWeighting(), pMap);
-        }
-    }
-
     private TurnWeighting createTurnWeightingForEdgeBased(Graph graph) {
         // important: do not simply take the turn cost storage from ghStorage, because we need the wrapped storage from
         // query graph!
@@ -485,7 +489,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     void close() {
-        CHGraphImpl cg = (CHGraphImpl) prepareGraph;
+        CHGraphImpl cg = (CHGraphImpl) chGraph;
         cg.flush();
         cg.close();
     }
diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java
index 3356938e1..2ef026656 100644
--- a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java
+++ b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java
@@ -260,11 +260,6 @@ class VirtualEdgeIterator implements EdgeIterator, CHEdgeIteratorState {
         throw new UnsupportedOperationException("Not supported.");
     }
 
-    @Override
-    public CHEdgeIteratorState setFirstAndLastOrigEdges(int firstOrigEdge, int lastOrigEdge) {
-        throw new UnsupportedOperationException("Not supported.");
-    }
-
     @Override
     public int getOrigEdgeFirst() {
         return getCurrentEdge().getOrigEdgeFirst();
diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java
index 3afaf084b..d5cf0e305 100644
--- a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java
+++ b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java
@@ -272,11 +272,6 @@ public class VirtualEdgeIteratorState implements EdgeIteratorState, CHEdgeIterat
         throw new UnsupportedOperationException("Not supported.");
     }
 
-    @Override
-    public CHEdgeIteratorState setFirstAndLastOrigEdges(int firstOrigEdge, int lastOrigEdge) {
-        throw new UnsupportedOperationException("Not supported.");
-    }
-
     @Override
     public int getOrigEdgeFirst() {
         return getEdge();
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraph.java b/core/src/main/java/com/graphhopper/storage/CHGraph.java
index 832f01761..fd5d82df3 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraph.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraph.java
@@ -23,7 +23,6 @@ import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.util.CHEdgeExplorer;
 import com.graphhopper.util.CHEdgeIteratorState;
 import com.graphhopper.util.EdgeExplorer;
-import com.graphhopper.util.EdgeIteratorState;
 
 /**
  * Extended graph interface which supports Contraction Hierarchies. Ie. storing and retrieving the
@@ -60,6 +59,11 @@ public interface CHGraph extends Graph {
 
     /**
      * like shortcut(), but for edge-based CH
+     *
+     * @param origFirst The first original edge that is skipped by this shortcut. For example for the following shortcut
+     *                  edge from x to y, which itself skips the shortcuts x->v and v->y the first original edge would
+     *                  be x->u: x->u->v->w->y
+     * @param origLast  like origFirst, but the last orig edge, i.e w->y in above example
      */
     int shortcutEdgeBased(int a, int b, int accessFlags, double weight, int skippedEdge1, int skippedEdge2, int origFirst, int origLast);
 
@@ -79,14 +83,8 @@ public interface CHGraph extends Graph {
     @Override
     AllCHEdgesIterator getAllEdges();
 
-    /**
-     * Disconnects the edges (higher to lower node) via the specified edgeState pointing from lower to
-     * higher node.
-     * <p>
-     *
-     * @param edgeState the edge from lower to higher
-     */
-    void disconnect(CHEdgeExplorer edgeExplorer, EdgeIteratorState edgeState);
+
+    void disconnectEdge(int edge, int adjNode, int prevEdge);
 
     /**
      * @return the number of original edges in this graph (without shortcuts)
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
index 22126da09..049c08509 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
@@ -261,24 +261,11 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     }
 
     @Override
-    public void disconnect(CHEdgeExplorer explorer, EdgeIteratorState edgeState) {
-        // search edge with opposite direction but we need to know the previousEdge for the internalEdgeDisconnect so we cannot simply do:
-        // EdgeIteratorState tmpIter = getEdgeIteratorState(iter.getEdge(), iter.getBaseNode());
-        CHEdgeIterator tmpIter = explorer.setBaseNode(edgeState.getAdjNode());
-        int tmpPrevEdge = EdgeIterator.NO_EDGE;
-        while (tmpIter.next()) {
-            // note that we do not disconnect original edges, because we are re-using the base graph for different profiles,
-            // even though this is not optimal from a speed performance point of view.
-            if (tmpIter.isShortcut() && tmpIter.getEdge() == edgeState.getEdge()) {
-                // TODO this is ugly, move this somehow into the underlying iteration logic
-                long edgePointer = !EdgeIterator.Edge.isValid(tmpPrevEdge) ? -1
-                        : isShortcut(tmpPrevEdge) ? chEdgeAccess.toPointer(tmpPrevEdge) : baseGraph.edgeAccess.toPointer(tmpPrevEdge);
-                chEdgeAccess.internalEdgeDisconnect(edgeState.getEdge(), edgePointer, edgeState.getAdjNode());
-                break;
-            }
-
-            tmpPrevEdge = tmpIter.getEdge();
-        }
+    public void disconnectEdge(int edge, int adjNode, int prevEdge) {
+        // TODO this is ugly, move this somehow into the underlying iteration logic
+        long edgePointer = !EdgeIterator.Edge.isValid(prevEdge) ? -1
+                : isShortcut(prevEdge) ? chEdgeAccess.toPointer(prevEdge) : baseGraph.edgeAccess.toPointer(prevEdge);
+        chEdgeAccess.internalEdgeDisconnect(edge, edgePointer, adjNode);
     }
 
     @Override
@@ -772,13 +759,6 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
             return shortcuts.getInt(edgeIterable.edgePointer + S_SKIP_EDGE2);
         }
 
-        @Override
-        public CHEdgeIteratorState setFirstAndLastOrigEdges(int firstOrigEdge, int lastOrigEdge) {
-            checkShortcutAndEdgeBased("setFirstAndLastOrigEdges");
-            chEdgeAccess.setFirstAndLastOrigEdges(edgeIterable.edgePointer, firstOrigEdge, lastOrigEdge);
-            return this;
-        }
-
         @Override
         public int getOrigEdgeFirst() {
             if (!isShortcut() || !chProfile.isEdgeBased()) {
diff --git a/core/src/main/java/com/graphhopper/util/CHEdgeIteratorState.java b/core/src/main/java/com/graphhopper/util/CHEdgeIteratorState.java
index 3804f3d5c..254539f5d 100644
--- a/core/src/main/java/com/graphhopper/util/CHEdgeIteratorState.java
+++ b/core/src/main/java/com/graphhopper/util/CHEdgeIteratorState.java
@@ -38,15 +38,6 @@ public interface CHEdgeIteratorState extends EdgeIteratorState {
      */
     CHEdgeIteratorState setSkippedEdges(int edge1, int edge2);
 
-    /**
-     * Sets the first and last original edges that are skipped by this shortcut. For example for the following shortcut
-     * edge from x to y, which itself skips the shortcuts x->v and v->y the first original edge would be x->u and the
-     * last original edge would be w->y
-     * <p>
-     * x --> u --> v --> w --> y
-     */
-    CHEdgeIteratorState setFirstAndLastOrigEdges(int firstOrigEdge, int lastOrigEdge);
-
     /**
      * @return true if this edge is a shortcut, false otherwise.
      */
diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java
index 5c3c64a3f..c949d37ba 100644
--- a/core/src/main/java/com/graphhopper/util/GHUtility.java
+++ b/core/src/main/java/com/graphhopper/util/GHUtility.java
@@ -24,7 +24,6 @@ import com.graphhopper.coll.GHIntArrayList;
 import com.graphhopper.coll.GHTBitSet;
 import com.graphhopper.routing.profiles.*;
 import com.graphhopper.routing.util.*;
-import com.graphhopper.routing.util.parsers.*;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.shapes.BBox;
 import org.slf4j.Logger;
@@ -816,11 +815,6 @@ public class GHUtility {
             throw new UnsupportedOperationException("Not supported. Edge is empty.");
         }
 
-        @Override
-        public CHEdgeIteratorState setFirstAndLastOrigEdges(int firstOrigEdge, int lastOrigEdge) {
-            throw new UnsupportedOperationException("Not supported. Edge is empty.");
-        }
-
         @Override
         public int getOrigEdgeFirst() {
             throw new UnsupportedOperationException("Not supported. Edge is empty.");


Test patch:
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 3be143372..9bfd62f7f 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
@@ -23,8 +23,10 @@ import com.graphhopper.routing.util.FlagEncoder;
 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.CHProfile;
+import com.graphhopper.storage.Directory;
+import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.RAMDirectory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -60,7 +62,7 @@ public class CHAlgoFactoryDecoratorTest {
         profileEdge3 = CHProfile.edgeBased(new ShortFastestWeighting(encoder, 0.1), 30);
         ghStorage = new GraphHopperStorage(
                 Arrays.asList(profileNode1, profileNode2, profileNode3, profileEdge1, profileEdge2, profileEdge3),
-                dir, encodingManager, false);
+                dir, encodingManager, false, true);
     }
 
     @Test
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 97b0c1218..8cd48e2a6 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
@@ -1383,9 +1383,9 @@ public class EdgeBasedNodeContractorTest {
     }
 
     private EdgeBasedNodeContractor createNodeContractor() {
-        PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
-        TurnWeighting turnWeighting = new TurnWeighting(preparationWeighting, graph.getTurnCostStorage(), uTurnCosts);
-        EdgeBasedNodeContractor nodeContractor = new EdgeBasedNodeContractor(chGraph, turnWeighting, new PMap());
+        TurnWeighting turnWeighting = new TurnWeighting(weighting, graph.getTurnCostStorage(), uTurnCosts);
+        PrepareCHGraph prepareGraph = PrepareCHGraph.edgeBased(chGraph, weighting, turnWeighting);
+        EdgeBasedNodeContractor nodeContractor = new EdgeBasedNodeContractor(prepareGraph, new PMap());
         nodeContractor.initFromGraph();
         return nodeContractor;
     }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java
index 946f24713..decb5a710 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java
@@ -37,17 +37,17 @@ public class EdgeBasedWitnessPathSearcherTest {
 
     private GraphHopperStorage graph;
     private CHGraph chGraph;
+    private Weighting weighting;
     private TurnWeighting chTurnWeighting;
 
     @Before
     public void setup() {
         CarFlagEncoder encoder = new CarFlagEncoder(5, 5, 10);
         EncodingManager encodingManager = EncodingManager.create(encoder);
-        Weighting weighting = new ShortestWeighting(encoder);
-        PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
+        weighting = new ShortestWeighting(encoder);
         graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting, INFINITE_U_TURN_COSTS)).create();
         TurnCostStorage turnCostStorage = graph.getTurnCostStorage();
-        chTurnWeighting = new TurnWeighting(preparationWeighting, turnCostStorage);
+        chTurnWeighting = new TurnWeighting(weighting, turnCostStorage);
         chGraph = graph.getCHGraph();
     }
 
@@ -126,7 +126,8 @@ public class EdgeBasedWitnessPathSearcherTest {
     }
 
     private EdgeBasedWitnessPathSearcher createFinder() {
-        return new EdgeBasedWitnessPathSearcher(chGraph, chTurnWeighting, new PMap());
+        PrepareCHGraph prepareGraph = PrepareCHGraph.edgeBased(chGraph, weighting, chTurnWeighting);
+        return new EdgeBasedWitnessPathSearcher(prepareGraph, new PMap());
     }
 
     private void setMaxLevelOnAllNodes() {
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 168a75896..02553707d 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
@@ -20,7 +20,6 @@ package com.graphhopper.routing.ch;
 import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.routing.Dijkstra;
 import com.graphhopper.routing.DijkstraBidirectionCH;
-import com.graphhopper.routing.DijkstraOneToMany;
 import com.graphhopper.routing.Path;
 import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.profiles.EncodedValue;
@@ -60,14 +59,14 @@ public class NodeBasedNodeContractorTest {
     private final Weighting weighting = new ShortestWeighting(encoder);
     private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
     private final CHGraph lg = graph.getCHGraph();
-    private final TraversalMode traversalMode = TraversalMode.NODE_BASED;
+    private final PrepareCHGraph pg = PrepareCHGraph.nodeBased(lg, weighting);
 
     private NodeContractor createNodeContractor() {
-        return createNodeContractor(lg, weighting);
+        return createNodeContractor(pg);
     }
 
-    private NodeContractor createNodeContractor(CHGraph chGraph, Weighting weighting) {
-        NodeContractor nodeContractor = new NodeBasedNodeContractor(chGraph, weighting, new PMap());
+    private NodeContractor createNodeContractor(PrepareCHGraph chGraph) {
+        NodeContractor nodeContractor = new NodeBasedNodeContractor(chGraph, new PMap());
         nodeContractor.initFromGraph();
         nodeContractor.prepareContraction();
         return nodeContractor;
@@ -93,12 +92,12 @@ public class NodeBasedNodeContractorTest {
     @Test
     public void testShortestPathSkipNode() {
         createExampleGraph();
-        final double normalDist = new Dijkstra(graph, weighting, traversalMode).calcPath(4, 2).getDistance();
-        DijkstraOneToMany algo = new DijkstraOneToMany(graph, weighting, traversalMode);
+        final double normalDist = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED).calcPath(4, 2).getDistance();
+        NodeBasedWitnessPathSearcher algo = new NodeBasedWitnessPathSearcher(pg);
 
         setMaxLevelOnAllNodes();
 
-        algo.setEdgeFilter(createIgnoreNodeFilter(3));
+        algo.ignoreNode(3);
         algo.setWeightLimit(100);
         int nodeEntry = algo.findEndNode(4, 2);
         assertTrue(algo.getWeight(nodeEntry) > normalDist);
@@ -112,13 +111,13 @@ public class NodeBasedNodeContractorTest {
     @Test
     public void testShortestPathSkipNode2() {
         createExampleGraph();
-        final double normalDist = new Dijkstra(graph, weighting, traversalMode).calcPath(4, 2).getDistance();
+        final double normalDist = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED).calcPath(4, 2).getDistance();
         assertEquals(3, normalDist, 1e-5);
-        DijkstraOneToMany algo = new DijkstraOneToMany(graph, weighting, traversalMode);
+        NodeBasedWitnessPathSearcher algo = new NodeBasedWitnessPathSearcher(pg);
 
         setMaxLevelOnAllNodes();
 
-        algo.setEdgeFilter(createIgnoreNodeFilter(3));
+        algo.ignoreNode(3);
         algo.setWeightLimit(10);
         int nodeEntry = algo.findEndNode(4, 2);
         assertEquals(4, algo.getWeight(nodeEntry), 1e-5);
@@ -130,11 +129,11 @@ public class NodeBasedNodeContractorTest {
     @Test
     public void testShortestPathLimit() {
         createExampleGraph();
-        DijkstraOneToMany algo = new DijkstraOneToMany(graph, weighting, traversalMode);
+        NodeBasedWitnessPathSearcher algo = new NodeBasedWitnessPathSearcher(pg);
 
         setMaxLevelOnAllNodes();
 
-        algo.setEdgeFilter(createIgnoreNodeFilter(0));
+        algo.ignoreNode(0);
         algo.setWeightLimit(2);
         int endNode = algo.findEndNode(4, 1);
         // did not reach endNode
@@ -346,6 +345,7 @@ public class NodeBasedNodeContractorTest {
         Weighting weighting = new FastestWeighting(encoder);
         GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
         CHGraph lg = graph.getCHGraph();
+        PrepareCHGraph pg = PrepareCHGraph.nodeBased(lg, weighting);
         // 0 ------------> 4
         //  \             /
         //   1 --> 2 --> 3
@@ -357,10 +357,10 @@ public class NodeBasedNodeContractorTest {
         graph.edge(2, 3, distances[3], false);
         graph.edge(3, 4, distances[4], false);
         graph.freeze();
-        setMaxLevelOnAllNodes(lg);
+        setMaxLevelOnAllNodes(pg);
 
         // perform CH contraction
-        contractInOrder(lg, weighting, 1, 3, 2, 0, 4);
+        contractInOrder(pg, 1, 3, 2, 0, 4);
 
         // first we compare dijkstra with CH to make sure they produce the same results
         int from = 0;
@@ -384,6 +384,7 @@ public class NodeBasedNodeContractorTest {
         Weighting weighting = new FastestWeighting(encoder);
         GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
         CHGraph lg = graph.getCHGraph();
+        PrepareCHGraph pg = PrepareCHGraph.nodeBased(lg, weighting);
         // 0 - 1 - 2 - 3
         // o           o
         graph.edge(0, 1, 1, true);
@@ -393,11 +394,11 @@ public class NodeBasedNodeContractorTest {
         graph.edge(3, 3, 1, true);
 
         graph.freeze();
-        setMaxLevelOnAllNodes(lg);
-        NodeContractor nodeContractor = createNodeContractor(lg, weighting);
+        setMaxLevelOnAllNodes(pg);
+        NodeContractor nodeContractor = createNodeContractor(pg);
         nodeContractor.contractNode(0);
         nodeContractor.contractNode(3);
-        checkNoShortcuts(lg);
+        checkNoShortcuts(pg);
     }
 
     @Test
@@ -422,11 +423,12 @@ public class NodeBasedNodeContractorTest {
         updateDistancesFor(graph, 6, 0.01, 0.01);
 
         graph.freeze();
-        setMaxLevelOnAllNodes(lg);
+        setMaxLevelOnAllNodes(pg);
 
         // perform CH contraction
         Weighting weighting = new FastestWeighting(encoder, new PMap().put(HEADING_PENALTY, Double.POSITIVE_INFINITY));
-        contractInOrder(lg, weighting, 0, 1, 2, 3, 4, 5, 6);
+        PrepareCHGraph pg = PrepareCHGraph.nodeBased(lg, weighting);
+        contractInOrder(pg, 0, 1, 2, 3, 4, 5, 6);
 
         // build query graph
         LocationIndexTree locationIndex = new LocationIndexTree(graph, new RAMDirectory());
@@ -459,11 +461,11 @@ public class NodeBasedNodeContractorTest {
     }
 
     private void contractInOrder(int... nodeIds) {
-        contractInOrder(lg, weighting, nodeIds);
+        contractInOrder(pg, nodeIds);
     }
 
-    private void contractInOrder(CHGraph chGraph, Weighting weighting, int... nodeIds) {
-        NodeContractor nodeContractor = createNodeContractor(chGraph, weighting);
+    private void contractInOrder(PrepareCHGraph chGraph, int... nodeIds) {
+        NodeContractor nodeContractor = createNodeContractor(chGraph);
         int level = 0;
         for (int n : nodeIds) {
             nodeContractor.contractNode(n);
@@ -476,10 +478,10 @@ public class NodeBasedNodeContractorTest {
      * Queries the ch graph and checks if the graph's shortcuts match the given expected shortcuts.
      */
     private void checkShortcuts(Shortcut... expectedShortcuts) {
-        checkShortcuts(lg, expectedShortcuts);
+        checkShortcuts(pg, expectedShortcuts);
     }
 
-    private void checkShortcuts(CHGraph chGraph, Shortcut... expectedShortcuts) {
+    private void checkShortcuts(PrepareCHGraph chGraph, Shortcut... expectedShortcuts) {
         Set<Shortcut> expected = setOf(expectedShortcuts);
         if (expected.size() != expectedShortcuts.length) {
             fail("was given duplicate shortcuts");
@@ -498,10 +500,10 @@ public class NodeBasedNodeContractorTest {
     }
 
     private void checkNoShortcuts() {
-        checkShortcuts(lg);
+        checkShortcuts(pg);
     }
 
-    private void checkNoShortcuts(CHGraph chGraph) {
+    private void checkNoShortcuts(PrepareCHGraph chGraph) {
         checkShortcuts(chGraph);
     }
 
@@ -526,20 +528,16 @@ public class NodeBasedNodeContractorTest {
     }
 
     private void setMaxLevelOnAllNodes() {
-        setMaxLevelOnAllNodes(lg);
+        setMaxLevelOnAllNodes(pg);
     }
 
-    private void setMaxLevelOnAllNodes(CHGraph chGraph) {
+    private void setMaxLevelOnAllNodes(PrepareCHGraph chGraph) {
         int nodes = chGraph.getNodes();
         for (int node = 0; node < nodes; node++) {
             chGraph.setLevel(node, nodes);
         }
     }
 
-    private IgnoreNodeFilter createIgnoreNodeFilter(int node) {
-        return new IgnoreNodeFilter(lg, graph.getNodes()).setAvoidNode(node);
-    }
-
     private static class Shortcut {
         int baseNode;
         int adjNode;
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
index 953be8f96..21098e3b2 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
@@ -147,7 +147,7 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         iter.next();
         assertEquals(3, iter.getAdjNode());
         assertEquals(1, GHUtility.count(chCarOutExplorer.setBaseNode(3)));
-        lg.disconnect(lg.createEdgeExplorer(), iter);
+        lg.disconnectEdge(iter.getEdge(), iter.getAdjNode(), -1);
         assertEquals(0, GHUtility.count(chCarOutExplorer.setBaseNode(3)));
         // no shortcuts visible
         assertEquals(0, GHUtility.count(baseCarOutExplorer.setBaseNode(3)));
@@ -156,13 +156,13 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         assertTrue(iter.next());
         assertEquals(0, iter.getAdjNode());
         assertEquals(1, GHUtility.count(tmpCarInExplorer.setBaseNode(0)));
-        lg.disconnect(lg.createEdgeExplorer(), iter);
+        lg.disconnectEdge(iter.getEdge(), iter.getAdjNode(), -1);
         assertEquals(0, GHUtility.count(tmpCarInExplorer.setBaseNode(0)));
 
         iter.next();
         assertEquals(2, iter.getAdjNode());
         assertEquals(1, GHUtility.count(chCarOutExplorer.setBaseNode(2)));
-        lg.disconnect(lg.createEdgeExplorer(), iter);
+        lg.disconnectEdge(iter.getEdge(), iter.getAdjNode(), -1);
         assertEquals(0, GHUtility.count(chCarOutExplorer.setBaseNode(2)));
 
         assertEquals(GHUtility.asSet(0, 2, 4), GHUtility.getNeighbors(chCarOutExplorer.setBaseNode(1)));