Repository: graphhopper/graphhopper
Pull request #1671 (2019-07-29 13:21:52)

Identifies CH graphs by new CHProfile class instead of Weighting.
Instead of a list of `Weighting`s for edge/node-based preparations `GraphHopperStorage` now takes a list of `CHProfile`:

```java
public GraphHopperStorage(List<CHProfile> chProfiles, Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage)
```

To get a `CHGraph` from `GraphHopperStorage` we are using now:
```java
graph.getCHGraph(CHProfile);
```

In contrast to the previous approach where the exact `Weighting` instance was used to identify a `CHGraph` the `CHProfile` now works like a finger-print of a `CHGraph` and it does not have to be the same object anymore (at least as long as the `Weighting` derives from `AbstractWeighting` which provides its own equals method).

Also see discussion in #1652.



karussell: Thanks! 

Is it possible with this PR to make this part cleaner here? https://github.com/graphhopper/map-matching/blob/master/matching-core/src/main/java/com/graphhopper/matching/MapMatching.java#L116
easbar: > Is it possible with this PR to make this part cleaner here?

Hmm, I am not sure (I do not understand this comment):

> I want to use my own instance of FastestWeighting because it uses its own U-Turn-penalty

How can `FastestWeighting` use a u-turn penalty ? Or should this read *heading*-penalty here ? If the heading penalty is meant this is not supported by CH and since the heading penalty is not included in `FastestWeighting#toString` it does not affect `AbstractWeighting#equals`. 

The important thing that changed in this PR is that `Weightings` are now identified by `equals` instead of `==`. All properties of a `Weighting` should be included in `toString`, such that the CH preparation matches the `Weighting` that we use. For the heading penalty its a special case, because this is not supported for CH.
easbar: Any idea why the CI build fails for open jdk 12 ? 

> [ERROR] Failed to execute goal de.thetaphi:forbiddenapis:2.6:check (default-cli) on project graphhopper-tools: Check for forbidden API calls failed while scanning class 'com.graphhopper.ui.MiniGraphUI' (MiniGraphUI.java): java.lang.ClassNotFoundException: com.graphhopper.storage.CHProfile (while looking up details about referenced class 'com.graphhopper.storage.CHProfile') -> [Help 1]
karussell: > Any idea why the CI build fails for open jdk 12 ?

Strange, can you try to remove the cache?

```
cache:
  directories:
  - $HOME/.m2
```

> How can FastestWeighting use a u-turn penalty ? 

@michaz do you remember what the problem was and why you had to create a separate Weighting instance?
easbar: Hmm, I do not get it. Disabling the cache did not help and adding gh-core as explicit dependency to tools did not help either. Somehow the wrong version of the core (that does not have `CHProfile`) must be pulled in for the forbiddenapis check. 

I also tried changin all versions to `0.13-ch_profile-test`, but then the Open JDK 12 build fails with another error:

```shell
[ERROR] Failed to execute goal on project graphhopper-web-api: Could not resolve dependencies for project com.graphhopper:graphhopper-web-api:jar:0.13-ch_profile-test: Could not find artifact com.graphhopper:graphhopper-api:jar:0.13-ch_profile-test in google-maven-central (https://maven-central.storage-download.googleapis.com/repos/central/data/) -> [Help 1]
```

So it seems that it is trying to obtain graphhopper module artifacts from maven central instead of using the local ones from the reactor build ?
karussell: Really strange.

I got something that seems to works ... don't ask why though ;)
I've just set the in-built "openjdk12" jdk for travis and it works! Although it does the same (downloads the install-jdk script and runs it). 
easbar: Thanks! I enabled the cache again and set back the artifact versions and the build passed a second time. No idea what this was...
michaz: 
> How can FastestWeighting use a u-turn penalty ? Or should this read heading-penalty here ? If the heading penalty is meant this is not supported by CH anyway. Since the heading penalty is not included in FastestWeighting#toString it does not affect AbstractWeighting#equals so for CHGraph#getCHGraph with this PR we will get the same CHGraph regardless what heading penalty we are using.

Yes, it should read heading-penalty. The heading-penalty is used only in the map-matching specific entry/exit phase. As you say, not during CH routing.

The entire comment is basically a complaint that I have to use a weighting that I don't want to route with (`((PrepareContractionHierarchies) routingAlgorithmFactory).getWeighting()`) as a key, just to get the `Graph` that I want, even though I know that my own weighting will work with that `Graph`.

And if that's what your change fixes, all is fine, and the ugliness _and_ the comment can go away.
michaz: Oh, by the way, this is the exact right moment to change this: In (one step of the) the customizable routing project, we will need to do exactly that: Have a fully dynamic weighting that we create on the fly, and use it on a `CHGraph` of which we ensure "externally" that it fits the weighting.
michaz: (Or is that the idea?) :-)
michaz: In the end, I think, profiles will just have names (strings):

- I will define profiles in cofiguration files, giving them a name
- Users will say which profile they want by query parameter
- ... so I should also be able to get a profile, or something pertaining to a profile, by name in Java.
easbar: > In the end, I think, profiles will just have names (strings):

Yes I agree. It is more or less like this at the moment, because `CHProfile#equals` uses `equals` from `Weighting`, which for `AbstractWeighting` uses `toString`... So should we add `getCHGraph(String)` ? This should be easy.

For the customizable scenario I think we should keep two things in mind:

- The 'definition' of a CH profile can get arbitrarily complex: Its not just about the weighting or node/edge based, but for example when blocking (multiple) areas or even specifying exact speeds for many single edges (think traffic conditions) a lot of information has to go into the 'profile' specification. So it would probably better to use some structured format for the 'profile-string' ?

- I am not sure if `graph.getCHGraph()` will be really what we want for customizable CH, because it should also be possible to create the CH graph on the fly (basically create it, run a request and throw it away immediately). So there will be no need to store it in `GraphHopperStorage` even. At the same time there might be places where it is more like creating a profile and then use it multiple times, in this case it might be best to assign some kind of ID to the profile to retrieve it later (using the full specification of the profile would be too much if, e.g. it includes specifying all traffic conditions etc.).

> (Or is that the idea?) :-)

The idea here was mostly that I wanted to be able to specify u-turn costs (#1672), but sure I think its a step in the right direction for more customized stuff as well. The key thing here is that everything that needs to be known at CH preparation time must be included in the CH profile (used to be just the weighting, then edge/node-based and with #1672 also u-turn costs).
michaz: > to assign some kind of ID to the profile to retrieve it later

Yes, exactly. Like a filename, almost. We already have this `Directory` abstraction -- don't know how far that carries, but basically, our "normal" profiles are currently:

/car/fastest
/car/shortest
/bike/fastest
/bike/shortest
.
.

(right?)

But maybe I want to do something outside that structure. Like, create a CH Graph where some parts of the graph are explicitly banned from contraction (so I can customize them). Maybe I want several of those. They would be created / programmed in user code, and I would give them a name in user code, maybe

/blockableRoadsExperiment/car/fastest

so I can get at them (in other parts of user code).

(Just a thought, maybe non-hierarchical names are enough in the end..)


karussell: > to assign some kind of ID to the profile to retrieve it later

I think, this should be another issue and should not be done before the actual code is required.

The flexible work (flex_vehicleprofile and flex_vehicleprofile2) indicates that everything can be weighting based (we do not need FlagEncoders anymore). I.e. we just use a weighting name as ID "mycar/fastest" and a base name "car/fastest" from which we extend behaviour. Customization properties are sent via a json/yaml file either through http or from the file system.
easbar: > I think, this should be another issue

Yes, is there anything that needs to be done for this PR ?

> The flexible work (flex_vehicleprofile and flex_vehicleprofile2) indicates that everything can be weighting based (we do not need FlagEncoders anymore). I.e. we just use a weighting name as ID "mycar/fastest" and a base name "car/fastest" from which we extend behaviour. 

What do you mean with 'we do not need FlagEncoders anymore' ? Will they be removed entirely ? "car/fastest" still sounds like a `FastestWeighting` with a `CarFlagEncoder` ? Is `flex_vehicleprofile2` just a more recent version of `flex_vehicleprofile` or are they something different ? Sorry I did not have a look at these yet.

karussell: > "car/fastest" still sounds like a FastestWeighting with a CarFlagEncoder ? 

Yes. Still conceptionally the FastestWeighting does not need CarFlagEncoder. In theory it is sufficient to have e.g. roadClassEnc, maxSpeedEnc and a few other EncodedValues that "read the properties of an endge" in the Weighting class. The "CarFlagEncoder-specific stuff" that precalculates e.g. average speed and access for car seems to be required only for performance reasons. Or maybe the otherway around: should we create a separate TruckFlagEncoder to store size and width or should we include this in CarFlagEncoder? But e.g. cargo bikes also need at least the width of an edge. And why should we force a single averageSpeedEnc for car, when in fact there could be many speeds e.g. depending on the time or if it is an electric car?
I do not know yet what a good replacement for FlagEncoder should look like as it is still handy to have some precalculated average speed for foot and car, but still IMO we should try to remove its usage as it is only a specific collection of EncodedValues (?) 
(For now EncodedValues handle edges only, later they could handle also nodes and relations.)

> Is flex_vehicleprofile2 just a more recent version of flex_vehicleprofile or are they something different ? 

Yes, flex_vehicleprofile2 uses a recent master and has no hacks I think, but has also not all features.
easbar: Ok, thanks for your explanations and I see it is difficult to find replacement for everything FlagEncoder does. To specify a CH profile I think we will eventually only need to define a weight for all edges of the base graph (in fwd&bwd direction) and give this collection of weights some name/id to retrieve the corresponding profile later. Of course for json/yaml configuration etc. specifying these weights needs to be possible on a much higher level (like 'weight=dist/speed', 'block this area', 'use this traffic information' etc.). 

Right now `Weighting` (combined with `FlagEncoder`) does all these things: It has a `calcWeight` method, there are different implementations that allow specifying how the weight is calculated from the different edge properties and it is used as identifier of the CH graph (therefore it is still part of `CHProfile`). Maybe at some point we need to separate these things from each other. For example the CH preparation should not be concerned with any details (encoded values, flag encoders, traffic information sources,...) *how* the weights are calculated (it only needs a way to retrieve the two weights per edge) and as @michaz pointed out it should be possible to choose an arbitrary name/id of a CH profile, because it will rather be impractical to include the full specification of a weighting (blocked areas etc.) in its name.

Patch:
diff --git a/.travis.yml b/.travis.yml
index 4e3dffb9c..b58fdd993 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,8 +13,7 @@ env:
 matrix:
   include:
     - jdk: openjdk8
-    - env: JDK='OpenJDK 12'
-      install: . ./install-jdk.sh -F 12 -C
+    - jdk: openjdk12
     - env: JDK='OpenJDK 13' SKIP_EXT_TESTS=true
       install: . ./install-jdk.sh -F 13 -C
     
@@ -66,4 +65,4 @@ notifications:
 
 cache:
   directories:
-  - $HOME/.m2
+    - $HOME/.m2
diff --git a/android/app/pom.xml b/android/app/pom.xml
index de30b700e..292a75bf0 100644
--- a/android/app/pom.xml
+++ b/android/app/pom.xml
@@ -15,7 +15,7 @@
     <parent>
         <relativePath>../..</relativePath>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>    	
+        <artifactId>graphhopper-parent</artifactId>
         <version>0.13-SNAPSHOT</version>
     </parent>
     <properties>
diff --git a/core/files/changelog.txt b/core/files/changelog.txt
index e369529d0..ca7c1a404 100644
--- a/core/files/changelog.txt
+++ b/core/files/changelog.txt
@@ -15,6 +15,7 @@
     to prevent u-turns when using edge-based algorithms it is now required to use TurnWeighting, #1640
     GraphHopperStorage.getGraph(Class) was replaced by GraphHopperStorage.getBase/CHGraph(), #1669
     CHGraph.shortcut(int, int) was removed (use .shortcut(int, int, ...) and/or .shortcutEdgeBased(int, int, ...) instead
+    CH graphs are now identified using CHProfile instead of Weighting, #1670
 
 0.12
     renamed VirtualEdgeIteratorState.getOriginalEdgeKey to more precise getOriginalEdgeKey #1549
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index 01efee239..692639422 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -747,8 +747,7 @@ public class GraphHopper implements GraphHopperAPI {
 
         if (chFactoryDecorator.isEnabled()) {
             initCHAlgoFactoryDecorator();
-            ghStorage = new GraphHopperStorage(chFactoryDecorator.getNodeBasedWeightings(), chFactoryDecorator.getEdgeBasedWeightings(),
-                    dir, encodingManager, hasElevation(), ext);
+            ghStorage = new GraphHopperStorage(chFactoryDecorator.getCHProfiles(), dir, encodingManager, hasElevation(), ext);
         } else {
             ghStorage = new GraphHopperStorage(dir, encodingManager, hasElevation(), ext);
         }
@@ -803,16 +802,16 @@ public class GraphHopper implements GraphHopperAPI {
     }
 
     private void initCHAlgoFactoryDecorator() {
-        if (!chFactoryDecorator.hasWeightings()) {
+        if (!chFactoryDecorator.hasCHProfiles()) {
             for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
-                for (String chWeightingStr : chFactoryDecorator.getWeightingsAsStrings()) {
+                for (String chWeightingStr : chFactoryDecorator.getCHProfileStrings()) {
                     // ghStorage is null at this point
                     CHAlgoFactoryDecorator.EdgeBasedCHMode edgeBasedCHMode = chFactoryDecorator.getEdgeBasedCHMode();
                     if (!(edgeBasedCHMode == EDGE_OR_NODE && encoder.supports(TurnWeighting.class))) {
-                        chFactoryDecorator.addNodeBasedWeighting(createWeighting(new HintsMap(chWeightingStr), encoder, null));
+                        chFactoryDecorator.addCHProfile(CHProfile.nodeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null)));
                     }
                     if (edgeBasedCHMode != OFF && encoder.supports(TurnWeighting.class)) {
-                        chFactoryDecorator.addEdgeBasedWeighting(createWeighting(new HintsMap(chWeightingStr), encoder, null));
+                        chFactoryDecorator.addCHProfile(CHProfile.edgeBased(createWeighting(new HintsMap(chWeightingStr), encoder, null)));
                     }
                 }
             }
@@ -1035,13 +1034,14 @@ public class GraphHopper implements GraphHopperAPI {
                     if (tmpAlgoFactory instanceof LMAlgoFactoryDecorator.LMRAFactory)
                         chAlgoFactory = ((LMAlgoFactoryDecorator.LMRAFactory) tmpAlgoFactory).getDefaultAlgoFactory();
 
-                    if (chAlgoFactory instanceof PrepareContractionHierarchies)
-                        weighting = ((PrepareContractionHierarchies) chAlgoFactory).getWeighting();
-                    else
+                    if (chAlgoFactory instanceof PrepareContractionHierarchies) {
+                        CHProfile chProfile = ((PrepareContractionHierarchies) chAlgoFactory).getCHProfile();
+                        queryGraph = new QueryGraph(ghStorage.getCHGraph(chProfile));
+                        queryGraph.lookup(qResults);
+                        weighting = chProfile.getWeighting();
+                    } else {
                         throw new IllegalStateException("Although CH was enabled a non-CH algorithm factory was returned " + tmpAlgoFactory);
-
-                    queryGraph = new QueryGraph(ghStorage.getCHGraph(weighting));
-                    queryGraph.lookup(qResults);
+                    }
                 } else {
                     checkNonChMaxWaypointDistance(points);
                     queryGraph = new QueryGraph(ghStorage);
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
index 719d50deb..015d17e15 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
@@ -20,9 +20,7 @@ package com.graphhopper.routing.ch;
 import com.graphhopper.routing.RoutingAlgorithmFactory;
 import com.graphhopper.routing.RoutingAlgorithmFactoryDecorator;
 import com.graphhopper.routing.util.HintsMap;
-import com.graphhopper.routing.util.TraversalMode;
-import com.graphhopper.routing.weighting.AbstractWeighting;
-import com.graphhopper.routing.weighting.Weighting;
+import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.StorableProperties;
 import com.graphhopper.util.CmdArgs;
@@ -48,11 +46,11 @@ import static com.graphhopper.util.Parameters.CH.DISABLE;
 public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator {
     private final Logger LOGGER = LoggerFactory.getLogger(getClass());
     private final List<PrepareContractionHierarchies> preparations = new ArrayList<>();
-    // we need to decouple weighting objects from the weighting list of strings 
-    // as we need the strings to create the GraphHopperStorage and the GraphHopperStorage to create the preparations from the Weighting objects currently requiring the encoders
-    private final List<Weighting> nodeBasedWeightings = new ArrayList<>();
-    private final List<Weighting> edgeBasedWeightings = new ArrayList<>();
-    private final Set<String> weightingsAsStrings = new LinkedHashSet<>();
+    // we need to decouple the CH profile objects from the list of CH profile strings
+    // as we need the strings to create the GraphHopperStorage and the GraphHopperStorage to create the preparations
+    // from the CHProfile objects currently requiring the encoders
+    private final List<CHProfile> chProfiles = new ArrayList<>();
+    private final Set<String> chProfileStrings = new LinkedHashSet<>();
     private boolean disablingAllowed = false;
     // for backward compatibility enable CH by default.
     private boolean enabled = true;
@@ -63,7 +61,7 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
 
     public CHAlgoFactoryDecorator() {
         setPreparationThreads(1);
-        setWeightingsAsStrings(Arrays.asList(getDefaultWeighting()));
+        setCHProfilesAsStrings(Arrays.asList(getDefaultProfile()));
     }
 
     @Override
@@ -78,16 +76,18 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
 
         // default is enabled & fastest
         String chWeightingsStr = args.get(CH.PREPARE + "weightings", "");
+        if (chWeightingsStr.contains("edge_based")) {
+            throw new IllegalArgumentException("Adding 'edge_based` to " + (CH.PREPARE + "weightings") + " is not allowed, to enable edge-based CH use " + (CH.PREPARE + "edge_based") + " instead.");
+        }
 
         if ("no".equals(chWeightingsStr) || "false".equals(chWeightingsStr)) {
             // default is fastest and we need to clear this explicitly
-            weightingsAsStrings.clear();
+            chProfileStrings.clear();
         } else if (!chWeightingsStr.isEmpty()) {
-            List<String> tmpCHWeightingList = Arrays.asList(chWeightingsStr.split(","));
-            setWeightingsAsStrings(tmpCHWeightingList);
+            setCHProfilesAsStrings(Arrays.asList(chWeightingsStr.split(",")));
         }
 
-        boolean enableThis = !weightingsAsStrings.isEmpty();
+        boolean enableThis = !chProfileStrings.isEmpty();
         setEnabled(enableThis);
         if (enableThis)
             setDisablingAllowed(args.getBool(CH.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
@@ -135,95 +135,100 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     }
 
     /**
-     * Decouple weightings from PrepareContractionHierarchies as we need weightings for the
+     * Decouple CH profiles from PrepareContractionHierarchies as we need CH profiles for the
      * graphstorage and the graphstorage for the preparation.
      */
-    public CHAlgoFactoryDecorator addNodeBasedWeighting(Weighting weighting) {
-        nodeBasedWeightings.add(weighting);
-        return this;
-    }
-
-    public CHAlgoFactoryDecorator addEdgeBasedWeighting(Weighting weighting) {
-        edgeBasedWeightings.add(weighting);
-        return this;
-    }
-
-    public CHAlgoFactoryDecorator addWeighting(String weighting) {
-        weightingsAsStrings.add(weighting);
+    public CHAlgoFactoryDecorator addCHProfile(CHProfile chProfile) {
+        chProfiles.add(chProfile);
         return this;
     }
 
     public CHAlgoFactoryDecorator addPreparation(PrepareContractionHierarchies pch) {
-        // we want to make sure that edge- and node-based preparations are added in the same order as their corresponding
-        // weightings, but changing the order between edge- and node-based preparations is accepted
-        int index = 0;
-        for (PrepareContractionHierarchies p : preparations) {
-            if (p.isEdgeBased() == pch.isEdgeBased()) {
-                index++;
-            }
+        // we want to make sure that CH preparations are added in the same order as their corresponding profiles
+        if (preparations.size() >= chProfiles.size()) {
+            throw new IllegalStateException("You need to add the corresponding CH profiles before adding preparations.");
+        }
+        CHProfile expectedProfile = chProfiles.get(preparations.size());
+        if (!pch.getCHProfile().equals(expectedProfile)) {
+            throw new IllegalArgumentException("CH profile of preparation: " + pch + " needs to be identical to previously added CH profile: " + expectedProfile);
         }
-        List<Weighting> weightings = pch.isEdgeBased() ? edgeBasedWeightings : nodeBasedWeightings;
-        if (index >= weightings.size())
-            throw new IllegalStateException("Cannot access weighting for PrepareContractionHierarchies with " + pch.getWeighting()
-                    + ". Call add(Weighting) before");
-
-        Weighting expectedWeighting = weightings.get(index);
-        if (pch.getWeighting() != expectedWeighting)
-            throw new IllegalArgumentException("Weighting of PrepareContractionHierarchies " + pch
-                    + " needs to be identical to previously added " + expectedWeighting);
-
         preparations.add(pch);
         return this;
     }
 
-    public final boolean hasWeightings() {
-        return !nodeBasedWeightings.isEmpty() || !edgeBasedWeightings.isEmpty();
+    public final boolean hasCHProfiles() {
+        return !chProfiles.isEmpty();
+    }
+
+    public List<CHProfile> getCHProfiles() {
+        return chProfiles;
     }
 
-    public final List<Weighting> getNodeBasedWeightings() {
-        return nodeBasedWeightings;
+    public List<CHProfile> getNodeBasedCHProfiles() {
+        List<CHProfile> result = new ArrayList<>();
+        for (CHProfile chProfile : chProfiles) {
+            if (!chProfile.getTraversalMode().isEdgeBased()) {
+                result.add(chProfile);
+            }
+        }
+        return result;
     }
 
-    public final List<Weighting> getEdgeBasedWeightings() {
-        return edgeBasedWeightings;
+    public List<CHProfile> getEdgeBasedCHProfiles() {
+        List<CHProfile> result = new ArrayList<>();
+        for (CHProfile chProfile : chProfiles) {
+            if (chProfile.getTraversalMode().isEdgeBased()) {
+                result.add(chProfile);
+            }
+        }
+        return result;
     }
 
     public EdgeBasedCHMode getEdgeBasedCHMode() {
         return edgeBasedCHMode;
     }
 
-    public CHAlgoFactoryDecorator setWeightingsAsStrings(String... weightingNames) {
-        return setWeightingsAsStrings(Arrays.asList(weightingNames));
+    public List<String> getCHProfileStrings() {
+        if (chProfileStrings.isEmpty())
+            throw new IllegalStateException("Potential bug: chProfileStrings is empty");
+
+        return new ArrayList<>(chProfileStrings);
     }
 
-    public List<String> getWeightingsAsStrings() {
-        if (this.weightingsAsStrings.isEmpty())
-            throw new IllegalStateException("Potential bug: weightingsAsStrings is empty");
+    public CHAlgoFactoryDecorator setCHProfileStrings(String... profileStrings) {
+        return setCHProfilesAsStrings(Arrays.asList(profileStrings));
+    }
 
-        return new ArrayList<>(this.weightingsAsStrings);
+    /**
+     * @param profileStrings A list of multiple CH profile strings
+     * @see #addCHProfileAsString(String)
+     */
+    public CHAlgoFactoryDecorator setCHProfilesAsStrings(List<String> profileStrings) {
+        if (profileStrings.isEmpty())
+            throw new IllegalArgumentException("It is not allowed to pass an empty list of CH profile strings");
+
+        chProfileStrings.clear();
+        for (String profileString : profileStrings) {
+            profileString = toLowerCase(profileString);
+            profileString = profileString.trim();
+            addCHProfileAsString(profileString);
+        }
+        return this;
     }
 
     /**
      * Enables the use of contraction hierarchies to reduce query times. Enabled by default.
      *
-     * @param weightingList A list containing multiple weightings like: "fastest", "shortest" or
-     *                      your own weight-calculation type.
+     * @param profileString String representation of a CH profile like: "fastest", "shortest|edge_based=true",
+     *                       "fastest|u_turn_costs=30 or your own weight-calculation type.
      */
-    public CHAlgoFactoryDecorator setWeightingsAsStrings(List<String> weightingList) {
-        if (weightingList.isEmpty())
-            throw new IllegalArgumentException("It is not allowed to pass an emtpy weightingList");
-
-        weightingsAsStrings.clear();
-        for (String strWeighting : weightingList) {
-            strWeighting = toLowerCase(strWeighting);
-            strWeighting = strWeighting.trim();
-            addWeighting(strWeighting);
-        }
+    public CHAlgoFactoryDecorator addCHProfileAsString(String profileString) {
+        chProfileStrings.add(profileString);
         return this;
     }
 
-    private String getDefaultWeighting() {
-        return weightingsAsStrings.isEmpty() ? "fastest" : weightingsAsStrings.iterator().next();
+    private String getDefaultProfile() {
+        return chProfileStrings.isEmpty() ? "fastest" : chProfileStrings.iterator().next();
     }
 
     public List<PrepareContractionHierarchies> getPreparations() {
@@ -236,12 +241,11 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         if (!isEnabled() || disablingAllowed && disableCH)
             return defaultAlgoFactory;
 
-        List<PrepareContractionHierarchies> allPreparations = getPreparations();
-        if (allPreparations.isEmpty())
+        if (preparations.isEmpty())
             throw new IllegalStateException("No preparations added to this decorator");
 
         if (map.getWeighting().isEmpty())
-            map.setWeighting(getDefaultWeighting());
+            map.setWeighting(getDefaultProfile());
 
         return getPreparation(map);
     }
@@ -251,13 +255,13 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         List<String> entriesStrs = new ArrayList<>();
         boolean weightingMatchesButNotEdgeBased = false;
         for (PrepareContractionHierarchies p : getPreparations()) {
-            boolean weightingMatches = p.getWeighting().matches(map);
+            boolean weightingMatches = p.getCHProfile().getWeighting().matches(map);
             if (p.isEdgeBased() == edgeBased && weightingMatches)
                 return p;
             else if (weightingMatches)
                 weightingMatchesButNotEdgeBased = true;
 
-            entriesStrs.add(p.getWeighting() + "|" + (p.isEdgeBased() ? "edge" : "node"));
+            entriesStrs.add(p.getCHProfile().getWeighting() + "|" + (p.getCHProfile().isEdgeBased() ? "edge" : "node"));
         }
 
         String hint = weightingMatchesButNotEdgeBased
@@ -280,12 +284,12 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     }
 
     public void prepare(final StorableProperties properties) {
-        ExecutorCompletionService completionService = new ExecutorCompletionService<>(threadPool);
+        ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(threadPool);
         int counter = 0;
         for (final PrepareContractionHierarchies prepare : getPreparations()) {
             LOGGER.info((++counter) + "/" + getPreparations().size() + " calling " +
-                    (prepare.isEdgeBased() ? "edge" : "node") + "-based CH prepare.doWork for " + prepare.getWeighting() + " ... (" + getMemInfo() + ")");
-            final String name = AbstractWeighting.weightingToFileName(prepare.getWeighting(), prepare.isEdgeBased());
+                    "CH prepare.doWork for " + prepare.getCHProfile() + " ... (" + getMemInfo() + ")");
+            final String name = prepare.getCHProfile().toFileName();
             completionService.submit(new Runnable() {
                 @Override
                 public void run() {
@@ -295,7 +299,6 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
                     properties.put(CH.PREPARE + "date." + name, createFormatter().format(new Date()));
                 }
             }, name);
-
         }
 
         threadPool.shutdown();
@@ -313,21 +316,16 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     public void createPreparations(GraphHopperStorage ghStorage) {
         if (!isEnabled() || !getPreparations().isEmpty())
             return;
-        if (!hasWeightings())
-            throw new IllegalStateException("No CH weightings found");
+        if (!hasCHProfiles())
+            throw new IllegalStateException("No CH profiles found");
 
-        for (Weighting weighting : nodeBasedWeightings) {
-            addPreparation(createCHPreparation(ghStorage, weighting, TraversalMode.NODE_BASED));
-        }
-        for (Weighting weighting : edgeBasedWeightings) {
-            addPreparation(createCHPreparation(ghStorage, weighting, TraversalMode.EDGE_BASED));
+        for (CHProfile chProfile : chProfiles) {
+            addPreparation(createCHPreparation(ghStorage, chProfile));
         }
     }
 
-    private PrepareContractionHierarchies createCHPreparation(GraphHopperStorage ghStorage, Weighting weighting,
-                                                              TraversalMode traversalMode) {
-        PrepareContractionHierarchies tmpPrepareCH = PrepareContractionHierarchies.fromGraphHopperStorage(
-                ghStorage, weighting, traversalMode);
+    private PrepareContractionHierarchies createCHPreparation(GraphHopperStorage ghStorage, CHProfile chProfile) {
+        PrepareContractionHierarchies tmpPrepareCH = PrepareContractionHierarchies.fromGraphHopperStorage(ghStorage, chProfile);
         tmpPrepareCH.setParams(pMap);
         return tmpPrepareCH;
     }
diff --git a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
index edaa39feb..d36c70816 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
@@ -52,9 +52,8 @@ import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
  */
 public class PrepareContractionHierarchies extends AbstractAlgoPreparation implements RoutingAlgorithmFactory {
     private final Logger logger = LoggerFactory.getLogger(getClass());
+    private final CHProfile chProfile;
     private final PreparationWeighting prepareWeighting;
-    private final Weighting weighting;
-    private final TraversalMode traversalMode;
     private final CHGraph prepareGraph;
     private final Random rand = new Random(123);
     private final StopWatch allSW = new StopWatch();
@@ -74,16 +73,15 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     private PMap pMap = new PMap();
     private int checkCounter;
 
-    public PrepareContractionHierarchies(CHGraph chGraph, Weighting weighting, TraversalMode traversalMode) {
+    public PrepareContractionHierarchies(CHGraph chGraph) {
         this.prepareGraph = chGraph;
-        this.traversalMode = traversalMode;
-        this.weighting = weighting;
-        prepareWeighting = new PreparationWeighting(weighting);
-        this.params = Params.forTraversalMode(traversalMode);
+        this.chProfile = chGraph.getCHProfile();
+        prepareWeighting = new PreparationWeighting(chProfile.getWeighting());
+        this.params = Params.forTraversalMode(chProfile.getTraversalMode());
     }
 
-    public static PrepareContractionHierarchies fromGraphHopperStorage(GraphHopperStorage ghStorage, Weighting weighting, TraversalMode traversalMode) {
-        return new PrepareContractionHierarchies(ghStorage.getCHGraph(weighting), weighting, traversalMode);
+    public static PrepareContractionHierarchies fromGraphHopperStorage(GraphHopperStorage ghStorage, CHProfile chProfile) {
+        return new PrepareContractionHierarchies(ghStorage.getCHGraph(chProfile));
     }
 
     public PrepareContractionHierarchies setParams(PMap pMap) {
@@ -150,7 +148,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     private AbstractBidirAlgo doCreateAlgo(Graph graph, AlgorithmOptions opts) {
-        if (traversalMode.isEdgeBased()) {
+        if (chProfile.isEdgeBased()) {
             return createAlgoEdgeBased(graph, opts);
         } else {
             return createAlgoNodeBased(graph, opts);
@@ -184,7 +182,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     public boolean isEdgeBased() {
-        return traversalMode.isEdgeBased();
+        return chProfile.isEdgeBased();
     }
 
     private void initFromGraph() {
@@ -201,7 +199,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
         //   but we need the additional oldPriorities array to keep the old value which is necessary for the update method
         sortedNodes = new GHTreeMapComposed();
         oldPriorities = new float[prepareGraph.getNodes()];
-        nodeContractor = createNodeContractor(prepareGraph, traversalMode);
+        nodeContractor = createNodeContractor(prepareGraph, chProfile.getTraversalMode());
         nodeContractor.initFromGraph();
     }
 
@@ -426,7 +424,11 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
     }
 
     public Weighting getWeighting() {
-        return weighting;
+        return chProfile.getWeighting();
+    }
+
+    public CHProfile getCHProfile() {
+        return chProfile;
     }
 
     private String getTimesAsString() {
@@ -449,7 +451,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
 
     @Override
     public String toString() {
-        return traversalMode.isEdgeBased() ? "prepare|dijkstrabi|edge|ch" : "prepare|dijkstrabi|ch";
+        return chProfile.isEdgeBased() ? "prepare|dijkstrabi|edge|ch" : "prepare|dijkstrabi|ch";
     }
 
     private NodeContractor createNodeContractor(Graph graph, TraversalMode traversalMode) {
@@ -457,7 +459,7 @@ public class PrepareContractionHierarchies extends AbstractAlgoPreparation imple
             TurnWeighting chTurnWeighting = createTurnWeightingForEdgeBased(graph);
             return new EdgeBasedNodeContractor(prepareGraph, chTurnWeighting, pMap);
         } else {
-            return new NodeBasedNodeContractor(prepareGraph, weighting, pMap);
+            return new NodeBasedNodeContractor(prepareGraph, chProfile.getWeighting(), pMap);
         }
     }
 
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
index b93eae331..12a2499c3 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
@@ -270,13 +270,13 @@ public class LMAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
      * @see com.graphhopper.routing.ch.CHAlgoFactoryDecorator#prepare(StorableProperties) for a very similar method
      */
     public boolean loadOrDoWork(final StorableProperties properties) {
-        ExecutorCompletionService completionService = new ExecutorCompletionService<>(threadPool);
+        ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(threadPool);
         int counter = 0;
         final AtomicBoolean prepared = new AtomicBoolean(false);
         for (final PrepareLandmarks plm : preparations) {
             counter++;
             final int tmpCounter = counter;
-            final String name = AbstractWeighting.weightingToFileName(plm.getWeighting(), false);
+            final String name = AbstractWeighting.weightingToFileName(plm.getWeighting());
             completionService.submit(new Runnable() {
                 @Override
                 public void run() {
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
index ae9090d9b..793db907e 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
@@ -106,7 +106,7 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
         // Edge based is not really necessary because when adding turn costs while routing we can still
         // use the node based traversal as this is a smaller weight approximation and will still produce correct results
         this.traversalMode = TraversalMode.NODE_BASED;
-        final String name = AbstractWeighting.weightingToFileName(weighting, false);
+        final String name = AbstractWeighting.weightingToFileName(weighting);
         this.landmarkWeightDA = dir.find("landmarks_" + name);
 
         this.landmarks = landmarks;
diff --git a/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
index 27ac7ebac..6fa3f8745 100644
--- a/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
@@ -64,7 +64,7 @@ public abstract class AbstractFlagEncoder implements FlagEncoder {
     protected BooleanEncodedValue roundaboutEnc;
     protected DecimalEncodedValue speedEncoder;
     protected PMap properties;
-    // This value determines the maximal possible speed of any road regardless the maxspeed value
+    // This value determines the maximal possible speed of any road regardless of the maxspeed value
     // lower values allow more compact representation of the routing graph
     protected int maxPossibleSpeed;
     /* Edge Flag Encoder fields */
diff --git a/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java
index cd4760a06..d2319b57d 100644
--- a/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/BikeCommonFlagEncoder.java
@@ -45,7 +45,7 @@ abstract public class BikeCommonFlagEncoder extends AbstractFlagEncoder {
      */
     public static final int K_UNPAVED = 100;
     protected static final int PUSHING_SECTION_SPEED = 4;
-    // Pushing section heighways are parts where you need to get off your bike and push it (German: Schiebestrecke)
+    // Pushing section highways are parts where you need to get off your bike and push it (German: Schiebestrecke)
     protected final HashSet<String> pushingSectionsHighways = new HashSet<>();
     protected final HashSet<String> oppositeLanes = new HashSet<>();
     protected final Set<String> preferHighwayTags = new HashSet<>();
diff --git a/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java
index 9e4ed568b..489a9f863 100644
--- a/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/BikeFlagEncoder.java
@@ -36,8 +36,8 @@ public class BikeFlagEncoder extends BikeCommonFlagEncoder {
     }
 
     public BikeFlagEncoder(PMap properties) {
-        this((int) properties.getLong("speed_bits", 4),
-                properties.getLong("speed_factor", 2),
+        this(properties.getInt("speed_bits", 4),
+                properties.getInt("speed_factor", 2),
                 properties.getBool("turn_costs", false) ? 1 : 0);
         this.properties = properties;
         this.setBlockFords(properties.getBool("block_fords", true));
diff --git a/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java
index d8f8a9463..da691baaa 100644
--- a/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/MotorcycleFlagEncoder.java
@@ -50,7 +50,7 @@ public class MotorcycleFlagEncoder extends CarFlagEncoder {
     }
 
     public MotorcycleFlagEncoder(PMap properties) {
-        this((int) properties.getLong("speed_bits", 5),
+        this(properties.getInt("speed_bits", 5),
                 properties.getDouble("speed_factor", 5),
                 properties.getBool("turn_costs", false) ? 1 : 0
         );
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
index 160921d85..087fac389 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java
@@ -104,8 +104,8 @@ public abstract class AbstractWeighting implements Weighting {
     /**
      * Replaces all characters which are not numbers, characters or underscores with underscores
      */
-    public static String weightingToFileName(Weighting w, boolean edgeBased) {
-        return toLowerCase(w.toString()).replaceAll("\\|", "_") + (edgeBased ? "_edge" : "_node");
+    public static String weightingToFileName(Weighting w) {
+        return toLowerCase(w.toString()).replaceAll("\\|", "_");
     }
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraph.java b/core/src/main/java/com/graphhopper/storage/CHGraph.java
index ca669fed8..5d492d0cb 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraph.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraph.java
@@ -45,6 +45,11 @@ public interface CHGraph extends Graph {
      */
     int getLevel(int nodeId);
 
+    /**
+     * Returns the profile of this CH graph. This is used to identify the CH graph.
+     */
+    CHProfile getCHProfile();
+
     boolean isShortcut(int edgeId);
 
     /**
diff --git a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
index 405973f90..e2bcb57c3 100644
--- a/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
+++ b/core/src/main/java/com/graphhopper/storage/CHGraphImpl.java
@@ -52,10 +52,9 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     final DataAccess shortcuts;
     final DataAccess nodesCH;
     final int scDirMask = PrepareEncoder.getScDirMask();
-    private final boolean edgeBased;
+    private final CHProfile chProfile;
     private final BaseGraph baseGraph;
     private final CHEdgeAccess chEdgeAccess;
-    private final Weighting weighting;
     int N_CH_REF;
     int shortcutEntryBytes;
     // the nodesCH storage is limited via baseGraph.nodeCount too
@@ -66,21 +65,20 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
     private int shortcutCount = 0;
     private boolean isReadyForContraction;
 
-    CHGraphImpl(Weighting w, Directory dir, final BaseGraph baseGraph, boolean edgeBased) {
-        if (w == null)
+    CHGraphImpl(CHProfile chProfile, Directory dir, final BaseGraph baseGraph) {
+        if (chProfile.getWeighting() == null)
             throw new IllegalStateException("Weighting for CHGraph cannot be null");
-
-        this.weighting = w;
+        this.chProfile = chProfile;
         this.baseGraph = baseGraph;
-        final String name = AbstractWeighting.weightingToFileName(w, edgeBased);
-        this.edgeBased = edgeBased;
+        final String name = chProfile.toFileName();
         this.nodesCH = dir.find("nodes_ch_" + name, DAType.getPreferredInt(dir.getDefaultType()));
         this.shortcuts = dir.find("shortcuts_" + name, DAType.getPreferredInt(dir.getDefaultType()));
         this.chEdgeAccess = new CHEdgeAccess(name);
     }
 
-    public final Weighting getWeighting() {
-        return weighting;
+    @Override
+    public CHProfile getCHProfile() {
+        return chProfile;
     }
 
     @Override
@@ -124,7 +122,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
     @Override
     public int shortcutEdgeBased(int a, int b, int accessFlags, double weight, double distance, int skippedEdge1, int skippedEdge2, int origFirst, int origLast) {
-        if (!edgeBased) {
+        if (!chProfile.isEdgeBased()) {
             throw new IllegalStateException("Edge-based shortcuts should only be added when CHGraph is edge-based");
         }
         int scId = shortcut(a, b, accessFlags, weight, distance, skippedEdge1, skippedEdge2);
@@ -333,7 +331,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         // shortcuts
         S_SKIP_EDGE1 = ea.E_FLAGS + 4;
         S_SKIP_EDGE2 = S_SKIP_EDGE1 + 4;
-        if (edgeBased) {
+        if (chProfile.isEdgeBased()) {
             S_ORIG_FIRST = S_SKIP_EDGE2 + 4;
             S_ORIG_LAST = S_ORIG_FIRST + 4;
             shortcutEntryBytes = S_ORIG_LAST + 4;
@@ -393,7 +391,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
     @Override
     public String toString() {
-        return "CHGraph|" + getWeighting().toString();
+        return "CHGraph|" + chProfile.toString();
     }
 
     public void debugPrint() {
@@ -411,7 +409,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         String formatShortcutsBase = "%12s | %12s | %12s | %12s | %12s | %12s | %12s | %12s | %12s";
         String formatShortcutExt = " | %12s | %12s";
         String header = String.format(Locale.ROOT, formatShortcutsBase, "#", "E_NODEA", "E_NODEB", "E_LINKA", "E_LINKB", "E_DIST", "E_FLAGS", "S_SKIP_EDGE1", "S_SKIP_EDGE2");
-        if (edgeBased) {
+        if (chProfile.isEdgeBased()) {
             header += String.format(Locale.ROOT, formatShortcutExt, "S_ORIG_FIRST", "S_ORIG_LAST");
         }
         System.out.println(header);
@@ -427,7 +425,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
                     chEdgeAccess.getShortcutFlags(edgePointer),
                     shortcuts.getInt(edgePointer + S_SKIP_EDGE1),
                     shortcuts.getInt(edgePointer + S_SKIP_EDGE2));
-            if (edgeBased) {
+            if (chProfile.isEdgeBased()) {
                 edgeString += String.format(Locale.ROOT, formatShortcutExt,
                         shortcuts.getInt(edgePointer + S_ORIG_FIRST),
                         shortcuts.getInt(edgePointer + S_ORIG_LAST));
@@ -519,7 +517,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         @Override
         public int getOrigEdgeFirst() {
-            if (!isShortcut() || !edgeBased) {
+            if (!isShortcut() || !chProfile.isEdgeBased()) {
                 return getEdge();
             }
             return shortcuts.getInt(edgePointer + S_ORIG_FIRST);
@@ -527,7 +525,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         @Override
         public int getOrigEdgeLast() {
-            if (!isShortcut() || !edgeBased) {
+            if (!isShortcut() || !chProfile.isEdgeBased()) {
                 return getEdge();
             }
             return shortcuts.getInt(edgePointer + S_ORIG_LAST);
@@ -593,7 +591,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         private void checkShortcutAndEdgeBased(String method) {
             checkShortcut(true, method);
-            if (!edgeBased) {
+            if (!chProfile.isEdgeBased()) {
                 throw new IllegalStateException("Method " + method + " only allowed when CH graph is configured for edge based traversal");
             }
         }
@@ -773,7 +771,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
 
         private void checkShortcutAndEdgeBased(String method) {
             checkShortcut(true, method);
-            if (!edgeBased) {
+            if (!chProfile.isEdgeBased()) {
                 throw new IllegalStateException("Method " + method + " not supported when turn costs are disabled");
             }
         }
@@ -868,7 +866,7 @@ public class CHGraphImpl implements CHGraph, Storable<CHGraph> {
         }
 
         public void setFirstAndLastOrigEdges(long edgePointer, int origFirst, int origLast) {
-            if (!edgeBased) {
+            if (!chProfile.isEdgeBased()) {
                 throw new IllegalStateException("Edge-based shortcuts should only be added when CHGraph is edge-based");
             }
             shortcuts.setInt(edgePointer + S_ORIG_FIRST, origFirst);
diff --git a/core/src/main/java/com/graphhopper/storage/CHProfile.java b/core/src/main/java/com/graphhopper/storage/CHProfile.java
new file mode 100644
index 000000000..3ade3bd71
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/storage/CHProfile.java
@@ -0,0 +1,69 @@
+package com.graphhopper.storage;
+
+import com.graphhopper.routing.util.TraversalMode;
+import com.graphhopper.routing.weighting.AbstractWeighting;
+import com.graphhopper.routing.weighting.Weighting;
+
+import java.util.Objects;
+
+/**
+ * Specifies all properties of a CH routing profile. Generally these properties cannot be changed after the CH
+ * pre-processing is finished and are stored on disk along with the prepared graph data.
+ *
+ * @author easbar
+ */
+public class CHProfile {
+    private final Weighting weighting;
+    private final boolean edgeBased;
+
+    public static CHProfile nodeBased(Weighting weighting) {
+        return new CHProfile(weighting, TraversalMode.NODE_BASED);
+    }
+
+    public static CHProfile edgeBased(Weighting weighting) {
+        return new CHProfile(weighting, TraversalMode.EDGE_BASED);
+    }
+
+    public CHProfile(Weighting weighting, TraversalMode traversalMode) {
+        this(weighting, traversalMode.isEdgeBased());
+    }
+
+    public CHProfile(Weighting weighting, boolean edgeBased) {
+        this.weighting = weighting;
+        this.edgeBased = edgeBased;
+    }
+
+    public Weighting getWeighting() {
+        return weighting;
+    }
+
+    public boolean isEdgeBased() {
+        return edgeBased;
+    }
+
+    public TraversalMode getTraversalMode() {
+        return edgeBased ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED;
+    }
+
+    public String toFileName() {
+        return AbstractWeighting.weightingToFileName(weighting) + "_" + (edgeBased ? "edge" : "node");
+    }
+
+    public String toString() {
+        return weighting + "|edge_based=" + edgeBased;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CHProfile chProfile = (CHProfile) o;
+        return edgeBased == chProfile.edgeBased &&
+                Objects.equals(weighting, chProfile.weighting);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(weighting, edgeBased);
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java b/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
index 0d2358995..245baec86 100644
--- a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
+++ b/core/src/main/java/com/graphhopper/storage/GraphBuilder.java
@@ -18,10 +18,11 @@
 package com.graphhopper.storage;
 
 import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.weighting.Weighting;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
 
 /**
  * For now this is just a helper class to quickly create a {@link GraphHopperStorage}
@@ -36,22 +37,28 @@ public class GraphBuilder {
     private boolean store;
     private boolean elevation;
     private boolean turnCosts;
-    private boolean edgeBasedCH;
     private long byteCapacity = 100;
-    private Weighting singleCHWeighting;
+    private List<CHProfile> chProfiles = Collections.emptyList();
 
     public GraphBuilder(EncodingManager encodingManager) {
         this.encodingManager = encodingManager;
     }
 
     /**
-     * This method enables creating a CHGraph with the specified weighting.
+     * This method enables creating CHGraphs with the specified CHProfiles
      */
-    public GraphBuilder setCHGraph(Weighting singleCHWeighting) {
-        this.singleCHWeighting = singleCHWeighting;
+    public GraphBuilder setCHProfiles(List<CHProfile> chProfiles) {
+        if (chProfiles.size() != new HashSet<>(chProfiles).size()) {
+            throw new IllegalArgumentException("Given CH profiles contain duplicates, given: " + chProfiles);
+        }
+        this.chProfiles = chProfiles;
         return this;
     }
 
+    public GraphBuilder setCHProfiles(CHProfile... chProfiles) {
+        return setCHProfiles(Arrays.asList(chProfiles));
+    }
+
     public GraphBuilder setLocation(String location) {
         this.location = location;
         return this;
@@ -82,11 +89,6 @@ public class GraphBuilder {
         return this;
     }
 
-    public GraphBuilder setEdgeBasedCH(boolean edgeBasedCH) {
-        this.edgeBasedCH = edgeBasedCH;
-        return this;
-    }
-
     public boolean hasElevation() {
         return elevation;
     }
@@ -94,8 +96,8 @@ public class GraphBuilder {
     /**
      * Creates a CHGraph
      */
-    public CHGraph chGraphCreate(Weighting singleCHWeighting) {
-        return setCHGraph(singleCHWeighting).create().getCHGraph(singleCHWeighting);
+    public CHGraph chGraphCreate(CHProfile chProfile) {
+        return setCHProfiles(chProfile).create().getCHGraph();
     }
 
     /**
@@ -112,11 +114,9 @@ public class GraphBuilder {
                 new TurnCostExtension() :
                 new TurnCostExtension.NoOpExtension();
 
-        return singleCHWeighting == null ?
+        return chProfiles.isEmpty() ?
                 new GraphHopperStorage(dir, encodingManager, elevation, graphExtension) :
-                edgeBasedCH ?
-                        new GraphHopperStorage(Collections.<Weighting>emptyList(), Arrays.asList(singleCHWeighting), dir, encodingManager, elevation, graphExtension) :
-                        new GraphHopperStorage(Arrays.asList(singleCHWeighting), Collections.<Weighting>emptyList(), dir, encodingManager, elevation, graphExtension);
+                new GraphHopperStorage(chProfiles, dir, encodingManager, elevation, graphExtension);
     }
 
     /**
diff --git a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java b/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
index 4a69f1800..fc9cffd47 100644
--- a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
+++ b/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java
@@ -46,20 +46,17 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
     private final StorableProperties properties;
     private final BaseGraph baseGraph;
     // same flush order etc
-    private final Collection<CHGraphImpl> nodeBasedCHGraphs = new ArrayList<>(5);
-    private final Collection<CHGraphImpl> edgeBasedCHGraphs = new ArrayList<>(5);
+    private final Collection<CHGraphImpl> chGraphs;
 
     public GraphHopperStorage(Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
-        this(Collections.<Weighting>emptyList(), Collections.<Weighting>emptyList(), dir, encodingManager, withElevation, extendedStorage);
+        this(Collections.<CHProfile>emptyList(), dir, encodingManager, withElevation, extendedStorage);
     }
 
-    public GraphHopperStorage(List<? extends Weighting> nodeBasedCHWeightings, Directory dir, EncodingManager encodingManager,
-                              boolean withElevation, GraphExtension extendedStorage) {
-        this(nodeBasedCHWeightings, Collections.<Weighting>emptyList(), dir, encodingManager, withElevation, extendedStorage);
+    public GraphHopperStorage(Collection<? extends Weighting> nodeBasedCHWeightings, Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
+        this(createProfilesForWeightings(nodeBasedCHWeightings), dir, encodingManager, withElevation, extendedStorage);
     }
 
-    public GraphHopperStorage(List<? extends Weighting> nodeBasedCHWeightings, List<? extends Weighting> edgeBasedCHWeightings,
-                              Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
+    public GraphHopperStorage(List<CHProfile> chProfiles, Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage) {
         if (extendedStorage == null)
             throw new IllegalArgumentException("GraphExtension cannot be null, use NoOpExtension");
 
@@ -86,11 +83,9 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
         };
 
         baseGraph = new BaseGraph(dir, encodingManager, withElevation, listener, extendedStorage);
-        for (Weighting w : nodeBasedCHWeightings) {
-            nodeBasedCHGraphs.add(new CHGraphImpl(w, dir, baseGraph, false));
-        }
-        for (Weighting w : edgeBasedCHWeightings) {
-            edgeBasedCHGraphs.add(new CHGraphImpl(w, dir, baseGraph, true));
+        this.chGraphs = new ArrayList<>(chProfiles.size());
+        for (CHProfile chProfile : chProfiles) {
+            chGraphs.add(new CHGraphImpl(chProfile, dir, baseGraph));
         }
     }
 
@@ -99,52 +94,54 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
         if (chGraphs.isEmpty()) {
             throw new IllegalStateException("There is no CHGraph");
         } else if (chGraphs.size() > 1) {
-            throw new IllegalStateException("There are multiple CHGraphs, use getCHGraph(Weighting) to retrieve a specific one");
+            throw new IllegalStateException("There are multiple CHGraphs, use getCHGraph(CHProfile) to retrieve a specific one");
         } else {
             return chGraphs.iterator().next();
         }
     }
 
     /**
-     * @return the {@link CHGraph} for the specified {@link Weighting}. The graph is identified by the weighting using
-     * reference identity (==)!
+     * @return the {@link CHGraph} for the specified {@link CHProfile}
      */
-    public CHGraph getCHGraph(Weighting weighting) {
+    public CHGraph getCHGraph(CHProfile profile) {
         Collection<CHGraphImpl> chGraphs = getAllCHGraphs();
         if (chGraphs.isEmpty())
             throw new IllegalStateException("There is no CHGraph");
 
-        if (weighting == null)
-            throw new IllegalStateException("Cannot find CHGraph with null weighting");
+        if (profile == null)
+            throw new IllegalStateException("Cannot find CHGraph with null CHProfile");
 
-        List<Weighting> existing = new ArrayList<>();
+        List<CHProfile> existing = new ArrayList<>();
         for (CHGraphImpl cg : chGraphs) {
-            if (cg.getWeighting() == weighting)
+            if (cg.getCHProfile().equals(profile))
                 return cg;
-            existing.add(cg.getWeighting());
+            existing.add(cg.getCHProfile());
         }
 
-        throw new IllegalStateException("Cannot find CHGraph for the specified weighting: " + weighting + ", existing:" + existing);
+        throw new IllegalStateException("Cannot find CHGraph for the specified profile: " + profile + ", existing:" + existing);
     }
 
     public boolean isCHPossible() {
         return !getAllCHGraphs().isEmpty();
     }
 
-    public List<Weighting> getNodeBasedCHWeightings() {
-        return getWeightingsFromGraphs(nodeBasedCHGraphs);
-    }
-
-    public List<Weighting> getEdgeBasedCHWeightings() {
-        return getWeightingsFromGraphs(edgeBasedCHGraphs);
+    public List<CHProfile> getCHProfiles() {
+        List<CHProfile> result = new ArrayList<>(chGraphs.size());
+        for (CHGraphImpl chGraph : chGraphs) {
+            result.add(chGraph.getCHProfile());
+        }
+        return result;
     }
 
-    private List<Weighting> getWeightingsFromGraphs(Collection<CHGraphImpl> graphs) {
-        List<Weighting> list = new ArrayList<>(graphs.size());
-        for (CHGraphImpl cg : graphs) {
-            list.add(cg.getWeighting());
+    public List<CHProfile> getCHProfiles(boolean edgeBased) {
+        List<CHProfile> result = new ArrayList<>();
+        List<CHProfile> chProfiles = getCHProfiles();
+        for (CHProfile profile : chProfiles) {
+            if (edgeBased == profile.isEdgeBased()) {
+                result.add(profile);
+            }
         }
-        return list;
+        return result;
     }
 
     /**
@@ -191,8 +188,7 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
             cg.create(byteCount);
         }
 
-        properties.put("graph.ch.weightings", getNodeBasedCHWeightings().toString());
-        properties.put("graph.ch.edge.weightings", getEdgeBasedCHWeightings().toString());
+        properties.put("graph.ch.profiles", getCHProfiles().toString());
         return this;
     }
 
@@ -284,28 +280,19 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
     }
 
     private void checkIfConfiguredAndLoadedWeightingsCompatible() {
-        String loadedStrNode = properties.get("graph.ch.weightings");
-        List<String> loadedNode = parseList(loadedStrNode);
-        String loadedStrEdge = properties.get("graph.ch.edge.weightings");
-        List<String> loadedEdge = parseList(loadedStrEdge);
-        List<Weighting> configuredNode = getNodeBasedCHWeightings();
-        List<Weighting> configuredEdge = getEdgeBasedCHWeightings();
+        String loadedStr = properties.get("graph.ch.profiles");
+        List<String> loaded = parseList(loadedStr);
+        List<CHProfile> configured = getCHProfiles();
         // todo: not entirely sure here. when no ch is configured at all (neither edge nor node), but there are any
         // ch graphs (edge or node) we throw an error ? previously we threw an error when no ch weighting was configured
         // even though there was a ch graph.
-        if ((configuredNode.isEmpty() && configuredEdge.isEmpty()) && (!loadedNode.isEmpty() || !loadedEdge.isEmpty())) {
-            throw new IllegalStateException("You loaded a CH graph, but you did not specify graph.ch.weightings");
-        }
-        for (Weighting w : configuredNode) {
-            if (!loadedNode.contains(w.toString())) {
-                throw new IllegalStateException("Configured weighting: " + w.toString() + " is not contained in loaded weightings for CH" + loadedStrNode + ".\n" +
-                        "You configured graph.ch.weightings: " + configuredNode);
-            }
+        if (configured.isEmpty() && !loaded.isEmpty()) {
+            throw new IllegalStateException("You loaded a CH graph, but you did not specify any CH weightings in prepare.ch.weightings");
         }
-        for (Weighting w : configuredEdge) {
-            if (!loadedEdge.contains(w.toString())) {
-                throw new IllegalStateException("Configured weighting: " + w.toString() + " is not contained in loaded weightings for edge-based CH" + loadedStrEdge + ".\n" +
-                        "You configured graph.ch.edge.weightings: " + configuredEdge);
+        for (CHProfile chProfile : configured) {
+            if (!loaded.contains(chProfile.toString())) {
+                throw new IllegalStateException("Configured CH profile: " + chProfile.toString() + " is not contained in loaded weightings for CH" + loadedStr + ".\n" +
+                        "You configured: " + configured);
             }
         }
     }
@@ -476,12 +463,14 @@ public final class GraphHopperStorage implements GraphStorage, Graph {
     }
 
     private Collection<CHGraphImpl> getAllCHGraphs() {
-        // todo: this method is only used to have a 'view' on the two collections. we could also create this only once
-        // as long as the graph collections are only modified in the constructor (otherwise we would have to make sure
-        // the lists are in sync). another option would be something like guava concat.
-        List<CHGraphImpl> result = new ArrayList<>(nodeBasedCHGraphs.size() + edgeBasedCHGraphs.size());
-        result.addAll(nodeBasedCHGraphs);
-        result.addAll(edgeBasedCHGraphs);
+        return chGraphs;
+    }
+
+    private static List<CHProfile> createProfilesForWeightings(Collection<? extends Weighting> weightings) {
+        List<CHProfile> result = new ArrayList<>(weightings.size());
+        for (Weighting weighting : weightings) {
+            result.add(CHProfile.nodeBased(weighting));
+        }
         return result;
     }
 }
diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java
index 6fff92e30..6afdaf496 100644
--- a/core/src/main/java/com/graphhopper/util/GHUtility.java
+++ b/core/src/main/java/com/graphhopper/util/GHUtility.java
@@ -406,7 +406,7 @@ public class GHUtility {
         Directory outdir = guessDirectory(store);
         boolean is3D = store.getNodeAccess().is3D();
 
-        return new GraphHopperStorage(store.getNodeBasedCHWeightings(), store.getEdgeBasedCHWeightings(), outdir, store.getEncodingManager(),
+        return new GraphHopperStorage(store.getCHProfiles(), outdir, store.getEncodingManager(),
                 is3D, store.getExtension()).
                 create(store.getNodes());
     }
diff --git a/reader-gtfs/pom.xml b/reader-gtfs/pom.xml
index bc3435462..369b8d6d8 100644
--- a/reader-gtfs/pom.xml
+++ b/reader-gtfs/pom.xml
@@ -11,7 +11,7 @@
 
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>    	
+        <artifactId>graphhopper-parent</artifactId>
         <version>0.13-SNAPSHOT</version>
     </parent>
 
diff --git a/reader-osm/pom.xml b/reader-osm/pom.xml
index 03d0a48b3..608aebf94 100644
--- a/reader-osm/pom.xml
+++ b/reader-osm/pom.xml
@@ -11,7 +11,7 @@
 
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>    	
+        <artifactId>graphhopper-parent</artifactId>
         <version>0.13-SNAPSHOT</version>
     </parent>
 
diff --git a/tools/pom.xml b/tools/pom.xml
index bc70bbb8b..76f38ce80 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -11,11 +11,16 @@
 
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>    	
+        <artifactId>graphhopper-parent</artifactId>
         <version>0.13-SNAPSHOT</version>
     </parent>
 
     <dependencies>
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>graphhopper-core</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
         <dependency>
             <groupId>com.graphhopper</groupId>
             <artifactId>graphhopper-reader-osm</artifactId>
diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java
index 76932bbd5..c10e355fa 100644
--- a/tools/src/main/java/com/graphhopper/tools/Measurement.java
+++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java
@@ -26,11 +26,7 @@ import com.graphhopper.coll.GHBitSetImpl;
 import com.graphhopper.reader.DataReader;
 import com.graphhopper.reader.osm.GraphHopperOSM;
 import com.graphhopper.routing.util.*;
-import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.Graph;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.NodeAccess;
+import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.util.*;
 import com.graphhopper.util.Parameters.Algorithms;
@@ -93,14 +89,14 @@ public class Measurement {
                 // note that we measure the total time of all (possibly edge&node) CH preparations
                 put(Parameters.CH.PREPARE + "time", sw.stop().getMillis());
                 int edges = getGraphHopperStorage().getEdges();
-                if (!getCHFactoryDecorator().getNodeBasedWeightings().isEmpty()) {
-                    Weighting weighting = getCHFactoryDecorator().getNodeBasedWeightings().get(0);
-                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(weighting).getEdges();
+                if (!getCHFactoryDecorator().getNodeBasedCHProfiles().isEmpty()) {
+                    CHProfile chProfile = getCHFactoryDecorator().getNodeBasedCHProfiles().get(0);
+                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(chProfile).getEdges();
                     put(Parameters.CH.PREPARE + "node.shortcuts", edgesAndShortcuts - edges);
                 }
-                if (!getCHFactoryDecorator().getEdgeBasedWeightings().isEmpty()) {
-                    Weighting weighting = getCHFactoryDecorator().getEdgeBasedWeightings().get(0);
-                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(weighting).getEdges();
+                if (!getCHFactoryDecorator().getEdgeBasedCHProfiles().isEmpty()) {
+                    CHProfile chProfile = getCHFactoryDecorator().getEdgeBasedCHProfiles().get(0);
+                    int edgesAndShortcuts = getGraphHopperStorage().getCHGraph(chProfile).getEdges();
                     put(Parameters.CH.PREPARE + "edge.shortcuts", edgesAndShortcuts - edges);
                 }
             }
@@ -173,16 +169,16 @@ public class Measurement {
 
                 isLM = false;
                 System.gc();
-                if (!hopper.getCHFactoryDecorator().getNodeBasedWeightings().isEmpty()) {
-                    Weighting weighting = hopper.getCHFactoryDecorator().getNodeBasedWeightings().get(0);
-                    CHGraph lg = g.getCHGraph(weighting);
+                if (!hopper.getCHFactoryDecorator().getNodeBasedCHProfiles().isEmpty()) {
+                    CHProfile chProfile = hopper.getCHFactoryDecorator().getNodeBasedCHProfiles().get(0);
+                    CHGraph lg = g.getCHGraph(chProfile);
                     fillAllowedEdges(lg.getAllEdges(), allowedEdges);
                     printMiscUnitPerfTests(lg, isCH, encoder, count * 100, allowedEdges);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH", vehicleStr, true, -1, true, false);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_no_sod", vehicleStr, true, -1, false, false);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_no_instr", vehicleStr, false, -1, true, false);
                 }
-                if (!hopper.getCHFactoryDecorator().getEdgeBasedWeightings().isEmpty()) {
+                if (!hopper.getCHFactoryDecorator().getEdgeBasedCHProfiles().isEmpty()) {
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_edge", vehicleStr, true, -1, false, true);
                     printTimeOfRouteQuery(hopper, isCH, isLM, count, "routingCH_edge_no_instr", vehicleStr, false, -1, false, true);
                 }
diff --git a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
index 55a98fcd3..53876812b 100644
--- a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
+++ b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
@@ -29,10 +29,7 @@ import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.Graph;
-import com.graphhopper.storage.NodeAccess;
-import com.graphhopper.storage.SPTEntry;
+import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.storage.index.QueryResult;
 import com.graphhopper.util.*;
@@ -97,8 +94,9 @@ public class MiniGraphUI {
         boolean ch = true;
         if (ch) {
             map.put(Parameters.Landmark.DISABLE, true);
-            weighting = hopper.getCHFactoryDecorator().getNodeBasedWeightings().get(0);
-            routingGraph = hopper.getGraphHopperStorage().getCHGraph(weighting);
+            CHProfile chProfile = hopper.getCHFactoryDecorator().getNodeBasedCHProfiles().get(0);
+            weighting = chProfile.getWeighting();
+            routingGraph = hopper.getGraphHopperStorage().getCHGraph(chProfile);
 
             final RoutingAlgorithmFactory tmpFactory = hopper.getAlgorithmFactory(map);
             algoFactory = new RoutingAlgorithmFactory() {


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java b/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
index b578638ca..c066e9df9 100644
--- a/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AStarBidirectionEdgeCHTest.java
@@ -19,16 +19,12 @@ package com.graphhopper.routing;
 
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.RAMDirectory;
-import com.graphhopper.storage.TurnCostExtension;
+import com.graphhopper.storage.*;
 import com.graphhopper.util.Parameters;
 
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.List;
 
 public class AStarBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTester {
@@ -41,20 +37,24 @@ public class AStarBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTester {
 
     @Override
     protected CHGraph getGraph(GraphHopperStorage ghStorage, Weighting weighting) {
-        return ghStorage.getCHGraph(weighting);
+        return ghStorage.getCHGraph(CHProfile.edgeBased(weighting));
     }
 
     @Override
     protected GraphHopperStorage createGHStorage(
             EncodingManager em, List<? extends Weighting> weightings, boolean is3D) {
-        return new GraphHopperStorage(Collections.<Weighting>emptyList(), weightings, new RAMDirectory(), em, is3D, new TurnCostExtension()).create(1000);
+        List<CHProfile> chProfiles = new ArrayList<>(weightings.size());
+        for (Weighting w : weightings) {
+            chProfiles.add(CHProfile.edgeBased(w));
+        }
+        return new GraphHopperStorage(chProfiles, new RAMDirectory(), em, is3D, new TurnCostExtension()).create(1000);
     }
 
     @Override
     public RoutingAlgorithmFactory createFactory(GraphHopperStorage ghStorage, AlgorithmOptions opts) {
         ghStorage.freeze();
         PrepareContractionHierarchies ch = PrepareContractionHierarchies.fromGraphHopperStorage(
-                ghStorage, opts.getWeighting(), TraversalMode.EDGE_BASED);
+                ghStorage, CHProfile.edgeBased(opts.getWeighting()));
         ch.doWork();
         return ch;
     }
diff --git a/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java b/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
index 584dcbdf7..d2209e47a 100644
--- a/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
+++ b/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
@@ -238,8 +238,7 @@ public abstract class AbstractRoutingAlgorithmTester {
     public void testCalcFastestPath() {
         GraphHopperStorage graphShortest = createGHStorage(false);
         initDirectedAndDiffSpeed(graphShortest, carEncoder);
-        Path p1 = createAlgo(graphShortest, defaultOpts).
-                calcPath(0, 3);
+        Path p1 = createAlgo(graphShortest, defaultOpts).calcPath(0, 3);
         assertEquals(IntArrayList.from(0, 1, 5, 2, 3), p1.calcNodes());
         assertEquals(p1.toString(), 402.3, p1.getDistance(), .1);
         assertEquals(p1.toString(), 144823, p1.getTime());
diff --git a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
index 7b176da6e..73c408c10 100644
--- a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
+++ b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java
@@ -54,7 +54,7 @@ public class CHQueryWithTurnCostsTest {
     private final FlagEncoder encoder = new MotorcycleFlagEncoder(5, 5, maxCost);
     private final EncodingManager encodingManager = EncodingManager.create(encoder);
     private final Weighting weighting = new ShortestWeighting(encoder);
-    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
+    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
     private final TurnCostExtension turnCostExtension = (TurnCostExtension) graph.getExtension();
     private final CHGraph chGraph = graph.getCHGraph();
     private String algoString;
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
index 7b6ac53a1..73b486abb 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
@@ -18,7 +18,6 @@
 package com.graphhopper.routing;
 
 import com.carrotsearch.hppc.IntArrayList;
-import com.graphhopper.routing.ch.NodeBasedNodeContractorTest;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
@@ -47,7 +46,7 @@ import static org.junit.Assert.assertEquals;
 public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
     @Override
     protected CHGraph getGraph(GraphHopperStorage ghStorage, Weighting weighting) {
-        return ghStorage.getCHGraph(weighting);
+        return ghStorage.getCHGraph(CHProfile.nodeBased(weighting));
     }
 
     @Override
@@ -61,7 +60,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
     public RoutingAlgorithmFactory createFactory(GraphHopperStorage ghStorage, AlgorithmOptions opts) {
         ghStorage.freeze();
         PrepareContractionHierarchies ch = PrepareContractionHierarchies.fromGraphHopperStorage(
-                ghStorage, opts.getWeighting(), TraversalMode.NODE_BASED);
+                ghStorage, CHProfile.nodeBased(opts.getWeighting()));
         ch.doWork();
         return ch;
     }
@@ -73,7 +72,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         EncodingManager em = EncodingManager.create(encoder);
         ShortestWeighting weighting = new ShortestWeighting(encoder);
         GraphHopperStorage ghStorage = createGHStorage(em, Arrays.asList(weighting), false);
-        CHGraph g2 = ghStorage.getCHGraph(weighting);
+        CHGraph g2 = ghStorage.getCHGraph();
         g2.edge(0, 1, 1, true);
         EdgeIteratorState iter1_1 = g2.edge(0, 2, 1.4, false);
         EdgeIteratorState iter1_2 = g2.edge(2, 5, 1.4, false);
@@ -103,9 +102,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         g2.setLevel(0, 7);
 
         AlgorithmOptions opts = new AlgorithmOptions(Parameters.Algorithms.DIJKSTRA_BI, weighting);
-        Path p = new PrepareContractionHierarchies(
-                g2, weighting, TraversalMode.NODE_BASED).
-                createAlgo(g2, opts).calcPath(0, 7);
+        Path p = new PrepareContractionHierarchies(g2).createAlgo(g2, opts).calcPath(0, 7);
 
         assertEquals(IntArrayList.from(0, 2, 5, 7), p.calcNodes());
         assertEquals(1064, p.getTime());
@@ -197,7 +194,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
             chGraph.setLevel(i, i);
         }
         graph.freeze();
-        RoutingAlgorithm algo = createCHAlgo(graph, chGraph, true, defaultOpts);
+        RoutingAlgorithm algo = createCHAlgo(chGraph, true, defaultOpts);
         Path p = algo.calcPath(1, 0);
         // node 3 will be stalled and nodes 4-7 won't be explored --> we visit 7 nodes
         // note that node 9 will be visited by both forward and backward searches
@@ -206,7 +203,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         assertEquals(p.toString(), IntArrayList.from(1, 8, 9, 0), p.calcNodes());
 
         // without stalling we visit 11 nodes
-        RoutingAlgorithm algoNoSod = createCHAlgo(graph, chGraph, false, defaultOpts);
+        RoutingAlgorithm algoNoSod = createCHAlgo(chGraph, false, defaultOpts);
         Path pNoSod = algoNoSod.calcPath(1, 0);
         assertEquals(11, algoNoSod.getVisitedNodes());
         assertEquals(102, pNoSod.getDistance(), 1.e-3);
@@ -250,15 +247,14 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         }
         graph.freeze();
 
-        RoutingAlgorithm algo = createCHAlgo(graph, chGraph, true, algoOpts);
+        RoutingAlgorithm algo = createCHAlgo(chGraph, true, algoOpts);
         Path p = algo.calcPath(from, to);
         assertEquals(3, p.getDistance(), 1.e-3);
         assertEquals(p.toString(), expectedPath, p.calcNodes());
     }
 
-    private RoutingAlgorithm createCHAlgo(GraphHopperStorage graph, CHGraph chGraph, boolean withSOD, AlgorithmOptions algorithmOptions) {
-        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(
-                chGraph, algorithmOptions.getWeighting(), TraversalMode.NODE_BASED);
+    private RoutingAlgorithm createCHAlgo(CHGraph chGraph, boolean withSOD, AlgorithmOptions algorithmOptions) {
+        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph);
         if (!withSOD) {
             algorithmOptions.getHints().put("stall_on_demand", false);
         }
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
index 5dc139b0e..c9e46f14a 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHTest.java
@@ -19,31 +19,33 @@ package com.graphhopper.routing;
 
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.List;
 
 public class DijkstraBidirectionEdgeCHTest extends AbstractRoutingAlgorithmTester {
     @Override
     protected CHGraph getGraph(GraphHopperStorage ghStorage, Weighting weighting) {
-        return ghStorage.getCHGraph(weighting);
+        return ghStorage.getCHGraph(CHProfile.edgeBased(weighting));
     }
 
     @Override
     protected GraphHopperStorage createGHStorage(
             EncodingManager em, List<? extends Weighting> weightings, boolean is3D) {
-        return new GraphHopperStorage(Collections.<Weighting>emptyList(), weightings, new RAMDirectory(),
-                em, is3D, new TurnCostExtension()).create(1000);
+        List<CHProfile> chProfiles = new ArrayList<>(weightings.size());
+        for (Weighting w : weightings) {
+            chProfiles.add(CHProfile.edgeBased(w));
+        }
+        return new GraphHopperStorage(chProfiles, new RAMDirectory(), em, is3D, new TurnCostExtension()).create(1000);
     }
 
     @Override
     public RoutingAlgorithmFactory createFactory(GraphHopperStorage ghStorage, AlgorithmOptions opts) {
         ghStorage.freeze();
         PrepareContractionHierarchies ch = PrepareContractionHierarchies.fromGraphHopperStorage(
-                ghStorage, opts.getWeighting(), TraversalMode.EDGE_BASED);
+                ghStorage, CHProfile.edgeBased(opts.getWeighting()));
         ch.doWork();
         return ch;
     }
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
index 158867463..01545fe46 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
@@ -21,6 +21,7 @@ import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.FastestWeighting;
+import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
@@ -172,7 +173,7 @@ public class DijkstraOneToManyTest extends AbstractRoutingAlgorithmTester {
 
     @Test
     public void testDifferentEdgeFilter() {
-        GraphHopperStorage g = new GraphBuilder(encodingManager).setCHGraph(new FastestWeighting(carEncoder)).create();
+        GraphHopperStorage g = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(new FastestWeighting(carEncoder))).create();
         g.edge(4, 3, 10, true);
         g.edge(3, 6, 10, true);
 
diff --git a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
index affbb9930..e21622bbd 100644
--- a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java
@@ -39,7 +39,6 @@ public class RandomCHRoutingTest {
     private Weighting weighting;
     private GraphHopperStorage graph;
     private LocationIndexTree locationIndex;
-    private CHGraph chGraph;
 
     @Parameterized.Parameters(name = "{0}")
     public static Object[] params() {
@@ -60,10 +59,9 @@ public class RandomCHRoutingTest {
         encoder = new CarFlagEncoder(5, 5, maxTurnCosts);
         EncodingManager em = EncodingManager.create(encoder);
         weighting = new FastestWeighting(encoder);
-        GraphBuilder graphBuilder = new GraphBuilder(em);
-        graphBuilder.setEdgeBasedCH(traversalMode.isEdgeBased());
-        graph = graphBuilder.setCHGraph(weighting).create();
-        chGraph = graph.getCHGraph();
+        graph = new GraphBuilder(em)
+                .setCHProfiles(new CHProfile(weighting, traversalMode.isEdgeBased()))
+                .create();
     }
 
     /**
@@ -135,7 +133,9 @@ public class RandomCHRoutingTest {
         locationIndex.prepareIndex();
 
         graph.freeze();
-        PrepareContractionHierarchies pch = new PrepareContractionHierarchies(chGraph, weighting, traversalMode);
+        CHProfile chProfile = new CHProfile(weighting, traversalMode);
+        CHGraph chGraph = graph.getCHGraph(chProfile);
+        PrepareContractionHierarchies pch = new PrepareContractionHierarchies(chGraph);
         pch.doWork();
 
         int numQueryGraph = 25;
diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
index 98c46678d..24dce0d94 100644
--- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
+++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmIT.java
@@ -25,10 +25,7 @@ import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.routing.util.TestAlgoCollector.AlgoHelperEntry;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.Directory;
-import com.graphhopper.storage.GraphBuilder;
-import com.graphhopper.storage.GraphHopperStorage;
+import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.util.Parameters;
@@ -92,25 +89,17 @@ public class RoutingAlgorithmIT {
             final HintsMap chHints = new HintsMap(defaultHints);
             chHints.put(Parameters.CH.DISABLE, false);
             chHints.put(Parameters.Routing.EDGE_BASED, tMode.isEdgeBased());
-            Weighting pickedWeighting = null;
-            for (Weighting tmpWeighting : hopper.getCHFactoryDecorator().getNodeBasedWeightings()) {
-                if (tmpWeighting.equals(weighting)) {
-                    pickedWeighting = tmpWeighting;
+            CHProfile pickedProfile = null;
+            for (CHProfile chProfile : hopper.getCHFactoryDecorator().getCHProfiles()) {
+                if (chProfile.getWeighting().equals(weighting) && tMode.isEdgeBased() == chProfile.getTraversalMode().isEdgeBased()) {
+                    pickedProfile = chProfile;
                     break;
                 }
             }
-            // todo: not so sure about this, can the edge based weighting entry overwrite the picked weighting found
-            // in the node based weightings ?
-            for (Weighting tmpWeighting : hopper.getCHFactoryDecorator().getEdgeBasedWeightings()) {
-                if (tmpWeighting.equals(weighting)) {
-                    pickedWeighting = tmpWeighting;
-                    break;
-                }
-            }
-            if (pickedWeighting == null)
-                throw new IllegalStateException("Didn't find weighting " + hints.getWeighting() + " in " + hopper.getCHFactoryDecorator().getNodeBasedWeightings());
+            if (pickedProfile == null)
+                throw new IllegalStateException("Didn't find weighting " + hints.getWeighting() + " in " + hopper.getCHFactoryDecorator().getCHProfiles());
 
-            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedWeighting),
+            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedProfile),
                     AlgorithmOptions.start(dijkstrabiOpts).hints(chHints).build(), idx, "dijkstrabi|ch|prepare|" + hints.getWeighting()) {
                 @Override
                 public RoutingAlgorithmFactory createRoutingFactory() {
@@ -118,7 +107,7 @@ public class RoutingAlgorithmIT {
                 }
             });
 
-            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedWeighting),
+            prepare.add(new AlgoHelperEntry(ghStorage.getCHGraph(pickedProfile),
                     AlgorithmOptions.start(astarbiOpts).hints(chHints).build(), idx, "astarbi|ch|prepare|" + hints.getWeighting()) {
                 @Override
                 public RoutingAlgorithmFactory createRoutingFactory() {
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
index b3915467b..5c5260f8c 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHAlgoFactoryDecoratorTest.java
@@ -20,23 +20,17 @@ package com.graphhopper.routing.ch;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.ShortFastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.Directory;
-import com.graphhopper.storage.GraphExtension;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.RAMDirectory;
+import com.graphhopper.storage.*;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.List;
 
-import static com.graphhopper.routing.util.TraversalMode.EDGE_BASED;
-import static com.graphhopper.routing.util.TraversalMode.NODE_BASED;
 import static org.junit.Assert.*;
 
 /**
@@ -44,9 +38,12 @@ import static org.junit.Assert.*;
  */
 public class CHAlgoFactoryDecoratorTest {
     private CHAlgoFactoryDecorator instance;
-    private Weighting weighting1;
-    private Weighting weighting2;
-    private Weighting weighting3;
+    private CHProfile profileNode1;
+    private CHProfile profileNode2;
+    private CHProfile profileNode3;
+    private CHProfile profileEdge1;
+    private CHProfile profileEdge2;
+    private CHProfile profileEdge3;
     private GraphHopperStorage ghStorage;
 
     @Before
@@ -55,87 +52,77 @@ public class CHAlgoFactoryDecoratorTest {
         Directory dir = new RAMDirectory();
         FlagEncoder encoder = new CarFlagEncoder();
         EncodingManager encodingManager = EncodingManager.create(encoder);
-        weighting1 = new FastestWeighting(encoder);
-        weighting2 = new ShortestWeighting(encoder);
-        weighting3 = new ShortFastestWeighting(encoder, 0.1);
+        profileNode1 = CHProfile.nodeBased(new FastestWeighting(encoder));
+        profileNode2 = CHProfile.nodeBased(new ShortestWeighting(encoder));
+        profileNode3 = CHProfile.nodeBased(new ShortFastestWeighting(encoder, 0.1));
+        profileEdge1 = CHProfile.edgeBased(new FastestWeighting(encoder));
+        profileEdge2 = CHProfile.edgeBased(new ShortestWeighting(encoder));
+        profileEdge3 = CHProfile.edgeBased(new ShortFastestWeighting(encoder, 0.1));
         ghStorage = new GraphHopperStorage(
-                Arrays.asList(weighting1, weighting2, weighting3),
-                Arrays.asList(weighting1, weighting2, weighting3),
+                Arrays.asList(profileNode1, profileNode2, profileNode3, profileEdge1, profileEdge2, profileEdge3),
                 dir, encodingManager, false, new GraphExtension.NoOpExtension());
     }
 
     @Test
-    public void testCreatePreparations() {
+    public void testDisablingAllowed() {
         assertFalse(instance.isDisablingAllowed());
         instance.setEnabled(false);
         assertTrue(instance.isDisablingAllowed());
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAddingPreparationBeforeWeighting_throws() {
-        PrepareContractionHierarchies preparation = createNodeBasedPreparation(weighting1);
+    public void testAddingPreparationBeforeProfile_throws() {
+        PrepareContractionHierarchies preparation = createPreparation(profileNode1);
         instance.addPreparation(preparation);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void testAddingPreparationWithWrongWeighting_throws() {
-        instance.addNodeBasedWeighting(weighting1);
-        PrepareContractionHierarchies preparation = createNodeBasedPreparation(weighting2);
+    public void testAddingPreparationWithWrongProfile_throws() {
+        instance.addCHProfile(profileNode1);
+        PrepareContractionHierarchies preparation = createPreparation(profileNode2);
         instance.addPreparation(preparation);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testAddingPreparationsInWrongOrder_throws() {
-        instance.addNodeBasedWeighting(weighting1);
-        instance.addNodeBasedWeighting(weighting2);
-        instance.addPreparation(createNodeBasedPreparation(weighting2));
-        instance.addPreparation(createNodeBasedPreparation(weighting1));
+        instance.addCHProfile(profileNode1);
+        instance.addCHProfile(profileNode2);
+        instance.addPreparation(createPreparation(profileNode2));
+        instance.addPreparation(createPreparation(profileNode1));
     }
 
     @Test
     public void testAddingPreparationsWithEdgeAndNodeBasedIntermixed_works() {
-        instance.addNodeBasedWeighting(weighting1);
-        instance.addEdgeBasedWeighting(weighting1);
-        instance.addPreparation(createEdgeBasedPreparation(weighting1));
-        instance.addPreparation(createNodeBasedPreparation(weighting1));
+        instance.addCHProfile(profileNode1);
+        instance.addCHProfile(profileEdge1);
+        instance.addCHProfile(profileNode2);
+        instance.addPreparation(createPreparation(profileNode1));
+        instance.addPreparation(createPreparation(profileEdge1));
+        instance.addPreparation(createPreparation(profileNode2));
     }
 
     @Test
     public void testAddingEdgeAndNodeBased_works() {
-        instance.addEdgeBasedWeighting(weighting1);
-        instance.addNodeBasedWeighting(weighting2);
-        instance.addNodeBasedWeighting(weighting1);
-        instance.addEdgeBasedWeighting(weighting2);
-        instance.addEdgeBasedWeighting(weighting3);
-        // we can change the order between edge and node based as long as within each group the weightings have the
-        // right order
-        instance.addPreparation(createEdgeBasedPreparation(weighting1));
-        instance.addPreparation(createNodeBasedPreparation(weighting2));
-        instance.addPreparation(createEdgeBasedPreparation(weighting2));
-        instance.addPreparation(createEdgeBasedPreparation(weighting3));
-        instance.addPreparation(createNodeBasedPreparation(weighting1));
-
-        Weighting[] expectedWeightings = new Weighting[]{weighting1, weighting2, weighting2, weighting3, weighting1};
-        boolean[] expectedEdgedBaseds = new boolean[]{true, false, true, true, false};
-
+        instance.addCHProfile(profileNode1);
+        instance.addCHProfile(profileNode2);
+        instance.addCHProfile(profileEdge1);
+        instance.addCHProfile(profileEdge2);
+        instance.addCHProfile(profileNode3);
+        instance.addPreparation(createPreparation(profileNode1));
+        instance.addPreparation(createPreparation(profileNode2));
+        instance.addPreparation(createPreparation(profileEdge1));
+        instance.addPreparation(createPreparation(profileEdge2));
+        instance.addPreparation(createPreparation(profileNode3));
+
+        CHProfile[] expectedProfiles = new CHProfile[]{profileNode1, profileNode2, profileEdge1, profileEdge2, profileNode3};
         List<PrepareContractionHierarchies> preparations = instance.getPreparations();
         for (int i = 0; i < preparations.size(); ++i) {
-            assertSame(expectedWeightings[i], preparations.get(i).getWeighting());
-            assertSame(expectedEdgedBaseds[i], preparations.get(i).isEdgeBased());
+            assertSame(expectedProfiles[i], preparations.get(i).getCHProfile());
         }
     }
 
-    private PrepareContractionHierarchies createNodeBasedPreparation(Weighting weighting) {
-        return createPreparation(weighting, false);
-    }
-
-    private PrepareContractionHierarchies createEdgeBasedPreparation(Weighting weighting) {
-        return createPreparation(weighting, true);
-    }
-
-    private PrepareContractionHierarchies createPreparation(Weighting weighting, boolean edgedBased) {
-        TraversalMode traversalMode = edgedBased ? EDGE_BASED : NODE_BASED;
-        return PrepareContractionHierarchies.fromGraphHopperStorage(ghStorage, weighting, traversalMode);
+    private PrepareContractionHierarchies createPreparation(CHProfile chProfile) {
+        return PrepareContractionHierarchies.fromGraphHopperStorage(ghStorage, chProfile);
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
index 5736f074e..edeb2600f 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java
@@ -59,7 +59,6 @@ public class CHTurnCostTest {
     private Weighting weighting;
     private GraphHopperStorage graph;
     private TurnCostExtension turnCostExtension;
-    private TurnWeighting turnWeighting;
     private CHGraph chGraph;
     private boolean checkStrict;
 
@@ -73,10 +72,10 @@ public class CHTurnCostTest {
         encoder = new CarFlagEncoder(5, 5, maxCost);
         EncodingManager encodingManager = EncodingManager.create(encoder);
         weighting = new ShortestWeighting(encoder);
-        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
-        turnCostExtension = (TurnCostExtension) graph.getExtension();
-        turnWeighting = new TurnWeighting(weighting, turnCostExtension);
+        CHProfile chProfile = CHProfile.edgeBased(weighting);
+        graph = new GraphBuilder(encodingManager).setCHProfiles(chProfile).create();
         chGraph = graph.getCHGraph();
+        turnCostExtension = (TurnCostExtension) graph.getExtension();
         checkStrict = true;
     }
 
@@ -1018,7 +1017,7 @@ public class CHTurnCostTest {
     }
 
     private Path findPathUsingDijkstra(int from, int to) {
-        Dijkstra dijkstra = new Dijkstra(graph, turnWeighting, TraversalMode.EDGE_BASED);
+        Dijkstra dijkstra = new Dijkstra(graph, new TurnWeighting(weighting, turnCostExtension), TraversalMode.EDGE_BASED);
         return dijkstra.calcPath(from, to);
     }
 
@@ -1042,7 +1041,7 @@ public class CHTurnCostTest {
                 return contractionOrder.size();
             }
         };
-        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph, weighting, TraversalMode.EDGE_BASED)
+        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph)
                 .useFixedNodeOrdering(nodeOrderingProvider);
         ch.doWork();
         return ch;
@@ -1054,8 +1053,7 @@ public class CHTurnCostTest {
         pMap.put(LAST_LAZY_NODES_UPDATES, 100);
         pMap.put(NEIGHBOR_UPDATES, 4);
         pMap.put(LOG_MESSAGES, 10);
-        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(
-                chGraph, weighting, TraversalMode.EDGE_BASED);
+        PrepareContractionHierarchies ch = new PrepareContractionHierarchies(chGraph);
         ch.setParams(pMap);
         ch.doWork();
         return ch;
@@ -1139,7 +1137,7 @@ public class CHTurnCostTest {
         turnCostExtension.addTurnInfo(inEdge.getEdge(), viaNode, outEdge.getEdge(), encoder.getTurnFlags(true, 0));
     }
 
-    private void addTurnCost(int from, int via, int to, int cost) {
+    private void addTurnCost(int from, int via, int to, double cost) {
         addTurnCost(getEdge(from, via), getEdge(via, to), via, cost);
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
index f2d3a8fad..8fcc23ee3 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java
@@ -27,16 +27,11 @@ import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.GraphBuilder;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.TurnCostExtension;
-import com.graphhopper.util.EdgeIterator;
+import com.graphhopper.storage.*;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.GHUtility;
 import com.graphhopper.util.PMap;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -59,8 +54,7 @@ public class EdgeBasedNodeContractorTest {
     private CarFlagEncoder encoder;
     private GraphHopperStorage graph;
     private TurnCostExtension turnCostExtension;
-    private TurnWeighting turnWeighting;
-    private TurnWeighting chTurnWeighting;
+    private Weighting weighting;
 
     @Rule
     public RepeatRule repeatRule = new RepeatRule();
@@ -74,12 +68,9 @@ public class EdgeBasedNodeContractorTest {
     private void initialize() {
         encoder = new CarFlagEncoder(5, 5, maxCost);
         EncodingManager encodingManager = EncodingManager.create(encoder);
-        Weighting weighting = new ShortestWeighting(encoder);
-        PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
-        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
+        weighting = new ShortestWeighting(encoder);
+        graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
         turnCostExtension = (TurnCostExtension) graph.getExtension();
-        turnWeighting = new TurnWeighting(weighting, turnCostExtension);
-        chTurnWeighting = new TurnWeighting(preparationWeighting, turnCostExtension);
         chGraph = graph.getCHGraph();
     }
 
@@ -390,8 +381,7 @@ public class EdgeBasedNodeContractorTest {
         graph.freeze();
         setMaxLevelOnAllNodes();
         contractNodes(2);
-        double weight = calcWeight(e3to2, e2to4);
-        checkShortcuts(createShortcut(3, 4, e3to2, e2to4, weight));
+        checkShortcuts(createShortcut(3, 4, e3to2, e2to4, 12));
     }
 
     @Test
@@ -1364,16 +1354,13 @@ public class EdgeBasedNodeContractorTest {
     }
 
     private EdgeBasedNodeContractor createNodeContractor() {
-        EdgeBasedNodeContractor nodeContractor = new EdgeBasedNodeContractor(chGraph, chTurnWeighting, new PMap());
+        PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
+        TurnWeighting turnWeighting = new TurnWeighting(preparationWeighting, turnCostExtension);
+        EdgeBasedNodeContractor nodeContractor = new EdgeBasedNodeContractor(chGraph, turnWeighting, new PMap());
         nodeContractor.initFromGraph();
         return nodeContractor;
     }
 
-    private double calcWeight(EdgeIteratorState edge1, EdgeIteratorState edge2) {
-        return turnWeighting.calcWeight(edge1, false, EdgeIterator.NO_EDGE) +
-                turnWeighting.calcWeight(edge2, false, edge1.getEdge());
-    }
-
     private void addRestriction(EdgeIteratorState inEdge, EdgeIteratorState outEdge, int viaNode) {
         turnCostExtension.addTurnInfo(inEdge.getEdge(), viaNode, outEdge.getEdge(), encoder.getTurnFlags(true, 0));
     }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
index 666e78358..0b9a49e1e 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java
@@ -26,12 +26,8 @@ import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.GraphBuilder;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.RAMDirectory;
+import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndexTree;
-import com.graphhopper.storage.index.QueryResult;
 import com.graphhopper.util.CHEdgeIteratorState;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
@@ -55,7 +51,7 @@ public class NodeBasedNodeContractorTest {
     private final CarFlagEncoder encoder = new CarFlagEncoder();
     private final EncodingManager encodingManager = EncodingManager.create(encoder);
     private final Weighting weighting = new ShortestWeighting(encoder);
-    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
+    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
     private final CHGraph lg = graph.getCHGraph();
     private final TraversalMode traversalMode = TraversalMode.NODE_BASED;
 
@@ -341,7 +337,7 @@ public class NodeBasedNodeContractorTest {
         CarFlagEncoder encoder = new CarFlagEncoder();
         EncodingManager encodingManager = EncodingManager.create(encoder);
         Weighting weighting = new FastestWeighting(encoder);
-        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
+        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
         CHGraph lg = graph.getCHGraph();
         // 0 ------------> 4
         //  \             /
@@ -379,7 +375,7 @@ public class NodeBasedNodeContractorTest {
         CarFlagEncoder encoder = new CarFlagEncoder();
         EncodingManager encodingManager = EncodingManager.create(encoder);
         Weighting weighting = new FastestWeighting(encoder);
-        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
+        GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
         CHGraph lg = graph.getCHGraph();
         // 0 - 1 - 2 - 3
         // o           o
diff --git a/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java b/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
index 4eeb5f876..6c2ee95ec 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/Path4CHTest.java
@@ -11,15 +11,13 @@ import com.graphhopper.routing.util.MotorcycleFlagEncoder;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.GraphBuilder;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.TurnCostExtension;
+import com.graphhopper.storage.*;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.GHUtility;
 import org.junit.Before;
 import org.junit.Test;
 
+
 import static org.junit.Assert.assertEquals;
 
 public class Path4CHTest {
@@ -35,7 +33,7 @@ public class Path4CHTest {
         encoder = new MotorcycleFlagEncoder(5, 5, maxTurnCosts);
         EncodingManager em = EncodingManager.create(encoder);
         weighting = new FastestWeighting(encoder);
-        graph = new GraphBuilder(em).setEdgeBasedCH(true).setCHGraph(weighting).create();
+        graph = new GraphBuilder(em).setCHProfiles(CHProfile.edgeBased(weighting)).create();
         chGraph = graph.getCHGraph();
         turnCostExtension = (TurnCostExtension) graph.getExtension();
     }
@@ -157,7 +155,7 @@ public class Path4CHTest {
 
     private AbstractBidirectionEdgeCHNoSOD createAlgo() {
         TurnWeighting chTurnWeighting = new TurnWeighting(new PreparationWeighting(weighting), turnCostExtension);
-        CHGraph lg = graph.getCHGraph(weighting);
+        CHGraph lg = graph.getCHGraph();
         AbstractBidirectionEdgeCHNoSOD algo = new DijkstraBidirectionEdgeCHNoSOD(lg, chTurnWeighting);
         algo.setEdgeFilter(new LevelEdgeFilter(lg));
         return algo;
diff --git a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
index 06832d0c6..23e3f998a 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
@@ -125,7 +125,7 @@ public class PrepareContractionHierarchiesTest {
     }
 
     GraphHopperStorage createGHStorage(Weighting w) {
-        return new GraphBuilder(encodingManager).setCHGraph(w).create();
+        return new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(w)).create();
     }
 
     GraphHopperStorage createExampleGraph() {
@@ -619,17 +619,17 @@ public class PrepareContractionHierarchiesTest {
         EncodingManager tmpEncodingManager = EncodingManager.create(tmpCarEncoder, tmpBikeEncoder);
 
         // FastestWeighting would lead to different shortcuts due to different default speeds for bike and car
-        Weighting carWeighting = new ShortestWeighting(tmpCarEncoder);
-        Weighting bikeWeighting = new ShortestWeighting(tmpBikeEncoder);
+        CHProfile carProfile = CHProfile.nodeBased(new ShortestWeighting(tmpCarEncoder));
+        CHProfile bikeProfile = CHProfile.nodeBased(new ShortestWeighting(tmpBikeEncoder));
 
-        List<Weighting> chWeightings = Arrays.asList(carWeighting, bikeWeighting);
-        GraphHopperStorage ghStorage = new GraphHopperStorage(chWeightings, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
+        List<CHProfile> profiles = Arrays.asList(carProfile, bikeProfile);
+        GraphHopperStorage ghStorage = new GraphHopperStorage(profiles, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
         initShortcutsGraph(ghStorage);
 
         ghStorage.freeze();
 
-        for (Weighting w : chWeightings) {
-            checkPath(ghStorage, w, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
+        for (CHProfile p : profiles) {
+            checkPath(ghStorage, p, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
         }
     }
 
@@ -639,20 +639,20 @@ public class PrepareContractionHierarchiesTest {
         BikeFlagEncoder tmpBikeEncoder = new BikeFlagEncoder();
         EncodingManager tmpEncodingManager = EncodingManager.create(tmpCarEncoder, tmpBikeEncoder);
 
-        Weighting carWeighting = new FastestWeighting(tmpCarEncoder);
-        Weighting bikeWeighting = new FastestWeighting(tmpBikeEncoder);
+        CHProfile carProfile = CHProfile.nodeBased(new FastestWeighting(tmpCarEncoder));
+        CHProfile bikeProfile = CHProfile.nodeBased(new FastestWeighting(tmpBikeEncoder));
 
-        List<Weighting> chWeightings = Arrays.asList(carWeighting, bikeWeighting);
-        GraphHopperStorage ghStorage = new GraphHopperStorage(chWeightings, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
+        List<CHProfile> profiles = Arrays.asList(carProfile, bikeProfile);
+        GraphHopperStorage ghStorage = new GraphHopperStorage(profiles, dir, tmpEncodingManager, false, new GraphExtension.NoOpExtension()).create(1000);
         initShortcutsGraph(ghStorage);
         EdgeIteratorState edge = GHUtility.getEdge(ghStorage, 9, 14).
                 set(tmpBikeEncoder.getAccessEnc(), false).setReverse(tmpBikeEncoder.getAccessEnc(), false);
 
         ghStorage.freeze();
 
-        checkPath(ghStorage, carWeighting, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
+        checkPath(ghStorage, carProfile, 7, 5, IntArrayList.from(3, 9, 14, 16, 13, 12));
         // detour around blocked 9,14
-        checkPath(ghStorage, bikeWeighting, 9, 5, IntArrayList.from(3, 10, 14, 16, 13, 12));
+        checkPath(ghStorage, bikeProfile, 9, 5, IntArrayList.from(3, 10, 14, 16, 13, 12));
     }
 
     @Test
@@ -660,10 +660,10 @@ public class PrepareContractionHierarchiesTest {
         CarFlagEncoder carFlagEncoder = new CarFlagEncoder();
         MotorcycleFlagEncoder motorCycleEncoder = new MotorcycleFlagEncoder();
         EncodingManager em = EncodingManager.create(carFlagEncoder, motorCycleEncoder);
-        Weighting carWeighting = new FastestWeighting(carFlagEncoder);
-        Weighting motorCycleWeighting = new FastestWeighting(motorCycleEncoder);
+        CHProfile carProfile = CHProfile.nodeBased(new FastestWeighting(carFlagEncoder));
+        CHProfile motorCycleProfile = CHProfile.nodeBased(new FastestWeighting(motorCycleEncoder));
         Directory dir = new RAMDirectory();
-        GraphHopperStorage ghStorage = new GraphHopperStorage(Arrays.asList(carWeighting, motorCycleWeighting), dir, em, false, new GraphExtension.NoOpExtension());
+        GraphHopperStorage ghStorage = new GraphHopperStorage(Arrays.asList(carProfile, motorCycleProfile), dir, em, false, new GraphExtension.NoOpExtension());
         ghStorage.create(1000);
 
         int numNodes = 5_000;
@@ -675,25 +675,24 @@ public class PrepareContractionHierarchiesTest {
 
         // create CH for cars
         StopWatch sw = new StopWatch().start();
-        CHGraph carCH = ghStorage.getCHGraph(carWeighting);
-        TraversalMode traversalMode = TraversalMode.NODE_BASED;
-        PrepareContractionHierarchies carPch = new PrepareContractionHierarchies(carCH, carWeighting, traversalMode);
+        CHGraph carCH = ghStorage.getCHGraph(carProfile);
+        PrepareContractionHierarchies carPch = new PrepareContractionHierarchies(carCH);
         carPch.doWork();
         long timeCar = sw.stop().getMillis();
 
         // create CH for motorcycles, re-use car contraction order
         // this speeds up contraction significantly, but can lead to slower queries
         sw = new StopWatch().start();
-        CHGraph motorCycleCH = ghStorage.getCHGraph(motorCycleWeighting);
+        CHGraph motorCycleCH = ghStorage.getCHGraph(motorCycleProfile);
         NodeOrderingProvider nodeOrderingProvider = carCH.getNodeOrderingProvider();
-        PrepareContractionHierarchies motorCyclePch = new PrepareContractionHierarchies(motorCycleCH, motorCycleWeighting, traversalMode)
+        PrepareContractionHierarchies motorCyclePch = new PrepareContractionHierarchies(motorCycleCH)
                 .useFixedNodeOrdering(nodeOrderingProvider);
         motorCyclePch.doWork();
 
         // run a few sample queries to check correctness
         for (int i = 0; i < numQueries; ++i) {
-            Dijkstra dijkstra = new Dijkstra(ghStorage, motorCycleWeighting, traversalMode);
-            RoutingAlgorithm chAlgo = motorCyclePch.createAlgo(motorCycleCH, AlgorithmOptions.start().weighting(motorCycleWeighting).build());
+            Dijkstra dijkstra = new Dijkstra(ghStorage, motorCycleProfile.getWeighting(), TraversalMode.NODE_BASED);
+            RoutingAlgorithm chAlgo = motorCyclePch.createAlgo(motorCycleCH, AlgorithmOptions.start().weighting(motorCycleProfile.getWeighting()).build());
 
             int from = rnd.nextInt(numNodes);
             int to = rnd.nextInt(numNodes);
@@ -706,15 +705,15 @@ public class PrepareContractionHierarchiesTest {
         assertTrue("reusing node ordering should speed up ch contraction", timeMotorCycle < 0.5 * timeCar);
     }
 
-    void checkPath(GraphHopperStorage g, Weighting w, int expShortcuts, double expDistance, IntIndexedContainer expNodes) {
-        CHGraph lg = g.getCHGraph(w);
-        PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g, lg, w);
+    void checkPath(GraphHopperStorage g, CHProfile p, int expShortcuts, double expDistance, IntIndexedContainer expNodes) {
+        CHGraph lg = g.getCHGraph(p);
+        PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g, lg, p);
         prepare.doWork();
-        assertEquals(w.toString(), expShortcuts, prepare.getShortcuts());
-        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, w, tMode));
-        Path p = algo.calcPath(3, 12);
-        assertEquals(w.toString(), expDistance, p.getDistance(), 1e-5);
-        assertEquals(w.toString(), expNodes, p.calcNodes());
+        assertEquals(p.toString(), expShortcuts, prepare.getShortcuts());
+        RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, p.getWeighting(), tMode));
+        Path path = algo.calcPath(3, 12);
+        assertEquals(path.toString(), expDistance, path.getDistance(), 1e-5);
+        assertEquals(path.toString(), expNodes, path.calcNodes());
     }
 
     private PrepareContractionHierarchies createPrepareContractionHierarchies(GraphHopperStorage g, CHGraph lg) {
@@ -722,8 +721,12 @@ public class PrepareContractionHierarchiesTest {
     }
 
     private PrepareContractionHierarchies createPrepareContractionHierarchies(GraphHopperStorage g, CHGraph lg, Weighting w) {
+        return createPrepareContractionHierarchies(g, lg, CHProfile.nodeBased(w));
+    }
+
+    private PrepareContractionHierarchies createPrepareContractionHierarchies(GraphHopperStorage g, CHGraph lg, CHProfile p) {
         g.freeze();
-        return new PrepareContractionHierarchies(lg, w, tMode);
+        return new PrepareContractionHierarchies(lg);
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java b/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java
index 897ef6496..5202944d9 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/WitnessPathSearcherTest.java
@@ -23,10 +23,7 @@ import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.routing.weighting.Weighting;
-import com.graphhopper.storage.CHGraph;
-import com.graphhopper.storage.GraphBuilder;
-import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.storage.TurnCostExtension;
+import com.graphhopper.storage.*;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.PMap;
 import org.junit.Before;
@@ -47,7 +44,7 @@ public class WitnessPathSearcherTest {
         EncodingManager encodingManager = EncodingManager.create(encoder);
         Weighting weighting = new ShortestWeighting(encoder);
         PreparationWeighting preparationWeighting = new PreparationWeighting(weighting);
-        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
+        graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
         TurnCostExtension turnCostExtension = (TurnCostExtension) graph.getExtension();
         chTurnWeighting = new TurnWeighting(preparationWeighting, turnCostExtension);
         chGraph = graph.getCHGraph();
diff --git a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
index 41d97b5e6..4a9fa9c3e 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
@@ -87,7 +87,7 @@ public class LandmarkStorageTest {
     public void testSetGetWeight() {
         ghStorage.edge(0, 1, 40, true);
         Directory dir = new RAMDirectory();
-        DataAccess da = dir.find("landmarks_fastest_car_node");
+        DataAccess da = dir.find("landmarks_fastest_car");
         da.create(2000);
 
         LandmarkStorage lms = new LandmarkStorage(ghStorage, dir, new FastestWeighting(encoder), 4).
diff --git a/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java b/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java
index 2f1f78e85..fd114b4f4 100644
--- a/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/DefaultEdgeFilterTest.java
@@ -23,6 +23,7 @@ import com.graphhopper.routing.ch.PrepareEncoder;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.util.CHEdgeExplorer;
@@ -35,7 +36,7 @@ public class DefaultEdgeFilterTest {
     private final CarFlagEncoder encoder = new CarFlagEncoder();
     private final EncodingManager encodingManager = EncodingManager.create(encoder);
     private final Weighting weighting = new ShortestWeighting(encoder);
-    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(true).create();
+    private final GraphHopperStorage graph = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.edgeBased(weighting)).create();
     private final CHGraph chGraph = graph.getCHGraph();
 
     @Test
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
index f71d19f0d..77c1a3710 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
@@ -109,7 +109,7 @@ public class DirectedRoutingTest {
             return;
         }
         if (prepareCH) {
-            pch = new PrepareContractionHierarchies(chGraph, weighting, TraversalMode.EDGE_BASED);
+            pch = new PrepareContractionHierarchies(chGraph);
             pch.doWork();
         }
         if (prepareLM) {
@@ -278,7 +278,7 @@ public class DirectedRoutingTest {
     }
 
     private GraphHopperStorage createGraph() {
-        GraphHopperStorage gh = new GraphHopperStorage(new ArrayList<Weighting>(), Collections.singletonList(weighting), dir, encodingManager,
+        GraphHopperStorage gh = new GraphHopperStorage(Collections.singletonList(CHProfile.edgeBased(weighting)), dir, encodingManager,
                 false, turnCostExtension);
         gh.create(1000);
         return gh;
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
index 5222015eb..92f4bd084 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageCHTest.java
@@ -57,11 +57,8 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
     }
 
     private GraphHopperStorage newGHStorage(Directory dir, boolean is3D, boolean forEdgeBasedTraversal) {
-        if (forEdgeBasedTraversal) {
-            return new GraphHopperStorage(Collections.<Weighting>emptyList(), Arrays.asList(new FastestWeighting(carEncoder)), dir, encodingManager, is3D, new GraphExtension.NoOpExtension());
-        } else {
-            return new GraphHopperStorage(Arrays.asList(new FastestWeighting(carEncoder)), Collections.<Weighting>emptyList(), dir, encodingManager, is3D, new GraphExtension.NoOpExtension());
-        }
+        CHProfile chProfile = new CHProfile(new FastestWeighting(carEncoder), forEdgeBasedTraversal);
+        return new GraphHopperStorage(Collections.singletonList(chProfile), dir, encodingManager, is3D, new GraphExtension.NoOpExtension());
     }
 
     @Test
@@ -210,11 +207,11 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         FlagEncoder customEncoder = new Bike2WeightFlagEncoder();
         EncodingManager em = EncodingManager.create(customEncoder);
         FastestWeighting weighting = new FastestWeighting(customEncoder);
-        GraphHopperStorage ghStorage = new GraphBuilder(em).setCHGraph(weighting).create();
+        GraphHopperStorage ghStorage = new GraphBuilder(em).setCHProfiles(CHProfile.nodeBased(weighting)).create();
         ghStorage.edge(0, 2);
         ghStorage.freeze();
 
-        CHGraph lg = ghStorage.getCHGraph(weighting);
+        CHGraph lg = ghStorage.getCHGraph();
         int sc1 = lg.shortcut(0, 1, PrepareEncoder.getScFwdDir(), 100.123, 0, NO_EDGE, NO_EDGE);
 
         assertEquals(100.123, lg.getEdgeIteratorState(sc1, 1).getWeight(), 1e-3);
@@ -466,23 +463,23 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         FlagEncoder tmpCar = new CarFlagEncoder();
         FlagEncoder tmpBike = new Bike2WeightFlagEncoder();
         EncodingManager em = EncodingManager.create(tmpCar, tmpBike);
-        List<Weighting> chWeightings = new ArrayList<>();
-        chWeightings.add(new FastestWeighting(tmpCar));
-        chWeightings.add(new FastestWeighting(tmpBike));
+        List<CHProfile> chProfiles = Arrays.asList(
+                CHProfile.nodeBased(new FastestWeighting(tmpCar)),
+                CHProfile.nodeBased(new FastestWeighting(tmpBike)));
         BooleanEncodedValue tmpCarAccessEnc = tmpCar.getAccessEnc();
 
-        graph = new GraphHopperStorage(chWeightings, new RAMDirectory(), em, false, new GraphExtension.NoOpExtension()).create(1000);
+        graph = new GraphHopperStorage(chProfiles, new RAMDirectory(), em, false, new GraphExtension.NoOpExtension()).create(1000);
         IntsRef edgeFlags = GHUtility.setProperties(em.createEdgeFlags(), tmpCar, 100, true, false);
         graph.edge(0, 1).setDistance(10).setFlags(GHUtility.setProperties(edgeFlags, tmpBike, 10, true, true));
         graph.edge(1, 2).setDistance(10).setFlags(edgeFlags);
 
         graph.freeze();
 
-        CHGraph carCHGraph = graph.getCHGraph(chWeightings.get(0));
+        CHGraph carCHGraph = graph.getCHGraph(chProfiles.get(0));
         // enable forward directions for car
         int carSC02 = carCHGraph.shortcut(0, 2, PrepareEncoder.getScFwdDir(), 10, 20, NO_EDGE, NO_EDGE);
 
-        CHGraph bikeCHGraph = graph.getCHGraph(chWeightings.get(1));
+        CHGraph bikeCHGraph = graph.getCHGraph(chProfiles.get(1));
         // enable both directions for bike
         int bikeSC02 = bikeCHGraph.shortcut(0, 2, PrepareEncoder.getScDirMask(), 10, 20, NO_EDGE, NO_EDGE);
 
@@ -566,15 +563,15 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         // now we create a new storage but only use one of the weightings, which should be ok
         GraphHopperStorage smallStorage = createStorageWithWeightings(edgeBased, weighting1);
         smallStorage.loadExisting();
-        assertEquals(edgeBased ? 0 : 1, smallStorage.getNodeBasedCHWeightings().size());
-        assertEquals(edgeBased ? 1 : 0, smallStorage.getEdgeBasedCHWeightings().size());
+        assertEquals(edgeBased ? 0 : 1, smallStorage.getCHProfiles(false).size());
+        assertEquals(edgeBased ? 1 : 0, smallStorage.getCHProfiles(true).size());
         smallStorage.flush();
 
         // now we create yet another storage that uses both weightings again, which still works
         GraphHopperStorage fullStorage = createStorageWithWeightings(edgeBased, weighting1, weighting2);
         fullStorage.loadExisting();
-        assertEquals(edgeBased ? 0 : 2, fullStorage.getNodeBasedCHWeightings().size());
-        assertEquals(edgeBased ? 2 : 0, fullStorage.getEdgeBasedCHWeightings().size());
+        assertEquals(edgeBased ? 0 : 2, fullStorage.getCHProfiles(false).size());
+        assertEquals(edgeBased ? 2 : 0, fullStorage.getCHProfiles(true).size());
         fullStorage.flush();
     }
 
@@ -592,17 +589,17 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
         // now we create a new storage but only use the edge weighting, which should be ok
         GraphHopperStorage edgeStorage = createStorageWithWeightings(true, weighting2);
         edgeStorage.loadExisting();
-        assertEquals(0, edgeStorage.getNodeBasedCHWeightings().size());
-        assertEquals(1, edgeStorage.getEdgeBasedCHWeightings().size());
+        assertEquals(0, edgeStorage.getCHProfiles(false).size());
+        assertEquals(1, edgeStorage.getCHProfiles(true).size());
         edgeStorage.flush();
 
         // now we create yet another storage that uses one of the node and the edge weighting, which still works
         GraphHopperStorage mixedStorage = createStorageWithWeightings(
-                Arrays.<Weighting>asList(weighting1),
-                Arrays.<Weighting>asList(weighting2));
+                Collections.<Weighting>singletonList(weighting1),
+                Collections.<Weighting>singletonList(weighting2));
         mixedStorage.loadExisting();
-        assertEquals(1, mixedStorage.getNodeBasedCHWeightings().size());
-        assertEquals(1, mixedStorage.getNodeBasedCHWeightings().size());
+        assertEquals(1, mixedStorage.getCHProfiles(false).size());
+        assertEquals(1, mixedStorage.getCHProfiles(true).size());
         mixedStorage.flush();
     }
 
@@ -613,7 +610,14 @@ public class GraphHopperStorageCHTest extends GraphHopperStorageTest {
     }
 
     private GraphHopperStorage createStorageWithWeightings(List<Weighting> nodeBasedCHWeightings, List<Weighting> edgeBasedCHWeightings) {
-        return new GraphHopperStorage(nodeBasedCHWeightings, edgeBasedCHWeightings,
+        List<CHProfile> profiles = new ArrayList<>(nodeBasedCHWeightings.size() + edgeBasedCHWeightings.size());
+        for (Weighting nodeBasedCHWeighting : nodeBasedCHWeightings) {
+            profiles.add(CHProfile.nodeBased(nodeBasedCHWeighting));
+        }
+        for (Weighting edgeBasedCHWeighting : edgeBasedCHWeightings) {
+            profiles.add(CHProfile.edgeBased(edgeBasedCHWeighting));
+        }
+        return new GraphHopperStorage(profiles,
                 new GHDirectory(defaultGraphLoc, DAType.RAM_STORE), encodingManager, false, new GraphExtension.NoOpExtension());
     }
 }
diff --git a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
index 840434c7d..54670866f 100644
--- a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
+++ b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java
@@ -49,8 +49,8 @@ public class ShortcutUnpackerTest {
         encoder = new MotorcycleFlagEncoder(5, 5, 10);
         EncodingManager encodingManager = EncodingManager.create(encoder);
         weighting = new FastestWeighting(encoder);
-        graph = new GraphBuilder(encodingManager).setCHGraph(weighting).setEdgeBasedCH(edgeBased).create();
-        chGraph = graph.getCHGraph(weighting);
+        graph = new GraphBuilder(encodingManager).setCHProfiles(new CHProfile(weighting, edgeBased)).create();
+        chGraph = graph.getCHGraph();
         if (edgeBased) {
             turnCostExtension = (TurnCostExtension) graph.getExtension();
         }
diff --git a/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java b/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java
index 02cfbb8e9..5e3d88181 100644
--- a/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java
+++ b/core/src/test/java/com/graphhopper/util/CHEdgeIteratorTest.java
@@ -25,6 +25,7 @@ import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.storage.CHGraph;
+import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import org.junit.Test;
@@ -43,8 +44,8 @@ public class CHEdgeIteratorTest {
         EncodingManager encodingManager = EncodingManager.create(carFlagEncoder);
         FastestWeighting weighting = new FastestWeighting(carFlagEncoder);
         EdgeFilter carOutFilter = DefaultEdgeFilter.outEdges(carFlagEncoder);
-        GraphHopperStorage ghStorage = new GraphBuilder(encodingManager).setCHGraph(weighting).create();
-        CHGraph g = ghStorage.getCHGraph(weighting);
+        GraphHopperStorage ghStorage = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(weighting)).create();
+        CHGraph g = ghStorage.getCHGraph();
         BooleanEncodedValue accessEnc = carFlagEncoder.getAccessEnc();
         DecimalEncodedValue avSpeedEnc = carFlagEncoder.getAverageSpeedEnc();
         g.edge(0, 1).setDistance(12).set(accessEnc, true).setReverse(accessEnc, true).set(avSpeedEnc, 10.0);
diff --git a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java
index 76c74de1f..557881867 100644
--- a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java
+++ b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java
@@ -111,7 +111,7 @@ public class GHUtilityTest {
         Graph g = initUnsorted(createGraph());
         g.edge(0, 0, 11, true);
 
-        CHGraph lg = new GraphBuilder(encodingManager).chGraphCreate(new FastestWeighting(carEncoder));
+        CHGraph lg = new GraphBuilder(encodingManager).chGraphCreate(CHProfile.nodeBased(new FastestWeighting(carEncoder)));
         GHUtility.copyTo(g, lg);
 
         assertEquals(g.getAllEdges().length(), lg.getEdges());
@@ -123,7 +123,7 @@ public class GHUtilityTest {
         EdgeIteratorState edgeState = g.edge(6, 5, 11, true);
         edgeState.setWayGeometry(Helper.createPointList(12, 10, -1, 3));
 
-        GraphHopperStorage newStore = new GraphBuilder(encodingManager).setCHGraph(new FastestWeighting(carEncoder)).create();
+        GraphHopperStorage newStore = new GraphBuilder(encodingManager).setCHProfiles(CHProfile.nodeBased(new FastestWeighting(carEncoder))).create();
         CHGraph lg = newStore.getCHGraph();
         GHUtility.copyTo(g, lg);
         newStore.freeze();
diff --git a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
index 8e780e985..8d4066630 100644
--- a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
@@ -853,7 +853,7 @@ public class GraphHopperIT {
                 setStoreOnFlush(true).
                 setGraphHopperLocation(tmpGraphFile).
                 setEncodingManager(EncodingManager.create(tmpImportVehicles));
-        tmpHopper.getCHFactoryDecorator().setWeightingsAsStrings(weightCalcStr);
+        tmpHopper.getCHFactoryDecorator().setCHProfileStrings(weightCalcStr);
         tmpHopper.importOrLoad();
 
         // same query as in testMonacoWithInstructions
@@ -942,7 +942,7 @@ public class GraphHopperIT {
                 setEncodingManager(EncodingManager.create("car"));
 
         tmpHopper.getCHFactoryDecorator().setEnabled(true).
-                setWeightingsAsStrings(Collections.singletonList("fastest")).
+                setCHProfilesAsStrings(Collections.singletonList("fastest")).
                 setDisablingAllowed(true);
 
         tmpHopper.getLMFactoryDecorator().setEnabled(true).
diff --git a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
index 214cf585f..087b20de8 100644
--- a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
@@ -225,7 +225,7 @@ public class GraphHopperOSMTest {
             gh.load(ghLoc);
             fail();
         } catch (Exception ex) {
-            assertTrue(ex.getMessage(), ex.getMessage().startsWith("You loaded a CH graph, but you did not specify graph.ch.weightings"));
+            assertTrue(ex.getMessage(), ex.getMessage().startsWith("You loaded a CH graph, but you did not specify any CH weightings in prepare.ch.weightings"));
         }
 
         Helper.removeDir(new File(ghLoc));
@@ -337,7 +337,7 @@ public class GraphHopperOSMTest {
                 setEncodingManager(EncodingManager.create("car")).
                 setGraphHopperLocation(ghLoc).
                 setDataReaderFile(testOsm);
-        instance.getCHFactoryDecorator().setWeightingsAsStrings("shortest");
+        instance.getCHFactoryDecorator().setCHProfileStrings("shortest");
         instance.importOrLoad();
         GHResponse rsp = instance.route(new GHRequest(51.2492152, 9.4317166, 51.2, 9.4).
                 setAlgorithm(DIJKSTRA_BI));
@@ -847,7 +847,7 @@ public class GraphHopperOSMTest {
         GraphHopper tmp = new GraphHopperOSM().
                 setCHEnabled(withCH).
                 setEncodingManager(encodingManager);
-        tmp.getCHFactoryDecorator().setWeightingsAsStrings("fastest");
+        tmp.getCHFactoryDecorator().setCHProfileStrings("fastest");
         tmp.setGraphHopperStorage(g);
         tmp.postProcessing();
 
@@ -934,7 +934,7 @@ public class GraphHopperOSMTest {
                 PrepareContractionHierarchies pch = (PrepareContractionHierarchies) raf;
                 assertTrue("Preparation wasn't run! [" + threadCount + "]", pch.isPrepared());
 
-                String name = AbstractWeighting.weightingToFileName(pch.getWeighting(), pch.isEdgeBased());
+                String name = pch.getCHProfile().toFileName();
                 Long singleThreadShortcutCount = shortcutCountMap.get(name);
                 if (singleThreadShortcutCount == null)
                     shortcutCountMap.put(name, pch.getShortcuts());
@@ -947,7 +947,7 @@ public class GraphHopperOSMTest {
 
                 String key = Parameters.CH.PREPARE + "date." + name;
                 String value = tmpGH.getGraphHopperStorage().getProperties().get(key);
-                assertTrue("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", !value.isEmpty());
+                assertFalse("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", value.isEmpty());
             }
             tmpGH.close();
         }
@@ -978,7 +978,7 @@ public class GraphHopperOSMTest {
             for (PrepareLandmarks prepLM : tmpGH.getLMFactoryDecorator().getPreparations()) {
                 assertTrue("Preparation wasn't run! [" + threadCount + "]", prepLM.isPrepared());
 
-                String name = AbstractWeighting.weightingToFileName(prepLM.getWeighting(), false);
+                String name = AbstractWeighting.weightingToFileName(prepLM.getWeighting());
                 Integer singleThreadShortcutCount = landmarkCount.get(name);
                 if (singleThreadShortcutCount == null)
                     landmarkCount.put(name, prepLM.getSubnetworksWithLandmarks());
@@ -991,7 +991,7 @@ public class GraphHopperOSMTest {
 
                 String key = Parameters.Landmark.PREPARE + "date." + name;
                 String value = tmpGH.getGraphHopperStorage().getProperties().get(key);
-                assertTrue("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", !value.isEmpty());
+                assertFalse("Properties for " + name + " did NOT contain finish date [" + threadCount + "]", value.isEmpty());
             }
             tmpGH.close();
         }
@@ -1009,10 +1009,10 @@ public class GraphHopperOSMTest {
         Weighting fwTruck = new FastestWeighting(truck);
         RAMDirectory ramDir = new RAMDirectory();
         GraphHopperStorage storage = new GraphHopperStorage(Arrays.asList(fwSimpleTruck, fwTruck), ramDir, em, false, new GraphExtension.NoOpExtension());
-        decorator.addNodeBasedWeighting(fwSimpleTruck);
-        decorator.addNodeBasedWeighting(fwTruck);
-        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, fwSimpleTruck, TraversalMode.NODE_BASED));
-        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, fwTruck, TraversalMode.NODE_BASED));
+        decorator.addCHProfile(CHProfile.nodeBased(fwSimpleTruck));
+        decorator.addCHProfile(CHProfile.nodeBased(fwTruck));
+        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, CHProfile.nodeBased(fwSimpleTruck)));
+        decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, CHProfile.nodeBased(fwTruck)));
 
         HintsMap wMap = new HintsMap("fastest");
         wMap.put("vehicle", "truck");
@@ -1021,10 +1021,10 @@ public class GraphHopperOSMTest {
         assertEquals("fastest|simple_truck", ((PrepareContractionHierarchies) decorator.getDecoratedAlgorithmFactory(null, wMap)).getWeighting().toString());
 
         // make sure weighting cannot be mixed
-        decorator.addNodeBasedWeighting(fwTruck);
-        decorator.addNodeBasedWeighting(fwSimpleTruck);
+        decorator.addCHProfile(CHProfile.nodeBased(fwTruck));
+        decorator.addCHProfile(CHProfile.nodeBased(fwSimpleTruck));
         try {
-            decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, fwSimpleTruck, TraversalMode.NODE_BASED));
+            decorator.addPreparation(PrepareContractionHierarchies.fromGraphHopperStorage(storage, CHProfile.nodeBased(fwSimpleTruck)));
             fail();
         } catch (Exception ex) {
         }
@@ -1032,17 +1032,17 @@ public class GraphHopperOSMTest {
 
     @Test
     public void testGetMultipleWeightingsForCH() {
-        EncodingManager em = EncodingManager.create(Arrays.asList(new CarFlagEncoder()), 8);
+        EncodingManager em = EncodingManager.create(Collections.singletonList(new CarFlagEncoder()), 8);
 
         GraphHopper tmpGH = new GraphHopperOSM().
                 setStoreOnFlush(false).
                 setEncodingManager(em);
-        tmpGH.getCHFactoryDecorator().setWeightingsAsStrings("fastest", "shortest");
+        tmpGH.getCHFactoryDecorator().setCHProfileStrings("fastest", "shortest");
 
-        assertEquals(2, tmpGH.getCHFactoryDecorator().getWeightingsAsStrings().size());
+        assertEquals(2, tmpGH.getCHFactoryDecorator().getCHProfileStrings().size());
     }
 
-    class TestEncoder extends CarFlagEncoder {
+    private static class TestEncoder extends CarFlagEncoder {
         private final String name;
 
         public TestEncoder(String name) {
diff --git a/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java b/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
index 1ea121c3d..f3ef03f86 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
@@ -569,7 +569,7 @@ public class RoutingAlgorithmWithOSMIT {
 
             if (withCH)
                 hopper.getCHFactoryDecorator().
-                        addWeighting(weightStr).
+                        addCHProfileAsString(weightStr).
                         setEnabled(true).
                         setEdgeBasedCHMode(CHAlgoFactoryDecorator.EdgeBasedCHMode.EDGE_OR_NODE).
                         setDisablingAllowed(true);
diff --git a/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java b/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
index d4c9d95dd..8021cac9a 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java
@@ -41,8 +41,6 @@ public class TrafficChangeWithNodeOrderingReusingTest {
     // make sure to increase xmx/xms for the JVM created by the surefire plugin in parent pom.xml
     private static final String OSM_FILE = "../local/maps/berlin-latest.osm.pbf";
 
-    private final Weighting baseWeighting;
-    private final Weighting trafficWeighting;
     private final GraphHopperStorage ghStorage;
     private final CHGraph baseCHGraph;
     private final CHGraph trafficCHGraph;
@@ -57,12 +55,12 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         this.maxDeviationPercentage = maxDeviationPercentage;
         FlagEncoder encoder = new CarFlagEncoder();
         EncodingManager em = EncodingManager.create(encoder);
-        baseWeighting = new FastestWeighting(encoder);
-        trafficWeighting = new RandomDeviationWeighting(baseWeighting, maxDeviationPercentage);
+        CHProfile baseProfile = CHProfile.nodeBased(new FastestWeighting(encoder));
+        CHProfile trafficProfile = CHProfile.nodeBased(new RandomDeviationWeighting(baseProfile.getWeighting(), maxDeviationPercentage));
         Directory dir = new RAMDirectory("traffic-change-test");
-        ghStorage = new GraphHopperStorage(Arrays.asList(baseWeighting, trafficWeighting), dir, em, false, new GraphExtension.NoOpExtension());
-        baseCHGraph = ghStorage.getCHGraph(baseWeighting);
-        trafficCHGraph = ghStorage.getCHGraph(trafficWeighting);
+        ghStorage = new GraphHopperStorage(Arrays.asList(baseProfile, trafficProfile), dir, em, false, new GraphExtension.NoOpExtension());
+        baseCHGraph = ghStorage.getCHGraph(baseProfile);
+        trafficCHGraph = ghStorage.getCHGraph(trafficProfile);
     }
 
     @Test
@@ -79,7 +77,7 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         ghStorage.freeze();
 
         // create CH
-        PrepareContractionHierarchies basePch = new PrepareContractionHierarchies(baseCHGraph, baseWeighting, TraversalMode.NODE_BASED);
+        PrepareContractionHierarchies basePch = new PrepareContractionHierarchies(baseCHGraph);
         basePch.doWork();
 
         // check correctness & performance
@@ -87,7 +85,7 @@ public class TrafficChangeWithNodeOrderingReusingTest {
         runPerformanceTest(ghStorage, baseCHGraph, basePch, seed, numQueries);
 
         // now we re-use the contraction order from the previous contraction and re-run it with the traffic weighting
-        PrepareContractionHierarchies trafficPch = new PrepareContractionHierarchies(trafficCHGraph, trafficWeighting, TraversalMode.NODE_BASED)
+        PrepareContractionHierarchies trafficPch = new PrepareContractionHierarchies(trafficCHGraph)
                 .useFixedNodeOrdering(baseCHGraph.getNodeOrderingProvider());
         trafficPch.doWork();