Repository: graphhopper/graphhopper (java)

Issue:
Request fails when not setting the weighting and/or the vehicle with CH and finite u-turn costs
If we specify something like `prepare.ch.weightings=fastest|u_turn_costs=40` in the configuration and then do not specify a weighting in `GHRequest`, GH tries to determine the weighting by looking at the `CHProfile(s)`. This leads to:

```
java.lang.IllegalArgumentException: Cannot find CH profile for weighting map {edge_based=true, weighting=fastest|u_turn_costs=40, vehicle=car} in entries: [fastest|car|edge_based=true|u_turn_costs=40.
```

Pull request description:
Select appropriate CH profile when no weighting or vehicle is given
Fixes #1788 .

This PR reworks the CH profile selection logic and now also handles the cases where the weighting or the vehicle is missing. It now works like this: A request comes in with or without `vehicle,weighting,edge_based,u_turn_costs`. Missing values are treated as wildcards (match everything). This means that we do not need to specify anything on the request as long as there is only one possible match. When there are multiple matches we throw an error (with one exception, see in code). 

The CH profile selection got much simpler. The error responses are now more generic and maybe even more informative. For example if no CH profile matches the request we get:

```
Cannot find matching CH profile for your request.
requested:  fastest|*|edge_based=true|u_turn_costs=40
available: [fastest|car|edge_based=false|u_turn_costs=-1, fastest|car|edge_based=true|u_turn_costs=30, fastest|car|edge_based=true|u_turn_costs=50]
```

And when there are are too many matches we get something like:

```
There are multiple CH profiles matching your request. Use the `weighting`,`vehicle`,`edge_based` and/or `u_turn_costs` parameters to be more specific.
requested:  *|car|edge_based=true|u_turn_costs=*
matched:   [fastest|car|edge_based=true|u_turn_costs=30, fastest|car|edge_based=true|u_turn_costs=50]
available: [fastest|car|edge_based=false|u_turn_costs=-1, fastest|car|edge_based=true|u_turn_costs=30, fastest|car|edge_based=true|u_turn_costs=50]
```

Here `*` represents a wildcard, e.g. if the vehicle/weighting/edge_based/u_turn_costs are not given.




Patch:
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
index 951b3c328..1da9a9dc8 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
@@ -20,14 +20,12 @@ package com.graphhopper.routing.ch;
 import com.graphhopper.routing.RoutingAlgorithmFactory;
 import com.graphhopper.routing.RoutingAlgorithmFactoryDecorator;
 import com.graphhopper.routing.util.HintsMap;
-import com.graphhopper.storage.CHGraphImpl;
 import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.StorableProperties;
 import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.PMap;
 import com.graphhopper.util.Parameters.CH;
-import com.graphhopper.util.Parameters.Routing;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,7 +33,6 @@ import java.util.*;
 import java.util.concurrent.ExecutorCompletionService;
 import java.util.concurrent.ExecutorService;
 
-import static com.graphhopper.routing.weighting.TurnWeighting.INFINITE_U_TURN_COSTS;
 import static com.graphhopper.util.Helper.*;
 import static com.graphhopper.util.Parameters.CH.DISABLE;
 
@@ -63,7 +60,8 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
 
     public CHAlgoFactoryDecorator() {
         setPreparationThreads(1);
-        setCHProfilesAsStrings(Arrays.asList(getDefaultProfile()));
+        // use fastest by default
+        setCHProfilesAsStrings(Collections.singletonList("fastest"));
     }
 
     @Override
@@ -229,10 +227,6 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         return this;
     }
 
-    private String getDefaultProfile() {
-        return chProfileStrings.isEmpty() ? "fastest" : chProfileStrings.iterator().next();
-    }
-
     public List<PrepareContractionHierarchies> getPreparations() {
         return preparations;
     }
@@ -246,16 +240,11 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         if (preparations.isEmpty())
             throw new IllegalStateException("No preparations added to this decorator");
 
-        if (map.getWeighting().isEmpty())
-            map.setWeighting(getDefaultProfile());
-
         return getPreparation(map);
     }
 
     public PrepareContractionHierarchies getPreparation(HintsMap map) {
-        Boolean edgeBased = map.has(Routing.EDGE_BASED) ? map.getBool(Routing.EDGE_BASED, false) : null;
-        Integer uTurnCosts = map.has(Routing.U_TURN_COSTS) ? map.getInt(Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS) : null;
-        CHProfile selectedProfile = selectProfile(map, edgeBased, uTurnCosts);
+        CHProfile selectedProfile = selectProfile(map);
         return getPreparation(selectedProfile);
     }
 
@@ -268,9 +257,9 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         throw new IllegalStateException("Could not find CH preparation for profile: " + chProfile);
     }
 
-    private CHProfile selectProfile(HintsMap map, Boolean edgeBased, Integer uTurnCosts) {
+    private CHProfile selectProfile(HintsMap map) {
         try {
-            return CHProfileSelector.select(chProfiles, map, edgeBased, uTurnCosts);
+            return CHProfileSelector.select(chProfiles, map);
         } catch (CHProfileSelectionException e) {
             throw new IllegalArgumentException(e.getMessage());
         }
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java b/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java
index d9be61562..93adf9c70 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHProfileSelector.java
@@ -18,168 +18,98 @@
 
 package com.graphhopper.routing.ch;
 
-import com.carrotsearch.hppc.IntObjectHashMap;
-import com.carrotsearch.hppc.IntObjectMap;
 import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.storage.CHProfile;
 import com.graphhopper.util.Parameters;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
+import static com.graphhopper.routing.weighting.TurnWeighting.INFINITE_U_TURN_COSTS;
+
 /**
  * This class is used to determine the appropriate CH profile given (or not given) some (request) parameters
  */
 public class CHProfileSelector {
     private final List<CHProfile> chProfiles;
-    private HintsMap weightingMap;
-    private IntObjectMap<CHProfile> edgeBasedCHProfilesByUTurnCosts;
-    private CHProfile nodeBasedCHProfile;
-    private List<String> entriesStrs;
+    // variables describing the requested CH profile
+    private final String weighting;
+    private final String vehicle;
+    private final Boolean edgeBased;
+    private final Integer uTurnCosts;
 
-    private CHProfileSelector(List<CHProfile> chProfiles) {
+    private CHProfileSelector(List<CHProfile> chProfiles, HintsMap hintsMap) {
         this.chProfiles = chProfiles;
+        weighting = hintsMap.getWeighting();
+        vehicle = hintsMap.getVehicle();
+        edgeBased = hintsMap.has(Parameters.Routing.EDGE_BASED) ? hintsMap.getBool(Parameters.Routing.EDGE_BASED, false) : null;
+        uTurnCosts = hintsMap.has(Parameters.Routing.U_TURN_COSTS) ? hintsMap.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS) : null;
     }
 
     /**
-     * @param chProfiles   the CH profiles to choose from
-     * @param weightingMap a map used to specify the weighting that shall be used
-     * @param edgeBased    whether or not edge-based CH shall be used or null if not specified explicitly
-     * @param uTurnCosts   specifies which value the u-turn costs of the CH profile should have, or null
+     * @param chProfiles the CH profiles to choose from
+     * @param hintsMap   a map used to describe the CH profile that shall be selected
      * @throws CHProfileSelectionException if no CH profile could be selected for the given parameters
      */
-    public static CHProfile select(
-            List<CHProfile> chProfiles, HintsMap weightingMap, Boolean edgeBased, Integer uTurnCosts) {
-        return new CHProfileSelector(chProfiles).select(weightingMap, edgeBased, uTurnCosts);
+    public static CHProfile select(List<CHProfile> chProfiles, HintsMap hintsMap) {
+        return new CHProfileSelector(chProfiles, hintsMap).select();
     }
 
-    private CHProfile select(HintsMap weightingMap, Boolean edgeBased, Integer uTurnCosts) {
-        this.weightingMap = weightingMap;
-        findCHProfilesMatchingWeighting();
-
-        if (!foundCHProfilesMatchingWeighting()) {
-            throw new CHProfileSelectionException("Cannot find CH profile for weighting map " + weightingMap + " in entries: " + entriesStrs + ".");
-        }
-
-        if (edgeBased != null && uTurnCosts != null) {
-            return selectUsingEdgeBasedAndUTurnCosts(edgeBased, uTurnCosts);
-        } else if (edgeBased != null) {
-            return selectUsingEdgeBased(edgeBased);
-        } else if (uTurnCosts != null) {
-            return selectUsingUTurnCosts(uTurnCosts);
-        } else {
-            return selectUsingWeightingOnly();
-        }
-    }
+    private CHProfile select() {
 
-    private CHProfile selectUsingEdgeBasedAndUTurnCosts(boolean edgeBased, int uTurnCosts) {
-        if (edgeBased) {
-            CHProfile edgeBasedCHProfile = edgeBasedCHProfilesByUTurnCosts.get(uTurnCosts);
-            if (edgeBasedCHProfile != null) {
-                return edgeBasedCHProfile;
-            } else if (!edgeBasedCHProfilesByUTurnCosts.isEmpty()) {
-                return throwFoundEdgeBasedButNotForRequestedUTurnCosts(uTurnCosts);
-            } else {
-                return throwRequestedEdgeBasedButOnlyFoundNodeBased();
+        List<CHProfile> matchingProfiles = new ArrayList<>();
+        for (CHProfile p : chProfiles) {
+            if (edgeBased != null && p.isEdgeBased() != edgeBased) {
+                continue;
             }
-        } else {
-            if (nodeBasedCHProfile != null) {
-                return nodeBasedCHProfile;
-            } else {
-                return throwRequestedNodeBasedButOnlyFoundEdgeBased();
+            if (uTurnCosts != null && p.getUTurnCostsInt() != uTurnCosts) {
+                continue;
             }
-        }
-    }
-
-    private CHProfile selectUsingEdgeBased(boolean edgeBased) {
-        if (edgeBased) {
-            // u-turn costs were not specified, so either there is only one edge-based profile and we take it
-            // or we throw an error
-            if (edgeBasedCHProfilesByUTurnCosts.size() == 1) {
-                return edgeBasedCHProfilesByUTurnCosts.iterator().next().value;
-            } else if (edgeBasedCHProfilesByUTurnCosts.isEmpty()) {
-                return throwRequestedEdgeBasedButOnlyFoundNodeBased();
-            } else {
-                return throwFoundEdgeBasedButUnclearWhichOneToTake();
+            if (!weighting.isEmpty() && !getWeighting(p).equals(weighting)) {
+                continue;
             }
-        } else {
-            if (nodeBasedCHProfile != null) {
-                return nodeBasedCHProfile;
-            } else {
-                return throwRequestedNodeBasedButOnlyFoundEdgeBased();
+            if (!vehicle.isEmpty() && !getVehicle(p).equals(vehicle)) {
+                continue;
             }
+            matchingProfiles.add(p);
         }
-    }
 
-    private CHProfile selectUsingUTurnCosts(int uTurnCosts) {
-        // no edge_based parameter was set, we determine the CH profile based on what is there (and prefer edge-based
-        // if we can choose)
-        CHProfile edgeBasedPCH = edgeBasedCHProfilesByUTurnCosts.get(uTurnCosts);
-        if (edgeBasedPCH != null) {
-            return edgeBasedPCH;
-        } else if (!edgeBasedCHProfilesByUTurnCosts.isEmpty()) {
-            return throwFoundEdgeBasedButNotForRequestedUTurnCosts(uTurnCosts);
+        if (matchingProfiles.isEmpty()) {
+            throw new CHProfileSelectionException("Cannot find matching CH profile for your request.\nrequested:  " + getRequestAsString() + "\navailable: " + chProfiles);
+        } else if (matchingProfiles.size() == 1) {
+            return matchingProfiles.get(0);
         } else {
-            return nodeBasedCHProfile;
-        }
-    }
-
-    private CHProfile selectUsingWeightingOnly() {
-        if (edgeBasedCHProfilesByUTurnCosts.size() == 1) {
-            return edgeBasedCHProfilesByUTurnCosts.iterator().next().value;
-        } else if (!edgeBasedCHProfilesByUTurnCosts.isEmpty()) {
-            return throwFoundEdgeBasedButUnclearWhichOneToTake();
-        } else {
-            return nodeBasedCHProfile;
-        }
-    }
-
-    private void findCHProfilesMatchingWeighting() {
-        entriesStrs = new ArrayList<>();
-        edgeBasedCHProfilesByUTurnCosts = new IntObjectHashMap<>(3);
-        nodeBasedCHProfile = null;
-        for (CHProfile p : chProfiles) {
-            boolean weightingMatches = p.getWeighting().matches(weightingMap);
-            if (weightingMatches) {
-                if (p.isEdgeBased()) {
-                    edgeBasedCHProfilesByUTurnCosts.put(p.getUTurnCostsInt(), p);
-                } else {
-                    nodeBasedCHProfile = p;
-                }
+            // special case: prefer edge-based over node-based if these are the only two options
+            CHProfile match1 = matchingProfiles.get(0);
+            CHProfile match2 = matchingProfiles.get(1);
+            if (edgeBased == null && matchingProfiles.size() == 2 &&
+                    getWeighting(match1).equals(getWeighting(match2)) &&
+                    getVehicle(match1).equals(getVehicle(match2)) &&
+                    match1.isEdgeBased() != match2.isEdgeBased()) {
+                return match1.isEdgeBased() ? match1 : match2;
             }
-            entriesStrs.add(p.toString());
+            throw new CHProfileSelectionException("There are multiple CH profiles matching your request. Use the `weighting`,`vehicle`,`edge_based` and/or `u_turn_costs` parameters to be more specific." +
+                    "\nrequested:  " + getRequestAsString() + "\nmatched:   " + matchingProfiles + "\navailable: " + chProfiles);
         }
     }
 
-    private boolean foundCHProfilesMatchingWeighting() {
-        return !edgeBasedCHProfilesByUTurnCosts.isEmpty() || nodeBasedCHProfile != null;
-    }
-
-    private CHProfile throwFoundEdgeBasedButUnclearWhichOneToTake() {
-        int[] availableUTurnCosts = edgeBasedCHProfilesByUTurnCosts.keys().toArray();
-        Arrays.sort(availableUTurnCosts);
-        throw new CHProfileSelectionException("Found matching edge-based CH profiles for multiple values of u-turn costs: " + Arrays.toString(availableUTurnCosts) +
-                ". You need to specify which one to use using the `" + Parameters.Routing.U_TURN_COSTS + "' parameter");
-    }
-
-    private CHProfile throwRequestedNodeBasedButOnlyFoundEdgeBased() {
-        throw new CHProfileSelectionException("Found " + edgeBasedCHProfilesByUTurnCosts.size() + " edge-based CH profile(s) for weighting map " + weightingMap
-                + ", but requested node-based CH. You either need to configure a node-based CH profile or set the '" + Parameters.Routing.EDGE_BASED + "' " +
-                "request parameter to 'true' (was 'false'). all entries: " + entriesStrs);
+    private String getVehicle(CHProfile match1) {
+        return match1.getWeighting().getFlagEncoder().toString();
     }
 
-    private CHProfile throwRequestedEdgeBasedButOnlyFoundNodeBased() {
-        throw new CHProfileSelectionException("Found a node-based CH profile for weighting map " + weightingMap + ", but requested edge-based CH. " +
-                "You either need to configure an edge-based CH profile or set the '" + Parameters.Routing.EDGE_BASED + "' " +
-                "request parameter to 'false' (was 'true'). all entries: " + entriesStrs);
+    private String getWeighting(CHProfile match1) {
+        return match1.getWeighting().getName();
     }
 
-    private CHProfile throwFoundEdgeBasedButNotForRequestedUTurnCosts(int uTurnCosts) {
-        int[] availableUTurnCosts = edgeBasedCHProfilesByUTurnCosts.keys().toArray();
-        Arrays.sort(availableUTurnCosts);
-        throw new CHProfileSelectionException("Found edge-based CH profiles for weighting map " + weightingMap + " but none for requested u-turn costs: " +
-                uTurnCosts + ", available: " + Arrays.toString(availableUTurnCosts) + ". You need to configure an edge-based CH profile for this value of u-turn costs or" +
-                " choose another value using the '" + Parameters.Routing.U_TURN_COSTS + "' request parameter.");
+    private String getRequestAsString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(weighting.isEmpty() ? "*" : weighting);
+        sb.append("|");
+        sb.append(vehicle.isEmpty() ? "*" : vehicle);
+        sb.append("|");
+        sb.append("edge_based=").append(edgeBased != null ? edgeBased : "*");
+        sb.append("|");
+        sb.append("u_turn_costs=").append(uTurnCosts != null ? uTurnCosts : "*");
+        return sb.toString();
     }
 }
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
index 93a32a000..06ea512e8 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
@@ -229,6 +229,11 @@ public class LMAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         if (preparations.isEmpty())
             throw new IllegalStateException("No preparations added to this decorator");
 
+        // if no weighting or vehicle is specified for this request and there is only one preparation, use it
+        if ((map.getWeighting().isEmpty() || map.getVehicle().isEmpty()) && preparations.size() == 1) {
+            return new LMRAFactory(preparations.get(0), defaultAlgoFactory);
+        }
+
         for (final PrepareLandmarks p : preparations) {
             if (p.getWeighting().matches(map))
                 return new LMRAFactory(p, defaultAlgoFactory);


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java
index 74d01342b..07dd958fd 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java
@@ -18,15 +18,17 @@
 
 package com.graphhopper.routing.ch;
 
-import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.routing.util.HintsMap;
+import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.FastestWeighting;
+import com.graphhopper.routing.weighting.ShortFastestWeighting;
+import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.CHProfile;
+import com.graphhopper.util.Parameters;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -36,36 +38,46 @@ import static org.junit.Assert.*;
 
 public class CHProfileSelectorTest {
 
-    private Weighting fastestWeighting;
+    private static final String MULTIPLE_MATCHES_ERROR = "There are multiple CH profiles matching your request. Use the `weighting`,`vehicle`,`edge_based` and/or `u_turn_costs` parameters to be more specific";
+    private static final String NO_MATCH_ERROR = "Cannot find matching CH profile for your request";
+
+    private Weighting weightingFastestCar;
+    private Weighting weightingFastestBike;
+    private Weighting weightingShortestCar;
+    private Weighting weightingShortestBike;
 
     @Before
     public void setup() {
-        EncodingManager em = EncodingManager.create("car");
-        FlagEncoder carEncoder = em.fetchEdgeEncoders().iterator().next();
-        fastestWeighting = new FastestWeighting(carEncoder);
+        FlagEncoder carEncoder = new CarFlagEncoder();
+        FlagEncoder bikeEncoder = new BikeFlagEncoder();
+        EncodingManager.create(carEncoder, bikeEncoder);
+        weightingFastestCar = new FastestWeighting(carEncoder);
+        weightingFastestBike = new FastestWeighting(bikeEncoder);
+        weightingShortestCar = new ShortestWeighting(carEncoder);
+        weightingShortestBike = new ShortestWeighting(bikeEncoder);
     }
 
     @Test
     public void onlyNodeBasedPresent() {
         List<CHProfile> chProfiles = Collections.singletonList(
-                CHProfile.nodeBased(fastestWeighting)
+                CHProfile.nodeBased(weightingFastestCar)
         );
-        assertCHProfileSelectionError("Found a node-based CH profile for weighting map {weighting=fastest, vehicle=car}, but requested edge-based CH", chProfiles, true, null);
-        assertCHProfileSelectionError("Found a node-based CH profile for weighting map {weighting=fastest, vehicle=car}, but requested edge-based CH", chProfiles, true, 20);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, true, null);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, true, 20);
         assertProfileFound(chProfiles.get(0), chProfiles, false, null);
-        assertProfileFound(chProfiles.get(0), chProfiles, false, 20);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, false, 20);
         assertProfileFound(chProfiles.get(0), chProfiles, null, null);
-        assertProfileFound(chProfiles.get(0), chProfiles, null, 30);
-        assertCHProfileSelectionError("Cannot find CH profile for weighting map", chProfiles, "foot", "fastest", false, null);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, null, 30);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, "foot", "fastest", false, null);
     }
 
     @Test
     public void onlyEdgeBasedPresent() {
         List<CHProfile> chProfiles = Collections.singletonList(
-                CHProfile.edgeBased(fastestWeighting, INFINITE_U_TURN_COSTS)
+                CHProfile.edgeBased(weightingFastestCar, INFINITE_U_TURN_COSTS)
         );
-        assertCHProfileSelectionError("Found 1 edge-based CH profile(s) for weighting map {weighting=fastest, vehicle=car}, but requested node-based CH", chProfiles, false, null);
-        assertCHProfileSelectionError("Found 1 edge-based CH profile(s) for weighting map {weighting=fastest, vehicle=car}, but requested node-based CH", chProfiles, false, 20);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, false, null);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, false, 20);
         assertProfileFound(chProfiles.get(0), chProfiles, true, null);
         assertProfileFound(chProfiles.get(0), chProfiles, null, null);
     }
@@ -73,8 +85,8 @@ public class CHProfileSelectorTest {
     @Test
     public void edgeAndNodePresent() {
         List<CHProfile> chProfiles = Arrays.asList(
-                CHProfile.nodeBased(fastestWeighting),
-                CHProfile.edgeBased(fastestWeighting, INFINITE_U_TURN_COSTS)
+                CHProfile.nodeBased(weightingFastestCar),
+                CHProfile.edgeBased(weightingFastestCar, INFINITE_U_TURN_COSTS)
         );
         // in case edge-based is not specified we prefer the edge-based profile over the node-based one
         assertProfileFound(chProfiles.get(1), chProfiles, null, null);
@@ -85,22 +97,111 @@ public class CHProfileSelectorTest {
     @Test
     public void multipleEdgeBased() {
         List<CHProfile> chProfiles = Arrays.asList(
-                CHProfile.nodeBased(fastestWeighting),
-                CHProfile.edgeBased(fastestWeighting, 30),
-                CHProfile.edgeBased(fastestWeighting, 50)
+                CHProfile.nodeBased(weightingFastestCar),
+                CHProfile.edgeBased(weightingFastestCar, 30),
+                CHProfile.edgeBased(weightingFastestCar, 50)
         );
         // when no u-turns are specified we throw
-        assertCHProfileSelectionError("Found matching edge-based CH profiles for multiple values of u-turn costs: [30, 50].",
-                chProfiles, true, null);
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, true, null);
         // when we request one that does not exist we throw
-        assertCHProfileSelectionError("but none for requested u-turn costs: 40, available: [30, 50]", chProfiles, true, 40);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, true, 40);
         // when we request one that exists it works
         assertProfileFound(chProfiles.get(1), chProfiles, true, 30);
 
         // without specifying edge-based
         assertProfileFound(chProfiles.get(1), chProfiles, null, 30);
-        assertCHProfileSelectionError("but none for requested u-turn costs: 40, available: [30, 50", chProfiles, null, 40);
-        assertCHProfileSelectionError("Found matching edge-based CH profiles for multiple values of u-turn costs: [30, 50].", chProfiles, null, null);
+        assertCHProfileSelectionError(NO_MATCH_ERROR, chProfiles, null, 40);
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, null, null);
+    }
+
+    @Test
+    public void missingVehicleOrWeighting() {
+        // when we do not set the weighting and/or the car but it can be derived from the profile the profile is returned
+        List<CHProfile> chProfiles = Collections.singletonList(CHProfile.nodeBased(weightingFastestCar));
+        assertProfileFound(chProfiles.get(0), chProfiles, "car", "fastest", null, null);
+        assertProfileFound(chProfiles.get(0), chProfiles, "car", "", null, null);
+        assertProfileFound(chProfiles.get(0), chProfiles, "", "fastest", null, null);
+        assertProfileFound(chProfiles.get(0), chProfiles, "", "", null, null);
+    }
+
+    @Test
+    public void missingVehicleOrWeighting_otherVehicleAndCar() {
+        List<CHProfile> chProfiles = Collections.singletonList(CHProfile.nodeBased(weightingShortestBike));
+        assertProfileFound(chProfiles.get(0), chProfiles, "bike", "shortest", null, null);
+        assertProfileFound(chProfiles.get(0), chProfiles, "bike", "", null, null);
+        assertProfileFound(chProfiles.get(0), chProfiles, "", "shortest", null, null);
+        assertProfileFound(chProfiles.get(0), chProfiles, "", "", null, null);
+    }
+
+
+    @Test
+    public void missingVehicleMultipleProfiles() {
+        List<CHProfile> chProfiles = Arrays.asList(
+                CHProfile.nodeBased(weightingFastestBike),
+                CHProfile.nodeBased(weightingShortestBike),
+                CHProfile.edgeBased(weightingFastestBike, 40)
+        );
+        // the vehicle is not given but only bike is used so its fine. note that we prefer edge-based because no edge_based parameter is specified
+        assertProfileFound(chProfiles.get(2), chProfiles, "", "fastest", null, null);
+        // if we do not specify the weighting its not clear what to return -> there is an error
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, "", "", null, null);
+        // if we do not specify the weighting but edge_based=true its clear what to return because for edge-based there is only one weighting
+        assertProfileFound(chProfiles.get(2), chProfiles, "", "", true, null);
+        // ... for edge_based=false this is an error because there are two node-based profiles
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, "", "", false, null);
+    }
+
+    @Test
+    public void missingWeightingMultipleProfiles() {
+        List<CHProfile> chProfiles = Arrays.asList(
+                CHProfile.nodeBased(weightingFastestBike),
+                CHProfile.edgeBased(weightingFastestCar, 10),
+                CHProfile.edgeBased(weightingFastestBike, 40)
+        );
+        // the weighting is not given but only fastest is used so its fine. note that we prefer edge-based because no edge_based parameter is specified
+        assertProfileFound(chProfiles.get(2), chProfiles, "bike", "", null, null);
+        // if we do not specify the vehicle its not clear what to return -> there is an error
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, "", "", null, null);
+        // if we do not specify the vehicle but edge_based=false its clear what to return because for node-based there is only one weighting
+        assertProfileFound(chProfiles.get(0), chProfiles, "", "", false, null);
+        // ... for edge_based=true this is an error, because there are two edge_based profiles
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, "", "", true, null);
+        // .. we can however get a clear match if we specify the u-turn costs
+        assertProfileFound(chProfiles.get(1), chProfiles, "", "", true, 10);
+    }
+
+    @Test
+    public void multipleVehiclesMissingWeighting() {
+        // this is a common use-case, there are multiple vehicles for one weighting
+        EncodingManager em = EncodingManager.create("car,bike,motorcycle,bike2,foot");
+        List<CHProfile> chProfiles = new ArrayList<>();
+        for (FlagEncoder encoder : em.fetchEdgeEncoders()) {
+            chProfiles.add(CHProfile.nodeBased(new ShortFastestWeighting(encoder, 1)));
+        }
+        // we do not specify the weighting but this is ok, because there is only one in use
+        String weighting = "";
+        assertProfileFound(chProfiles.get(0), chProfiles, "car", weighting, null, null);
+        assertProfileFound(chProfiles.get(1), chProfiles, "bike", weighting, null, null);
+        assertProfileFound(chProfiles.get(2), chProfiles, "motorcycle", weighting, null, null);
+        assertProfileFound(chProfiles.get(3), chProfiles, "bike2", weighting, null, null);
+        assertProfileFound(chProfiles.get(4), chProfiles, "foot", weighting, null, null);
+    }
+
+
+    @Test
+    public void missingVehicle_multiplePossibilities_throws() {
+        List<CHProfile> chProfiles = Arrays.asList(
+                CHProfile.nodeBased(weightingFastestBike), CHProfile.nodeBased(weightingFastestCar)
+        );
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, "", "fastest", null, null);
+    }
+
+    @Test
+    public void missingWeighting_multiplePossibilities_throws() {
+        List<CHProfile> chProfiles = Arrays.asList(
+                CHProfile.nodeBased(weightingFastestBike), CHProfile.nodeBased(weightingShortestBike)
+        );
+        assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, chProfiles, "bike", "", null, null);
     }
 
     private void assertProfileFound(CHProfile expectedProfile, List<CHProfile> profiles, Boolean edgeBased, Integer uTurnCosts) {
@@ -108,12 +209,12 @@ public class CHProfileSelectorTest {
     }
 
     private void assertProfileFound(CHProfile expectedProfile, List<CHProfile> profiles, String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) {
-        HintsMap weightingMap = new HintsMap().setWeighting(weighting).setVehicle(vehicle);
+        HintsMap hintsMap = createHintsMap(vehicle, weighting, edgeBased, uTurnCosts);
         try {
-            CHProfile selectedProfile = CHProfileSelector.select(profiles, weightingMap, edgeBased, uTurnCosts);
+            CHProfile selectedProfile = CHProfileSelector.select(profiles, hintsMap);
             assertEquals(expectedProfile, selectedProfile);
         } catch (CHProfileSelectionException e) {
-            fail("no profile found, but expected: " + expectedProfile + ", error: " + e.getMessage());
+            fail("no profile found\nexpected: " + expectedProfile + "\nerror: " + e.getMessage());
         }
     }
 
@@ -122,13 +223,24 @@ public class CHProfileSelectorTest {
     }
 
     private void assertCHProfileSelectionError(String expectedError, List<CHProfile> profiles, String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) {
-        HintsMap weightingMap = new HintsMap().setWeighting(weighting).setVehicle(vehicle);
+        HintsMap hintsMap = createHintsMap(vehicle, weighting, edgeBased, uTurnCosts);
         try {
-            CHProfileSelector.select(profiles, weightingMap, edgeBased, uTurnCosts);
+            CHProfileSelector.select(profiles, hintsMap);
             fail("There should have been an error");
         } catch (CHProfileSelectionException e) {
-            assertTrue("There should have been an error message containing '" + expectedError + "', but was: '" + e.getMessage() + "'",
+            assertTrue("There should have been an error message containing:\n'" + expectedError + "'\nbut was:\n'" + e.getMessage() + "'",
                     e.getMessage().contains(expectedError));
         }
     }
+
+    private HintsMap createHintsMap(String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) {
+        HintsMap hintsMap = new HintsMap().setWeighting(weighting).setVehicle(vehicle);
+        if (edgeBased != null) {
+            hintsMap.put(Parameters.Routing.EDGE_BASED, edgeBased);
+        }
+        if (uTurnCosts != null) {
+            hintsMap.put(Parameters.Routing.U_TURN_COSTS, uTurnCosts);
+        }
+        return hintsMap;
+    }
 }
\ No newline at end of file
diff --git a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
index d579f5b73..9cae50112 100644
--- a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
@@ -1206,8 +1206,8 @@ public class GraphHopperIT {
         assertMoscowNodeBased(tmpHopper, "false", true);
         GHResponse rsp = runMoscow(tmpHopper, "true", true);
         assertEquals(1, rsp.getErrors().size());
-        assertTrue(rsp.getErrors().toString().contains("Found a node-based CH profile"));
-        assertTrue(rsp.getErrors().toString().contains("but requested edge-based CH"));
+        assertTrue("unexpected error: " + rsp.getErrors().toString(), rsp.getErrors().toString().contains(
+                "Cannot find matching CH profile for your request.\nrequested:  *|car|edge_based=true|u_turn_costs=*\navailable: [fastest|car|edge_based=false|u_turn_costs=-1]"));
     }
 
     @Test
@@ -1229,8 +1229,8 @@ public class GraphHopperIT {
         assertMoscowEdgeBased(tmpHopper, "true", true);
         GHResponse rsp = runMoscow(tmpHopper, "false", true);
         assertTrue(rsp.hasErrors());
-        assertTrue(rsp.getErrors().toString().contains("Found 1 edge-based CH profile"));
-        assertTrue(rsp.getErrors().toString().contains("but requested node-based CH"));
+        assertTrue("unexpected error: " + rsp.getErrors(), rsp.getErrors().toString().contains(
+                "Cannot find matching CH profile for your request.\nrequested:  *|car|edge_based=false|u_turn_costs=*\navailable: [fastest|car|edge_based=true|u_turn_costs=-1]"));
     }
 
     private GHResponse assertMoscowNodeBased(GraphHopper tmpHopper, String edgeBasedParam, boolean ch) {
@@ -1381,4 +1381,32 @@ public class GraphHopperIT {
         return tmpHopper.route(req);
     }
 
+    @Test
+    public void testCHWithFiniteUTurnCostsAndMissingWeighting() {
+        GraphHopper h = new GraphHopperOSM().
+                setDataReaderFile(DIR + "/monaco.osm.gz").
+                setCHEnabled(true).
+                setGraphHopperLocation(tmpGraphFile).
+                setEncodingManager(EncodingManager.create("car|turn_costs=true"));
+        h.getCHFactoryDecorator()
+                .setCHProfileStrings("fastest|u_turn_costs=40")
+                .setEdgeBasedCHMode(CHAlgoFactoryDecorator.EdgeBasedCHMode.EDGE_OR_NODE);
+        h.importOrLoad();
+
+        GHPoint p = new GHPoint(43.73397, 7.414173);
+        GHPoint q = new GHPoint(43.73222, 7.415557);
+        GHRequest req = new GHRequest(p, q);
+        // note that we do *not* set the weighting on the request, it will be determined automatically from the
+        // CH profile, see #1788
+        req.setWeighting("fastest");
+        // we force the start/target directions such that there are u-turns right after we start and right before
+        // we reach the target
+        req.setCurbsides(Arrays.asList("right", "right"));
+        GHResponse res = h.route(req);
+        assertFalse("routing should not fail", res.hasErrors());
+        assertEquals(266.8, res.getBest().getRouteWeight(), 0.1);
+        assertEquals(2116, res.getBest().getDistance(), 1);
+        assertEquals(266800, res.getBest().getTime(), 1000);
+    }
+
 }