All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.storage.GraphHopperStorage 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.storage;

import com.graphhopper.routing.util.AllEdgesIterator;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.util.EdgeExplorer;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.Helper;
import com.graphhopper.util.shapes.BBox;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * This class manages all storage related methods and delegates the calls to the associated graphs.
 * The associated graphs manage their own necessary data structures and are used to provide e.g.
 * different traversal methods. By default this class implements the graph interface and results in
 * identical behavior as the Graph instance from getBaseGraph()
 * 

* * @author Peter Karich * @see GraphBuilder to create a (CH)Graph easier */ public final class GraphHopperStorage implements GraphStorage, Graph { private final Directory dir; private final EncodingManager encodingManager; private final StorableProperties properties; private final BaseGraph baseGraph; // same flush order etc private final Collection chGraphs; private final int segmentSize; public GraphHopperStorage(Directory dir, EncodingManager encodingManager, boolean withElevation) { this(dir, encodingManager, withElevation, false); } public GraphHopperStorage(Directory dir, EncodingManager encodingManager, boolean withElevation, boolean withTurnCosts) { this(dir, encodingManager, withElevation, withTurnCosts, -1); } public GraphHopperStorage(Directory dir, EncodingManager encodingManager, boolean withElevation, boolean withTurnCosts, int segmentSize) { if (encodingManager == null) throw new IllegalArgumentException("EncodingManager needs to be non-null since 0.7. Create one using EncodingManager.create or EncodingManager.create(flagEncoderFactory, ghLocation)"); this.encodingManager = encodingManager; this.dir = dir; this.properties = new StorableProperties(dir); this.segmentSize = segmentSize; InternalGraphEventListener listener = new InternalGraphEventListener() { @Override public void initStorage() { for (CHGraphImpl cg : chGraphs) { cg.initStorage(); } } @Override public void freeze() { for (CHGraphImpl cg : chGraphs) { cg._prepareForContraction(); } } }; baseGraph = new BaseGraph(dir, encodingManager, withElevation, listener, withTurnCosts, segmentSize); chGraphs = new ArrayList<>(); } /** * Adds a {@link CHGraph} for the given {@link CHConfig}. You need to call this method before calling {@link #create(long)} * or {@link #loadExisting()}. */ public GraphHopperStorage addCHGraph(CHConfig chConfig) { baseGraph.checkNotInitialized(); if (getCHConfigs().contains(chConfig)) { throw new IllegalArgumentException("For the given CH profile a CHGraph already exists: '" + chConfig.getName() + "'"); } chGraphs.add(new CHGraphImpl(chConfig, dir, baseGraph, segmentSize)); return this; } /** * @see #addCHGraph(CHConfig) */ public GraphHopperStorage addCHGraphs(List chConfigs) { for (CHConfig chConfig : chConfigs) { addCHGraph(chConfig); } return this; } /** * If possible use {@link #getRoutingCHGraph(String)} instead, as CHGraph will be removed at some point. */ public CHGraph getCHGraph() { if (chGraphs.isEmpty()) { throw new IllegalStateException("There is no CHGraph"); } else if (chGraphs.size() > 1) { throw new IllegalStateException("There are multiple CHGraphs, use getCHGraph(CHConfig) to retrieve a specific one"); } else { return chGraphs.iterator().next(); } } /** * @return the {@link CHGraph} for the specified profile name, or null if it does not exist */ public CHGraph getCHGraph(String chGraphName) { for (CHGraphImpl cg : chGraphs) { if (cg.getCHConfig().getName().equals(chGraphName)) return cg; } return null; } public RoutingCHGraph getRoutingCHGraph() { return new RoutingCHGraphImpl(getCHGraph()); } public RoutingCHGraph getRoutingCHGraph(String chGraphName) { CHGraph chGraph = getCHGraph(chGraphName); return chGraph == null ? null : new RoutingCHGraphImpl(chGraph); } public List getCHGraphNames() { List result = new ArrayList<>(); for (CHGraphImpl cg : chGraphs) result.add(cg.getCHConfig().getName()); return result; } public boolean isCHPossible() { return !chGraphs.isEmpty(); } public List getCHConfigs() { List result = new ArrayList<>(chGraphs.size()); for (CHGraphImpl chGraph : chGraphs) { result.add(chGraph.getCHConfig()); } return result; } public List getCHConfigs(boolean edgeBased) { List result = new ArrayList<>(); List chConfigs = getCHConfigs(); for (CHConfig profile : chConfigs) { if (edgeBased == profile.isEdgeBased()) { result.add(profile); } } return result; } /** * @return the directory where this graph is stored. */ @Override public Directory getDirectory() { return dir; } /** * After configuring this storage you need to create it explicitly. */ @Override public GraphHopperStorage create(long byteCount) { baseGraph.checkNotInitialized(); if (encodingManager == null) throw new IllegalStateException("EncodingManager can only be null if you call loadExisting"); dir.create(); long initSize = Math.max(byteCount, 100); properties.create(100); properties.put("graph.encoded_values", encodingManager.toEncodedValuesAsString()); properties.put("graph.flag_encoders", encodingManager.toFlagEncodersAsString()); properties.put("graph.byte_order", dir.getByteOrder()); properties.put("graph.dimension", baseGraph.nodeAccess.getDimension()); properties.putCurrentVersions(); baseGraph.create(initSize); for (CHGraphImpl cg : chGraphs) { cg.create(byteCount); } List chConfigs = getCHConfigs(); List chProfileNames = new ArrayList<>(chConfigs.size()); for (CHConfig chConfig : chConfigs) { chProfileNames.add(chConfig.getName()); } properties.put("graph.ch.profiles", chProfileNames.toString()); return this; } @Override public EncodingManager getEncodingManager() { return encodingManager; } @Override public StorableProperties getProperties() { return properties; } @Override public boolean loadExisting() { baseGraph.checkNotInitialized(); if (properties.loadExisting()) { properties.checkVersions(false); // check encoding for compatibility String flagEncodersStr = properties.get("graph.flag_encoders"); if (!encodingManager.toFlagEncodersAsString().equalsIgnoreCase(flagEncodersStr)) { throw new IllegalStateException("Encoding does not match:" + "\nGraphhopper config: " + encodingManager.toFlagEncodersAsString() + "\nGraph: " + flagEncodersStr + "\nChange configuration to match the graph or delete " + dir.getLocation()); } String encodedValueStr = properties.get("graph.encoded_values"); if (!encodingManager.toEncodedValuesAsString().equalsIgnoreCase(encodedValueStr)) { throw new IllegalStateException("Encoded values do not match:" + "\nGraphhopper config: " + encodingManager.toEncodedValuesAsString() + "\nGraph: " + encodedValueStr + "\nChange configuration to match the graph or delete " + dir.getLocation()); } String byteOrder = properties.get("graph.byte_order"); if (!byteOrder.equalsIgnoreCase("" + dir.getByteOrder())) throw new IllegalStateException("Configured graph.byte_order (" + dir.getByteOrder() + ") is not equal to loaded " + byteOrder + ""); String dim = properties.get("graph.dimension"); baseGraph.loadExisting(dim); checkIfConfiguredAndLoadedWeightingsCompatible(); for (CHGraphImpl cg : chGraphs) { if (!cg.loadExisting()) throw new IllegalStateException("Cannot load " + cg); } return true; } return false; } private void checkIfConfiguredAndLoadedWeightingsCompatible() { String loadedStr = properties.get("graph.ch.profiles"); List loaded = Helper.parseList(loadedStr); List configured = getCHConfigs(); List configuredNames = new ArrayList<>(configured.size()); for (CHConfig p : configured) { configuredNames.add(p.getName()); } for (String configuredName : configuredNames) { if (!loaded.contains(configuredName)) { throw new IllegalStateException("Configured CH profile: '" + configuredName + "' is not contained in loaded CH profiles: '" + loadedStr + "'.\n" + "You configured: " + configuredNames); } } } @Override public void flush() { for (CHGraphImpl cg : chGraphs) { if (!cg.isClosed()) cg.flush(); } baseGraph.flush(); properties.flush(); } @Override public void close() { properties.close(); baseGraph.close(); for (CHGraphImpl cg : chGraphs) { if (!cg.isClosed()) cg.close(); } } @Override public boolean isClosed() { return baseGraph.nodes.isClosed(); } @Override public long getCapacity() { long cnt = baseGraph.getCapacity() + properties.getCapacity(); for (CHGraphImpl cg : chGraphs) { cnt += cg.getCapacity(); } return cnt; } /** * Avoid that edges and nodes of the base graph are further modified. Necessary as hook for e.g. * ch graphs on top to initialize themselves */ public synchronized void freeze() { if (!baseGraph.isFrozen()) baseGraph.freeze(); } boolean isFrozen() { return baseGraph.isFrozen(); } @Override public String toDetailsString() { String str = baseGraph.toDetailsString(); for (CHGraphImpl cg : chGraphs) { str += ", " + cg.toDetailsString(); } return str; } @Override public String toString() { return (isCHPossible() ? "CH|" : "") + encodingManager + "|" + getDirectory().getDefaultType() + "|" + baseGraph.nodeAccess.getDimension() + "D" + "|" + (baseGraph.supportsTurnCosts() ? baseGraph.turnCostStorage : "no_turn_cost") + "|" + getProperties().versionsToString(); } // now delegate all Graph methods to BaseGraph to avoid ugly programming flow ala // GraphHopperStorage storage = ..; // Graph g = storage.getBaseGraph(); // instead directly the storage can be used to traverse the base graph @Override public Graph getBaseGraph() { return baseGraph; } @Override public int getNodes() { return baseGraph.getNodes(); } @Override public int getEdges() { return baseGraph.getEdges(); } @Override public NodeAccess getNodeAccess() { return baseGraph.getNodeAccess(); } @Override public BBox getBounds() { return baseGraph.getBounds(); } @Override public EdgeIteratorState edge(int a, int b) { return baseGraph.edge(a, b); } @Override public EdgeIteratorState getEdgeIteratorState(int edgeId, int adjNode) { return baseGraph.getEdgeIteratorState(edgeId, adjNode); } @Override public EdgeIteratorState getEdgeIteratorStateForKey(int edgeKey) { return baseGraph.getEdgeIteratorStateForKey(edgeKey); } @Override public AllEdgesIterator getAllEdges() { return baseGraph.getAllEdges(); } @Override public EdgeExplorer createEdgeExplorer(EdgeFilter filter) { return baseGraph.createEdgeExplorer(filter); } @Override public Graph copyTo(Graph g) { return baseGraph.copyTo(g); } @Override public TurnCostStorage getTurnCostStorage() { return baseGraph.getTurnCostStorage(); } @Override public Weighting wrapWeighting(Weighting weighting) { return weighting; } @Override public int getOtherNode(int edge, int node) { return baseGraph.getOtherNode(edge, node); } @Override public boolean isAdjacentToNode(int edge, int node) { return baseGraph.isAdjacentToNode(edge, node); } /** * Flush and free base graph resources like way geometries and StringIndex */ public void flushAndCloseEarly() { baseGraph.flushAndCloseGeometryAndNameStorage(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy