Repository: graphhopper/graphhopper
Pull request #1899 (2020-02-12 06:50:03)

Use LMProfile instead of just Weighting for Landmark preparations
Here we maintain a pair of a profile name and a `Weighting` (=`LMProfile`) instead of just the `Weighting` for every LM preparation. This will be used to select an LM preparation by name similar to `CHProfile`. For now simply use default names `AbstractWeighting.weightingToFileName` so everything should work as before.


Patch:
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index f5d75f310..3f2e78182 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -28,6 +28,7 @@ import com.graphhopper.routing.RoutingAlgorithmFactorySimple;
 import com.graphhopper.routing.ch.CHPreparationHandler;
 import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
 import com.graphhopper.routing.lm.LMPreparationHandler;
+import com.graphhopper.routing.lm.LMProfile;
 import com.graphhopper.routing.profiles.DefaultEncodedValueFactory;
 import com.graphhopper.routing.profiles.EncodedValueFactory;
 import com.graphhopper.routing.profiles.EnumEncodedValue;
@@ -774,7 +775,7 @@ public class GraphHopper implements GraphHopperAPI {
 
         GHLock lock = null;
         try {
-            // create locks only if writes are allowed, if they are not allowed a lock cannot be created 
+            // create locks only if writes are allowed, if they are not allowed a lock cannot be created
             // (e.g. on a read only filesystem locks would fail)
             if (ghStorage.getDirectory().getDefaultType().isStoring() && isAllowWrites()) {
                 lockFactory.setLockDir(new File(ghLocation));
@@ -821,6 +822,9 @@ public class GraphHopper implements GraphHopperAPI {
 
     private void initCHPreparationHandler() {
         if (!chPreparationHandler.hasCHProfiles()) {
+            if (chPreparationHandler.getCHProfileStrings().isEmpty())
+                throw new IllegalStateException("Potential bug: chProfileStrings is empty");
+
             for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
                 for (String chWeightingStr : chPreparationHandler.getCHProfileStrings()) {
                     // ghStorage is null at this point
@@ -851,14 +855,17 @@ public class GraphHopper implements GraphHopperAPI {
     }
 
     private void initLMPreparationHandler() {
-        if (lmPreparationHandler.hasWeightings())
+        if (lmPreparationHandler.hasLMProfiles())
             return;
 
+        if (lmPreparationHandler.getLMProfileStrings().isEmpty()) {
+            throw new IllegalStateException("Potential bug: lmProfileStrings is empty");
+        }
         for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
-            for (String lmWeightingStr : lmPreparationHandler.getWeightingsAsStrings()) {
+            for (String lmWeightingStr : lmPreparationHandler.getLMProfileStrings()) {
                 // note that we do not consider turn costs during LM preparation?
                 Weighting weighting = createWeighting(new HintsMap(lmWeightingStr), encoder, NO_TURN_COST_PROVIDER);
-                lmPreparationHandler.addWeighting(weighting);
+                lmPreparationHandler.addLMProfile(new LMProfile(weighting));
             }
         }
     }
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java
index b3ecea9e0..604a6ff12 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java
@@ -79,19 +79,19 @@ public class CHPreparationHandler {
 
         if ("no".equals(chWeightingsStr) || "false".equals(chWeightingsStr)) {
             // default is fastest and we need to clear this explicitly
-            chProfileStrings.clear();
+            setCHProfilesAsStrings(Collections.<String>emptyList());
         } else if (!chWeightingsStr.isEmpty()) {
             setCHProfilesAsStrings(Arrays.asList(chWeightingsStr.split(",")));
         }
 
-        boolean enableThis = !chProfileStrings.isEmpty();
+        boolean enableThis = !getCHProfileStrings().isEmpty();
         setEnabled(enableThis);
         if (enableThis)
             setDisablingAllowed(ghConfig.getBool(CH.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
 
         String edgeBasedCHStr = ghConfig.get(CH.PREPARE + "edge_based", "off").trim();
         edgeBasedCHStr = edgeBasedCHStr.equals("false") ? "off" : edgeBasedCHStr;
-        edgeBasedCHMode = EdgeBasedCHMode.valueOf(edgeBasedCHStr.toUpperCase(Locale.ROOT));
+        setEdgeBasedCHMode(EdgeBasedCHMode.valueOf(edgeBasedCHStr.toUpperCase(Locale.ROOT)));
 
         pMap = ghConfig.asPMap();
     }
@@ -185,9 +185,6 @@ public class CHPreparationHandler {
     }
 
     public List<String> getCHProfileStrings() {
-        if (chProfileStrings.isEmpty())
-            throw new IllegalStateException("Potential bug: chProfileStrings is empty");
-
         return new ArrayList<>(chProfileStrings);
     }
 
@@ -200,9 +197,6 @@ public class CHPreparationHandler {
      * @see #addCHProfileAsString(String)
      */
     public CHPreparationHandler 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);
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java b/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java
index 8c86f1965..58d1ee342 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java
@@ -26,8 +26,6 @@ import com.graphhopper.routing.RoutingAlgorithmFactory;
 import com.graphhopper.routing.RoutingAlgorithmFactorySimple;
 import com.graphhopper.routing.ch.CHPreparationHandler;
 import com.graphhopper.routing.util.HintsMap;
-import com.graphhopper.routing.weighting.AbstractWeighting;
-import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.StorableProperties;
@@ -59,8 +57,8 @@ public class LMPreparationHandler {
     private final List<PrepareLandmarks> preparations = new ArrayList<>();
     // input weighting list from configuration file
     // one such entry can result into multiple Weighting objects e.g. fastest & car,foot => fastest|car and fastest|foot
-    private final List<String> weightingsAsStrings = new ArrayList<>();
-    private final List<Weighting> weightings = new ArrayList<>();
+    private final List<String> lmProfileStrings = new ArrayList<>();
+    private final List<LMProfile> lmProfiles = new ArrayList<>();
     private final Map<String, Double> maximumWeights = new HashMap<>();
     private boolean enabled = false;
     private int minNodes = -1;
@@ -89,10 +87,10 @@ public class LMPreparationHandler {
         String lmWeightingsStr = ghConfig.get(Landmark.PREPARE + "weightings", "");
         if (!lmWeightingsStr.isEmpty() && !lmWeightingsStr.equalsIgnoreCase("no") && !lmWeightingsStr.equalsIgnoreCase("false")) {
             List<String> tmpLMWeightingList = Arrays.asList(lmWeightingsStr.split(","));
-            setWeightingsAsStrings(tmpLMWeightingList);
+            setLMProfileStrings(tmpLMWeightingList);
         }
 
-        boolean enableThis = !weightingsAsStrings.isEmpty();
+        boolean enableThis = !getLMProfileStrings().isEmpty();
         setEnabled(enableThis);
         if (enableThis)
             setDisablingAllowed(ghConfig.getBool(Landmark.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
@@ -137,40 +135,34 @@ public class LMPreparationHandler {
     }
 
     /**
-     * Enables the use of contraction hierarchies to reduce query times. Enabled by default.
+     * Enables the use of landmarks to reduce query times.
      *
-     * @param weightingList A list containing multiple weightings like: "fastest", "shortest" or
-     *                      your own weight-calculation type.
+     * @param lmProfilesStrings A list containing multiple lm profiles like: "fastest", "shortest" or
+     *                          your own weight-calculation type.
      */
-    public LMPreparationHandler 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 LMPreparationHandler setLMProfileStrings(List<String> lmProfilesStrings) {
+        lmProfileStrings.clear();
+        for (String profileStr : lmProfilesStrings) {
+            profileStr = toLowerCase(profileStr);
+            profileStr = profileStr.trim();
+            addLMProfileAsString(profileStr);
         }
         return this;
     }
 
-    public List<String> getWeightingsAsStrings() {
-        if (this.weightingsAsStrings.isEmpty())
-            throw new IllegalStateException("Potential bug: weightingsAsStrings is empty");
-
-        return this.weightingsAsStrings;
+    public List<String> getLMProfileStrings() {
+        return lmProfileStrings;
     }
 
-    public LMPreparationHandler addWeighting(String weighting) {
-        String[] str = weighting.split("\\|");
+    public LMPreparationHandler addLMProfileAsString(String profile) {
+        String[] str = profile.split("\\|");
         double value = -1;
         if (str.length > 1) {
-            PMap map = new PMap(weighting);
+            PMap map = new PMap(profile);
             value = map.getDouble("maximum", -1);
         }
 
-        weightingsAsStrings.add(str[0]);
+        lmProfileStrings.add(str[0]);
         maximumWeights.put(str[0], value);
         return this;
     }
@@ -179,26 +171,26 @@ public class LMPreparationHandler {
      * Decouple weightings from PrepareLandmarks as we need weightings for the graphstorage and the
      * graphstorage for the preparation.
      */
-    public LMPreparationHandler addWeighting(Weighting weighting) {
-        weightings.add(weighting);
+    public LMPreparationHandler addLMProfile(LMProfile lmProfile) {
+        lmProfiles.add(lmProfile);
         return this;
     }
 
     public LMPreparationHandler addPreparation(PrepareLandmarks plm) {
         preparations.add(plm);
         int lastIndex = preparations.size() - 1;
-        if (lastIndex >= weightings.size())
-            throw new IllegalStateException("Cannot access weighting for PrepareLandmarks with " + plm.getWeighting()
-                    + ". Call add(Weighting) before");
+        if (lastIndex >= lmProfiles.size())
+            throw new IllegalStateException("Cannot access profile for PrepareLandmarks with " + plm.getLMProfile()
+                    + ". Call add(LMProfile) before");
 
-        if (preparations.get(lastIndex).getWeighting() != weightings.get(lastIndex))
-            throw new IllegalArgumentException("Weighting of PrepareContractionHierarchies " + preparations.get(lastIndex).getWeighting()
-                    + " needs to be identical to previously added " + weightings.get(lastIndex));
+        if (preparations.get(lastIndex).getLMProfile() != lmProfiles.get(lastIndex))
+            throw new IllegalArgumentException("LMProfile of PrepareLandmarks " + preparations.get(lastIndex).getLMProfile()
+                    + " needs to be identical to previously added " + lmProfiles.get(lastIndex));
         return this;
     }
 
-    public boolean hasWeightings() {
-        return !weightings.isEmpty();
+    public boolean hasLMProfiles() {
+        return !lmProfiles.isEmpty();
     }
 
     public boolean hasPreparations() {
@@ -209,10 +201,6 @@ public class LMPreparationHandler {
         return preparations.size();
     }
 
-    public List<Weighting> getWeightings() {
-        return weightings;
-    }
-
     public List<PrepareLandmarks> getPreparations() {
         return preparations;
     }
@@ -230,10 +218,10 @@ public class LMPreparationHandler {
             return new LMRoutingAlgorithmFactory(preparations.get(0), new RoutingAlgorithmFactorySimple());
         }
 
-        List<Weighting> lmWeightings = new ArrayList<>(preparations.size());
+        List<String> lmProfiles = new ArrayList<>(preparations.size());
         for (final PrepareLandmarks p : preparations) {
-            lmWeightings.add(p.getWeighting());
-            if (p.getWeighting().matches(map))
+            lmProfiles.add(p.getLMProfile().getName());
+            if (p.getLMProfile().getWeighting().matches(map))
                 return new LMRoutingAlgorithmFactory(p, new RoutingAlgorithmFactorySimple());
         }
 
@@ -244,7 +232,7 @@ public class LMPreparationHandler {
         String requestedString = (map.getWeighting().isEmpty() ? "*" : map.getWeighting()) + "|" +
                 (map.getVehicle().isEmpty() ? "*" : map.getVehicle());
         throw new IllegalArgumentException("Cannot find matching LM profile for your request." +
-                "\nrequested: " + requestedString + "\navailable: " + lmWeightings);
+                "\nrequested: " + requestedString + "\navailable: " + lmProfiles);
     }
 
     /**
@@ -271,9 +259,9 @@ public class LMPreparationHandler {
     }
 
     /**
-     * This method calculates the landmark data for all weightings (optionally in parallel) or if already existent loads it.
+     * This method calculates the landmark data for all profiles (optionally in parallel) or if already existent loads it.
      *
-     * @return true if the preparation data for at least one weighting was calculated.
+     * @return true if the preparation data for at least one profile was calculated.
      * @see CHPreparationHandler#prepare(StorableProperties, boolean) for a very similar method
      */
     public boolean loadOrDoWork(final StorableProperties properties, final boolean closeEarly) {
@@ -283,14 +271,14 @@ public class LMPreparationHandler {
         for (final PrepareLandmarks plm : preparations) {
             counter++;
             final int tmpCounter = counter;
-            final String name = AbstractWeighting.weightingToFileName(plm.getWeighting());
+            final String name = plm.getLMProfile().getName();
             completionService.submit(new Runnable() {
                 @Override
                 public void run() {
                     if (plm.loadExisting())
                         return;
 
-                    LOGGER.info(tmpCounter + "/" + getPreparations().size() + " calling LM prepare.doWork for " + plm.getWeighting() + " ... (" + getMemInfo() + ")");
+                    LOGGER.info(tmpCounter + "/" + getPreparations().size() + " calling LM prepare.doWork for " + plm.getLMProfile().getWeighting() + " ... (" + getMemInfo() + ")");
                     prepared.set(true);
                     Thread.currentThread().setName(name);
                     plm.doWork();
@@ -321,7 +309,7 @@ public class LMPreparationHandler {
     public void createPreparations(GraphHopperStorage ghStorage, LocationIndex locationIndex) {
         if (!isEnabled() || !preparations.isEmpty())
             return;
-        if (weightings.isEmpty())
+        if (lmProfiles.isEmpty())
             throw new IllegalStateException("No landmark weightings found");
 
         List<LandmarkSuggestion> lmSuggestions = new ArrayList<>(lmSuggestionsLocations.size());
@@ -335,14 +323,14 @@ public class LMPreparationHandler {
             }
         }
 
-        for (Weighting weighting : getWeightings()) {
-            Double maximumWeight = maximumWeights.get(weighting.getName());
+        for (LMProfile lmProfile : lmProfiles) {
+            Double maximumWeight = maximumWeights.get(lmProfile.getWeighting().getName());
             if (maximumWeight == null)
                 throw new IllegalStateException("maximumWeight cannot be null. Default should be just negative. " +
-                        "Couldn't find " + weighting.getName() + " in " + maximumWeights);
+                        "Couldn't find " + lmProfile.getName() + " in " + maximumWeights);
 
             PrepareLandmarks tmpPrepareLM = new PrepareLandmarks(ghStorage.getDirectory(), ghStorage,
-                    weighting, landmarkCount, activeLandmarkCount).
+                    lmProfile, landmarkCount, activeLandmarkCount).
                     setLandmarkSuggestions(lmSuggestions).
                     setMaximumWeight(maximumWeight).
                     setLogDetails(logDetails);
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMProfile.java b/core/src/main/java/com/graphhopper/routing/lm/LMProfile.java
new file mode 100644
index 000000000..0e0200fdd
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/lm/LMProfile.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for
+ *  additional information regarding copyright ownership.
+ *
+ *  GraphHopper GmbH licenses this file to you under the Apache License,
+ *  Version 2.0 (the "License"); you may not use this file except in
+ *  compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.graphhopper.routing.lm;
+
+import com.graphhopper.routing.weighting.AbstractWeighting;
+import com.graphhopper.routing.weighting.Weighting;
+
+import java.util.Objects;
+
+public class LMProfile {
+    private final String profileName;
+    private final Weighting weighting;
+
+    public LMProfile(Weighting weighting) {
+        this(AbstractWeighting.weightingToFileName(weighting), weighting);
+    }
+
+    public LMProfile(String profileName, Weighting weighting) {
+        this.profileName = profileName;
+        this.weighting = weighting;
+    }
+
+    public String getName() {
+        return profileName;
+    }
+
+    public Weighting getWeighting() {
+        return weighting;
+    }
+
+    @Override
+    public String toString() {
+        return profileName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        LMProfile lmProfile = (LMProfile) o;
+        return Objects.equals(profileName, lmProfile.profileName);
+    }
+
+    @Override
+    public int hashCode() {
+        return profileName.hashCode();
+    }
+}
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 5023b1e91..a773d4141 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java
@@ -30,7 +30,6 @@ import com.graphhopper.routing.subnetwork.TarjansSCCAlgorithm;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.util.spatialrules.SpatialRule;
 import com.graphhopper.routing.util.spatialrules.SpatialRuleLookup;
-import com.graphhopper.routing.weighting.AbstractWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
@@ -78,6 +77,7 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
     private final GraphHopperStorage graph;
     private final FlagEncoder encoder;
     private final Weighting weighting;
+    private final LMProfile lmProfile;
     private Weighting lmSelectionWeighting;
     private final TraversalMode traversalMode;
     private boolean initialized;
@@ -91,11 +91,12 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
      */
     static final long PRECISION = 1 << 16;
 
-    public LandmarkStorage(GraphHopperStorage graph, Directory dir, final Weighting weighting, int landmarks) {
+    public LandmarkStorage(GraphHopperStorage graph, Directory dir, final LMProfile lmProfile, int landmarks) {
         this.graph = graph;
         this.minimumNodes = Math.min(graph.getNodes() / 2, 500_000);
+        this.lmProfile = lmProfile;
+        this.weighting = lmProfile.getWeighting();
         this.encoder = weighting.getFlagEncoder();
-        this.weighting = weighting;
         // allowing arbitrary weighting is too dangerous
         this.lmSelectionWeighting = new ShortestWeighting(encoder) {
             @Override
@@ -118,8 +119,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);
-        this.landmarkWeightDA = dir.find("landmarks_" + name);
+        this.landmarkWeightDA = dir.find("landmarks_" + lmProfile.getName());
 
         this.landmarks = landmarks;
         // one short per landmark and two directions => 2*2 byte
@@ -127,7 +127,7 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
         this.FROM_OFFSET = 0;
         this.TO_OFFSET = 2;
         this.landmarkIDs = new ArrayList<>();
-        this.subnetworkStorage = new SubnetworkStorage(dir, "landmarks_" + name);
+        this.subnetworkStorage = new SubnetworkStorage(dir, "landmarks_" + lmProfile.getName());
     }
 
     /**
@@ -395,7 +395,7 @@ public class LandmarkStorage implements Storable<LandmarkStorage> {
                 explorer.runAlgo();
                 tmpLandmarkNodeIds[lmIdx + 1] = explorer.getLastNode();
                 if (logDetails && lmIdx % logOffset == 0)
-                    LOGGER.info("Finding landmarks [" + weighting + "] in network [" + explorer.getVisitedNodes() + "]. "
+                    LOGGER.info("Finding landmarks [" + lmProfile + "] in network [" + explorer.getVisitedNodes() + "]. "
                             + "Progress " + (int) (100.0 * lmIdx / tmpLandmarkNodeIds.length) + "%, " + Helper.getMemInfo());
             }
 
diff --git a/core/src/main/java/com/graphhopper/routing/lm/PrepareLandmarks.java b/core/src/main/java/com/graphhopper/routing/lm/PrepareLandmarks.java
index f9c2d7262..82bc63095 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/PrepareLandmarks.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/PrepareLandmarks.java
@@ -44,19 +44,19 @@ public class PrepareLandmarks extends AbstractAlgoPreparation {
     private static final Logger LOGGER = LoggerFactory.getLogger(PrepareLandmarks.class);
     private final Graph graph;
     private final LandmarkStorage lms;
-    private final Weighting weighting;
+    private final LMProfile lmProfile;
     private int defaultActiveLandmarks;
 
-    public PrepareLandmarks(Directory dir, GraphHopperStorage graph, Weighting weighting,
+    public PrepareLandmarks(Directory dir, GraphHopperStorage graph, LMProfile lmProfile,
                             int landmarks, int activeLandmarks) {
         if (activeLandmarks > landmarks)
             throw new IllegalArgumentException("Default value for active landmarks " + activeLandmarks
                     + " should be less or equal to landmark count of " + landmarks);
         this.graph = graph;
         this.defaultActiveLandmarks = activeLandmarks;
-        this.weighting = weighting;
+        this.lmProfile = lmProfile;
 
-        lms = new LandmarkStorage(graph, dir, weighting, landmarks);
+        lms = new LandmarkStorage(graph, dir, lmProfile, landmarks);
     }
 
     /**
@@ -113,8 +113,8 @@ public class PrepareLandmarks extends AbstractAlgoPreparation {
         return lms.getSubnetworksWithLandmarks();
     }
 
-    public Weighting getWeighting() {
-        return weighting;
+    public LMProfile getLMProfile() {
+        return lmProfile;
     }
 
     public boolean loadExisting() {
@@ -143,7 +143,7 @@ public class PrepareLandmarks extends AbstractAlgoPreparation {
 
             double epsilon = opts.getHints().getDouble(Parameters.Algorithms.AStar.EPSILON, 1);
             AStar astar = (AStar) algo;
-            astar.setApproximation(new LMApproximator(qGraph, weighting, this.graph.getNodes(), lms, activeLM, lms.getFactor(), false).
+            astar.setApproximation(new LMApproximator(qGraph, lmProfile.getWeighting(), this.graph.getNodes(), lms, activeLM, lms.getFactor(), false).
                     setEpsilon(epsilon));
             return algo;
         } else if (algo instanceof AStarBidirection) {
@@ -152,7 +152,7 @@ public class PrepareLandmarks extends AbstractAlgoPreparation {
 
             double epsilon = opts.getHints().getDouble(Parameters.Algorithms.AStarBi.EPSILON, 1);
             AStarBidirection astarbi = (AStarBidirection) algo;
-            astarbi.setApproximation(new LMApproximator(qGraph, weighting, this.graph.getNodes(), lms, activeLM, lms.getFactor(), false).
+            astarbi.setApproximation(new LMApproximator(qGraph, lmProfile.getWeighting(), this.graph.getNodes(), lms, activeLM, lms.getFactor(), false).
                     setEpsilon(epsilon));
             return algo;
         } else if (algo instanceof AlternativeRoute) {
@@ -161,7 +161,7 @@ public class PrepareLandmarks extends AbstractAlgoPreparation {
 
             double epsilon = opts.getHints().getDouble(Parameters.Algorithms.AStarBi.EPSILON, 1);
             AlternativeRoute altRoute = (AlternativeRoute) algo;
-            altRoute.setApproximation(new LMApproximator(qGraph, weighting, this.graph.getNodes(), lms, activeLM, lms.getFactor(), false).
+            altRoute.setApproximation(new LMApproximator(qGraph, lmProfile.getWeighting(), this.graph.getNodes(), lms, activeLM, lms.getFactor(), false).
                     setEpsilon(epsilon));
             // landmark algorithm follows good compromise between fast response and exploring 'interesting' paths so we
             // can decrease this exploration factor further (1->dijkstra, 0.8->bidir. A*)


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java
index fa5f2df9a..dc43386fe 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java
@@ -69,7 +69,7 @@ public class LMApproximatorTest {
 
         Weighting weighting = new FastestWeighting(encoder);
 
-        PrepareLandmarks lm = new PrepareLandmarks(dir, graph, weighting, 16, 8);
+        PrepareLandmarks lm = new PrepareLandmarks(dir, graph, new LMProfile(weighting), 16, 8);
         lm.setMaximumWeight(10000);
         lm.doWork();
         LandmarkStorage landmarkStorage = lm.getLandmarkStorage();
diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java
index 9d0b31a6a..2de0e93a6 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java
@@ -22,18 +22,18 @@ public class LMPreparationHandlerTest {
     public void addWeighting() {
         LMPreparationHandler handler = new LMPreparationHandler();
         handler.setEnabled(true);
-        handler.addWeighting("fastest");
-        assertEquals(Arrays.asList("fastest"), handler.getWeightingsAsStrings());
+        handler.addLMProfileAsString("fastest");
+        assertEquals(Arrays.asList("fastest"), handler.getLMProfileStrings());
 
         // special parameters like the maximum weight
         handler = new LMPreparationHandler().setEnabled(true);
-        handler.addWeighting("fastest|maximum=65000");
-        handler.addWeighting("shortest|maximum=20000");
-        assertEquals(Arrays.asList("fastest", "shortest"), handler.getWeightingsAsStrings());
+        handler.addLMProfileAsString("fastest|maximum=65000");
+        handler.addLMProfileAsString("shortest|maximum=20000");
+        assertEquals(Arrays.asList("fastest", "shortest"), handler.getLMProfileStrings());
 
         FlagEncoder car = new CarFlagEncoder();
         EncodingManager em = EncodingManager.create(car);
-        handler.addWeighting(new FastestWeighting(car)).addWeighting(new ShortestWeighting(car));
+        handler.addLMProfile(new LMProfile(new FastestWeighting(car))).addLMProfile(new LMProfile(new ShortestWeighting(car)));
         handler.createPreparations(new GraphHopperStorage(new RAMDirectory(), em, false), null);
         assertEquals(1, handler.getPreparations().get(0).getLandmarkStorage().getFactor(), .1);
         assertEquals(0.3, handler.getPreparations().get(1).getLandmarkStorage().getFactor(), .1);
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 b1e63e2c5..589acacd4 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java
@@ -65,24 +65,24 @@ public class LandmarkStorageTest {
     }
 
     @Test
-    public void testInfinitWeight() {
+    public void testInfiniteWeight() {
         Directory dir = new RAMDirectory();
         EdgeIteratorState edge = ghStorage.edge(0, 1);
-        int res = new LandmarkStorage(ghStorage, dir, new FastestWeighting(encoder) {
+        int res = new LandmarkStorage(ghStorage, dir, new LMProfile(new FastestWeighting(encoder) {
             @Override
             public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
                 return Integer.MAX_VALUE * 2L;
             }
-        }, 8).setMaximumWeight(LandmarkStorage.PRECISION).calcWeight(edge, false);
+        }), 8).setMaximumWeight(LandmarkStorage.PRECISION).calcWeight(edge, false);
         assertEquals(Integer.MAX_VALUE, res);
 
         dir = new RAMDirectory();
-        res = new LandmarkStorage(ghStorage, dir, new FastestWeighting(encoder) {
+        res = new LandmarkStorage(ghStorage, dir, new LMProfile(new FastestWeighting(encoder) {
             @Override
             public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
                 return Double.POSITIVE_INFINITY;
             }
-        }, 8).setMaximumWeight(LandmarkStorage.PRECISION).calcWeight(edge, false);
+        }), 8).setMaximumWeight(LandmarkStorage.PRECISION).calcWeight(edge, false);
         assertEquals(Integer.MAX_VALUE, res);
     }
 
@@ -93,7 +93,7 @@ public class LandmarkStorageTest {
         DataAccess da = dir.find("landmarks_fastest_car");
         da.create(2000);
 
-        LandmarkStorage lms = new LandmarkStorage(ghStorage, dir, new FastestWeighting(encoder), 4).
+        LandmarkStorage lms = new LandmarkStorage(ghStorage, dir, new LMProfile(new FastestWeighting(encoder)), 4).
                 setMaximumWeight(LandmarkStorage.PRECISION);
         // 2^16=65536, use -1 for infinity and -2 for maximum
         lms.setWeight(0, 65536);
@@ -122,7 +122,7 @@ public class LandmarkStorageTest {
         ghStorage.edge(4, 5, 10, true);
         ghStorage.edge(5, 6, 10, false);
 
-        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new FastestWeighting(encoder), 2);
+        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new LMProfile(new FastestWeighting(encoder)), 2);
         storage.setMinimumNodes(2);
         storage.createLandmarks();
         assertEquals(3, storage.getSubnetworksWithLandmarks());
@@ -140,7 +140,7 @@ public class LandmarkStorageTest {
         ghStorage.edge(3, 2, 10, false);
         ghStorage.edge(3, 4, 10, true);
 
-        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new FastestWeighting(encoder), 2);
+        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new LMProfile(new FastestWeighting(encoder)), 2);
         storage.setMinimumNodes(3);
         storage.createLandmarks();
         assertEquals(2, storage.getSubnetworksWithLandmarks());
@@ -158,7 +158,7 @@ public class LandmarkStorageTest {
         ghStorage.edge(4, 5, 10, true);
         ghStorage.edge(5, 2, 10, false);
 
-        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new FastestWeighting(encoder), 2);
+        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new LMProfile(new FastestWeighting(encoder)), 2);
         storage.setMinimumNodes(2);
         storage.createLandmarks();
 
@@ -173,7 +173,7 @@ public class LandmarkStorageTest {
         GHUtility.setProperties(ghStorage.edge(1, 2).setDistance(10), encoder, 0.9, true, true);
         ghStorage.edge(2, 3, 10, true);
 
-        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new FastestWeighting(encoder), 2);
+        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new LMProfile(new FastestWeighting(encoder)), 2);
         storage.setMinimumNodes(2);
         storage.createLandmarks();
 
@@ -185,7 +185,7 @@ public class LandmarkStorageTest {
     public void testWithBorderBlocking() {
         RoutingAlgorithmTest.initBiGraph(ghStorage);
 
-        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new FastestWeighting(encoder), 2);
+        LandmarkStorage storage = new LandmarkStorage(ghStorage, new RAMDirectory(), new LMProfile(new FastestWeighting(encoder)), 2);
         final SpatialRule ruleRight = new DefaultSpatialRule() {
             @Override
             public String getId() {
diff --git a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
index 13d286be5..cb49a1d7b 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java
@@ -96,7 +96,8 @@ public class PrepareLandmarksTest {
 
         int lm = 5, activeLM = 2;
         Weighting weighting = new FastestWeighting(encoder);
-        LandmarkStorage store = new LandmarkStorage(graph, dir, weighting, lm);
+        LMProfile lmProfile = new LMProfile(weighting);
+        LandmarkStorage store = new LandmarkStorage(graph, dir, lmProfile, lm);
         store.setMinimumNodes(2);
         store.createLandmarks();
 
@@ -135,7 +136,7 @@ public class PrepareLandmarksTest {
         AlgorithmOptions opts = AlgorithmOptions.start().weighting(weighting).traversalMode(tm).
                 build();
 
-        PrepareLandmarks prepare = new PrepareLandmarks(new RAMDirectory(), graph, weighting, 4, 2);
+        PrepareLandmarks prepare = new PrepareLandmarks(new RAMDirectory(), graph, lmProfile, 4, 2);
         prepare.setMinimumNodes(2);
         prepare.doWork();
 
@@ -184,7 +185,8 @@ public class PrepareLandmarksTest {
 
         Directory dir = new RAMDirectory(fileStr, true).create();
         Weighting weighting = new FastestWeighting(encoder);
-        PrepareLandmarks plm = new PrepareLandmarks(dir, graph, weighting, 2, 2);
+        LMProfile lmProfile = new LMProfile(weighting);
+        PrepareLandmarks plm = new PrepareLandmarks(dir, graph, lmProfile, 2, 2);
         plm.setMinimumNodes(2);
         plm.doWork();
 
@@ -196,7 +198,7 @@ public class PrepareLandmarksTest {
         assertEquals(4791, Math.round(plm.getLandmarkStorage().getFromWeight(0, 1) * expectedFactor));
 
         dir = new RAMDirectory(fileStr, true);
-        plm = new PrepareLandmarks(dir, graph, weighting, 2, 2);
+        plm = new PrepareLandmarks(dir, graph, lmProfile, 2, 2);
         assertTrue(plm.loadExisting());
         assertEquals(expectedFactor, plm.getLandmarkStorage().getFactor(), 1e-6);
         assertEquals(Arrays.toString(new int[]{
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 6ddce2cb0..2d8090155 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/DirectedRoutingTest.java
@@ -4,6 +4,7 @@ import com.graphhopper.Repeat;
 import com.graphhopper.RepeatRule;
 import com.graphhopper.routing.*;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
+import com.graphhopper.routing.lm.LMProfile;
 import com.graphhopper.routing.lm.PrepareLandmarks;
 import com.graphhopper.routing.querygraph.QueryGraph;
 import com.graphhopper.routing.util.*;
@@ -50,6 +51,7 @@ public class DirectedRoutingTest {
     private Directory dir;
     private GraphHopperStorage graph;
     private CHProfile chProfile;
+    private LMProfile lmProfile;
     private CHGraph chGraph;
     private CarFlagEncoder encoder;
     private TurnCostStorage turnCostStorage;
@@ -104,6 +106,7 @@ public class DirectedRoutingTest {
         turnCostStorage = graph.getTurnCostStorage();
         weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCosts));
         chProfile = CHProfile.edgeBased(weighting);
+        lmProfile = new LMProfile(weighting);
         graph.addCHGraph(chProfile);
         graph.create(1000);
     }
@@ -119,7 +122,7 @@ public class DirectedRoutingTest {
             chGraph = graph.getCHGraph(chProfile);
         }
         if (prepareLM) {
-            lm = new PrepareLandmarks(dir, graph, weighting, 16, 8);
+            lm = new PrepareLandmarks(dir, graph, lmProfile, 16, 8);
             lm.setMaximumWeight(1000);
             lm.doWork();
         }
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/RandomizedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/RandomizedRoutingTest.java
index 1bc012d77..22c777550 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/RandomizedRoutingTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/RandomizedRoutingTest.java
@@ -6,6 +6,7 @@ import com.graphhopper.Repeat;
 import com.graphhopper.RepeatRule;
 import com.graphhopper.routing.*;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
+import com.graphhopper.routing.lm.LMProfile;
 import com.graphhopper.routing.lm.PerfectApproximator;
 import com.graphhopper.routing.lm.PrepareLandmarks;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
@@ -120,7 +121,7 @@ public class RandomizedRoutingTest {
             chGraph = graph.getCHGraph(chProfile);
         }
         if (prepareLM) {
-            lm = new PrepareLandmarks(dir, graph, weighting, 16, 8);
+            lm = new PrepareLandmarks(dir, graph, new LMProfile(weighting), 16, 8);
             lm.setMaximumWeight(10000);
             lm.doWork();
         }
diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java
index eb40dc7d9..4dd63f5ef 100644
--- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java
+++ b/core/src/test/java/com/graphhopper/storage/GraphHopperStorageLMTest.java
@@ -49,7 +49,7 @@ public class GraphHopperStorageLMTest {
         graph.close();
 
         GraphHopper hopper = new GraphHopper().setGraphHopperLocation(defaultGraphLoc).setCHEnabled(false);
-        hopper.getLMPreparationHandler().setEnabled(true).setWeightingsAsStrings(Arrays.asList("fastest"));
+        hopper.getLMPreparationHandler().setEnabled(true).setLMProfileStrings(Arrays.asList("fastest"));
         // does lm preparation
         hopper.importOrLoad();
         EncodingManager em = hopper.getEncodingManager();
@@ -58,7 +58,7 @@ public class GraphHopperStorageLMTest {
         assertEquals(16, hopper.getLMPreparationHandler().getLandmarks());
 
         hopper = new GraphHopper().setGraphHopperLocation(defaultGraphLoc).setCHEnabled(false);
-        hopper.getLMPreparationHandler().setEnabled(true).setWeightingsAsStrings(Arrays.asList("fastest"));
+        hopper.getLMPreparationHandler().setEnabled(true).setLMProfileStrings(Arrays.asList("fastest"));
         // just loads the LM data
         hopper.importOrLoad();
         assertEquals(1, em.fetchEdgeEncoders().size());
diff --git a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
index 00cf97800..0c9218e91 100644
--- a/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/GraphHopperIT.java
@@ -191,7 +191,7 @@ public class GraphHopperIT {
         if (lm) {
             tmpHopper.getLMPreparationHandler().
                     setEnabled(true).
-                    setWeightingsAsStrings(Collections.singletonList(weightCalcStr)).
+                    setLMProfileStrings(Collections.singletonList(weightCalcStr)).
                     setDisablingAllowed(true);
         }
         tmpHopper.importAndClose();
@@ -206,7 +206,7 @@ public class GraphHopperIT {
         if (lm) {
             tmpHopper.getLMPreparationHandler().
                     setEnabled(true).
-                    setWeightingsAsStrings(Collections.singletonList(weightCalcStr)).
+                    setLMProfileStrings(Collections.singletonList(weightCalcStr)).
                     setDisablingAllowed(true);
         }
         tmpHopper.importOrLoad();
@@ -1095,7 +1095,7 @@ public class GraphHopperIT {
                 setDisablingAllowed(true);
 
         tmpHopper.getLMPreparationHandler().setEnabled(true).
-                setWeightingsAsStrings(Collections.singletonList("fastest|maximum=2000")).
+                setLMProfileStrings(Collections.singletonList("fastest|maximum=2000")).
                 setDisablingAllowed(true);
 
         tmpHopper.importOrLoad();
@@ -1152,7 +1152,7 @@ public class GraphHopperIT {
                 setDisablingAllowed(true);
 
         hopper.getLMPreparationHandler().setEnabled(true).
-                setWeightingsAsStrings(Collections.singletonList("fastest|maximum=2000")).
+                setLMProfileStrings(Collections.singletonList("fastest|maximum=2000")).
                 setDisablingAllowed(true);
 
         hopper.importOrLoad();
@@ -1193,7 +1193,7 @@ public class GraphHopperIT {
                 setGraphHopperLocation(tmpGraphFile);
         hopper.getCHPreparationHandler().setEnabled(false);
         hopper.getLMPreparationHandler().setEnabled(true).
-                setWeightingsAsStrings(Collections.singletonList("fastest|maximum=2000")).
+                setLMProfileStrings(Collections.singletonList("fastest|maximum=2000")).
                 setDisablingAllowed(true);
         hopper.importOrLoad();
 
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 d03664c14..46b4bf8a7 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
@@ -28,7 +28,6 @@ import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
 import com.graphhopper.routing.ch.PrepareContractionHierarchies;
 import com.graphhopper.routing.lm.PrepareLandmarks;
 import com.graphhopper.routing.util.*;
-import com.graphhopper.routing.weighting.AbstractWeighting;
 import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.CHProfile;
@@ -884,7 +883,7 @@ public class GraphHopperOSMTest {
                     setGraphHopperLocation(ghLoc).
                     setDataReaderFile(testOsm);
             tmpGH.getLMPreparationHandler().
-                    addWeighting("fastest").
+                    addLMProfileAsString("fastest").
                     setEnabled(true).
                     setPreparationThreads(threadCount);
 
@@ -894,7 +893,7 @@ public class GraphHopperOSMTest {
             for (PrepareLandmarks prepLM : tmpGH.getLMPreparationHandler().getPreparations()) {
                 assertTrue("Preparation wasn't run! [" + threadCount + "]", prepLM.isPrepared());
 
-                String name = AbstractWeighting.weightingToFileName(prepLM.getWeighting());
+                String name = prepLM.getLMProfile().getName();
                 Integer singleThreadShortcutCount = landmarkCount.get(name);
                 if (singleThreadShortcutCount == null)
                     landmarkCount.put(name, prepLM.getSubnetworksWithLandmarks());
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 567f094e1..9c991ecf0 100644
--- a/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
+++ b/reader-osm/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMIT.java
@@ -563,7 +563,7 @@ public class RoutingAlgorithmWithOSMIT {
             hopper.setWayPointMaxDistance(0);
 
             // always enable landmarks
-            hopper.getLMPreparationHandler().addWeighting(weightStr).
+            hopper.getLMPreparationHandler().addLMProfileAsString(weightStr).
                     setEnabled(true).setDisablingAllowed(true);
 
             if (withCH)