Repository: graphhopper/graphhopper
Pull request #2284 (2021-04-17 17:53:04)

Bug wrong exception type




Patch:
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index b4184f078..7ec3f2f82 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -35,6 +35,7 @@ Here is an overview:
  * HelgeKrueger, modularization of javascript, #590
  * henningvs, doc improvements
  * highsource, more efficient geometry update, UI fixes
+ * hoofstephan, bug fix   
  * IsNull, improvements like #708
  * Janekdererste, GUI for public transit
  * jansoe, many improvements regarding A* algorithm, forcing direction, roundabouts etc
diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java b/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java
index 5fda9202b..d483fa19c 100644
--- a/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java
+++ b/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java
@@ -102,7 +102,6 @@ public abstract class GHMatrixAbstractRequester {
 
         putStrings(requestJson, "snap_preventions", ghRequest.getSnapPreventions());
         putStrings(requestJson, "out_arrays", outArraysList);
-        requestJson.put("vehicle", ghRequest.getHints().getString("vehicle", ""));
         // requestJson.put("elevation", ghRequest.getHints().getBool("elevation", false));
         requestJson.put("fail_fast", ghRequest.getFailFast());
 
diff --git a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java
index 5074867bd..7ab544e78 100644
--- a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java
+++ b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java
@@ -37,10 +37,7 @@ import okhttp3.ResponseBody;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 import static com.graphhopper.api.GraphHopperMatrixWeb.*;
@@ -106,7 +103,6 @@ public class GraphHopperWeb implements GraphHopperAPI {
         ignoreSet.add("algorithm");
         ignoreSet.add("locale");
         ignoreSet.add("point");
-        ignoreSet.add("vehicle");
 
         // some are special and need to be avoided
         ignoreSet.add("points_encoded");
@@ -136,14 +132,16 @@ public class GraphHopperWeb implements GraphHopperAPI {
     }
 
     public GraphHopperWeb setKey(String key) {
-        if (key == null || key.isEmpty()) {
-            throw new IllegalStateException("Key cannot be empty");
+        Objects.requireNonNull(key,"Key must not be null");
+        if (key.isEmpty()) {
+            throw new IllegalArgumentException("Key must not be empty");
         }
 
         this.key = key;
         return this;
     }
 
+
     /**
      * Use new endpoint 'POST /route' instead of 'GET /route'
      */
@@ -286,7 +284,7 @@ public class GraphHopperWeb implements GraphHopperAPI {
         return builder.build();
     }
 
-    private Request createGetRequest(GHRequest ghRequest) {
+    Request createGetRequest(GHRequest ghRequest) {
         boolean tmpInstructions = ghRequest.getHints().getBool(INSTRUCTIONS, instructions);
         boolean tmpCalcPoints = ghRequest.getHints().getBool(CALC_POINTS, calcPoints);
         String tmpOptimize = ghRequest.getHints().getString("optimize", optimize);
@@ -318,10 +316,6 @@ public class GraphHopperWeb implements GraphHopperAPI {
                 + "&elevation=" + tmpElevation
                 + "&optimize=" + tmpOptimize;
 
-        if (ghRequest.getHints().has("vehicle")) {
-            url += "&vehicle=" + ghRequest.getHints().getString("vehicle", "");
-        }
-
         for (String details : ghRequest.getPathDetails()) {
             url += "&" + Parameters.Details.PATH_DETAILS + "=" + details;
         }
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index 51a2bab68..1772bab13 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -974,6 +974,11 @@ public class GraphHopper implements GraphHopperAPI {
                             ? lmPreparationHandler.getPreparation(lmp.getPreparationProfile()).getLandmarkStorage()
                             : lmPreparationHandler.getPreparation(lmp.getProfile()).getLandmarkStorage());
         }
+        return doCreateRouter(ghStorage, locationIndex, profilesByName, pathBuilderFactory,
+                trMap, routerConfig, createWeightingFactory(), chGraphs, landmarks);
+    }
+
+    protected Router doCreateRouter(GraphHopperStorage ghStorage, LocationIndex locationIndex, Map<String, Profile> profilesByName, PathDetailsBuilderFactory pathBuilderFactory, TranslationMap trMap, RouterConfig routerConfig, WeightingFactory weightingFactory, Map<String, CHGraph> chGraphs, Map<String, LandmarkStorage> landmarks) {
         return new Router(ghStorage, locationIndex, profilesByName, pathBuilderFactory,
                 trMap, routerConfig, createWeightingFactory(), chGraphs, landmarks
         );
diff --git a/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java b/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java
index 0e87f29cd..b208fadf4 100644
--- a/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java
+++ b/core/src/main/java/com/graphhopper/routing/AlgorithmOptions.java
@@ -18,91 +18,53 @@
 package com.graphhopper.routing;
 
 import com.graphhopper.routing.util.TraversalMode;
-import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.PMap;
 import com.graphhopper.util.Parameters;
 
 /**
- * The algorithm options. Create an immutable object via:
- * <pre>
- * AlgorithmOptions algoOpts = AlgorithmOptions.start().
- *        algorithm(Parameters.Algorithms.DIJKSTRA).
- *        weighting(weighting).
- *        build();
- * </pre>
- * <p>
- *
  * @author Peter Karich
  */
 public class AlgorithmOptions {
-    private final PMap hints = new PMap(5);
+    private PMap hints = new PMap();
     private String algorithm = Parameters.Algorithms.DIJKSTRA_BI;
-    private Weighting weighting;
     private TraversalMode traversalMode = TraversalMode.NODE_BASED;
     private int maxVisitedNodes = Integer.MAX_VALUE;
 
-    private AlgorithmOptions() {
+    public AlgorithmOptions() {
     }
 
-    /**
-     * Default traversal mode NODE_BASED is used.
-     */
-    public AlgorithmOptions(String algorithm, Weighting weighting) {
-        this.algorithm = algorithm;
-        this.weighting = weighting;
+    public AlgorithmOptions(AlgorithmOptions b) {
+        setAlgorithm(b.getAlgorithm());
+        setTraversalMode(b.getTraversalMode());
+        setMaxVisitedNodes(b.getMaxVisitedNodes());
+        setHints(b.getHints());
     }
 
-    public AlgorithmOptions(String algorithm, Weighting weighting, TraversalMode tMode) {
+    public AlgorithmOptions setAlgorithm(String algorithm) {
         this.algorithm = algorithm;
-        this.weighting = weighting;
-        this.traversalMode = tMode;
+        return this;
     }
 
-    /**
-     * This method starts the building process for AlgorithmOptions.
-     */
-    public static Builder start() {
-        return new Builder();
+    public AlgorithmOptions setTraversalMode(TraversalMode traversalMode) {
+        this.traversalMode = traversalMode;
+        return this;
     }
 
-    /**
-     * This method clones the specified AlgorithmOption object with the possibility for further
-     * changes.
-     */
-    public static Builder start(AlgorithmOptions opts) {
-        Builder b = new Builder();
-        if (opts.algorithm != null)
-            b.algorithm(opts.getAlgorithm());
-        if (opts.traversalMode != null)
-            b.traversalMode(opts.getTraversalMode());
-        if (opts.weighting != null)
-            b.weighting(opts.getWeighting());
-        if (opts.maxVisitedNodes >= 0)
-            b.maxVisitedNodes(opts.maxVisitedNodes);
-        if (!opts.hints.isEmpty())
-            b.hints(opts.hints);
+    public AlgorithmOptions setMaxVisitedNodes(int maxVisitedNodes) {
+        this.maxVisitedNodes = maxVisitedNodes;
+        return this;
+    }
 
-        return b;
+    public AlgorithmOptions setHints(PMap pMap) {
+        this.hints = new PMap(pMap);
+        return this;
     }
 
-    /**
-     * @return the traversal mode, where node-based is the default.
-     */
     public TraversalMode getTraversalMode() {
         return traversalMode;
     }
 
-    public boolean hasWeighting() {
-        return weighting != null;
-    }
-
-    public Weighting getWeighting() {
-        assertNotNull(weighting, "weighting");
-        return weighting;
-    }
-
     public String getAlgorithm() {
-        assertNotNull(algorithm, "algorithm");
         return algorithm;
     }
 
@@ -114,57 +76,9 @@ public class AlgorithmOptions {
         return hints;
     }
 
-    private void assertNotNull(Object optionValue, String optionName) {
-        if (optionValue == null)
-            throw new NullPointerException("Option '" + optionName + "' must NOT be null");
-    }
-
     @Override
     public String toString() {
-        return algorithm + ", " + weighting + ", " + traversalMode;
+        return algorithm + ", " + traversalMode;
     }
 
-    public static class Builder {
-        private AlgorithmOptions opts = new AlgorithmOptions();
-        private boolean buildCalled;
-
-        public Builder traversalMode(TraversalMode traversalMode) {
-            if (traversalMode == null)
-                throw new IllegalArgumentException("null as traversal mode is not allowed");
-
-            this.opts.traversalMode = traversalMode;
-            return this;
-        }
-
-        public Builder weighting(Weighting weighting) {
-            this.opts.weighting = weighting;
-            return this;
-        }
-
-        /**
-         * For possible values see {@link Parameters.Algorithms}
-         */
-        public Builder algorithm(String algorithm) {
-            this.opts.algorithm = algorithm;
-            return this;
-        }
-
-        public Builder maxVisitedNodes(int maxVisitedNodes) {
-            this.opts.maxVisitedNodes = maxVisitedNodes;
-            return this;
-        }
-
-        public Builder hints(PMap hints) {
-            this.opts.hints.putAll(hints);
-            return this;
-        }
-
-        public AlgorithmOptions build() {
-            if (buildCalled)
-                throw new IllegalStateException("Cannot call AlgorithmOptions.Builder.build() twice");
-
-            buildCalled = true;
-            return opts;
-        }
-    }
 }
diff --git a/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java b/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java
index 04f947021..fb5e47cb3 100644
--- a/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java
+++ b/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java
@@ -20,6 +20,7 @@ package com.graphhopper.routing;
 
 import com.carrotsearch.hppc.cursors.IntCursor;
 import com.graphhopper.routing.querygraph.QueryGraph;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.util.Parameters;
 import com.graphhopper.util.StopWatch;
 
@@ -31,13 +32,15 @@ import static com.graphhopper.util.EdgeIterator.ANY_EDGE;
 public class FlexiblePathCalculator implements PathCalculator {
     private final QueryGraph queryGraph;
     private final RoutingAlgorithmFactory algoFactory;
+    private Weighting weighting;
     private AlgorithmOptions algoOpts;
     private String debug;
     private int visitedNodes;
 
-    public FlexiblePathCalculator(QueryGraph queryGraph, RoutingAlgorithmFactory algoFactory, AlgorithmOptions algoOpts) {
+    public FlexiblePathCalculator(QueryGraph queryGraph, RoutingAlgorithmFactory algoFactory, Weighting weighting, AlgorithmOptions algoOpts) {
         this.queryGraph = queryGraph;
         this.algoFactory = algoFactory;
+        this.weighting = weighting;
         this.algoOpts = algoOpts;
     }
 
@@ -49,7 +52,7 @@ public class FlexiblePathCalculator implements PathCalculator {
 
     private RoutingAlgorithm createAlgo() {
         StopWatch sw = new StopWatch().start();
-        RoutingAlgorithm algo = algoFactory.createAlgo(queryGraph, algoOpts);
+        RoutingAlgorithm algo = algoFactory.createAlgo(queryGraph, weighting, algoOpts);
         debug = ", algoInit:" + (sw.stop().getNanos() / 1000) + " μs";
         return algo;
     }
@@ -97,11 +100,11 @@ public class FlexiblePathCalculator implements PathCalculator {
         return visitedNodes;
     }
 
-    public AlgorithmOptions getAlgoOpts() {
-        return algoOpts;
+    public Weighting getWeighting() {
+        return weighting;
     }
 
-    public void setAlgoOpts(AlgorithmOptions algoOpts) {
-        this.algoOpts = algoOpts;
+    public void setWeighting(Weighting weighting) {
+        this.weighting = weighting;
     }
 }
\ No newline at end of file
diff --git a/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java b/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java
index 7145213b8..18d6890f0 100644
--- a/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java
+++ b/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java
@@ -67,9 +67,8 @@ public class RoundTripRouting {
         }
     }
 
-    public static List<Snap> lookup(List<GHPoint> points, Weighting weighting, LocationIndex locationIndex, Params params) {
+    public static List<Snap> lookup(List<GHPoint> points, EdgeFilter edgeFilter, LocationIndex locationIndex, Params params) {
         // todo: no snap preventions for round trip so far
-        EdgeFilter edgeFilter = new FiniteWeightFilter(weighting);
         if (points.size() != 1)
             throw new IllegalArgumentException("For round trip calculation exactly one point is required");
 
@@ -152,12 +151,10 @@ public class RoundTripRouting {
         RoundTripCalculator(FlexiblePathCalculator pathCalculator) {
             this.pathCalculator = pathCalculator;
             // we make the path calculator use our avoid edges weighting
-            AvoidEdgesWeighting avoidPreviousPathsWeighting = new AvoidEdgesWeighting(pathCalculator.getAlgoOpts().getWeighting())
+            AvoidEdgesWeighting avoidPreviousPathsWeighting = new AvoidEdgesWeighting(pathCalculator.getWeighting())
                     .setEdgePenaltyFactor(5);
             avoidPreviousPathsWeighting.setAvoidedEdges(previousEdges);
-            AlgorithmOptions algoOpts = AlgorithmOptions.start(pathCalculator.getAlgoOpts()).
-                    weighting(avoidPreviousPathsWeighting).build();
-            pathCalculator.setAlgoOpts(algoOpts);
+            pathCalculator.setWeighting(avoidPreviousPathsWeighting);
         }
 
         Path calcPath(int from, int to) {
diff --git a/core/src/main/java/com/graphhopper/routing/Router.java b/core/src/main/java/com/graphhopper/routing/Router.java
index 1e8a145f7..14fd0e138 100644
--- a/core/src/main/java/com/graphhopper/routing/Router.java
+++ b/core/src/main/java/com/graphhopper/routing/Router.java
@@ -27,6 +27,7 @@ import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
 import com.graphhopper.routing.lm.LMRoutingAlgorithmFactory;
 import com.graphhopper.routing.lm.LandmarkStorage;
 import com.graphhopper.routing.querygraph.QueryGraph;
+import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FiniteWeightFilter;
 import com.graphhopper.routing.util.TraversalMode;
@@ -93,48 +94,26 @@ public class Router {
 
     public GHResponse route(GHRequest request) {
         try {
-            validateRequest(request);
-            final boolean disableCH = getDisableCH(request.getHints());
-            final boolean disableLM = getDisableLM(request.getHints());
-            Profile profile = profilesByName.get(request.getProfile());
-            if (profile == null)
-                throw new IllegalArgumentException("The requested profile '" + request.getProfile() + "' does not exist.\nAvailable profiles: " + profilesByName.keySet());
-            if (!profile.isTurnCosts() && !request.getCurbsides().isEmpty())
-                throw new IllegalArgumentException("To make use of the " + CURBSIDE + " parameter you need to use a profile that supports turn costs" +
-                        "\nThe following profiles do support turn costs: " + getTurnCostProfiles());
-            if (request.getCustomModel() != null && !(profile instanceof CustomProfile))
-                throw new IllegalArgumentException("The requested profile '" + request.getProfile() + "' cannot be used with `custom_model`, because it has weighting=" + profile.getWeighting());
-
-            // todo later: should we be able to control this using the edge_based parameter?
-            TraversalMode traversalMode = profile.isTurnCosts() ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED;
-            final int uTurnCostsInt = request.getHints().getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS);
-            if (uTurnCostsInt != INFINITE_U_TURN_COSTS && !traversalMode.isEdgeBased()) {
-                throw new IllegalArgumentException("Finite u-turn costs can only be used for edge-based routing, you need to use a profile that" +
-                        " supports turn costs. Currently the following profiles that support turn costs are available: " + getTurnCostProfiles());
-            }
-            final boolean passThrough = getPassThrough(request.getHints());
-            final boolean forceCurbsides = request.getHints().getBool(FORCE_CURBSIDE, true);
-            int maxVisitedNodesForRequest = request.getHints().getInt(Parameters.Routing.MAX_VISITED_NODES, routerConfig.getMaxVisitedNodes());
-            if (maxVisitedNodesForRequest > routerConfig.getMaxVisitedNodes())
-                throw new IllegalArgumentException("The max_visited_nodes parameter has to be below or equal to:" + routerConfig.getMaxVisitedNodes());
-
-            PMap requestHints = new PMap(request.getHints());
-            requestHints.putObject(CustomModel.KEY, request.getCustomModel());
-            Weighting weighting = createWeighting(profile, requestHints, request.getPoints(), disableCH);
-            AlgorithmOptions algoOpts = AlgorithmOptions.start().
-                    algorithm(request.getAlgorithm()).
-                    traversalMode(traversalMode).
-                    weighting(weighting).
-                    maxVisitedNodes(maxVisitedNodesForRequest).
-                    hints(request.getHints()).
-                    build();
+            checkNoLegacyParameters(request);
+            checkAtLeastOnePoint(request);
+            checkIfPointsAreInBounds(request.getPoints());
+            checkHeadings(request);
+            checkPointHints(request);
+            checkCurbsides(request);
+            checkNoBlockAreaWithCustomModel(request);
+
+            Solver solver = createSolver(request);
+            solver.checkRequest();
+            solver.init();
 
             if (ROUND_TRIP.equalsIgnoreCase(request.getAlgorithm())) {
-                return routeRoundTrip(request, algoOpts, weighting, profile, disableLM);
+                if (!(solver instanceof FlexSolver))
+                    throw new IllegalArgumentException("algorithm=round_trip only works with a flexible algorithm");
+                return routeRoundTrip(request, (FlexSolver) solver);
             } else if (ALT_ROUTE.equalsIgnoreCase(request.getAlgorithm())) {
-                return routeAlt(request, algoOpts, weighting, profile, passThrough, forceCurbsides, disableCH, disableLM);
+                return routeAlt(request, solver);
             } else {
-                return routeVia(request, algoOpts, weighting, profile, passThrough, forceCurbsides, disableCH, disableLM);
+                return routeVia(request, solver);
             }
         } catch (MultiplePointsNotFoundException ex) {
             GHResponse ghRsp = new GHResponse();
@@ -149,146 +128,145 @@ public class Router {
         }
     }
 
-    protected GHResponse routeRoundTrip(GHRequest request, AlgorithmOptions algoOpts, Weighting weighting, Profile profile, boolean disableLM) {
+    private void checkNoLegacyParameters(GHRequest request) {
+        if (request.getHints().has("vehicle"))
+            throw new IllegalArgumentException("GHRequest may no longer contain a vehicle, use the profile parameter instead, see docs/core/profiles.md");
+        if (request.getHints().has("weighting"))
+            throw new IllegalArgumentException("GHRequest may no longer contain a weighting, use the profile parameter instead, see docs/core/profiles.md");
+        if (request.getHints().has(Parameters.Routing.TURN_COSTS))
+            throw new IllegalArgumentException("GHRequest may no longer contain the turn_costs=true/false parameter, use the profile parameter instead, see docs/core/profiles.md");
+        if (request.getHints().has(Parameters.Routing.EDGE_BASED))
+            throw new IllegalArgumentException("GHRequest may no longer contain the edge_based=true/false parameter, use the profile parameter instead, see docs/core/profiles.md");
+    }
+
+    private void checkAtLeastOnePoint(GHRequest request) {
+        if (request.getPoints().isEmpty())
+            throw new IllegalArgumentException("You have to pass at least one point");
+    }
+
+    private void checkIfPointsAreInBounds(List<GHPoint> points) {
+        BBox bounds = ghStorage.getBounds();
+        for (int i = 0; i < points.size(); i++) {
+            GHPoint point = points.get(i);
+            if (!bounds.contains(point.getLat(), point.getLon())) {
+                throw new PointOutOfBoundsException("Point " + i + " is out of bounds: " + point + ", the bounds are: " + bounds, i);
+            }
+        }
+    }
+
+    private void checkHeadings(GHRequest request) {
+        if (request.getHeadings().size() > 1 && request.getHeadings().size() != request.getPoints().size())
+            throw new IllegalArgumentException("The number of 'heading' parameters must be zero, one "
+                    + "or equal to the number of points (" + request.getPoints().size() + ")");
+        for (int i = 0; i < request.getHeadings().size(); i++)
+            if (!GHRequest.isAzimuthValue(request.getHeadings().get(i)))
+                throw new IllegalArgumentException("Heading for point " + i + " must be in range [0,360) or NaN, but was: " + request.getHeadings().get(i));
+    }
+
+    private void checkPointHints(GHRequest request) {
+        if (request.getPointHints().size() > 0 && request.getPointHints().size() != request.getPoints().size())
+            throw new IllegalArgumentException("If you pass " + POINT_HINT + ", you need to pass exactly one hint for every point, empty hints will be ignored");
+    }
+
+    private void checkCurbsides(GHRequest request) {
+        if (request.getCurbsides().size() > 0 && request.getCurbsides().size() != request.getPoints().size())
+            throw new IllegalArgumentException("If you pass " + CURBSIDE + ", you need to pass exactly one curbside for every point, empty curbsides will be ignored");
+    }
+
+    private void checkNoBlockAreaWithCustomModel(GHRequest request) {
+        if (request.getCustomModel() != null && request.getHints().has(BLOCK_AREA))
+            throw new IllegalArgumentException("When using `custom_model` do not use `block_area`. Use `areas` in the custom model instead");
+    }
+
+    protected Solver createSolver(GHRequest request) {
+        final boolean disableCH = getDisableCH(request.getHints());
+        final boolean disableLM = getDisableLM(request.getHints());
+        if (chEnabled && !disableCH) {
+            return new CHSolver(request, profilesByName, routerConfig, chGraphs);
+        } else if (lmEnabled && !disableLM) {
+            return new LMSolver(request, profilesByName, routerConfig, weightingFactory, ghStorage, locationIndex, landmarks);
+        } else {
+            return new FlexSolver(request, profilesByName, routerConfig, weightingFactory, ghStorage, locationIndex);
+        }
+    }
+
+    protected GHResponse routeRoundTrip(GHRequest request, FlexSolver solver) {
         GHResponse ghRsp = new GHResponse();
         StopWatch sw = new StopWatch().start();
         double startHeading = request.getHeadings().isEmpty() ? Double.NaN : request.getHeadings().get(0);
         RoundTripRouting.Params params = new RoundTripRouting.Params(request.getHints(), startHeading, routerConfig.getMaxRoundTripRetries());
-        List<Snap> qResults = RoundTripRouting.lookup(request.getPoints(), weighting, locationIndex, params);
+        List<Snap> snaps = RoundTripRouting.lookup(request.getPoints(), solver.getSnappingFilter(), locationIndex, params);
         ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
 
-        // use A* for round trips
-        AlgorithmOptions roundTripAlgoOpts = AlgorithmOptions
-                .start(algoOpts)
-                .algorithm(Parameters.Algorithms.ASTAR_BI)
-                .build();
-        roundTripAlgoOpts.getHints().putObject(Parameters.Algorithms.AStarBi.EPSILON, 2);
-        QueryGraph queryGraph = QueryGraph.create(ghStorage, qResults);
-        FlexiblePathCalculator pathCalculator = createFlexiblePathCalculator(queryGraph, profile, roundTripAlgoOpts, disableLM);
+        QueryGraph queryGraph = QueryGraph.create(ghStorage, snaps);
+        FlexiblePathCalculator pathCalculator = solver.createPathCalculator(queryGraph);
 
-        RoundTripRouting.Result result = RoundTripRouting.calcPaths(qResults, pathCalculator);
+        RoundTripRouting.Result result = RoundTripRouting.calcPaths(snaps, pathCalculator);
         // we merge the different legs of the roundtrip into one response path
-        ResponsePath responsePath = concatenatePaths(request, weighting, queryGraph, result.paths, getWaypoints(qResults));
+        ResponsePath responsePath = concatenatePaths(request, solver.weighting, queryGraph, result.paths, getWaypoints(snaps));
         ghRsp.add(responsePath);
         ghRsp.getHints().putObject("visited_nodes.sum", result.visitedNodes);
-        ghRsp.getHints().putObject("visited_nodes.average", (float) result.visitedNodes / (qResults.size() - 1));
+        ghRsp.getHints().putObject("visited_nodes.average", (float) result.visitedNodes / (snaps.size() - 1));
         return ghRsp;
     }
 
-    protected GHResponse routeAlt(GHRequest request, AlgorithmOptions algoOpts, Weighting weighting, Profile profile, boolean passThrough, boolean forceCurbsides, boolean disableCH, boolean disableLM) {
+    protected GHResponse routeAlt(GHRequest request, Solver solver) {
         if (request.getPoints().size() > 2)
             throw new IllegalArgumentException("Currently alternative routes work only with start and end point. You tried to use: " + request.getPoints().size() + " points");
         GHResponse ghRsp = new GHResponse();
         StopWatch sw = new StopWatch().start();
-        List<Snap> qResults = ViaRouting.lookup(encodingManager, request.getPoints(), weighting, locationIndex, request.getSnapPreventions(), request.getPointHints());
+        List<Snap> snaps = ViaRouting.lookup(encodingManager, request.getPoints(), solver.getSnappingFilter(), locationIndex, request.getSnapPreventions(), request.getPointHints());
         ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
-        QueryGraph queryGraph = QueryGraph.create(ghStorage, qResults);
-        PathCalculator pathCalculator = createPathCalculator(queryGraph, profile, algoOpts, disableCH, disableLM);
-
+        QueryGraph queryGraph = QueryGraph.create(ghStorage, snaps);
+        PathCalculator pathCalculator = solver.createPathCalculator(queryGraph);
+        boolean passThrough = getPassThrough(request.getHints());
+        boolean forceCurbsides = getForceCurbsides(request.getHints());
         if (passThrough)
             throw new IllegalArgumentException("Alternative paths and " + PASS_THROUGH + " at the same time is currently not supported");
         if (!request.getCurbsides().isEmpty())
             throw new IllegalArgumentException("Alternative paths do not support the " + CURBSIDE + " parameter yet");
 
-        ViaRouting.Result result = ViaRouting.calcPaths(request.getPoints(), queryGraph, qResults, weighting, pathCalculator, request.getCurbsides(), forceCurbsides, request.getHeadings(), passThrough);
+        ViaRouting.Result result = ViaRouting.calcPaths(request.getPoints(), queryGraph, snaps, solver.weighting, pathCalculator, request.getCurbsides(), forceCurbsides, request.getHeadings(), passThrough);
         if (result.paths.isEmpty())
             throw new RuntimeException("Empty paths for alternative route calculation not expected");
 
         // each path represents a different alternative and we do the path merging for each of them
-        PathMerger pathMerger = createPathMerger(request, weighting, queryGraph);
+        PathMerger pathMerger = createPathMerger(request, solver.weighting, queryGraph);
         for (Path path : result.paths) {
-            PointList waypoints = getWaypoints(qResults);
+            PointList waypoints = getWaypoints(snaps);
             ResponsePath responsePath = pathMerger.doWork(waypoints, Collections.singletonList(path), encodingManager, translationMap.getWithFallBack(request.getLocale()));
             ghRsp.add(responsePath);
         }
         ghRsp.getHints().putObject("visited_nodes.sum", result.visitedNodes);
-        ghRsp.getHints().putObject("visited_nodes.average", (float) result.visitedNodes / (qResults.size() - 1));
+        ghRsp.getHints().putObject("visited_nodes.average", (float) result.visitedNodes / (snaps.size() - 1));
         return ghRsp;
     }
 
-    protected GHResponse routeVia(GHRequest request, AlgorithmOptions algoOpts, Weighting weighting, Profile profile, boolean passThrough, boolean forceCurbsides, boolean disableCH, boolean disableLM) {
+    protected GHResponse routeVia(GHRequest request, Solver solver) {
         GHResponse ghRsp = new GHResponse();
         StopWatch sw = new StopWatch().start();
-        List<Snap> qResults = ViaRouting.lookup(encodingManager, request.getPoints(), weighting, locationIndex, request.getSnapPreventions(), request.getPointHints());
+        List<Snap> snaps = ViaRouting.lookup(encodingManager, request.getPoints(), solver.getSnappingFilter(), locationIndex, request.getSnapPreventions(), request.getPointHints());
         ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
         // (base) query graph used to resolve headings, curbsides etc. this is not necessarily the same thing as
         // the (possibly implementation specific) query graph used by PathCalculator
-        QueryGraph queryGraph = QueryGraph.create(ghStorage, qResults);
-        PathCalculator pathCalculator = createPathCalculator(queryGraph, profile, algoOpts, disableCH, disableLM);
-        ViaRouting.Result result = ViaRouting.calcPaths(request.getPoints(), queryGraph, qResults, weighting, pathCalculator, request.getCurbsides(), forceCurbsides, request.getHeadings(), passThrough);
+        QueryGraph queryGraph = QueryGraph.create(ghStorage, snaps);
+        PathCalculator pathCalculator = solver.createPathCalculator(queryGraph);
+        boolean passThrough = getPassThrough(request.getHints());
+        boolean forceCurbsides = getForceCurbsides(request.getHints());
+        ViaRouting.Result result = ViaRouting.calcPaths(request.getPoints(), queryGraph, snaps, solver.weighting, pathCalculator, request.getCurbsides(), forceCurbsides, request.getHeadings(), passThrough);
 
         if (request.getPoints().size() != result.paths.size() + 1)
             throw new RuntimeException("There should be exactly one more point than paths. points:" + request.getPoints().size() + ", paths:" + result.paths.size());
 
         // here each path represents one leg of the via-route and we merge them all together into one response path
-        ResponsePath responsePath = concatenatePaths(request, weighting, queryGraph, result.paths, getWaypoints(qResults));
+        ResponsePath responsePath = concatenatePaths(request, solver.weighting, queryGraph, result.paths, getWaypoints(snaps));
         responsePath.addDebugInfo(result.debug);
         ghRsp.add(responsePath);
         ghRsp.getHints().putObject("visited_nodes.sum", result.visitedNodes);
-        ghRsp.getHints().putObject("visited_nodes.average", (float) result.visitedNodes / (qResults.size() - 1));
+        ghRsp.getHints().putObject("visited_nodes.average", (float) result.visitedNodes / (snaps.size() - 1));
         return ghRsp;
     }
 
-    private Weighting createWeighting(Profile profile, PMap requestHints, List<GHPoint> points, boolean disableCH) {
-        if (chEnabled && !disableCH) {
-            // todo: do not allow things like short_fastest.distance_factor or u_turn_costs unless CH is disabled
-            // and only under certain conditions for LM
-
-            // the request hints are ignored for CH as we cannot change the profile after the preparation like this.
-            // the weighting here needs to be the same as the one we later use for CHPathCalculator and as it was
-            // used for the preparation
-            return getRoutingCHGraph(profile.getName()).getWeighting();
-        } else {
-            Weighting weighting = weightingFactory.createWeighting(profile, requestHints, false);
-            if (requestHints.has(Parameters.Routing.BLOCK_AREA)) {
-                GraphEdgeIdFinder.BlockArea blockArea = GraphEdgeIdFinder.createBlockArea(ghStorage, locationIndex,
-                        points, requestHints, new FiniteWeightFilter(weighting));
-                weighting = new BlockAreaWeighting(weighting, blockArea);
-            }
-            return weighting;
-        }
-    }
-
-    private PathCalculator createPathCalculator(QueryGraph queryGraph, Profile profile, AlgorithmOptions algoOpts, boolean disableCH, boolean disableLM) {
-        if (chEnabled && !disableCH) {
-            PMap opts = new PMap(algoOpts.getHints());
-            opts.putObject(ALGORITHM, algoOpts.getAlgorithm());
-            opts.putObject(MAX_VISITED_NODES, algoOpts.getMaxVisitedNodes());
-            return createCHPathCalculator(queryGraph, profile, opts);
-        } else {
-            return createFlexiblePathCalculator(queryGraph, profile, algoOpts, disableLM);
-        }
-    }
-
-    private PathCalculator createCHPathCalculator(QueryGraph queryGraph, Profile profile, PMap opts) {
-        return new CHPathCalculator(new CHRoutingAlgorithmFactory(getRoutingCHGraph(profile.getName()), queryGraph), opts);
-    }
-
-    private FlexiblePathCalculator createFlexiblePathCalculator(QueryGraph queryGraph, Profile profile, AlgorithmOptions algoOpts, boolean disableLM) {
-        RoutingAlgorithmFactory algorithmFactory;
-        // for now do not allow mixing CH&LM #1082,#1889
-        if (lmEnabled && !disableLM) {
-            LandmarkStorage landmarkStorage = landmarks.get(profile.getName());
-            if (landmarkStorage == null)
-                throw new IllegalArgumentException("Cannot find LM preparation for the requested profile: '" + profile.getName() + "'" +
-                        "\nYou can try disabling LM using " + Parameters.Landmark.DISABLE + "=true" +
-                        "\navailable LM profiles: " + landmarks.keySet());
-            algorithmFactory = new LMRoutingAlgorithmFactory(landmarkStorage).setDefaultActiveLandmarks(routerConfig.getActiveLandmarkCount());
-        } else {
-            algorithmFactory = new RoutingAlgorithmFactorySimple();
-        }
-        return new FlexiblePathCalculator(queryGraph, algorithmFactory, algoOpts);
-    }
-
-    private RoutingCHGraph getRoutingCHGraph(String profileName) {
-        RoutingCHGraph chGraph = chGraphs.get(profileName);
-        if (chGraph == null)
-            throw new IllegalArgumentException("Cannot find CH preparation for the requested profile: '" + profileName + "'" +
-                    "\nYou can try disabling CH using " + Parameters.CH.DISABLE + "=true" +
-                    "\navailable CH profiles: " + chGraphs.keySet());
-        return chGraph;
-    }
-
     private PathMerger createPathMerger(GHRequest request, Weighting weighting, Graph graph) {
         boolean enableInstructions = request.getHints().getBool(Parameters.Routing.INSTRUCTIONS, encodingManager.isEnableInstructions());
         boolean calcPoints = request.getHints().getBool(Parameters.Routing.CALC_POINTS, routerConfig.isCalcPoints());
@@ -323,40 +301,111 @@ public class Router {
         return pointList;
     }
 
-    protected void validateRequest(GHRequest request) {
-        if (Helper.isEmpty(request.getProfile()))
-            throw new IllegalArgumentException("You need to specify a profile to perform a routing request, see docs/core/profiles.md");
+    private static boolean getDisableLM(PMap hints) {
+        return hints.getBool(Parameters.Landmark.DISABLE, false);
+    }
 
-        if (request.getHints().has("vehicle"))
-            throw new IllegalArgumentException("GHRequest may no longer contain a vehicle, use the profile parameter instead, see docs/core/profiles.md");
-        if (request.getHints().has("weighting"))
-            throw new IllegalArgumentException("GHRequest may no longer contain a weighting, use the profile parameter instead, see docs/core/profiles.md");
-        if (request.getHints().has(Parameters.Routing.TURN_COSTS))
-            throw new IllegalArgumentException("GHRequest may no longer contain the turn_costs=true/false parameter, use the profile parameter instead, see docs/core/profiles.md");
-        if (request.getHints().has(Parameters.Routing.EDGE_BASED))
-            throw new IllegalArgumentException("GHRequest may no longer contain the edge_based=true/false parameter, use the profile parameter instead, see docs/core/profiles.md");
+    private static boolean getDisableCH(PMap hints) {
+        return hints.getBool(Parameters.CH.DISABLE, false);
+    }
 
-        if (request.getPoints().isEmpty())
-            throw new IllegalArgumentException("You have to pass at least one point");
-        checkIfPointsAreInBounds(request.getPoints());
+    private static boolean getPassThrough(PMap hints) {
+        return hints.getBool(PASS_THROUGH, false);
+    }
 
-        if (request.getHeadings().size() > 1 && request.getHeadings().size() != request.getPoints().size())
-            throw new IllegalArgumentException("The number of 'heading' parameters must be zero, one "
-                    + "or equal to the number of points (" + request.getPoints().size() + ")");
-        for (int i = 0; i < request.getHeadings().size(); i++)
-            if (!GHRequest.isAzimuthValue(request.getHeadings().get(i)))
-                throw new IllegalArgumentException("Heading for point " + i + " must be in range [0,360) or NaN, but was: " + request.getHeadings().get(i));
+    private static boolean getForceCurbsides(PMap hints) {
+        return hints.getBool(FORCE_CURBSIDE, true);
+    }
 
-        if (request.getPointHints().size() > 0 && request.getPointHints().size() != request.getPoints().size())
-            throw new IllegalArgumentException("If you pass " + POINT_HINT + ", you need to pass exactly one hint for every point, empty hints will be ignored");
-        if (request.getCurbsides().size() > 0 && request.getCurbsides().size() != request.getPoints().size())
-            throw new IllegalArgumentException("If you pass " + CURBSIDE + ", you need to pass exactly one curbside for every point, empty curbsides will be ignored");
+    public static abstract class Solver {
+        protected final GHRequest request;
+        private final Map<String, Profile> profilesByName;
+        private final RouterConfig routerConfig;
+        protected Profile profile;
+        protected Weighting weighting;
+
+        public Solver(GHRequest request, Map<String, Profile> profilesByName, RouterConfig routerConfig) {
+            this.request = request;
+            this.profilesByName = profilesByName;
+            this.routerConfig = routerConfig;
+        }
 
-        if (request.getCustomModel() != null && request.getHints().has(BLOCK_AREA))
-            throw new IllegalArgumentException("When using `custom_model` do not use `block_area`. Use `areas` in the custom model instead");
+        protected void checkRequest() {
+            checkProfileSpecified();
+            checkMaxVisitedNodes();
+        }
 
-        boolean disableCH = getDisableCH(request.getHints());
-        if (chEnabled && !disableCH) {
+        private void checkProfileSpecified() {
+            if (Helper.isEmpty(request.getProfile()))
+                throw new IllegalArgumentException("You need to specify a profile to perform a routing request, see docs/core/profiles.md");
+        }
+
+        private void checkMaxVisitedNodes() {
+            if (getMaxVisitedNodes(request.getHints()) > routerConfig.getMaxVisitedNodes())
+                throw new IllegalArgumentException("The max_visited_nodes parameter has to be below or equal to:" + routerConfig.getMaxVisitedNodes());
+        }
+
+        private void init() {
+            profile = getProfile();
+            checkProfileCompatibility();
+            weighting = createWeighting();
+        }
+
+        protected Profile getProfile() {
+            Profile profile = profilesByName.get(request.getProfile());
+            if (profile == null)
+                throw new IllegalArgumentException("The requested profile '" + request.getProfile() + "' does not exist.\nAvailable profiles: " + profilesByName.keySet());
+            return profile;
+        }
+
+        protected void checkProfileCompatibility() {
+            if (!profile.isTurnCosts() && !request.getCurbsides().isEmpty())
+                throw new IllegalArgumentException("To make use of the " + CURBSIDE + " parameter you need to use a profile that supports turn costs" +
+                        "\nThe following profiles do support turn costs: " + getTurnCostProfiles());
+            if (request.getCustomModel() != null && !(profile instanceof CustomProfile))
+                throw new IllegalArgumentException("The requested profile '" + request.getProfile() + "' cannot be used with `custom_model`, because it has weighting=" + profile.getWeighting());
+
+            final int uTurnCostsInt = request.getHints().getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS);
+            if (uTurnCostsInt != INFINITE_U_TURN_COSTS && !profile.isTurnCosts()) {
+                throw new IllegalArgumentException("Finite u-turn costs can only be used for edge-based routing, you need to use a profile that" +
+                        " supports turn costs. Currently the following profiles that support turn costs are available: " + getTurnCostProfiles());
+            }
+        }
+
+        protected abstract Weighting createWeighting();
+
+        protected EdgeFilter getSnappingFilter() {
+            return new FiniteWeightFilter(weighting);
+        }
+
+        protected abstract PathCalculator createPathCalculator(QueryGraph queryGraph);
+
+        private List<String> getTurnCostProfiles() {
+            List<String> turnCostProfiles = new ArrayList<>();
+            for (Profile p : profilesByName.values()) {
+                if (p.isTurnCosts()) {
+                    turnCostProfiles.add(p.getName());
+                }
+            }
+            return turnCostProfiles;
+        }
+
+        int getMaxVisitedNodes(PMap hints) {
+            return hints.getInt(Parameters.Routing.MAX_VISITED_NODES, routerConfig.getMaxVisitedNodes());
+        }
+    }
+
+    private static class CHSolver extends Solver {
+        private final Map<String, RoutingCHGraph> chGraphs;
+
+        CHSolver(GHRequest request, Map<String, Profile> profilesByName, RouterConfig routerConfig, Map<String, RoutingCHGraph> chGraphs) {
+            super(request, profilesByName, routerConfig);
+            this.chGraphs = chGraphs;
+        }
+
+        @Override
+        protected void checkRequest() {
+            super.checkRequest();
             if (!request.getHeadings().isEmpty())
                 throw new IllegalArgumentException("The 'heading' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`. See issue #483");
 
@@ -368,60 +417,133 @@ public class Router {
 
             if (request.getCustomModel() != null)
                 throw new IllegalArgumentException("The 'custom_model' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`.");
-        } else {
-            checkNonChMaxWaypointDistance(request.getPoints());
+
+            if (ROUND_TRIP.equalsIgnoreCase(request.getAlgorithm()))
+                throw new IllegalArgumentException("algorithm=round_trip cannot be used with CH");
+        }
+
+        @Override
+        protected Weighting createWeighting() {
+            // todo: do not allow things like short_fastest.distance_factor or u_turn_costs unless CH is disabled
+            // and only under certain conditions for LM
+
+            // the request hints are ignored for CH as we cannot change the profile after the preparation like this.
+            // the weighting here needs to be the same as the one we later use for CHPathCalculator and as it was
+            // used for the preparation
+            return getRoutingCHGraph(profile.getName()).getWeighting();
+        }
+
+        @Override
+        protected PathCalculator createPathCalculator(QueryGraph queryGraph) {
+            PMap opts = new PMap(request.getHints());
+            opts.putObject(ALGORITHM, request.getAlgorithm());
+            opts.putObject(MAX_VISITED_NODES, getMaxVisitedNodes(request.getHints()));
+            return new CHPathCalculator(new CHRoutingAlgorithmFactory(getRoutingCHGraph(profile.getName()), queryGraph), opts);
+        }
+
+        private RoutingCHGraph getRoutingCHGraph(String profileName) {
+            RoutingCHGraph chGraph = chGraphs.get(profileName);
+            if (chGraph == null)
+                throw new IllegalArgumentException("Cannot find CH preparation for the requested profile: '" + profileName + "'" +
+                        "\nYou can try disabling CH using " + Parameters.CH.DISABLE + "=true" +
+                        "\navailable CH profiles: " + chGraphs.keySet());
+            return chGraph;
         }
     }
 
-    private List<String> getTurnCostProfiles() {
-        List<String> turnCostProfiles = new ArrayList<>();
-        for (Profile p : profilesByName.values()) {
-            if (p.isTurnCosts()) {
-                turnCostProfiles.add(p.getName());
+    private static class FlexSolver extends Solver {
+        protected final RouterConfig routerConfig;
+        private final WeightingFactory weightingFactory;
+        private final GraphHopperStorage ghStorage;
+        private final LocationIndex locationIndex;
+
+        FlexSolver(GHRequest request, Map<String, Profile> profilesByName, RouterConfig routerConfig, WeightingFactory weightingFactory, GraphHopperStorage ghStorage, LocationIndex locationIndex) {
+            super(request, profilesByName, routerConfig);
+            this.routerConfig = routerConfig;
+            this.weightingFactory = weightingFactory;
+            this.ghStorage = ghStorage;
+            this.locationIndex = locationIndex;
+        }
+
+        @Override
+        protected void checkRequest() {
+            super.checkRequest();
+            checkNonChMaxWaypointDistance(request.getPoints());
+        }
+
+        @Override
+        protected Weighting createWeighting() {
+            PMap requestHints = new PMap(request.getHints());
+            requestHints.putObject(CustomModel.KEY, request.getCustomModel());
+            Weighting weighting = weightingFactory.createWeighting(profile, requestHints, false);
+            if (requestHints.has(Parameters.Routing.BLOCK_AREA)) {
+                GraphEdgeIdFinder.BlockArea blockArea = GraphEdgeIdFinder.createBlockArea(ghStorage, locationIndex,
+                        request.getPoints(), requestHints, new FiniteWeightFilter(weighting));
+                weighting = new BlockAreaWeighting(weighting, blockArea);
             }
+            return weighting;
         }
-        return turnCostProfiles;
-    }
 
-    private static boolean getDisableLM(PMap hints) {
-        return hints.getBool(Parameters.Landmark.DISABLE, false);
-    }
+        @Override
+        protected FlexiblePathCalculator createPathCalculator(QueryGraph queryGraph) {
+            RoutingAlgorithmFactory algorithmFactory = new RoutingAlgorithmFactorySimple();
+            return new FlexiblePathCalculator(queryGraph, algorithmFactory, weighting, getAlgoOpts());
+        }
 
-    private static boolean getDisableCH(PMap hints) {
-        return hints.getBool(Parameters.CH.DISABLE, false);
-    }
+        AlgorithmOptions getAlgoOpts() {
+            AlgorithmOptions algoOpts = new AlgorithmOptions().
+                    setAlgorithm(request.getAlgorithm()).
+                    setTraversalMode(profile.isTurnCosts() ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED).
+                    setMaxVisitedNodes(getMaxVisitedNodes(request.getHints())).
+                    setHints(request.getHints());
 
-    private static boolean getPassThrough(PMap hints) {
-        return hints.getBool(PASS_THROUGH, false);
-    }
+            // use A* for round trips
+            if (ROUND_TRIP.equalsIgnoreCase(request.getAlgorithm())) {
+                algoOpts.setAlgorithm(Parameters.Algorithms.ASTAR_BI);
+                algoOpts.getHints().putObject(Parameters.Algorithms.AStarBi.EPSILON, 2);
+            }
+            return algoOpts;
+        }
 
-    private void checkIfPointsAreInBounds(List<GHPoint> points) {
-        BBox bounds = ghStorage.getBounds();
-        for (int i = 0; i < points.size(); i++) {
-            GHPoint point = points.get(i);
-            if (!bounds.contains(point.getLat(), point.getLon())) {
-                throw new PointOutOfBoundsException("Point " + i + " is out of bounds: " + point + ", the bounds are: " + bounds, i);
+        private void checkNonChMaxWaypointDistance(List<GHPoint> points) {
+            if (routerConfig.getNonChMaxWaypointDistance() == Integer.MAX_VALUE) {
+                return;
+            }
+            GHPoint lastPoint = points.get(0);
+            GHPoint point;
+            double dist;
+            for (int i = 1; i < points.size(); i++) {
+                point = points.get(i);
+                dist = DIST_EARTH.calcDist(lastPoint.getLat(), lastPoint.getLon(), point.getLat(), point.getLon());
+                if (dist > routerConfig.getNonChMaxWaypointDistance()) {
+                    Map<String, Object> detailMap = new HashMap<>(2);
+                    detailMap.put("from", i - 1);
+                    detailMap.put("to", i);
+                    throw new PointDistanceExceededException("Point " + i + " is too far from Point " + (i - 1) + ": " + point, detailMap);
+                }
+                lastPoint = point;
             }
         }
     }
 
-    private void checkNonChMaxWaypointDistance(List<GHPoint> points) {
-        if (routerConfig.getNonChMaxWaypointDistance() == Integer.MAX_VALUE) {
-            return;
+    private static class LMSolver extends FlexSolver {
+        private final Map<String, LandmarkStorage> landmarks;
+
+        LMSolver(GHRequest request, Map<String, Profile> profilesByName, RouterConfig routerConfig, WeightingFactory weightingFactory, GraphHopperStorage ghStorage, LocationIndex locationIndex, Map<String, LandmarkStorage> landmarks) {
+            super(request, profilesByName, routerConfig, weightingFactory, ghStorage, locationIndex);
+            this.landmarks = landmarks;
         }
-        GHPoint lastPoint = points.get(0);
-        GHPoint point;
-        double dist;
-        for (int i = 1; i < points.size(); i++) {
-            point = points.get(i);
-            dist = DIST_EARTH.calcDist(lastPoint.getLat(), lastPoint.getLon(), point.getLat(), point.getLon());
-            if (dist > routerConfig.getNonChMaxWaypointDistance()) {
-                Map<String, Object> detailMap = new HashMap<>(2);
-                detailMap.put("from", i - 1);
-                detailMap.put("to", i);
-                throw new PointDistanceExceededException("Point " + i + " is too far from Point " + (i - 1) + ": " + point, detailMap);
-            }
-            lastPoint = point;
+
+        @Override
+        protected FlexiblePathCalculator createPathCalculator(QueryGraph queryGraph) {
+            // for now do not allow mixing CH&LM #1082,#1889
+            LandmarkStorage landmarkStorage = landmarks.get(profile.getName());
+            if (landmarkStorage == null)
+                throw new IllegalArgumentException("Cannot find LM preparation for the requested profile: '" + profile.getName() + "'" +
+                        "\nYou can try disabling LM using " + Parameters.Landmark.DISABLE + "=true" +
+                        "\navailable LM profiles: " + landmarks.keySet());
+            RoutingAlgorithmFactory routingAlgorithmFactory = new LMRoutingAlgorithmFactory(landmarkStorage).setDefaultActiveLandmarks(routerConfig.getActiveLandmarkCount());
+            return new FlexiblePathCalculator(queryGraph, routingAlgorithmFactory, weighting, getAlgoOpts());
         }
     }
 }
diff --git a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactory.java b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactory.java
index ad490d898..ef8a523ad 100644
--- a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactory.java
+++ b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactory.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing;
 
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 
 /**
@@ -28,5 +29,5 @@ public interface RoutingAlgorithmFactory {
     /**
      * This method creates an algorithm out of this factory based on the specified opts and graph.
      */
-    RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts);
+    RoutingAlgorithm createAlgo(Graph g, Weighting w, AlgorithmOptions opts);
 }
diff --git a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java
index 963503770..f6bd5185a 100644
--- a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java
+++ b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java
@@ -38,10 +38,10 @@ import static com.graphhopper.util.Parameters.Algorithms.AltRoute.*;
  */
 public class RoutingAlgorithmFactorySimple implements RoutingAlgorithmFactory {
     @Override
-    public RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts) {
+    public RoutingAlgorithm createAlgo(Graph g, Weighting w, AlgorithmOptions opts) {
         RoutingAlgorithm ra;
         String algoStr = opts.getAlgorithm();
-        Weighting weighting = g.wrapWeighting(opts.getWeighting());
+        Weighting weighting = g.wrapWeighting(w);
         if (DIJKSTRA_BI.equalsIgnoreCase(algoStr)) {
             ra = new DijkstraBidirectionRef(g, weighting, opts.getTraversalMode());
         } else if (DIJKSTRA.equalsIgnoreCase(algoStr)) {
@@ -58,7 +58,7 @@ public class RoutingAlgorithmFactorySimple implements RoutingAlgorithmFactory {
 
         } else if (ASTAR.equalsIgnoreCase(algoStr)) {
             AStar aStar = new AStar(g, weighting, opts.getTraversalMode());
-            aStar.setApproximation(getApproximation(ASTAR, opts.getHints(), opts.getWeighting(), g.getNodeAccess()));
+            aStar.setApproximation(getApproximation(ASTAR, opts.getHints(), w, g.getNodeAccess()));
             ra = aStar;
 
         } else if (ALT_ROUTE.equalsIgnoreCase(algoStr)) {
diff --git a/core/src/main/java/com/graphhopper/routing/ViaRouting.java b/core/src/main/java/com/graphhopper/routing/ViaRouting.java
index 90124eed3..6966dd1e5 100644
--- a/core/src/main/java/com/graphhopper/routing/ViaRouting.java
+++ b/core/src/main/java/com/graphhopper/routing/ViaRouting.java
@@ -54,13 +54,12 @@ public class ViaRouting {
     /**
      * @throws MultiplePointsNotFoundException in case one or more points could not be resolved
      */
-    public static List<Snap> lookup(EncodedValueLookup lookup, List<GHPoint> points, Weighting weighting, LocationIndex locationIndex, List<String> snapPreventions, List<String> pointHints) {
+    public static List<Snap> lookup(EncodedValueLookup lookup, List<GHPoint> points, EdgeFilter edgeFilter, LocationIndex locationIndex, List<String> snapPreventions, List<String> pointHints) {
         if (points.size() < 2)
             throw new IllegalArgumentException("At least 2 points have to be specified, but was:" + points.size());
 
         final EnumEncodedValue<RoadClass> roadClassEnc = lookup.getEnumEncodedValue(RoadClass.KEY, RoadClass.class);
         final EnumEncodedValue<RoadEnvironment> roadEnvEnc = lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class);
-        EdgeFilter edgeFilter = new FiniteWeightFilter(weighting);
         EdgeFilter strictEdgeFilter = snapPreventions.isEmpty()
                 ? edgeFilter
                 : new SnapPreventionEdgeFilter(edgeFilter, roadClassEnc, roadEnvEnc, snapPreventions);
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java b/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java
index da606fb50..11a3bc594 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java
@@ -43,12 +43,12 @@ public class LMRoutingAlgorithmFactory implements RoutingAlgorithmFactory {
     }
 
     @Override
-    public RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts) {
+    public RoutingAlgorithm createAlgo(Graph g, Weighting w, AlgorithmOptions opts) {
         if (!lms.isInitialized())
             throw new IllegalStateException("Initialize landmark storage before creating algorithms");
         int activeLM = Math.max(1, opts.getHints().getInt(Parameters.Landmark.ACTIVE_COUNT, defaultActiveLandmarks));
         final String algoStr = opts.getAlgorithm();
-        final Weighting weighting = g.wrapWeighting(opts.getWeighting());
+        final Weighting weighting = g.wrapWeighting(w);
         if (ASTAR.equalsIgnoreCase(algoStr)) {
             double epsilon = opts.getHints().getDouble(Parameters.Algorithms.AStar.EPSILON, 1);
             AStar algo = new AStar(g, weighting, opts.getTraversalMode());
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java
index fca6c682a..b06475c09 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/AvoidEdgesWeighting.java
@@ -41,8 +41,9 @@ public class AvoidEdgesWeighting extends AbstractAdjustedWeighting {
         return this;
     }
 
-    public void setAvoidedEdges(IntSet avoidedEdges) {
+    public AvoidEdgesWeighting setAvoidedEdges(IntSet avoidedEdges) {
         this.avoidedEdges = avoidedEdges;
+        return this;
     }
 
     @Override
diff --git a/docs/core/quickstart-from-source.md b/docs/core/quickstart-from-source.md
index 13a1aa5f9..3d3acc99f 100644
--- a/docs/core/quickstart-from-source.md
+++ b/docs/core/quickstart-from-source.md
@@ -76,7 +76,9 @@ Have a look into the [Java API documentation](../index.md#developer) for further
 be embedded](./routing.md) into your application and how you create a [custom weighting](./weighting.md).
 
 Look [here](https://github.com/graphhopper/graphhopper#maven) for the maven snippet to use GraphHopper in your
-application. To use an unreleased snapshot version of GraphHopper you need the following snippet in your pom.xml
+application.
+
+To use an **unreleased** snapshot version of GraphHopper you need the following snippet in your pom.xml
 as those versions are not in maven central:
 
 ```xml
@@ -96,29 +98,39 @@ as those versions are not in maven central:
 
 ### Web UI (JavaScript)
 
-To setup the JavaScript development environment you
+Running `mvn package` from the root folder will install a local copy of node/npm and build the javascript bundle for GH
+maps. You just need to start the server and GH maps and if you use the default port GH maps will be visible at
+`http://localhost:8989/`.
+
+To develop the web UI running the whole maven build usually takes too long so here are the separate steps that you need
+to perform when you make changes to the JavaScript code:
 
-1. install the [node package
-manager](https://github.com/nvm-sh/nvm#install--update-script). For windows use [nvm-windows](https://github.com/coreybutler/nvm-windows).
-3. Build the custom-model-editor component: `sh -c "cd web-bundle/src/main/js/custom-model-editor && npm install && npm run build"`
-2. Build the Web UI: `cd web-bundle && npm install && npm run bundle`
+1. install the [node package manager](https://github.com/nvm-sh/nvm#install--update-script). For windows
+   use [nvm-windows](https://github.com/coreybutler/nvm-windows).
+2. Build the custom-model-editor
+   component: `sh -c "cd web-bundle/src/main/js/custom-model-editor && npm install && npm run build"`
+3. Build the Web UI: `cd web-bundle && npm install && npm run bundle` which results in the `main.js` file
+4. Restart the GH server so it picks up the latest version of the UI bundle
 
-Now the main.js file was created and you should be able to see GraphHopper Maps.
+You can achieve an even faster development cycle by running `npm run watch` which will update `main.js` whenever you
+make changes to one of the .js files. The same command works in the custom-model-editor folder in case you want to make
+changes there. To hot-reload your changes in the browser the best option is to serve GH maps from a separate server like
+live-server. You can do this by running `npm run serve` from a separate terminal and pointing the routing.host property
+in src/main/resources/com/graphhopper/maps/js/config/options.js to your GH server:
 
-To see all changes in the browser without restarting the server you can either run the GH server in debug mode from
-IntelliJ (use `Run->Debugging Actions->Reload Changed Classes` and refresh your browser window) or you start a separate server. To start a separate server for the Web UI you change the routing.host property in src/main/resources/com/graphhopper/maps/js/config/options.js:
 ```js
 ...
-  routing: {host: 'http://localhost:8989', api_key: ''},
+routing: {
+   host: 'http://localhost:8989', api_key
+:
+   ''
+}
 ...
 ```
 
-And then in a second shell do:
-
-```
-npm install -g live-server
-live-server --open=src/main/resources/com/graphhopper/maps/
-```
+Re-building `main.js` on every change might cause your IDE (like IntelliJ) to re-index the file all the time. Therefore
+it is a good idea to remove `main.js` from your editor's index. For example in IntelliJ right-click the file and choose
+`Mark as plain text`.
 
 The following npm commands are available in the `web-bundle` directory:
 
@@ -144,5 +156,3 @@ You can use Graphhopper on the Desktop with the help of mapsforge too. No exampl
 
 For smallish graph (e.g. size of Berlin) use a RAMDataAccess driven GraphStorage (loads all into memory).
 For larger ones use the ContractionHierarchies preparation class and MMapDataAccess to avoid OutOfMemoryErrors if you have only few RAM. 
-
-Raspberry Pi usage is also possible. Have a look into this [blog post](https://karussell.wordpress.com/2014/01/09/road-routing-on-raspberry-pi-with-graphhopper/).
diff --git a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
index 1a8ffb9db..3e09738d8 100644
--- a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
+++ b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
@@ -346,24 +346,24 @@ public class MiniGraphUI {
         } else {
             Weighting weighting = hopper.createWeighting(profile, new PMap());
             final PrepareLandmarks preparation = hopper.getLMPreparationHandler().getPreparation(profile.getName());
-            RoutingAlgorithmFactory algoFactory = (g, opts) -> {
-                RoutingAlgorithm algo = preparation.getRoutingAlgorithmFactory().createAlgo(g, opts);
+            RoutingAlgorithmFactory algoFactory = (g, w, opts) -> {
+                RoutingAlgorithm algo = preparation.getRoutingAlgorithmFactory().createAlgo(g, w, opts);
                 if (algo instanceof AStarBidirection) {
-                    return new DebugAStarBi(g, opts.getWeighting(), opts.getTraversalMode(), mg).
+                    return new DebugAStarBi(g, w, opts.getTraversalMode(), mg).
                             setApproximation(((AStarBidirection) algo).getApproximation());
                 } else if (algo instanceof AStar) {
-                    return new DebugAStar(g, opts.getWeighting(), opts.getTraversalMode(), mg);
+                    return new DebugAStar(g, w, opts.getTraversalMode(), mg);
                 } else if (algo instanceof DijkstraBidirectionRef) {
-                    return new DebugDijkstraBidirection(g, opts.getWeighting(), opts.getTraversalMode(), mg);
+                    return new DebugDijkstraBidirection(g, w, opts.getTraversalMode(), mg);
                 } else if (algo instanceof Dijkstra) {
-                    return new DebugDijkstraSimple(g, opts.getWeighting(), opts.getTraversalMode(), mg);
+                    return new DebugDijkstraSimple(g, w, opts.getTraversalMode(), mg);
                 }
                 return algo;
             };
-            AlgorithmOptions algoOpts = new AlgorithmOptions(Algorithms.ASTAR_BI, weighting);
+            AlgorithmOptions algoOpts = new AlgorithmOptions().setAlgorithm(Algorithms.ASTAR_BI);
             logger.info("algoOpts:" + algoOpts + ", weighting: " + weighting);
             QueryGraph qGraph = QueryGraph.create(graph, fromRes, toRes);
-            return algoFactory.createAlgo(qGraph, algoOpts);
+            return algoFactory.createAlgo(qGraph, weighting, algoOpts);
         }
     }
 
diff --git a/web-bundle/package.json b/web-bundle/package.json
index 472667078..bed6cecf2 100644
--- a/web-bundle/package.json
+++ b/web-bundle/package.json
@@ -11,7 +11,8 @@
     "bundleDebug": "browserify src/main/resources/com/graphhopper/maps/js/main-template.js --debug -o src/main/resources/com/graphhopper/maps/js/main.js",
     "bundleProduction": "browserify -g uglifyify src/main/resources/com/graphhopper/maps/js/main-template.js -o src/main/resources/com/graphhopper/maps/js/main.js",
     "test": "JASMINE_CONFIG_PATH=src/test/resources/com/graphhopper/maps/spec/jasmine.json jasmine",
-    "lint": "jshint src/main/resources/com/graphhopper/maps/js/"
+    "lint": "jshint src/main/resources/com/graphhopper/maps/js/",
+    "serve": "live-server --port=8991 --mount=/:src/main/resources/com/graphhopper/maps/ --wait=1000"
   },
   "browserify": {
     "transform": [
@@ -38,6 +39,7 @@
     "browserify-swap": "0.2.2",
     "jasmine": "3.1.0",
     "jshint": "^2.10.2",
+    "live-server": "^1.2.1",
     "uglifyify": "5.0.2",
     "watchify": "^3.11.1"
   }
diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/config/options.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/config/options.js
index 09b9f665b..b44962d90 100644
--- a/web-bundle/src/main/resources/com/graphhopper/maps/js/config/options.js
+++ b/web-bundle/src/main/resources/com/graphhopper/maps/js/config/options.js
@@ -11,7 +11,7 @@
 exports.options = {
     with_tiles: true,
     environment: "development",
-    // for local development use this (assuming your GH server is running on the standard port)
+    // use this if you serve the web UI from a different server like live-server and the GH server runs on the standard port
     // routing: {host: 'http://localhost:8989', api_key: ''},
     routing: {host: '', api_key: ''},
     geocoding: {host: '', api_key: ''},


Test patch:
diff --git a/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java b/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java
index 7a80c34ac..85dc06498 100644
--- a/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java
+++ b/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java
@@ -1,8 +1,9 @@
 package com.graphhopper.api;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.graphhopper.util.shapes.GHPoint;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -10,7 +11,8 @@ import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.Collections;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
+
 
 /**
  * @author Peter Karich
@@ -21,6 +23,8 @@ public abstract class AbstractGHMatrixWebTester {
 
     abstract GraphHopperMatrixWeb createMatrixClient(String json) throws IOException;
 
+    abstract GHMatrixAbstractRequester createRequester(String url);
+
     public static GHMRequest createRequest() {
         GHMRequest req = new GHMRequest();
         req.addPoint(new GHPoint(51.534377, -0.087891));
@@ -178,6 +182,22 @@ public abstract class AbstractGHMatrixWebTester {
         assertEquals(886, rsp.getTime(0, 1) / 1000);
     }
 
+    @Test
+    public void noVehicleWhenNotSpecified() {
+        GHMatrixBatchRequester requester = new GHMatrixBatchRequester("url");
+        JsonNode json = requester.createPostRequest(new GHMRequest(5), Collections.singletonList("weights"));
+        assertEquals("{\"out_arrays\":[\"weights\"],\"fail_fast\":true}", json.toString());
+    }
+
+    @Test
+    public void hasHintsWhenSpecified() {
+        GHMatrixAbstractRequester requester = createRequester("url");
+        GHMRequest ghmRequest = new GHMRequest(5);
+        ghmRequest.putHint("vehicle", "my_car").putHint("profile", "my_profile");
+        JsonNode json = requester.createPostRequest(ghmRequest, Collections.singletonList("weights"));
+        assertEquals("{\"out_arrays\":[\"weights\"],\"fail_fast\":true,\"vehicle\":\"my_car\",\"profile\":\"my_profile\"}", json.toString());
+    }
+
     public static String readFile(Reader simpleReader) throws IOException {
         try (BufferedReader reader = new BufferedReader(simpleReader)) {
             String res = "";
diff --git a/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java b/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java
index f20fcb606..e75ea9c5c 100644
--- a/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java
+++ b/client-hc/src/test/java/com/graphhopper/api/GHMatrixBatchTest.java
@@ -26,4 +26,9 @@ public class GHMatrixBatchTest extends AbstractGHMatrixWebTester {
             }
         }.setSleepAfterGET(0));
     }
+
+    @Override
+    GHMatrixAbstractRequester createRequester(String url) {
+        return new GHMatrixBatchRequester(url);
+    }
 }
diff --git a/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java b/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java
index 67c079d8e..67b9a6a75 100644
--- a/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java
+++ b/client-hc/src/test/java/com/graphhopper/api/GHMatrixSyncTest.java
@@ -27,4 +27,9 @@ public class GHMatrixSyncTest extends AbstractGHMatrixWebTester {
             }
         });
     }
+
+    @Override
+    GHMatrixAbstractRequester createRequester(String url) {
+        return new GHMatrixSyncRequester(url);
+    }
 }
diff --git a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java
index 650035fb9..56d51e05f 100644
--- a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java
+++ b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java
@@ -2,6 +2,7 @@ package com.graphhopper.api;
 
 import com.graphhopper.GHRequest;
 import com.graphhopper.util.shapes.GHPoint;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
@@ -25,4 +26,18 @@ public class GraphHopperWebTest {
         assertEquals(5, gh.getClientForRequest(req).connectTimeoutMillis());
     }
 
+    @Test
+    public void vehicleIncludedAsGiven() {
+        GraphHopperWeb hopper = new GraphHopperWeb("https://localhost:8000/route");
+        // no vehicle -> no vehicle
+        assertEquals("https://localhost:8000/route?&profile=&type=json&instructions=true&points_encoded=true" +
+                        "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false",
+                hopper.createGetRequest(new GHRequest()).url().toString());
+
+        // vehicle given -> vehicle used in url
+        assertEquals("https://localhost:8000/route?&profile=my_profile&type=json&instructions=true&points_encoded=true" +
+                        "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false&vehicle=my_car",
+                hopper.createGetRequest(new GHRequest().putHint("vehicle", "my_car").setProfile("my_profile")).url().toString());
+    }
+
 }
\ No newline at end of file
diff --git a/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java b/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java
index a5520be6d..c98e06a1a 100644
--- a/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java
+++ b/core/src/main/java/com/graphhopper/routing/util/TestAlgoCollector.java
@@ -23,6 +23,7 @@ import com.graphhopper.routing.Path;
 import com.graphhopper.routing.RoutingAlgorithm;
 import com.graphhopper.routing.RoutingAlgorithmFactorySimple;
 import com.graphhopper.routing.querygraph.QueryGraph;
+import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.Snap;
@@ -62,7 +63,7 @@ public class TestAlgoCollector {
             altPaths.add(path);
         }
 
-        PathMerger pathMerger = new PathMerger(queryGraph.getBaseGraph(), algoEntry.getAlgorithmOptions().getWeighting()).
+        PathMerger pathMerger = new PathMerger(queryGraph.getBaseGraph(), algoEntry.getWeighting()).
                 setCalcPoints(true).
                 setSimplifyResponse(false).
                 setEnableInstructions(true);
@@ -137,26 +138,24 @@ public class TestAlgoCollector {
         private Graph graph;
         private boolean ch;
         private String expectedAlgo;
+        private Weighting weighting;
         private AlgorithmOptions opts;
 
-        public AlgoHelperEntry(Graph g, boolean ch, AlgorithmOptions opts, LocationIndex idx, String expectedAlgo) {
+        public AlgoHelperEntry(Graph g, boolean ch, Weighting weighting, AlgorithmOptions opts, LocationIndex idx, String expectedAlgo) {
             this.graph = g;
             this.ch = ch;
+            this.weighting = weighting;
             this.opts = opts;
             this.idx = idx;
             this.expectedAlgo = expectedAlgo;
         }
 
-        public void setAlgorithmOptions(AlgorithmOptions opts) {
-            this.opts = opts;
-        }
-
         public RoutingAlgorithm createAlgo(Graph graph) {
-            return new RoutingAlgorithmFactorySimple().createAlgo(graph, opts);
+            return new RoutingAlgorithmFactorySimple().createAlgo(graph, weighting, opts);
         }
 
-        public AlgorithmOptions getAlgorithmOptions() {
-            return opts;
+        public Weighting getWeighting() {
+            return weighting;
         }
 
         public LocationIndex getIdx() {
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
index 543e0a94f..cfca931a6 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
@@ -67,7 +67,7 @@ public class DijkstraBidirectionCHTest {
         prepareCH(ghStorage, CHConfig.nodeBased(weighting.getName(), weighting));
 
         // use base graph for solving normal Dijkstra
-        Path p1 = new RoutingAlgorithmFactorySimple().createAlgo(ghStorage, AlgorithmOptions.start().weighting(weighting).build()).calcPath(0, 3);
+        Path p1 = new RoutingAlgorithmFactorySimple().createAlgo(ghStorage, weighting, new AlgorithmOptions()).calcPath(0, 3);
         assertEquals(IntArrayList.from(0, 1, 5, 2, 3), p1.calcNodes());
         assertEquals(p1.toString(), 402.29, p1.getDistance(), 1e-2);
         assertEquals(p1.toString(), 144823, p1.getTime());
@@ -78,13 +78,11 @@ public class DijkstraBidirectionCHTest {
         EncodingManager em = EncodingManager.create("foot,car");
         FlagEncoder footEncoder = em.getEncoder("foot");
         FlagEncoder carEncoder = em.getEncoder("car");
-        AlgorithmOptions footOptions = AlgorithmOptions.start().
-                weighting(new FastestWeighting(footEncoder)).build();
-        AlgorithmOptions carOptions = AlgorithmOptions.start().
-                weighting(new FastestWeighting(carEncoder)).build();
+        FastestWeighting footWeighting = new FastestWeighting(footEncoder);
+        FastestWeighting carWeighting = new FastestWeighting(carEncoder);
 
-        CHConfig footConfig = CHConfig.nodeBased("p_foot", footOptions.getWeighting());
-        CHConfig carConfig = CHConfig.nodeBased("p_car", carOptions.getWeighting());
+        CHConfig footConfig = CHConfig.nodeBased("p_foot", footWeighting);
+        CHConfig carConfig = CHConfig.nodeBased("p_car", carWeighting);
         GraphHopperStorage g = new GraphBuilder(em).setCHConfigs(footConfig, carConfig).create();
         RoutingAlgorithmTest.initFootVsCar(carEncoder, footEncoder, g);
 
@@ -98,13 +96,13 @@ public class DijkstraBidirectionCHTest {
         assertEquals(p1.toString(), 15000, p1.getDistance(), 1e-6);
 
         // use base graph for solving normal Dijkstra via car
-        Path p2 = new RoutingAlgorithmFactorySimple().createAlgo(g, carOptions).calcPath(0, 7);
+        Path p2 = new RoutingAlgorithmFactorySimple().createAlgo(g, carWeighting, new AlgorithmOptions()).calcPath(0, 7);
         assertEquals(IntArrayList.from(0, 4, 6, 7), p2.calcNodes());
         assertEquals(p2.toString(), 15000, p2.getDistance(), 1e-6);
         assertEquals(p2.toString(), 2700 * 1000, p2.getTime());
 
         // use base graph for solving normal Dijkstra via foot
-        Path p4 = new RoutingAlgorithmFactorySimple().createAlgo(g, footOptions).calcPath(0, 7);
+        Path p4 = new RoutingAlgorithmFactorySimple().createAlgo(g, footWeighting, new AlgorithmOptions()).calcPath(0, 7);
         assertEquals(p4.toString(), 17000, p4.getDistance(), 1e-6);
         assertEquals(p4.toString(), 12240 * 1000, p4.getTime());
         assertEquals(IntArrayList.from(0, 4, 5, 7), p4.calcNodes());
diff --git a/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java
index 99ec1dd4b..597f87e99 100644
--- a/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java
@@ -173,7 +173,7 @@ public class DirectedRoutingTest {
                 return algoFactory.createAlgo(new PMap().putObject(ALGORITHM, ASTAR_BI));
             }
             case LM:
-                return (BidirRoutingAlgorithm) lm.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).traversalMode(TraversalMode.EDGE_BASED).build());
+                return (BidirRoutingAlgorithm) lm.getRoutingAlgorithmFactory().createAlgo(graph, weighting, new AlgorithmOptions().setAlgorithm(ASTAR_BI).setTraversalMode(TraversalMode.EDGE_BASED));
             default:
                 throw new IllegalArgumentException("unknown algo " + algo);
         }
diff --git a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
index 79665253a..7188723fe 100644
--- a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
+++ b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
@@ -103,12 +103,10 @@ public class EdgeBasedRoutingAlgorithmTest {
     }
 
     public RoutingAlgorithm createAlgo(Graph g, Weighting weighting, TraversalMode traversalMode) {
-        return createAlgo(g, AlgorithmOptions.start().weighting(weighting).traversalMode(traversalMode).build());
-    }
-
-    public RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts) {
-        opts = AlgorithmOptions.start(opts).algorithm(algoStr).build();
-        return new RoutingAlgorithmFactorySimple().createAlgo(g, opts);
+        AlgorithmOptions opts = new AlgorithmOptions()
+                .setTraversalMode(traversalMode)
+                .setAlgorithm(algoStr);
+        return new RoutingAlgorithmFactorySimple().createAlgo(g, weighting, opts);
     }
 
     private GraphHopperStorage createStorage(EncodingManager em) {
diff --git a/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java
index 824e4b0c6..4283278c6 100644
--- a/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java
@@ -184,9 +184,9 @@ public class RandomizedRoutingTest {
                 return algoFactory.createAlgo(new PMap().putObject(ALGORITHM, ASTAR_BI));
             }
             case LM_BIDIR:
-                return lm.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).traversalMode(traversalMode).build());
+                return lm.getRoutingAlgorithmFactory().createAlgo(graph, weighting, new AlgorithmOptions().setAlgorithm(ASTAR_BI).setTraversalMode(traversalMode));
             case LM_UNIDIR:
-                return lm.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR).traversalMode(traversalMode).build());
+                return lm.getRoutingAlgorithmFactory().createAlgo(graph, weighting, new AlgorithmOptions().setAlgorithm(ASTAR).setTraversalMode(traversalMode));
             case PERFECT_ASTAR: {
                 AStarBidirection perfectAStarBi = new AStarBidirection(graph, weighting, traversalMode);
                 perfectAStarBi.setApproximation(new PerfectApproximator(graph, weighting, traversalMode, false));
diff --git a/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java
index a10e69574..e8caa5697 100644
--- a/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java
@@ -57,7 +57,7 @@ public class RoundTripRoutingTest {
 
     @Test(expected = IllegalArgumentException.class)
     public void lookup_throwsIfNumberOfPointsNotOne() {
-        RoundTripRouting.lookup(Arrays.asList(ghPoint1, ghPoint2), fastestWeighting, null, new RoundTripRouting.Params());
+        RoundTripRouting.lookup(Arrays.asList(ghPoint1, ghPoint2), new FiniteWeightFilter(fastestWeighting), null, new RoundTripRouting.Params());
     }
 
     @Test
@@ -73,7 +73,7 @@ public class RoundTripRoutingTest {
         hints.putObject(Parameters.Algorithms.RoundTrip.POINTS, numPoints);
         hints.putObject(Parameters.Algorithms.RoundTrip.DISTANCE, roundTripDistance);
         LocationIndex locationIndex = new LocationIndexTree(g, new RAMDirectory()).prepareIndex();
-        List<Snap> stagePoints = RoundTripRouting.lookup(Collections.singletonList(start), fastestWeighting, locationIndex,
+        List<Snap> stagePoints = RoundTripRouting.lookup(Collections.singletonList(start), new FiniteWeightFilter(fastestWeighting), locationIndex,
                 new RoundTripRouting.Params(hints, heading, 3));
         assertEquals(3, stagePoints.size());
         assertEquals(0, stagePoints.get(0).getClosestNode());
@@ -82,7 +82,7 @@ public class RoundTripRoutingTest {
 
         QueryGraph queryGraph = QueryGraph.create(g, stagePoints);
         List<Path> paths = RoundTripRouting.calcPaths(stagePoints, new FlexiblePathCalculator(queryGraph,
-                new RoutingAlgorithmFactorySimple(), new AlgorithmOptions(DIJKSTRA_BI, fastestWeighting, tMode))).paths;
+                new RoutingAlgorithmFactorySimple(), fastestWeighting, new AlgorithmOptions().setAlgorithm(DIJKSTRA_BI).setTraversalMode(tMode))).paths;
         // make sure the resulting paths are connected and form a round trip starting and ending at the start node 0
         assertEquals(2, paths.size());
         assertEquals(IntArrayList.from(0, 7, 6, 5), paths.get(0).calcNodes());
@@ -104,7 +104,7 @@ public class RoundTripRoutingTest {
         QueryGraph qGraph = QueryGraph.create(g, Arrays.asList(snap4, snap5));
 
         FlexiblePathCalculator pathCalculator = new FlexiblePathCalculator(
-                qGraph, new RoutingAlgorithmFactorySimple(), new AlgorithmOptions(DIJKSTRA_BI, fastestWeighting, tMode));
+                qGraph, new RoutingAlgorithmFactorySimple(), fastestWeighting, new AlgorithmOptions().setAlgorithm(DIJKSTRA_BI).setTraversalMode(tMode));
         List<Path> paths = RoundTripRouting.calcPaths(Arrays.asList(snap5, snap4, snap5), pathCalculator).paths;
         assertEquals(2, paths.size());
         assertEquals(IntArrayList.from(5, 6, 3), paths.get(0).calcNodes());
@@ -112,7 +112,7 @@ public class RoundTripRoutingTest {
 
         qGraph = QueryGraph.create(g, Arrays.asList(snap4, snap6));
         pathCalculator = new FlexiblePathCalculator(
-                qGraph, new RoutingAlgorithmFactorySimple(), new AlgorithmOptions(DIJKSTRA_BI, fastestWeighting, tMode));
+                qGraph, new RoutingAlgorithmFactorySimple(), fastestWeighting, new AlgorithmOptions().setAlgorithm(DIJKSTRA_BI).setTraversalMode(tMode));
         paths = RoundTripRouting.calcPaths(Arrays.asList(snap6, snap4, snap6), pathCalculator).paths;
         assertEquals(2, paths.size());
         assertEquals(IntArrayList.from(6, 3), paths.get(0).calcNodes());
diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java
index 7cf97263a..acb4671cb 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java
@@ -79,9 +79,9 @@ public class LMIssueTest {
             case ASTAR_BIDIR:
                 return new AStarBidirection(graph, weighting, NODE_BASED);
             case LM_BIDIR:
-                return lm.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR_BI).traversalMode(NODE_BASED).build());
+                return lm.getRoutingAlgorithmFactory().createAlgo(graph, weighting, new AlgorithmOptions().setAlgorithm(ASTAR_BI).setTraversalMode(NODE_BASED));
             case LM_UNIDIR:
-                return lm.getRoutingAlgorithmFactory().createAlgo(graph, AlgorithmOptions.start().weighting(weighting).algorithm(ASTAR).traversalMode(NODE_BASED).build());
+                return lm.getRoutingAlgorithmFactory().createAlgo(graph, weighting, new AlgorithmOptions().setAlgorithm(ASTAR).setTraversalMode(NODE_BASED));
             case PERFECT_ASTAR:
                 AStarBidirection perfectastarbi = new AStarBidirection(graph, weighting, NODE_BASED);
                 perfectastarbi.setApproximation(new PerfectApproximator(graph, weighting, NODE_BASED, false));
diff --git a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
index 6eb523a8f..e9a1e18c9 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
@@ -30,7 +30,6 @@ import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Directory;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.RAMDirectory;
-import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.storage.index.Snap;
 import com.graphhopper.util.GHUtility;
@@ -151,8 +150,8 @@ public class PrepareLandmarksTest {
         PMap hints = new PMap().putObject(Parameters.Landmark.ACTIVE_COUNT, 2);
 
         // landmarks with A*
-        RoutingAlgorithm oneDirAlgoWithLandmarks = prepare.getRoutingAlgorithmFactory().createAlgo(graph,
-                AlgorithmOptions.start().algorithm(ASTAR).weighting(weighting).traversalMode(tm).hints(hints).build());
+        RoutingAlgorithm oneDirAlgoWithLandmarks = prepare.getRoutingAlgorithmFactory().createAlgo(graph, weighting,
+                new AlgorithmOptions().setAlgorithm(ASTAR).setTraversalMode(tm).setHints(hints));
 
         Path path = oneDirAlgoWithLandmarks.calcPath(41, 183);
 
@@ -161,8 +160,8 @@ public class PrepareLandmarksTest {
         assertEquals(expectedAlgo.getVisitedNodes() - 135, oneDirAlgoWithLandmarks.getVisitedNodes());
 
         // landmarks with bidir A*
-        RoutingAlgorithm biDirAlgoWithLandmarks = prepare.getRoutingAlgorithmFactory().createAlgo(graph,
-                AlgorithmOptions.start().algorithm(ASTAR_BI).weighting(weighting).traversalMode(tm).hints(hints).build());
+        RoutingAlgorithm biDirAlgoWithLandmarks = prepare.getRoutingAlgorithmFactory().createAlgo(graph, weighting,
+                new AlgorithmOptions().setAlgorithm(ASTAR_BI).setTraversalMode(tm).setHints(hints));
         path = biDirAlgoWithLandmarks.calcPath(41, 183);
         assertEquals(expectedPath.getWeight(), path.getWeight(), .1);
         assertEquals(expectedPath.calcNodes(), path.calcNodes());
@@ -173,8 +172,8 @@ public class PrepareLandmarksTest {
         Snap fromSnap = index.findClosest(-0.0401, 0.2201, EdgeFilter.ALL_EDGES);
         Snap toSnap = index.findClosest(-0.2401, 0.0601, EdgeFilter.ALL_EDGES);
         QueryGraph qGraph = QueryGraph.create(graph, fromSnap, toSnap);
-        RoutingAlgorithm qGraphOneDirAlgo = prepare.getRoutingAlgorithmFactory().createAlgo(qGraph,
-                AlgorithmOptions.start().algorithm(ASTAR).weighting(weighting).traversalMode(tm).hints(hints).build());
+        RoutingAlgorithm qGraphOneDirAlgo = prepare.getRoutingAlgorithmFactory().createAlgo(qGraph, weighting,
+                new AlgorithmOptions().setAlgorithm(ASTAR).setTraversalMode(tm).setHints(hints));
         path = qGraphOneDirAlgo.calcPath(fromSnap.getClosestNode(), toSnap.getClosestNode());
 
         expectedAlgo = new AStar(qGraph, weighting, tm);
diff --git a/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java b/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java
index 5ee3a1319..dc540eda3 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java
@@ -77,26 +77,26 @@ public class RoutingAlgorithmWithOSMTest {
                 .putObject("vehicle", profile.getVehicle())
                 .putObject("weighting", profile.getWeighting());
 
-        AlgorithmOptions defaultOpts = AlgorithmOptions.start(new AlgorithmOptions("", weighting, tMode)).hints(defaultHints).build();
+        AlgorithmOptions defaultOpts = new AlgorithmOptions().setAlgorithm("").setTraversalMode(tMode).setHints(defaultHints);
         List<AlgoHelperEntry> algos = new ArrayList<>();
-        algos.add(new AlgoHelperEntry(ghStorage, false, AlgorithmOptions.start(defaultOpts).algorithm(ASTAR).build(), idx, "astar|beeline|" + addStr + weighting));
+        algos.add(new AlgoHelperEntry(ghStorage, false, weighting, new AlgorithmOptions(defaultOpts).setAlgorithm(ASTAR), idx, "astar|beeline|" + addStr + weighting));
         // later: include dijkstraOneToMany
-        algos.add(new AlgoHelperEntry(ghStorage, false, AlgorithmOptions.start(defaultOpts).algorithm(DIJKSTRA).build(), idx, "dijkstra|" + addStr + weighting));
+        algos.add(new AlgoHelperEntry(ghStorage, false, weighting, new AlgorithmOptions(defaultOpts).setAlgorithm(DIJKSTRA), idx, "dijkstra|" + addStr + weighting));
 
-        AlgorithmOptions astarbiOpts = AlgorithmOptions.start(defaultOpts).algorithm(ASTAR_BI).build();
+        AlgorithmOptions astarbiOpts = new AlgorithmOptions(defaultOpts).setAlgorithm(ASTAR_BI);
         astarbiOpts.getHints().putObject(ASTAR_BI + ".approximation", "BeelineSimplification");
-        AlgorithmOptions dijkstrabiOpts = AlgorithmOptions.start(defaultOpts).algorithm(DIJKSTRA_BI).build();
-        algos.add(new AlgoHelperEntry(ghStorage, false, astarbiOpts, idx, "astarbi|beeline|" + addStr + weighting));
-        algos.add(new AlgoHelperEntry(ghStorage, false, dijkstrabiOpts, idx, "dijkstrabi|" + addStr + weighting));
+        AlgorithmOptions dijkstrabiOpts = new AlgorithmOptions(defaultOpts).setAlgorithm(DIJKSTRA_BI);
+        algos.add(new AlgoHelperEntry(ghStorage, false, weighting, astarbiOpts, idx, "astarbi|beeline|" + addStr + weighting));
+        algos.add(new AlgoHelperEntry(ghStorage, false, weighting, dijkstrabiOpts, idx, "dijkstrabi|" + addStr + weighting));
 
         // add additional preparations if CH and LM preparation are enabled
         if (hopper.getLMPreparationHandler().isEnabled()) {
             final PMap lmHints = new PMap(defaultHints).putObject(Parameters.Landmark.DISABLE, false);
-            final AlgorithmOptions opts = AlgorithmOptions.start(astarbiOpts).hints(lmHints).build();
-            algos.add(new AlgoHelperEntry(ghStorage, false, opts, idx, "astarbi|landmarks|" + weighting) {
+            final AlgorithmOptions opts = new AlgorithmOptions(astarbiOpts).setHints(lmHints);
+            algos.add(new AlgoHelperEntry(ghStorage, false, weighting, opts, idx, "astarbi|landmarks|" + weighting) {
                 @Override
                 public RoutingAlgorithm createAlgo(Graph graph) {
-                    return hopper.getLMPreparationHandler().getPreparation(profile.getName()).getRoutingAlgorithmFactory().createAlgo(graph, opts);
+                    return hopper.getLMPreparationHandler().getPreparation(profile.getName()).getRoutingAlgorithmFactory().createAlgo(graph, weighting, opts);
                 }
             });
         }
@@ -105,8 +105,8 @@ public class RoutingAlgorithmWithOSMTest {
             final PMap chHints = new PMap(defaultHints);
             chHints.putObject(Parameters.CH.DISABLE, false);
             chHints.putObject(Parameters.Routing.EDGE_BASED, tMode.isEdgeBased());
-            final AlgorithmOptions dijkstraOpts = AlgorithmOptions.start(dijkstrabiOpts).hints(chHints).build();
-            algos.add(new AlgoHelperEntry(ghStorage, true, dijkstraOpts, idx, "dijkstrabi|ch|prepare|" + profile.getWeighting()) {
+            final AlgorithmOptions dijkstraOpts = new AlgorithmOptions(dijkstrabiOpts).setHints(chHints);
+            algos.add(new AlgoHelperEntry(ghStorage, true, weighting, dijkstraOpts, idx, "dijkstrabi|ch|prepare|" + profile.getWeighting()) {
                 @Override
                 public RoutingAlgorithm createAlgo(Graph g) {
                     PrepareContractionHierarchies pch = hopper.getCHPreparationHandler().getPreparation(profile.getName());
@@ -117,8 +117,8 @@ public class RoutingAlgorithmWithOSMTest {
                 }
             });
 
-            final AlgorithmOptions astarOpts = AlgorithmOptions.start(astarbiOpts).hints(chHints).build();
-            algos.add(new AlgoHelperEntry(ghStorage, true, astarOpts, idx, "astarbi|ch|prepare|" + profile.getWeighting()) {
+            final AlgorithmOptions astarOpts = new AlgorithmOptions(astarbiOpts).setHints(chHints);
+            algos.add(new AlgoHelperEntry(ghStorage, true, weighting, astarOpts, idx, "astarbi|ch|prepare|" + profile.getWeighting()) {
                 public RoutingAlgorithm createAlgo(Graph g) {
                     PrepareContractionHierarchies pch = hopper.getCHPreparationHandler().getPreparation(profile.getName());
                     RoutingCHGraph routingCHGraph = ghStorage.getRoutingCHGraph(pch.getCHConfig().getName());
@@ -735,8 +735,8 @@ public class RoutingAlgorithmWithOSMTest {
                         @Override
                         public void run() {
                             OneRun oneRun = instances.get(instanceIndex);
-                            AlgorithmOptions opts = AlgorithmOptions.start().weighting(weighting).algorithm(algoStr).build();
-                            testCollector.assertDistance(encodingManager, new AlgoHelperEntry(g, false, opts, idx, algoStr + "|" + weighting),
+                            AlgorithmOptions opts = new AlgorithmOptions().setAlgorithm(algoStr);
+                            testCollector.assertDistance(encodingManager, new AlgoHelperEntry(g, false, weighting, opts, idx, algoStr + "|" + weighting),
                                     oneRun.getList(idx, filter), oneRun);
                             integ.addAndGet(1);
                         }