Repository: graphhopper/graphhopper
Pull request #1819 (2019-12-05 14:19:11)

Take routing algorithm factory out of `PrepareContractionHierarchies`
This PR puts the CH algorithm factory into a separate class instead of making `PrepareContractionHierarchies` a routing algorithm factory. With this change I was able to change the `PrepareContractionHierarchies` constructor to what I think it should look like now:

```java
PrepareContractionHierarchies(GraphHopperStorage graph, CHProfile chProfile);
```

We get the base graph (`GraphHopperStorage`) and a `CHProfile` and our goal is to create a CH preparation (a `CHGraph` currently) for this profile on the base graph. This is all this class should be concerned with. Creating an algorithm on an already prepared CH graph is another story and now done in `CHRoutingAlgorithmFactory`. I still kept `PrepareContractionHierarchies#getRoutingAlgorithmFactory()` for convenience for now.


Patch:
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index 4081b885d..013033462 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -23,7 +23,7 @@ import com.graphhopper.reader.dem.*;
 import com.graphhopper.reader.osm.conditional.DateRangeParser;
 import com.graphhopper.routing.*;
 import com.graphhopper.routing.ch.CHAlgoFactoryDecorator;
-import com.graphhopper.routing.ch.PrepareContractionHierarchies;
+import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
 import com.graphhopper.routing.lm.LMAlgoFactoryDecorator;
 import com.graphhopper.routing.profiles.DefaultEncodedValueFactory;
 import com.graphhopper.routing.profiles.EncodedValueFactory;
@@ -1065,8 +1065,8 @@ public class GraphHopper implements GraphHopperAPI {
                     if (tmpAlgoFactory instanceof LMAlgoFactoryDecorator.LMRAFactory)
                         chAlgoFactory = ((LMAlgoFactoryDecorator.LMRAFactory) tmpAlgoFactory).getDefaultAlgoFactory();
 
-                    if (chAlgoFactory instanceof PrepareContractionHierarchies) {
-                        CHProfile chProfile = ((PrepareContractionHierarchies) chAlgoFactory).getCHProfile();
+                    if (chAlgoFactory instanceof CHRoutingAlgorithmFactory) {
+                        CHProfile chProfile = ((CHRoutingAlgorithmFactory) chAlgoFactory).getCHProfile();
                         queryGraph = QueryGraph.lookup(ghStorage.getCHGraph(chProfile), qResults);
                         weighting = chProfile.getWeighting();
                     } else {
diff --git a/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java b/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java
index f07193037..24f4d5219 100644
--- a/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java
+++ b/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java
@@ -40,7 +40,7 @@ import static com.graphhopper.util.EdgeIterator.ANY_EDGE;
  * @author Peter Karich
  * @author easbar
  */
-public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
+public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm implements BidirRoutingAlgorithm {
     protected int from;
     protected int to;
     protected int fromOutEdge;
@@ -102,15 +102,7 @@ public abstract class AbstractBidirAlgo extends AbstractRoutingAlgorithm {
         return calcPath(from, to, ANY_EDGE, ANY_EDGE);
     }
 
-    /**
-     * like {@link #calcPath(int, int)}, but this method also allows to strictly restrict the edge the
-     * path will begin with and the edge it will end with.
-     *
-     * @param fromOutEdge the edge id of the first edge of the path. using {@link EdgeIterator#ANY_EDGE} means
-     *                    not enforcing the first edge of the path
-     * @param toInEdge    the edge id of the last edge of the path. using {@link EdgeIterator#ANY_EDGE} means
-     *                    not enforcing the last edge of the path
-     */
+    @Override
     public Path calcPath(int from, int to, int fromOutEdge, int toInEdge) {
         if ((fromOutEdge != ANY_EDGE || toInEdge != ANY_EDGE) && !traversalMode.isEdgeBased()) {
             throw new IllegalArgumentException("Restricting the start/target edges is only possible for edge-based graph traversal");
diff --git a/core/src/main/java/com/graphhopper/routing/BidirRoutingAlgorithm.java b/core/src/main/java/com/graphhopper/routing/BidirRoutingAlgorithm.java
new file mode 100644
index 000000000..c78595391
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/BidirRoutingAlgorithm.java
@@ -0,0 +1,35 @@
+/*
+ *  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;
+
+import com.graphhopper.util.EdgeIterator;
+
+public interface BidirRoutingAlgorithm extends RoutingAlgorithm {
+    /**
+     * like {@link #calcPath(int, int)}, but this method also allows to strictly restrict the edge the
+     * path will begin with and the edge it will end with.
+     *
+     * @param fromOutEdge the edge id of the first edge of the path. using {@link EdgeIterator#ANY_EDGE} means
+     *                    not enforcing the first edge of the path
+     * @param toInEdge    the edge id of the last edge of the path. using {@link EdgeIterator#ANY_EDGE} means
+     *                    not enforcing the last edge of the path
+     */
+    // todo: in principle its also possible to implement this method for unidirectional algorithms (but not sure
+    // if it is really worth it).
+    Path calcPath(int from, int to, int fromOutEdge, int toInEdge);
+}
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
index 1da9a9dc8..1b07588d9 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
@@ -240,7 +240,7 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         if (preparations.isEmpty())
             throw new IllegalStateException("No preparations added to this decorator");
 
-        return getPreparation(map);
+        return getPreparation(map).getRoutingAlgorithmFactory();
     }
 
     public PrepareContractionHierarchies getPreparation(HintsMap map) {
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java b/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java
new file mode 100644
index 000000000..33e35aab4
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java
@@ -0,0 +1,107 @@
+/*
+ *  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.*;
+import com.graphhopper.routing.util.LevelEdgeFilter;
+import com.graphhopper.routing.weighting.TurnWeighting;
+import com.graphhopper.routing.weighting.Weighting;
+import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.CHProfile;
+import com.graphhopper.storage.Graph;
+import com.graphhopper.storage.TurnCostStorage;
+
+import static com.graphhopper.util.Parameters.Algorithms.ASTAR_BI;
+import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
+
+public class CHRoutingAlgorithmFactory implements RoutingAlgorithmFactory {
+    private final CHGraph chGraph;
+    private final CHProfile chProfile;
+    private final PreparationWeighting prepareWeighting;
+
+    public CHRoutingAlgorithmFactory(CHGraph chGraph) {
+        this.chGraph = chGraph;
+        this.chProfile = chGraph.getCHProfile();
+        prepareWeighting = new PreparationWeighting(chProfile.getWeighting());
+    }
+
+    @Override
+    public RoutingAlgorithm createAlgo(Graph graph, AlgorithmOptions opts) {
+        // todo: This method does not really fit for CH: We are passed a graph, but really we already know which
+        // graph we have to use: the CH graph. Same with  opts.weighting: The CHProfile already contains a weighting
+        // and we cannot really use it here. The real reason we do this the way its done atm is that graph might be
+        // a QueryGraph that wraps (our) CHGraph.
+        AbstractBidirAlgo algo = doCreateAlgo(graph, opts);
+        algo.setEdgeFilter(new LevelEdgeFilter(chGraph));
+        algo.setMaxVisitedNodes(opts.getMaxVisitedNodes());
+        return algo;
+    }
+
+    private AbstractBidirAlgo doCreateAlgo(Graph graph, AlgorithmOptions opts) {
+        if (chProfile.isEdgeBased()) {
+            return createAlgoEdgeBased(graph, opts);
+        } else {
+            return createAlgoNodeBased(graph, opts);
+        }
+    }
+
+    private AbstractBidirAlgo createAlgoEdgeBased(Graph graph, AlgorithmOptions opts) {
+        if (ASTAR_BI.equals(opts.getAlgorithm())) {
+            return new AStarBidirectionEdgeCHNoSOD(graph, createTurnWeightingForEdgeBased(graph))
+                    .setApproximation(RoutingAlgorithmFactorySimple.getApproximation(ASTAR_BI, opts, graph.getNodeAccess()));
+        } else if (DIJKSTRA_BI.equals(opts.getAlgorithm())) {
+            return new DijkstraBidirectionEdgeCHNoSOD(graph, createTurnWeightingForEdgeBased(graph));
+        } else {
+            throw new IllegalArgumentException("Algorithm " + opts.getAlgorithm() + " not supported for edge-based Contraction Hierarchies. Try with ch.disable=true");
+        }
+    }
+
+    private AbstractBidirAlgo createAlgoNodeBased(Graph graph, AlgorithmOptions opts) {
+        if (ASTAR_BI.equals(opts.getAlgorithm())) {
+            return new AStarBidirectionCH(graph, prepareWeighting)
+                    .setApproximation(RoutingAlgorithmFactorySimple.getApproximation(ASTAR_BI, opts, graph.getNodeAccess()));
+        } else if (DIJKSTRA_BI.equals(opts.getAlgorithm())) {
+            if (opts.getHints().getBool("stall_on_demand", true)) {
+                return new DijkstraBidirectionCH(graph, prepareWeighting);
+            } else {
+                return new DijkstraBidirectionCHNoSOD(graph, prepareWeighting);
+            }
+        } else {
+            throw new IllegalArgumentException("Algorithm " + opts.getAlgorithm() + " not supported for node-based Contraction Hierarchies. Try with ch.disable=true");
+        }
+    }
+
+    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!
+        TurnCostStorage turnCostStorage = graph.getTurnCostStorage();
+        if (turnCostStorage == null) {
+            throw new IllegalArgumentException("For edge-based CH you need a turn cost storage");
+        }
+        return new TurnWeighting(prepareWeighting, turnCostStorage, chProfile.getUTurnCosts());
+    }
+
+    public Weighting getWeighting() {
+        return chProfile.getWeighting();
+    }
+
+    public CHProfile getCHProfile() {
+        return chProfile;
+    }
+}
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 b116e9557..a08f11bc9 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
@@ -38,8 +38,6 @@ import java.util.Random;
 
 import static com.graphhopper.routing.ch.CHParameters.*;
 import static com.graphhopper.util.Helper.nf;
-import static com.graphhopper.util.Parameters.Algorithms.ASTAR_BI;
-import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
 
 /**
  * This class prepares the graph for a bidirectional algorithm supporting contraction hierarchies
@@ -54,10 +52,9 @@ import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
  *
  * @author Peter Karich
  */
-public class PrepareContractionHierarchies extends AbstractAlgoPreparation implements RoutingAlgorithmFactory {
+public class PrepareContractionHierarchies extends AbstractAlgoPreparation {
     private final Logger logger = LoggerFactory.getLogger(getClass());
     private final CHProfile chProfile;
-    private final PreparationWeighting prepareWeighting;
     private final CHGraph chGraph;
     private final PrepareCHGraph prepareGraph;
     private final Random rand = new Random(123);
@@ -79,10 +76,13 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     private PMap pMap = new PMap();
     private int checkCounter;
 
-    public PrepareContractionHierarchies(CHGraph chGraph) {
-        this.chGraph = chGraph;
-        chProfile = chGraph.getCHProfile();
-        prepareWeighting = new PreparationWeighting(chProfile.getWeighting());
+    public static PrepareContractionHierarchies fromGraphHopperStorage(GraphHopperStorage ghStorage, CHProfile chProfile) {
+        return new PrepareContractionHierarchies(ghStorage, chProfile);
+    }
+
+    private PrepareContractionHierarchies(GraphHopperStorage ghStorage, CHProfile chProfile) {
+        this.chGraph = ghStorage.getCHGraph(chProfile);
+        this.chProfile = chProfile;
         params = Params.forTraversalMode(chProfile.getTraversalMode());
         updatedNeighbors = new IntHashSet(50);
         if (chProfile.getTraversalMode().isEdgeBased()) {
@@ -99,10 +99,6 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         }
     }
 
-    public static PrepareContractionHierarchies fromGraphHopperStorage(GraphHopperStorage ghStorage, CHProfile chProfile) {
-        return new PrepareContractionHierarchies(ghStorage.getCHGraph(chProfile));
-    }
-
     public PrepareContractionHierarchies setParams(PMap pMap) {
         this.pMap = pMap;
         params.setPeriodicUpdatesPercentage(pMap.getInt(PERIODIC_UPDATES, params.getPeriodicUpdatesPercentage()));
@@ -160,48 +156,6 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         }
     }
 
-    @Override
-    public RoutingAlgorithm createAlgo(Graph graph, AlgorithmOptions opts) {
-        AbstractBidirAlgo algo = doCreateAlgo(graph, opts);
-        algo.setEdgeFilter(new LevelEdgeFilter(chGraph));
-        algo.setMaxVisitedNodes(opts.getMaxVisitedNodes());
-        return algo;
-    }
-
-    private AbstractBidirAlgo doCreateAlgo(Graph graph, AlgorithmOptions opts) {
-        if (chProfile.isEdgeBased()) {
-            return createAlgoEdgeBased(graph, opts);
-        } else {
-            return createAlgoNodeBased(graph, opts);
-        }
-    }
-
-    private AbstractBidirAlgo createAlgoEdgeBased(Graph graph, AlgorithmOptions opts) {
-        if (ASTAR_BI.equals(opts.getAlgorithm())) {
-            return new AStarBidirectionEdgeCHNoSOD(graph, createTurnWeightingForEdgeBased(graph))
-                    .setApproximation(RoutingAlgorithmFactorySimple.getApproximation(ASTAR_BI, opts, graph.getNodeAccess()));
-        } else if (DIJKSTRA_BI.equals(opts.getAlgorithm())) {
-            return new DijkstraBidirectionEdgeCHNoSOD(graph, createTurnWeightingForEdgeBased(graph));
-        } else {
-            throw new IllegalArgumentException("Algorithm " + opts.getAlgorithm() + " not supported for edge-based Contraction Hierarchies. Try with ch.disable=true");
-        }
-    }
-
-    private AbstractBidirAlgo createAlgoNodeBased(Graph graph, AlgorithmOptions opts) {
-        if (ASTAR_BI.equals(opts.getAlgorithm())) {
-            return new AStarBidirectionCH(graph, prepareWeighting)
-                    .setApproximation(RoutingAlgorithmFactorySimple.getApproximation(ASTAR_BI, opts, graph.getNodeAccess()));
-        } else if (DIJKSTRA_BI.equals(opts.getAlgorithm())) {
-            if (opts.getHints().getBool("stall_on_demand", true)) {
-                return new DijkstraBidirectionCH(graph, prepareWeighting);
-            } else {
-                return new DijkstraBidirectionCHNoSOD(graph, prepareWeighting);
-            }
-        } else {
-            throw new IllegalArgumentException("Algorithm " + opts.getAlgorithm() + " not supported for node-based Contraction Hierarchies. Try with ch.disable=true");
-        }
-    }
-
     public boolean isEdgeBased() {
         return chProfile.isEdgeBased();
     }
@@ -472,16 +426,6 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         return chProfile.isEdgeBased() ? "prepare|dijkstrabi|edge|ch" : "prepare|dijkstrabi|ch";
     }
 
-    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!
-        TurnCostStorage turnCostStorage = graph.getTurnCostStorage();
-        if (turnCostStorage == null) {
-            throw new IllegalArgumentException("For edge-based CH you need a turn cost storage");
-        }
-        return new TurnWeighting(prepareWeighting, turnCostStorage, chProfile.getUTurnCosts());
-    }
-
     private void _close() {
         nodeContractor.close();
         sortedNodes = null;
@@ -494,6 +438,10 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         cg.close();
     }
 
+    public RoutingAlgorithmFactory getRoutingAlgorithmFactory() {
+        return new CHRoutingAlgorithmFactory(chGraph);
+    }
+
     private static class Params {
         /**
          * Specifies after how many contracted nodes a full refresh of the queue of remaining/not contracted nodes


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java b/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
index f85c2bf75..d0e03a353 100644
--- a/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
@@ -65,7 +65,7 @@ public class AStarBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTester {
         if (chGraph.getEdges() == chGraph.getBaseGraph().getEdges()) {
             ch.doWork();
         }
-        return ch;
+        return ch.getRoutingAlgorithmFactory();
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
index 91b36ca85..ab9eb92bc 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
@@ -18,6 +18,7 @@
 package com.graphhopper.routing;
 
 import com.carrotsearch.hppc.IntArrayList;
+import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
@@ -68,7 +69,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         if (ghStorage.getCHGraph(chProfile).getEdges() == ghStorage.getEdges()) {
             ch.doWork();
         }
-        return ch;
+        return ch.getRoutingAlgorithmFactory();
     }
 
     @Test
@@ -108,7 +109,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         lg.setLevel(0, 7);
 
         AlgorithmOptions opts = new AlgorithmOptions(Parameters.Algorithms.DIJKSTRA_BI, weighting);
-        Path p = new PrepareContractionHierarchies(lg).createAlgo(lg, opts).calcPath(0, 7);
+        Path p = new CHRoutingAlgorithmFactory(lg).createAlgo(lg, opts).calcPath(0, 7);
 
         assertEquals(IntArrayList.from(0, 2, 5, 7), p.calcNodes());
         assertEquals(1064, p.getTime());
@@ -260,10 +261,9 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
     }
 
     private RoutingAlgorithm createCHAlgo(CHGraph chGraph, boolean withSOD, AlgorithmOptions algorithmOptions) {
-        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph);
         if (!withSOD) {
             algorithmOptions.getHints().put("stall_on_demand", false);
         }
-        return ch.createAlgo(chGraph, algorithmOptions);
+        return new CHRoutingAlgorithmFactory(chGraph).createAlgo(chGraph, algorithmOptions);
     }
 }
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
index 25ecef2e4..f9efc6732 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
@@ -56,7 +56,7 @@ public class DijkstraBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTeste
         if (chGraph.getEdges() == chGraph.getBaseGraph().getEdges()) {
             ch.doWork();
         }
-        return ch;
+        return ch.getRoutingAlgorithmFactory();
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
index 28ea5c7b6..81fca83a7 100644
--- a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
@@ -169,7 +169,7 @@ public class RandomCHRoutingTest {
                     continue;
                 }
 
-                RoutingAlgorithm algo = pch.createAlgo(chQueryGraph, AlgorithmOptions.start().hints(new PMap().put("stall_on_demand", true)).build());
+                RoutingAlgorithm algo = pch.getRoutingAlgorithmFactory().createAlgo(chQueryGraph, AlgorithmOptions.start().hints(new PMap().put("stall_on_demand", true)).build());
                 Path path = algo.calcPath(from, to);
                 if (!path.isFound()) {
                     fail("path not found for " + from + "->" + to + ", expected weight: " + refWeight);
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
index 1f570edfe..9b1576ba7 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
@@ -1130,7 +1130,7 @@ public class CHTurnCostTest {
                 .useFixedNodeOrdering(nodeOrderingProvider);
         ch.doWork();
         chGraph = graph.getCHGraph(chProfile);
-        return ch;
+        return ch.getRoutingAlgorithmFactory();
     }
 
     private RoutingAlgorithmFactory automaticPrepareCH() {
@@ -1143,7 +1143,7 @@ public class CHTurnCostTest {
         ch.setParams(pMap);
         ch.doWork();
         chGraph = graph.getCHGraph(chProfile);
-        return ch;
+        return ch.getRoutingAlgorithmFactory();
     }
 
     private void automaticCompareCHWithDijkstra(int numQueries) {
diff --git a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
index 94581afa2..4cf839e2b 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
@@ -176,7 +176,7 @@ public class PrepareContractionHierarchiesTest {
         assertEquals(2, prepare.getShortcuts());
         assertEquals(oldCount, lg.getOriginalEdges());
         assertEquals(oldCount + 2,lg.getEdges());
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
+        RoutingAlgorithm algo = prepare.getRoutingAlgorithmFactory().createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(4, 2);
         assertEquals(3, p.getDistance(), 1e-6);
         assertEquals(IntArrayList.from(4, 3, 5, 2), p.calcNodes());
@@ -198,7 +198,7 @@ public class PrepareContractionHierarchiesTest {
         assertEquals(oldCount, lg.getOriginalEdges());
         assertEquals(oldCount + numShortcuts, lg.getEdges());
         assertEquals(oldCount + numShortcuts, GHUtility.count(lg.getAllEdges()));
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
+        RoutingAlgorithm algo = prepare.getRoutingAlgorithmFactory().createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(0, 10);
         assertEquals(10, p.getDistance(), 1e-6);
         assertEquals(IntArrayList.from(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), p.calcNodes());
@@ -264,7 +264,7 @@ public class PrepareContractionHierarchiesTest {
         assertEquals(oldCount, g.getEdges());
         assertEquals(oldCount, lg.getOriginalEdges());
         assertEquals(oldCount + 23, lg.getEdges());
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
+        RoutingAlgorithm algo = prepare.getRoutingAlgorithmFactory().createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(4, 7);
         assertEquals(IntArrayList.from(4, 5, 6, 7), p.calcNodes());
     }
@@ -322,7 +322,7 @@ public class PrepareContractionHierarchiesTest {
         initUnpackingGraph(g, lg, weighting);
         PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g);
         // do not call prepare.doWork() here
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
+        RoutingAlgorithm algo = prepare.getRoutingAlgorithmFactory().createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(10, 6);
         assertEquals(7, p.getDistance(), 1e-5);
         assertEquals(IntArrayList.from(10, 0, 1, 2, 3, 4, 5, 6), p.calcNodes());
@@ -335,7 +335,7 @@ public class PrepareContractionHierarchiesTest {
 
         PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g);
         // do not call prepare.doWork() here
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
+        RoutingAlgorithm algo = prepare.getRoutingAlgorithmFactory().createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(10, 6);
         assertEquals(7, p.getDistance(), 1e-1);
         assertEquals(IntArrayList.from(10, 0, 1, 2, 3, 4, 5, 6), p.calcNodes());
@@ -481,7 +481,7 @@ public class PrepareContractionHierarchiesTest {
         assertTrue("incoming shortcut weight 3->2 should be smaller than sptWeight at node 2 to make sure 2 gets stalled", scWeight23 < sptWeight2);
         assertTrue("sptWeight at node 4 should be smaller than shortcut weight 3->4 to make sure node 4 gets stalled", sptWeight4 < scWeight34);
 
-        Path path = pch.createAlgo(queryGraph, AlgorithmOptions.start().build()).calcPath(0, 7);
+        Path path = pch.getRoutingAlgorithmFactory().createAlgo(queryGraph, AlgorithmOptions.start().build()).calcPath(0, 7);
         assertEquals("wrong or no path found", IntArrayList.from(0, 3, 8, 1, 2, 4, 5, 6, 7), path.calcNodes());
     }
 
@@ -654,7 +654,7 @@ public class PrepareContractionHierarchiesTest {
         // run a few sample queries to check correctness
         for (int i = 0; i < numQueries; ++i) {
             Dijkstra dijkstra = new Dijkstra(ghStorage, motorCycleProfile.getWeighting(), TraversalMode.NODE_BASED);
-            RoutingAlgorithm chAlgo = motorCyclePch.createAlgo(motorCycleCH, AlgorithmOptions.start().weighting(motorCycleProfile.getWeighting()).build());
+            RoutingAlgorithm chAlgo = motorCyclePch.getRoutingAlgorithmFactory().createAlgo(motorCycleCH, AlgorithmOptions.start().weighting(motorCycleProfile.getWeighting()).build());
 
             int from = rnd.nextInt(numNodes);
             int to = rnd.nextInt(numNodes);
@@ -672,7 +672,7 @@ public class PrepareContractionHierarchiesTest {
         PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g, p);
         prepare.doWork();
         assertEquals(p.toString(), expShortcuts, prepare.getShortcuts());
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, p.getWeighting(), tMode));
+        RoutingAlgorithm algo = prepare.getRoutingAlgorithmFactory().createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, p.getWeighting(), tMode));
         Path path = algo.calcPath(3, 12);
         assertEquals(path.toString(), expDistance, path.getDistance(), 1e-5);
         assertEquals(path.toString(), expNodes, path.calcNodes());
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/BidirectionalRoutingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/BidirectionalRoutingTest.java
index 5b085a2f9..ccf1c6d77 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/BidirectionalRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/BidirectionalRoutingTest.java
@@ -111,21 +111,21 @@ public class BidirectionalRoutingTest {
         }
     }
 
-    private AbstractBidirAlgo createAlgo() {
+    private BidirRoutingAlgorithm createAlgo() {
         return createAlgo(prepareCH ? chGraph : graph);
     }
 
-    private AbstractBidirAlgo createAlgo(Graph graph) {
+    private BidirRoutingAlgorithm createAlgo(Graph graph) {
         switch (algo) {
             case ASTAR:
                 return new AStarBidirection(graph, weighting, traversalMode);
             case CH_DIJKSTRA:
-                return (AbstractBidirAlgo) pch.createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(DIJKSTRA_BI).build());
+                return (BidirRoutingAlgorithm) pch.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(DIJKSTRA_BI).build());
             case CH_ASTAR:
-                return (AbstractBidirAlgo) pch.createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).build());
+                return (BidirRoutingAlgorithm) pch.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).build());
             case LM:
                 AStarBidirection astarbi = new AStarBidirection(graph, weighting, traversalMode);
-                return (AbstractBidirAlgo) lm.getDecoratedAlgorithm(graph, astarbi, AlgorithmOptions.start().build());
+                return (BidirRoutingAlgorithm) lm.getDecoratedAlgorithm(graph, astarbi, AlgorithmOptions.start().build());
             default:
                 throw new IllegalArgumentException("unknown algo " + algo);
         }
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
index 41153d11b..98b7d85d3 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
@@ -123,21 +123,21 @@ public class DirectedRoutingTest {
         }
     }
 
-    private AbstractBidirAlgo createAlgo() {
+    private BidirRoutingAlgorithm createAlgo() {
         return createAlgo(prepareCH ? chGraph : graph);
     }
 
-    private AbstractBidirAlgo createAlgo(Graph graph) {
+    private BidirRoutingAlgorithm createAlgo(Graph graph) {
         switch (algo) {
             case ASTAR:
                 return new AStarBidirection(graph, createTurnWeighting(graph), TraversalMode.EDGE_BASED);
             case CH_DIJKSTRA:
-                return (AbstractBidirAlgo) pch.createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(DIJKSTRA_BI).build());
+                return (BidirRoutingAlgorithm) pch.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(DIJKSTRA_BI).build());
             case CH_ASTAR:
-                return (AbstractBidirAlgo) pch.createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).build());
+                return (BidirRoutingAlgorithm) pch.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).build());
             case LM:
                 AStarBidirection astarbi = new AStarBidirection(graph, createTurnWeighting(graph), TraversalMode.EDGE_BASED);
-                return (AbstractBidirAlgo) lm.getDecoratedAlgorithm(graph, astarbi, AlgorithmOptions.start().build());
+                return (BidirRoutingAlgorithm) lm.getDecoratedAlgorithm(graph, astarbi, AlgorithmOptions.start().build());
             default:
                 throw new IllegalArgumentException("unknown algo " + algo);
         }
diff --git a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
index 9baefff03..61aeb2549 100644
--- a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
@@ -27,6 +27,7 @@ import com.graphhopper.coll.GHBitSetImpl;
 import com.graphhopper.reader.DataReader;
 import com.graphhopper.routing.*;
 import com.graphhopper.routing.ch.CHAlgoFactoryDecorator;
+import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.lm.PrepareLandmarks;
 import com.graphhopper.routing.util.*;
@@ -914,8 +915,7 @@ public class GraphHopperOSMTest {
             tmpGH.importOrLoad();
 
             assertEquals(5, tmpGH.getCHFactoryDecorator().getPreparations().size());
-            for (RoutingAlgorithmFactory raf : tmpGH.getCHFactoryDecorator().getPreparations()) {
-                PrepareContractionHierarchies pch = (PrepareContractionHierarchies) raf;
+            for (PrepareContractionHierarchies pch : tmpGH.getCHFactoryDecorator().getPreparations()) {
                 assertTrue("Preparation wasn't run! [" + threadCount + "]", pch.isPrepared());
 
                 String name = pch.getCHProfile().toFileName();
@@ -1000,9 +1000,9 @@ public class GraphHopperOSMTest {
 
         HintsMap wMap = new HintsMap("fastest");
         wMap.put("vehicle", "truck");
-        assertEquals("fastest|truck", ((PrepareContractionHierarchies) decorator.getDecoratedAlgorithmFactory(null, wMap)).getWeighting().toString());
+        assertEquals("fastest|truck", ((CHRoutingAlgorithmFactory) decorator.getDecoratedAlgorithmFactory(null, wMap)).getWeighting().toString());
         wMap.put("vehicle", "simple_truck");
-        assertEquals("fastest|simple_truck", ((PrepareContractionHierarchies) decorator.getDecoratedAlgorithmFactory(null, wMap)).getWeighting().toString());
+        assertEquals("fastest|simple_truck", ((CHRoutingAlgorithmFactory) decorator.getDecoratedAlgorithmFactory(null, wMap)).getWeighting().toString());
 
         // make sure weighting cannot be mixed
         decorator.addCHProfile(CHProfile.nodeBased(fwTruck));
diff --git a/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java b/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
index bfb48559c..5c67c00bb 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
@@ -100,7 +100,7 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         int numFails = 0;
         for (int i = 0; i < numQueries; ++i) {
             Dijkstra dijkstra = new Dijkstra(ghStorage, pch.getWeighting(), TraversalMode.NODE_BASED);
-            RoutingAlgorithm chAlgo = pch.createAlgo(chGraph, AlgorithmOptions.start().weighting(pch.getWeighting()).build());
+            RoutingAlgorithm chAlgo = pch.getRoutingAlgorithmFactory().createAlgo(chGraph, AlgorithmOptions.start().weighting(pch.getWeighting()).build());
 
             int from = rnd.nextInt(ghStorage.getNodes());
             int to = rnd.nextInt(ghStorage.getNodes());
@@ -139,7 +139,7 @@ public class TrafficChangeWithNodeOrderingReusingTest {
                 int from = random.nextInt(numNodes);
                 int to = random.nextInt(numNodes);
                 long start = nanoTime();
-                RoutingAlgorithm algo = pch.createAlgo(chGraph, AlgorithmOptions.start().weighting(pch.getWeighting()).build());
+                RoutingAlgorithm algo = pch.getRoutingAlgorithmFactory().createAlgo(chGraph, AlgorithmOptions.start().weighting(pch.getWeighting()).build());
                 Path path = algo.calcPath(from, to);
                 if (!warmup && !path.isFound())
                     return 1;