
com.graphhopper.routing.ch.CHPreparationHandler Maven / Gradle / Ivy
/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.graphhopper.routing.ch;
import com.graphhopper.GraphHopperConfig;
import com.graphhopper.config.CHProfile;
import com.graphhopper.storage.CHConfig;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.storage.StorableProperties;
import com.graphhopper.util.PMap;
import com.graphhopper.util.Parameters.CH;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import static com.graphhopper.util.Helper.createFormatter;
import static com.graphhopper.util.Helper.getMemInfo;
/**
* This class handles the different CH preparations
*
* @author Peter Karich
* @author easbar
*/
public class CHPreparationHandler {
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
private final List preparations = new ArrayList<>();
// we first add the profiles and later read them to create the config objects (because they require
// the actual Weightings)
private final List chProfiles = new ArrayList<>();
private final List chConfigs = new ArrayList<>();
private int preparationThreads;
private ExecutorService threadPool;
private PMap pMap = new PMap();
public CHPreparationHandler() {
setPreparationThreads(1);
}
public void init(GraphHopperConfig ghConfig) {
// throw explicit error for deprecated configs
if (ghConfig.has("prepare.threads"))
throw new IllegalStateException("Use " + CH.PREPARE + "threads instead of prepare.threads");
if (ghConfig.has("prepare.chWeighting") || ghConfig.has("prepare.chWeightings") || ghConfig.has("prepare.ch.weightings"))
throw new IllegalStateException("Use profiles_ch instead of prepare.chWeighting, prepare.chWeightings or prepare.ch.weightings, see #1922 and docs/core/profiles.md");
if (ghConfig.has("prepare.ch.edge_based"))
throw new IllegalStateException("Use profiles_ch instead of prepare.ch.edge_based, see #1922 and docs/core/profiles.md");
setPreparationThreads(ghConfig.getInt(CH.PREPARE + "threads", getPreparationThreads()));
setCHProfiles(ghConfig.getCHProfiles());
pMap = ghConfig.asPMap();
}
public final boolean isEnabled() {
return !chProfiles.isEmpty() || !chConfigs.isEmpty() || !preparations.isEmpty();
}
/**
* Decouple CH profiles from PrepareContractionHierarchies as we need CH profiles for the
* graphstorage and the graphstorage for the preparation.
*/
public CHPreparationHandler addCHConfig(CHConfig chConfig) {
chConfigs.add(chConfig);
return this;
}
public CHPreparationHandler addPreparation(PrepareContractionHierarchies pch) {
// we want to make sure that CH preparations are added in the same order as their corresponding profiles
if (preparations.size() >= chConfigs.size()) {
throw new IllegalStateException("You need to add the corresponding CH configs before adding preparations.");
}
CHConfig expectedConfig = chConfigs.get(preparations.size());
if (!pch.getCHConfig().equals(expectedConfig)) {
throw new IllegalArgumentException("CH config of preparation: " + pch + " needs to be identical to previously added CH config: " + expectedConfig);
}
preparations.add(pch);
return this;
}
public final boolean hasCHConfigs() {
return !chConfigs.isEmpty();
}
public List getCHConfigs() {
return chConfigs;
}
public List getNodeBasedCHConfigs() {
List result = new ArrayList<>();
for (CHConfig chConfig : chConfigs) {
if (!chConfig.getTraversalMode().isEdgeBased()) {
result.add(chConfig);
}
}
return result;
}
public List getEdgeBasedCHConfigs() {
List result = new ArrayList<>();
for (CHConfig chConfig : chConfigs) {
if (chConfig.getTraversalMode().isEdgeBased()) {
result.add(chConfig);
}
}
return result;
}
public CHPreparationHandler setCHProfiles(CHProfile... chProfiles) {
setCHProfiles(Arrays.asList(chProfiles));
return this;
}
/**
* Enables the use of contraction hierarchies to reduce query times.
* "fastest|u_turn_costs=30 or your own weight-calculation type.
*/
public CHPreparationHandler setCHProfiles(Collection chProfiles) {
this.chProfiles.clear();
this.chProfiles.addAll(chProfiles);
return this;
}
public List getCHProfiles() {
return chProfiles;
}
public List getPreparations() {
return preparations;
}
public PrepareContractionHierarchies getPreparation(String profile) {
if (preparations.isEmpty())
throw new IllegalStateException("No CH preparations added yet");
List profileNames = new ArrayList<>(preparations.size());
for (PrepareContractionHierarchies preparation : preparations) {
profileNames.add(preparation.getCHConfig().getName());
if (preparation.getCHConfig().getName().equalsIgnoreCase(profile)) {
return preparation;
}
}
throw new IllegalArgumentException("Cannot find CH preparation for the requested profile: '" + profile + "'" +
"\nYou can try disabling CH using " + CH.DISABLE + "=true" +
"\navailable CH profiles: " + profileNames);
}
public PrepareContractionHierarchies getPreparation(CHConfig chConfig) {
return getPreparation(chConfig.getName());
}
public int getPreparationThreads() {
return preparationThreads;
}
/**
* This method changes the number of threads used for preparation on import. Default is 1. Make
* sure that you have enough memory when increasing this number!
*/
public void setPreparationThreads(int preparationThreads) {
this.preparationThreads = preparationThreads;
this.threadPool = java.util.concurrent.Executors.newFixedThreadPool(preparationThreads);
}
public void prepare(final StorableProperties properties, final boolean closeEarly) {
ExecutorCompletionService completionService = new ExecutorCompletionService<>(threadPool);
int counter = 0;
for (final PrepareContractionHierarchies prepare : preparations) {
LOGGER.info((++counter) + "/" + preparations.size() + " calling " +
"CH prepare.doWork for profile '" + prepare.getCHConfig().getName() + "' " + prepare.getCHConfig().getTraversalMode() + " ... (" + getMemInfo() + ")");
final String name = prepare.getCHConfig().getName();
completionService.submit(() -> {
// toString is not taken into account so we need to cheat, see http://stackoverflow.com/q/6113746/194609 for other options
Thread.currentThread().setName(name);
prepare.doWork();
if (closeEarly)
prepare.close();
properties.put(CH.PREPARE + "date." + name, createFormatter().format(new Date()));
}, name);
}
threadPool.shutdown();
try {
for (int i = 0; i < preparations.size(); i++) {
completionService.take().get();
}
} catch (Exception e) {
threadPool.shutdownNow();
throw new RuntimeException(e);
}
LOGGER.info("Finished CH preparation, {}", getMemInfo());
}
public void createPreparations(GraphHopperStorage ghStorage) {
if (!isEnabled() || !preparations.isEmpty())
return;
if (!hasCHConfigs())
throw new IllegalStateException("No CH profiles found");
LOGGER.info("Creating CH preparations, {}", getMemInfo());
for (CHConfig chConfig : chConfigs) {
addPreparation(createCHPreparation(ghStorage, chConfig));
}
}
private PrepareContractionHierarchies createCHPreparation(GraphHopperStorage ghStorage, CHConfig chConfig) {
PrepareContractionHierarchies pch = PrepareContractionHierarchies.fromGraphHopperStorage(ghStorage, chConfig);
pch.setParams(pMap);
return pch;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy