com.graphhopper.storage.GraphHopperStorage Maven / Gradle / Ivy
Show all versions of graphhopper Show documentation
/*
* Licensed to GraphHopper and Peter Karich under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper 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.*;
import com.graphhopper.util.EdgeExplorer;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.shapes.BBox;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
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 getGraph(Graph.class)
*
* @author Peter Karich
* @see GraphBuilder to create a (CH)Graph easier
* @see #getGraph(java.lang.Class)
*/
public final class GraphHopperStorage implements GraphStorage, Graph
{
private final Directory dir;
private EncodingManager encodingManager;
private final StorableProperties properties;
private final BaseGraph baseGraph;
// same flush order etc
private final Collection chGraphs = new ArrayList(5);
public GraphHopperStorage( Directory dir, EncodingManager encodingManager, boolean withElevation, GraphExtension extendedStorage )
{
this(Collections.emptyList(), dir, encodingManager, withElevation, extendedStorage);
}
public GraphHopperStorage( List extends Weighting> chWeightings, Directory dir, final EncodingManager encodingManager,
boolean withElevation, GraphExtension extendedStorage )
{
if (extendedStorage == null)
throw new IllegalArgumentException("GraphExtension cannot be null, use NoOpExtension");
this.encodingManager = encodingManager;
this.dir = dir;
this.properties = new StorableProperties(dir);
InternalGraphEventListener listener = new InternalGraphEventListener()
{
@Override
public void initStorage()
{
for (CHGraphImpl cg : chGraphs)
{
cg.initStorage();
}
}
@Override
public void freeze()
{
for (CHGraphImpl cg : chGraphs)
{
cg._freeze();
}
}
};
this.baseGraph = new BaseGraph(dir, encodingManager, withElevation, listener, extendedStorage);
for (Weighting w : chWeightings)
{
chGraphs.add(new CHGraphImpl(w, dir, this.baseGraph));
}
}
/**
* This method returns the routing graph for the specified weighting, could be potentially
* filled with shortcuts.
*/
public T getGraph( Class clazz, Weighting weighting )
{
if (clazz.equals(Graph.class))
return (T) baseGraph;
if (chGraphs.isEmpty())
throw new IllegalStateException("Cannot find graph implementation for " + clazz);
if (weighting == null)
throw new IllegalStateException("Cannot find CHGraph with null weighting");
List existing = new ArrayList();
for (CHGraphImpl cg : chGraphs)
{
if (cg.getWeighting() == weighting)
return (T) cg;
existing.add(cg.getWeighting());
}
throw new IllegalStateException("Cannot find CHGraph for specified weighting: " + weighting + ", existing:" + existing);
}
public T getGraph( Class clazz )
{
if (clazz.equals(Graph.class))
return (T) baseGraph;
if (chGraphs.isEmpty())
throw new IllegalStateException("Cannot find graph implementation for " + clazz);
CHGraph cg = chGraphs.iterator().next();
return (T) cg;
}
public boolean isCHPossible()
{
return !chGraphs.isEmpty();
}
public List getCHWeightings()
{
List list = new ArrayList(chGraphs.size());
for (CHGraphImpl cg : chGraphs)
{
list.add(cg.getWeighting());
}
return list;
}
/**
* @return the directory where this graph is stored.
*/
@Override
public Directory getDirectory()
{
return dir;
}
@Override
public void setSegmentSize( int bytes )
{
baseGraph.setSegmentSize(bytes);
for (CHGraphImpl cg : chGraphs)
{
cg.setSegmentSize(bytes);
}
}
/**
* After configuring this storage you need to create it explicitly.
*/
@Override
public GraphHopperStorage create( long byteCount )
{
baseGraph.checkInit();
if (encodingManager == null)
throw new IllegalStateException("EncodingManager can only be null if you call loadExisting");
long initSize = Math.max(byteCount, 100);
properties.create(100);
properties.put("graph.bytesForFlags", encodingManager.getBytesForFlags());
properties.put("graph.flagEncoders", encodingManager.toDetailsString());
properties.put("graph.byteOrder", dir.getByteOrder());
properties.put("graph.dimension", baseGraph.nodeAccess.getDimension());
properties.putCurrentVersions();
baseGraph.create(initSize);
for (CHGraphImpl cg : chGraphs)
{
cg.create(byteCount);
}
properties.put("graph.chWeightings", getCHWeightings().toString());
return this;
}
@Override
public EncodingManager getEncodingManager()
{
return encodingManager;
}
@Override
public StorableProperties getProperties()
{
return properties;
}
public void setAdditionalEdgeField( long edgePointer, int value )
{
baseGraph.setAdditionalEdgeField(edgePointer, value);
}
@Override
public void markNodeRemoved( int index )
{
baseGraph.getRemovedNodes().add(index);
}
@Override
public boolean isNodeRemoved( int index )
{
return baseGraph.getRemovedNodes().contains(index);
}
@Override
public void optimize()
{
if (isFrozen())
throw new IllegalStateException("do not optimize after graph was frozen");
int delNodes = baseGraph.getRemovedNodes().getCardinality();
if (delNodes <= 0)
return;
// Deletes only nodes.
// It reduces the fragmentation of the node space but introduces new unused edges.
baseGraph.inPlaceNodeRemove(delNodes);
// Reduce memory usage
baseGraph.trimToSize();
}
@Override
public boolean loadExisting()
{
baseGraph.checkInit();
if (properties.loadExisting())
{
properties.checkVersions(false);
// check encoding for compatiblity
String acceptStr = properties.get("graph.flagEncoders");
if (encodingManager == null)
{
if (acceptStr.isEmpty())
throw new IllegalStateException("No EncodingManager was configured. And no one was found in the graph: "
+ dir.getLocation());
int bytesForFlags = 4;
if ("8".equals(properties.get("graph.bytesForFlags")))
bytesForFlags = 8;
encodingManager = new EncodingManager(acceptStr, bytesForFlags);
} else if (!acceptStr.isEmpty() && !encodingManager.toDetailsString().equalsIgnoreCase(acceptStr))
{
throw new IllegalStateException("Encoding does not match:\nGraphhopper config: " + encodingManager.toDetailsString()
+ "\nGraph: " + acceptStr + ", dir:" + dir.getLocation());
}
String byteOrder = properties.get("graph.byteOrder");
if (!byteOrder.equalsIgnoreCase("" + dir.getByteOrder()))
throw new IllegalStateException("Configured graph.byteOrder (" + dir.getByteOrder() + ") is not equal to loaded " + byteOrder + "");
String bytesForFlags = properties.get("graph.bytesForFlags");
if (!bytesForFlags.equalsIgnoreCase("" + encodingManager.getBytesForFlags()))
throw new IllegalStateException("Configured graph.bytesForFlags (" + encodingManager.getBytesForFlags() + ") is not equal to loaded " + bytesForFlags);
String dim = properties.get("graph.dimension");
baseGraph.loadExisting(dim);
String loadedCHWeightings = properties.get("graph.chWeightings");
String configuredCHWeightings = getCHWeightings().toString();
if (!loadedCHWeightings.equals(configuredCHWeightings))
throw new IllegalStateException("Configured graph.chWeightings: " + configuredCHWeightings + " is not equal to loaded " + loadedCHWeightings);
for (CHGraphImpl cg : chGraphs)
{
if (!cg.loadExisting())
throw new IllegalStateException("Cannot load " + cg);
}
return true;
}
return false;
}
@Override
public void flush()
{
for (CHGraphImpl cg : chGraphs)
{
cg.setEdgesHeader();
cg.flush();
}
baseGraph.flush();
properties.flush();
}
@Override
public void close()
{
properties.close();
baseGraph.close();
for (CHGraphImpl cg : chGraphs)
{
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 initilize themself
*/
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.extStorage
+ "|" + getProperties().versionsToString();
}
// now all delegation graph method to avoid ugly programming flow ala
// GraphHopperStorage storage = ..;
// Graph g = storage.getGraph(Graph.class);
// instead directly the storage can be used to traverse the base graph
@Override
public Graph getBaseGraph()
{
return baseGraph;
}
@Override
public final int getNodes()
{
return baseGraph.getNodes();
}
@Override
public final NodeAccess getNodeAccess()
{
return baseGraph.getNodeAccess();
}
@Override
public final BBox getBounds()
{
return baseGraph.getBounds();
}
@Override
public final EdgeIteratorState edge( int a, int b )
{
return baseGraph.edge(a, b);
}
@Override
public final EdgeIteratorState edge( int a, int b, double distance, boolean bothDirections )
{
return baseGraph.edge(a, b, distance, bothDirections);
}
@Override
public final EdgeIteratorState getEdgeIteratorState( int edgeId, int adjNode )
{
return baseGraph.getEdgeIteratorState(edgeId, adjNode);
}
@Override
public final AllEdgesIterator getAllEdges()
{
return baseGraph.getAllEdges();
}
@Override
public final EdgeExplorer createEdgeExplorer( EdgeFilter filter )
{
return baseGraph.createEdgeExplorer(filter);
}
@Override
public final EdgeExplorer createEdgeExplorer()
{
return baseGraph.createEdgeExplorer();
}
@Override
public final Graph copyTo( Graph g )
{
return baseGraph.copyTo(g);
}
@Override
public final GraphExtension getExtension()
{
return baseGraph.getExtension();
}
}