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

com.graphhopper.storage.CHGraphImpl Maven / Gradle / Ivy

There is a newer version: 0.8.0
Show newest version
/*
 *  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.ch.PrepareEncoder;
import com.graphhopper.routing.util.*;
import com.graphhopper.storage.BaseGraph.AllEdgeIterator;
import com.graphhopper.storage.BaseGraph.CommonEdgeIterator;
import com.graphhopper.storage.BaseGraph.EdgeIterable;
import com.graphhopper.util.*;
import static com.graphhopper.util.Helper.nf;
import com.graphhopper.util.shapes.BBox;

/**
 * A Graph implementation necessary for Contraction Hierarchies. This class enables the storage to
 * hold the level of a node and shortcut edges per edge.
 * 

* @author Peter Karich */ public class CHGraphImpl implements CHGraph, Storable { private static final double WEIGHT_FACTOR = 1000f; // 2 bits for access, for now only 32bit => not Long.MAX private static final long MAX_WEIGHT_LONG = (Integer.MAX_VALUE >> 2) << 2; private static final double MAX_WEIGHT = (Integer.MAX_VALUE >> 2) / WEIGHT_FACTOR; private int N_LEVEL; int N_CH_REF; // shortcut memory layout is synced with edges indices until E_FLAGS, then: private int S_SKIP_EDGE1, S_SKIP_EDGE2; int shortcutEntryBytes; private int shortcutCount = 0; final DataAccess shortcuts; // the nodesCH storage is limited via baseGraph.nodeCount too int nodeCHEntryBytes; final DataAccess nodesCH; final long scDirMask = PrepareEncoder.getScDirMask(); private final BaseGraph baseGraph; private final EdgeAccess chEdgeAccess; private final Weighting weighting; CHGraphImpl( Weighting w, Directory dir, final BaseGraph baseGraph ) { if (w == null) throw new IllegalStateException("Weighting for CHGraph cannot be null"); this.weighting = w; this.baseGraph = baseGraph; final String name = AbstractWeighting.weightingToFileName(w); this.nodesCH = dir.find("nodes_ch_" + name); this.shortcuts = dir.find("shortcuts_" + name); this.chEdgeAccess = new EdgeAccess(shortcuts, baseGraph.bitUtil) { @Override final EdgeIterable createSingleEdge( EdgeFilter edgeFilter ) { return new CHEdgeIteratorImpl(baseGraph, this, edgeFilter); } @Override final int getEdgeRef( int nodeId ) { return nodesCH.getInt((long) nodeId * nodeCHEntryBytes + N_CH_REF); } @Override final void setEdgeRef( int nodeId, int edgeId ) { nodesCH.setInt((long) nodeId * nodeCHEntryBytes + N_CH_REF, edgeId); } @Override final int getEntryBytes() { return shortcutEntryBytes; } @Override final long toPointer( int shortcutId ) { assert isInBounds(shortcutId) : "shortcutId " + shortcutId + " not in bounds [" + baseGraph.edgeCount + ", " + (baseGraph.edgeCount + shortcutCount) + ")"; return (long) (shortcutId - baseGraph.edgeCount) * shortcutEntryBytes; } @Override final boolean isInBounds( int shortcutId ) { int tmp = shortcutId - baseGraph.edgeCount; return tmp < shortcutCount && tmp >= 0; } @Override final long reverseFlags( long edgePointer, long flags ) { boolean isShortcut = edgePointer >= toPointer(baseGraph.edgeCount); if (!isShortcut) return baseGraph.edgeAccess.reverseFlags(edgePointer, flags); // we need a special swapping for level graph if it is a shortcut as we only store the weight and access flags then long dir = flags & scDirMask; if (dir == scDirMask || dir == 0) return flags; // swap the last bits with this mask return flags ^ scDirMask; } @Override public String toString() { return "ch edge access " + name; } }; } public final Weighting getWeighting() { return weighting; } @Override public boolean isShortcut( int edgeId ) { assert baseGraph.isFrozen() : "level graph not yet frozen"; return edgeId >= baseGraph.edgeCount; } @Override public final void setLevel( int nodeIndex, int level ) { checkNodeId(nodeIndex); nodesCH.setInt((long) nodeIndex * nodeCHEntryBytes + N_LEVEL, level); } @Override public final int getLevel( int nodeIndex ) { checkNodeId(nodeIndex); return nodesCH.getInt((long) nodeIndex * nodeCHEntryBytes + N_LEVEL); } final void checkNodeId( int nodeId ) { assert nodeId < baseGraph.getNodes() : "node " + nodeId + " is invalid. Not in [0," + baseGraph.getNodes() + ")"; } @Override public CHEdgeIteratorState shortcut( int a, int b ) { if (!baseGraph.isFrozen()) throw new IllegalStateException("Cannot create shortcut if graph is not yet frozen"); checkNodeId(a); checkNodeId(b); int scId = chEdgeAccess.internalEdgeAdd(nextShortcutId(), a, b); CHEdgeIteratorImpl iter = new CHEdgeIteratorImpl(baseGraph, chEdgeAccess, EdgeFilter.ALL_EDGES); boolean ret = iter.init(scId, b); assert ret; iter.setSkippedEdges(EdgeIterator.NO_EDGE, EdgeIterator.NO_EDGE); return iter; } protected int nextShortcutId() { int nextSC = shortcutCount; shortcutCount++; if (shortcutCount < 0) throw new IllegalStateException("too many shortcuts. new shortcut id would be negative. " + toString()); shortcuts.ensureCapacity(((long) shortcutCount + 1) * shortcutEntryBytes); return nextSC + baseGraph.edgeCount; } @Override public EdgeIteratorState edge( int a, int b, double distance, boolean bothDirections ) { return edge(a, b).setDistance(distance).setFlags(baseGraph.encodingManager.flagsDefault(true, bothDirections)); } @Override public CHEdgeIteratorState edge( int a, int b ) { // increase edge array not for shortcuts baseGraph.ensureNodeIndex(Math.max(a, b)); int edgeId = baseGraph.edgeAccess.internalEdgeAdd(baseGraph.nextEdgeId(), a, b); CHEdgeIteratorImpl iter = new CHEdgeIteratorImpl(baseGraph, baseGraph.edgeAccess, EdgeFilter.ALL_EDGES); boolean ret = iter.init(edgeId, b); assert ret; return iter; } @Override public CHEdgeExplorer createEdgeExplorer() { return createEdgeExplorer(EdgeFilter.ALL_EDGES); } @Override public CHEdgeExplorer createEdgeExplorer( EdgeFilter filter ) { return new CHEdgeIteratorImpl(baseGraph, chEdgeAccess, filter); } @Override public final CHEdgeIteratorState getEdgeIteratorState( int edgeId, int endNode ) { if (isShortcut(edgeId)) { if (!chEdgeAccess.isInBounds(edgeId)) throw new IllegalStateException("shortcutId " + edgeId + " out of bounds"); } else { if (!baseGraph.edgeAccess.isInBounds(edgeId)) throw new IllegalStateException("edgeId " + edgeId + " out of bounds"); } return (CHEdgeIteratorState) chEdgeAccess.getEdgeProps(edgeId, endNode); } @Override public int getNodes() { return baseGraph.getNodes(); } @Override public NodeAccess getNodeAccess() { return baseGraph.getNodeAccess(); } @Override public BBox getBounds() { return baseGraph.getBounds(); } void _freeze() { long maxCapacity = ((long) getNodes()) * nodeCHEntryBytes; nodesCH.ensureCapacity(maxCapacity); long baseCapacity = baseGraph.nodes.getCapacity(); // copy normal edge refs into ch edge refs for (long pointer = N_CH_REF, basePointer = baseGraph.N_EDGE_REF; pointer < maxCapacity; pointer += nodeCHEntryBytes, basePointer += baseGraph.nodeEntryBytes) { if (basePointer >= baseCapacity) throw new IllegalStateException("Cannot copy edge refs into ch graph. " + "pointer:" + pointer + ", cap:" + maxCapacity + ", basePtr:" + basePointer + ", baseCap:" + baseCapacity); nodesCH.setInt(pointer, baseGraph.nodes.getInt(basePointer)); } } String toDetailsString() { return toString() + ", shortcuts:" + nf(shortcutCount) + ", nodesCH:(" + nodesCH.getCapacity() / Helper.MB + "MB)"; } class CHEdgeIteratorImpl extends EdgeIterable implements CHEdgeExplorer, CHEdgeIterator { public CHEdgeIteratorImpl( BaseGraph baseGraph, EdgeAccess edgeAccess, EdgeFilter filter ) { super(baseGraph, edgeAccess, filter); } @Override public final long getFlags() { checkShortcut(false, "getFlags"); return super.getDirectFlags(); } @Override public final CHEdgeIterator setBaseNode( int baseNode ) { assert baseGraph.isFrozen() : "Traversal CHGraph is only possible if BaseGraph is frozen"; // always use ch edge access setEdgeId(chEdgeAccess.getEdgeRef(baseNode)); _setBaseNode(baseNode); return this; } @Override public final void setSkippedEdges( int edge1, int edge2 ) { checkShortcut(true, "setSkippedEdges"); if (EdgeIterator.Edge.isValid(edge1) != EdgeIterator.Edge.isValid(edge2)) { throw new IllegalStateException("Skipped edges of a shortcut needs " + "to be both valid or invalid but they were not " + edge1 + ", " + edge2); } shortcuts.setInt(edgePointer + S_SKIP_EDGE1, edge1); shortcuts.setInt(edgePointer + S_SKIP_EDGE2, edge2); } @Override public final int getSkippedEdge1() { checkShortcut(true, "getSkippedEdge1"); return shortcuts.getInt(edgePointer + S_SKIP_EDGE1); } @Override public final int getSkippedEdge2() { checkShortcut(true, "getSkippedEdge2"); return shortcuts.getInt(edgePointer + S_SKIP_EDGE2); } @Override public final boolean isShortcut() { // assert baseGraph.isFrozen() : "chgraph not yet frozen"; return edgeId >= baseGraph.edgeCount; } @Override public boolean isBackward( FlagEncoder encoder ) { assert encoder == weighting.getFlagEncoder() : encoder + " vs. " + weighting.getFlagEncoder(); if (isShortcut()) return (getDirectFlags() & PrepareEncoder.getScBwdDir()) != 0; return encoder.isBackward(getDirectFlags()); } @Override public boolean isForward( FlagEncoder encoder ) { assert encoder == weighting.getFlagEncoder() : encoder + " vs. " + weighting.getFlagEncoder(); if (isShortcut()) return (getDirectFlags() & PrepareEncoder.getScFwdDir()) != 0; return encoder.isForward(getDirectFlags()); } @Override public final CHEdgeIteratorState setWeight( double weight ) { checkShortcut(true, "setWeight"); CHGraphImpl.this.setWeight(this, weight); return this; } @Override public final double getWeight() { checkShortcut(true, "getWeight"); return CHGraphImpl.this.getWeight(this); } @Override protected final void selectEdgeAccess() { if (nextEdgeId < baseGraph.edgeCount) // iterate over edges edgeAccess = baseGraph.edgeAccess; else // ... or shortcuts edgeAccess = chEdgeAccess; } public void checkShortcut( boolean shouldBeShortcut, String methodName ) { if (isShortcut()) { if (!shouldBeShortcut) throw new IllegalStateException("Cannot call " + methodName + " on shortcut " + getEdge()); } else { if (shouldBeShortcut) throw new IllegalStateException("Method " + methodName + " only for shortcuts " + getEdge()); } } @Override public final String getName() { checkShortcut(false, "getName"); return super.getName(); } @Override public final EdgeIteratorState setName( String name ) { checkShortcut(false, "setName"); return super.setName(name); } @Override public final PointList fetchWayGeometry( int mode ) { checkShortcut(false, "fetchWayGeometry"); return super.fetchWayGeometry(mode); } @Override public final EdgeIteratorState setWayGeometry( PointList list ) { checkShortcut(false, "setWayGeometry"); return super.setWayGeometry(list); } @Override public boolean canBeOverwritten( long flags ) { return PrepareEncoder.canBeOverwritten(getDirectFlags(), flags); } } /** * Disconnects the edges (higher to lower node) via the specified edgeState pointing from lower to * higher node. *

* @param edgeState the edge from lower to higher */ public void disconnect( CHEdgeExplorer explorer, EdgeIteratorState edgeState ) { // search edge with opposite direction but we need to know the previousEdge for the internalEdgeDisconnect so we cannot simply do: // EdgeIteratorState tmpIter = getEdgeProps(iter.getEdge(), iter.getBaseNode()); CHEdgeIterator tmpIter = explorer.setBaseNode(edgeState.getAdjNode()); int tmpPrevEdge = EdgeIterator.NO_EDGE; while (tmpIter.next()) { if (tmpIter.isShortcut() && tmpIter.getEdge() == edgeState.getEdge()) { // TODO this is ugly, move this somehow into the underlying iteration logic long edgePointer = tmpPrevEdge == EdgeIterator.NO_EDGE ? -1 : isShortcut(tmpPrevEdge) ? chEdgeAccess.toPointer(tmpPrevEdge) : baseGraph.edgeAccess.toPointer(tmpPrevEdge); chEdgeAccess.internalEdgeDisconnect(edgeState.getEdge(), edgePointer, edgeState.getAdjNode(), edgeState.getBaseNode()); break; } tmpPrevEdge = tmpIter.getEdge(); } } @Override public AllCHEdgesIterator getAllEdges() { return new AllCHEdgesIteratorImpl(baseGraph); } class AllCHEdgesIteratorImpl extends AllEdgeIterator implements AllCHEdgesIterator { public AllCHEdgesIteratorImpl( BaseGraph baseGraph ) { super(baseGraph); } @Override protected final boolean checkRange() { if (isShortcut()) return edgeId < shortcutCount; if (super.checkRange()) return true; // iterate over shortcuts edgeAccess = chEdgeAccess; edgeId = 0; edgePointer = (long) edgeId * shortcutEntryBytes; return edgeId < shortcutCount; } @Override public int getEdge() { if (isShortcut()) return baseGraph.edgeCount + edgeId; return super.getEdge(); } @Override public boolean isBackward( FlagEncoder encoder ) { assert encoder == weighting.getFlagEncoder() : encoder + " vs. " + weighting.getFlagEncoder(); if (isShortcut()) return (getDirectFlags() & PrepareEncoder.getScBwdDir()) != 0; return encoder.isBackward(getDirectFlags()); } @Override public boolean isForward( FlagEncoder encoder ) { assert encoder == weighting.getFlagEncoder() : encoder + " vs. " + weighting.getFlagEncoder(); if (isShortcut()) return (getDirectFlags() & PrepareEncoder.getScFwdDir()) != 0; return encoder.isForward(getDirectFlags()); } @Override public final long getFlags() { if (isShortcut()) throw new IllegalStateException("Shortcut should not need to return raw flags!"); return getDirectFlags(); } @Override public int getMaxId() { return super.getMaxId() + shortcutCount; } @Override public final void setSkippedEdges( int edge1, int edge2 ) { baseGraph.edges.setInt(edgePointer + S_SKIP_EDGE1, edge1); baseGraph.edges.setInt(edgePointer + S_SKIP_EDGE2, edge2); } @Override public final int getSkippedEdge1() { return baseGraph.edges.getInt(edgePointer + S_SKIP_EDGE1); } @Override public final int getSkippedEdge2() { return baseGraph.edges.getInt(edgePointer + S_SKIP_EDGE2); } @Override public final boolean isShortcut() { assert baseGraph.isFrozen() : "level graph not yet frozen"; return edgeAccess == chEdgeAccess; } @Override public final CHEdgeIteratorState setWeight( double weight ) { CHGraphImpl.this.setWeight(this, weight); return this; } @Override public final double getWeight() { return CHGraphImpl.this.getWeight(this); } @Override public boolean canBeOverwritten( long flags ) { return PrepareEncoder.canBeOverwritten(getDirectFlags(), flags); } } final void setWeight( CommonEdgeIterator edge, double weight ) { if (weight < 0) throw new IllegalArgumentException("weight cannot be negative but was " + weight); long weightLong; if (weight > MAX_WEIGHT) weightLong = MAX_WEIGHT_LONG; else weightLong = ((long) (weight * WEIGHT_FACTOR)) << 2; long accessFlags = edge.getDirectFlags() & scDirMask; edge.setFlags(weightLong | accessFlags); } final double getWeight( CommonEdgeIterator edge ) { // no need for reverseFlags call (shortcut has identical weight if both dies) and also no need for 64bit long flags32bit = edge.getDirectFlags(); double weight = (flags32bit >>> 2) / WEIGHT_FACTOR; if (weight >= MAX_WEIGHT) return Double.POSITIVE_INFINITY; return weight; } protected int loadEdgesHeader() { shortcutCount = shortcuts.getHeader(0 * 4); shortcutEntryBytes = shortcuts.getHeader(1 * 4); return 3; } protected int setEdgesHeader() { shortcuts.setHeader(0 * 4, shortcutCount); shortcuts.setHeader(1 * 4, shortcutEntryBytes); return 3; } @Override public GraphExtension getExtension() { return baseGraph.getExtension(); } @Override public Graph getBaseGraph() { return baseGraph; } @Override public Graph copyTo( Graph g ) { CHGraphImpl tmpG = ((CHGraphImpl) g); nodesCH.copyTo(tmpG.nodesCH); shortcuts.copyTo(tmpG.shortcuts); tmpG.N_LEVEL = N_LEVEL; tmpG.N_CH_REF = N_CH_REF; tmpG.nodeCHEntryBytes = nodeCHEntryBytes; return g; } void initStorage() { EdgeAccess ea = baseGraph.edgeAccess; chEdgeAccess.init(ea.E_NODEA, ea.E_NODEB, ea.E_LINKA, ea.E_LINKB, ea.E_DIST, ea.E_FLAGS, false); // shortcuts S_SKIP_EDGE1 = ea.E_FLAGS + 4; S_SKIP_EDGE2 = S_SKIP_EDGE1 + 4; shortcutEntryBytes = S_SKIP_EDGE2 + 4; // node based data: N_LEVEL = 0; N_CH_REF = N_LEVEL + 4; nodeCHEntryBytes = N_CH_REF + 4; } void setSegmentSize( int bytes ) { nodesCH.setSegmentSize(bytes); shortcuts.setSegmentSize(bytes); } @Override public CHGraph create( long bytes ) { nodesCH.create(bytes); shortcuts.create(bytes); return this; } @Override public boolean loadExisting() { if (!nodesCH.loadExisting() || !shortcuts.loadExisting()) return false; loadEdgesHeader(); return true; } @Override public void flush() { nodesCH.flush(); shortcuts.flush(); } @Override public void close() { nodesCH.close(); shortcuts.close(); } @Override public boolean isClosed() { return nodesCH.isClosed(); } @Override public long getCapacity() { return nodesCH.getCapacity() + shortcuts.getCapacity(); } @Override public String toString() { return "CHGraph|" + getWeighting().toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy