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

com.graphhopper.storage.BaseGraph 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.ev.*;
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.search.StringIndex;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.BBox;

import java.util.Collections;
import java.util.Locale;

import static com.graphhopper.util.Helper.nf;

/**
 * The base graph handles nodes and edges file format. It can be used with different Directory
 * implementations like RAMDirectory for fast access or via MMapDirectory for virtual-memory and not
 * thread safe usage.
 * 

* Note: A RAM DataAccess Object is thread-safe in itself but if used in this Graph implementation * it is not write thread safe. *

* Life cycle: (1) object creation, (2) configuration via setters & getters, (3) create or * loadExisting, (4) usage, (5) flush, (6) close */ class BaseGraph implements Graph { // Currently distances are stored as 4 byte integers. using a conversion factor of 1000 the minimum distance // that is not considered zero is 0.0005m (=0.5mm) and the maximum distance per edge is about 2.147.483m=2147km. // See OSMReader.addEdge and #1871. private static final double INT_DIST_FACTOR = 1000d; static double MAX_DIST = Integer.MAX_VALUE / INT_DIST_FACTOR; final DataAccess edges; final DataAccess nodes; final BBox bounds; final NodeAccess nodeAccess; private final static String STRING_IDX_NAME_KEY = "name"; final StringIndex stringIndex; // can be null if turn costs are not supported final TurnCostStorage turnCostStorage; final BitUtil bitUtil; final EncodingManager encodingManager; private final int intsForFlags; // length | nodeA | nextNode | ... | nodeB // as we use integer index in 'egdes' area => 'geometry' area is limited to 4GB (we use pos&neg values!) private final DataAccess wayGeometry; private final Directory dir; private final InternalGraphEventListener listener; /** * interval [0,n) */ protected int edgeCount; // node memory layout: protected int N_EDGE_REF, N_LAT, N_LON, N_ELE, N_TC; // edge memory layout: int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_FLAGS, E_DIST, E_GEO, E_NAME; /** * Specifies how many entries (integers) are used per edge. */ int edgeEntryBytes; /** * Specifies how many entries (integers) are used per node */ int nodeEntryBytes; private boolean initialized = false; /** * interval [0,n) */ private int nodeCount; private int edgeEntryIndex, nodeEntryIndex; private long maxGeoRef; private boolean frozen = false; public BaseGraph(Directory dir, final EncodingManager encodingManager, boolean withElevation, InternalGraphEventListener listener, boolean withTurnCosts, int segmentSize) { this.dir = dir; this.encodingManager = encodingManager; this.intsForFlags = encodingManager.getIntsForFlags(); this.bitUtil = BitUtil.get(dir.getByteOrder()); this.wayGeometry = dir.find("geometry"); this.stringIndex = new StringIndex(dir); this.nodes = dir.find("nodes", DAType.getPreferredInt(dir.getDefaultType())); this.edges = dir.find("edges", DAType.getPreferredInt(dir.getDefaultType())); this.listener = listener; this.bounds = BBox.createInverse(withElevation); this.nodeAccess = new GHNodeAccess(this, withElevation); if (withTurnCosts) { turnCostStorage = new TurnCostStorage(this, dir.find("turn_costs")); } else { turnCostStorage = null; } if (segmentSize >= 0) { setSegmentSize(segmentSize); } } boolean isInBounds(int edgeId) { return edgeId < edgeCount && edgeId >= 0; } private void setEdgeRef(long nodeId, int edgeId) { nodes.setInt(nodeId * nodeEntryBytes + N_EDGE_REF, edgeId); } int getEdgeRef(long nodeId) { return nodes.getInt(nodeId * nodeEntryBytes + N_EDGE_REF); } private int getNodeA(long edgePointer) { return edges.getInt(edgePointer + E_NODEA); } private int getNodeB(long edgePointer) { return edges.getInt(edgePointer + E_NODEB); } private int getLinkA(long edgePointer) { return edges.getInt(edgePointer + E_LINKA); } private int getLinkB(long edgePointer) { return edges.getInt(edgePointer + E_LINKB); } private long toPointer(int edgeId) { assert isInBounds(edgeId) : "edgeId " + edgeId + " not in bounds [0," + edgeCount + ")"; return (long) edgeId * edgeEntryBytes; } private int getOtherNode(int nodeThis, long edgePointer) { int nodeA = getNodeA(edgePointer); return nodeThis == nodeA ? getNodeB(edgePointer) : nodeA; } private boolean isAdjacentToNode(int node, long edgePointer) { return getNodeA(edgePointer) == node || getNodeB(edgePointer) == node; } private static boolean isTestingEnabled() { boolean enableIfAssert = false; assert (enableIfAssert = true) : true; return enableIfAssert; } @Override public Graph getBaseGraph() { return this; } void checkNotInitialized() { if (initialized) throw new IllegalStateException("You cannot configure this GraphStorage " + "after calling create or loadExisting. Calling one of the methods twice is also not allowed."); } void checkInitialized() { if (!initialized) throw new IllegalStateException("The graph has not yet been initialized."); } protected int loadNodesHeader() { nodeEntryBytes = nodes.getHeader(1 * 4); nodeCount = nodes.getHeader(2 * 4); bounds.minLon = Helper.intToDegree(nodes.getHeader(3 * 4)); bounds.maxLon = Helper.intToDegree(nodes.getHeader(4 * 4)); bounds.minLat = Helper.intToDegree(nodes.getHeader(5 * 4)); bounds.maxLat = Helper.intToDegree(nodes.getHeader(6 * 4)); if (bounds.hasElevation()) { bounds.minEle = Helper.intToEle(nodes.getHeader(7 * 4)); bounds.maxEle = Helper.intToEle(nodes.getHeader(8 * 4)); } frozen = nodes.getHeader(9 * 4) == 1; return 10; } protected int setNodesHeader() { nodes.setHeader(1 * 4, nodeEntryBytes); nodes.setHeader(2 * 4, nodeCount); nodes.setHeader(3 * 4, Helper.degreeToInt(bounds.minLon)); nodes.setHeader(4 * 4, Helper.degreeToInt(bounds.maxLon)); nodes.setHeader(5 * 4, Helper.degreeToInt(bounds.minLat)); nodes.setHeader(6 * 4, Helper.degreeToInt(bounds.maxLat)); if (bounds.hasElevation()) { nodes.setHeader(7 * 4, Helper.eleToInt(bounds.minEle)); nodes.setHeader(8 * 4, Helper.eleToInt(bounds.maxEle)); } nodes.setHeader(9 * 4, isFrozen() ? 1 : 0); return 10; } protected int loadEdgesHeader() { edgeEntryBytes = edges.getHeader(0 * 4); edgeCount = edges.getHeader(1 * 4); return 5; } protected int setEdgesHeader() { edges.setHeader(0, edgeEntryBytes); edges.setHeader(1 * 4, edgeCount); edges.setHeader(2 * 4, encodingManager.hashCode()); edges.setHeader(3 * 4, supportsTurnCosts() ? turnCostStorage.hashCode() : -1); return 5; } protected int loadWayGeometryHeader() { maxGeoRef = bitUtil.combineIntsToLong(wayGeometry.getHeader(0), wayGeometry.getHeader(4)); return 1; } protected int setWayGeometryHeader() { wayGeometry.setHeader(0, bitUtil.getIntLow(maxGeoRef)); wayGeometry.setHeader(4, bitUtil.getIntHigh(maxGeoRef)); return 1; } void initStorage() { edgeEntryIndex = 0; nodeEntryIndex = 0; E_NODEA = nextEdgeEntryIndex(4); E_NODEB = nextEdgeEntryIndex(4); E_LINKA = nextEdgeEntryIndex(4); E_LINKB = nextEdgeEntryIndex(4); E_FLAGS = nextEdgeEntryIndex(intsForFlags * 4); E_DIST = nextEdgeEntryIndex(4); E_GEO = nextEdgeEntryIndex(4); E_NAME = nextEdgeEntryIndex(4); N_EDGE_REF = nextNodeEntryIndex(4); N_LAT = nextNodeEntryIndex(4); N_LON = nextNodeEntryIndex(4); if (nodeAccess.is3D()) N_ELE = nextNodeEntryIndex(4); else N_ELE = -1; if (supportsTurnCosts()) N_TC = nextNodeEntryIndex(4); else N_TC = -1; initNodeAndEdgeEntrySize(); listener.initStorage(); initialized = true; } boolean supportsTurnCosts() { return turnCostStorage != null; } /** * Initializes the node storage such that each node has no edge and no turn cost entry */ void initNodeRefs(long oldCapacity, long newCapacity) { for (long pointer = oldCapacity + N_EDGE_REF; pointer < newCapacity; pointer += nodeEntryBytes) { nodes.setInt(pointer, EdgeIterator.NO_EDGE); } if (supportsTurnCosts()) { for (long pointer = oldCapacity + N_TC; pointer < newCapacity; pointer += nodeEntryBytes) { nodes.setInt(pointer, TurnCostStorage.NO_TURN_ENTRY); } } } protected final int nextEdgeEntryIndex(int sizeInBytes) { int tmp = edgeEntryIndex; edgeEntryIndex += sizeInBytes; return tmp; } protected final int nextNodeEntryIndex(int sizeInBytes) { int tmp = nodeEntryIndex; nodeEntryIndex += sizeInBytes; return tmp; } protected final void initNodeAndEdgeEntrySize() { nodeEntryBytes = nodeEntryIndex; edgeEntryBytes = edgeEntryIndex; } /** * Check if byte capacity of DataAcess nodes object is sufficient to include node index, else * extend byte capacity */ final void ensureNodeIndex(int nodeIndex) { checkInitialized(); if (nodeIndex < nodeCount) return; long oldNodes = nodeCount; nodeCount = nodeIndex + 1; boolean capacityIncreased = nodes.ensureCapacity((long) nodeCount * nodeEntryBytes); if (capacityIncreased) { long newBytesCapacity = nodes.getCapacity(); initNodeRefs(oldNodes * nodeEntryBytes, newBytesCapacity); } } @Override public int getNodes() { return nodeCount; } @Override public int getEdges() { return edgeCount; } @Override public NodeAccess getNodeAccess() { return nodeAccess; } @Override public BBox getBounds() { return bounds; } private void setSegmentSize(int bytes) { checkNotInitialized(); nodes.setSegmentSize(bytes); edges.setSegmentSize(bytes); wayGeometry.setSegmentSize(bytes); stringIndex.setSegmentSize(bytes); if (supportsTurnCosts()) { turnCostStorage.setSegmentSize(bytes); } } synchronized void freeze() { if (isFrozen()) throw new IllegalStateException("base graph already frozen"); frozen = true; listener.freeze(); } synchronized boolean isFrozen() { return frozen; } public void checkFreeze() { if (isFrozen()) throw new IllegalStateException("Cannot add edge or node after baseGraph.freeze was called"); } void create(long initSize) { nodes.create(initSize); edges.create(initSize); initSize = Math.min(initSize, 2000); wayGeometry.create(initSize); stringIndex.create(initSize); if (supportsTurnCosts()) { turnCostStorage.create(initSize); } initStorage(); // 0 stands for no separate geoRef maxGeoRef = 4; initNodeRefs(0, nodes.getCapacity()); } String toDetailsString() { return "edges:" + nf(edgeCount) + "(" + edges.getCapacity() / Helper.MB + "MB), " + "nodes:" + nf(getNodes()) + "(" + nodes.getCapacity() / Helper.MB + "MB), " + "name:(" + stringIndex.getCapacity() / Helper.MB + "MB), " + "geo:" + nf(maxGeoRef) + "(" + wayGeometry.getCapacity() / Helper.MB + "MB), " + "bounds:" + bounds; } public void debugPrint() { final int printMax = 100; System.out.println("nodes:"); String formatNodes = "%12s | %12s | %12s | %12s \n"; System.out.format(Locale.ROOT, formatNodes, "#", "N_EDGE_REF", "N_LAT", "N_LON"); NodeAccess nodeAccess = getNodeAccess(); for (int i = 0; i < Math.min(nodeCount, printMax); ++i) { System.out.format(Locale.ROOT, formatNodes, i, getEdgeRef(i), nodeAccess.getLat(i), nodeAccess.getLon(i)); } if (nodeCount > printMax) { System.out.format(Locale.ROOT, " ... %d more nodes\n", nodeCount - printMax); } System.out.println("edges:"); String formatEdges = "%12s | %12s | %12s | %12s | %12s | %12s | %12s \n"; System.out.format(Locale.ROOT, formatEdges, "#", "E_NODEA", "E_NODEB", "E_LINKA", "E_LINKB", "E_FLAGS", "E_DIST"); IntsRef intsRef = new IntsRef(intsForFlags); for (int i = 0; i < Math.min(edgeCount, printMax); ++i) { long edgePointer = toPointer(i); readFlags(edgePointer, intsRef); System.out.format(Locale.ROOT, formatEdges, i, getNodeA(edgePointer), getNodeB(edgePointer), getLinkA(edgePointer), getLinkB(edgePointer), intsRef, getDist(edgePointer)); } if (edgeCount > printMax) { System.out.printf(Locale.ROOT, " ... %d more edges", edgeCount - printMax); } } /** * Flush and free resources that are not needed for post-processing (way geometries and StringIndex). */ void flushAndCloseGeometryAndNameStorage() { setWayGeometryHeader(); wayGeometry.flush(); wayGeometry.close(); stringIndex.flush(); stringIndex.close(); } public void flush() { if (!wayGeometry.isClosed()) { setWayGeometryHeader(); wayGeometry.flush(); } if (!stringIndex.isClosed()) stringIndex.flush(); setNodesHeader(); setEdgesHeader(); edges.flush(); nodes.flush(); if (supportsTurnCosts()) { turnCostStorage.flush(); } } public void close() { if (!wayGeometry.isClosed()) wayGeometry.close(); if (!stringIndex.isClosed()) stringIndex.close(); edges.close(); nodes.close(); if (supportsTurnCosts()) { turnCostStorage.close(); } } long getCapacity() { return edges.getCapacity() + nodes.getCapacity() + stringIndex.getCapacity() + wayGeometry.getCapacity() + (supportsTurnCosts() ? turnCostStorage.getCapacity() : 0); } long getMaxGeoRef() { return maxGeoRef; } void loadExisting(String dim) { if (!nodes.loadExisting()) throw new IllegalStateException("Cannot load nodes. corrupt file or directory? " + dir); if (!dim.equalsIgnoreCase("" + nodeAccess.getDimension())) throw new IllegalStateException("Configured dimension (" + nodeAccess.getDimension() + ") is not equal " + "to dimension of loaded graph (" + dim + ")"); if (!edges.loadExisting()) throw new IllegalStateException("Cannot load edges. corrupt file or directory? " + dir); if (!wayGeometry.loadExisting()) throw new IllegalStateException("Cannot load geometry. corrupt file or directory? " + dir); if (!stringIndex.loadExisting()) throw new IllegalStateException("Cannot load name index. corrupt file or directory? " + dir); if (supportsTurnCosts() && !turnCostStorage.loadExisting()) throw new IllegalStateException("Cannot load turn cost storage. corrupt file or directory? " + dir); // first define header indices of this storage initStorage(); // now load some properties from stored data loadNodesHeader(); loadEdgesHeader(); loadWayGeometryHeader(); } /** * This method copies the properties of one {@link EdgeIteratorState} to another. * * @return the updated iterator the properties where copied to. */ EdgeIteratorState copyProperties(EdgeIteratorState from, EdgeIteratorStateImpl to) { long edgePointer = toPointer(to.getEdge()); writeFlags(edgePointer, from.getFlags()); // copy the rest with higher level API to.setDistance(from.getDistance()). setName(from.getName()). setWayGeometry(from.fetchWayGeometry(FetchMode.PILLAR_ONLY)); return to; } /** * Create edge between nodes a and b * * @return EdgeIteratorState of newly created edge */ @Override public EdgeIteratorState edge(int nodeA, int nodeB) { if (isFrozen()) throw new IllegalStateException("Cannot create edge if graph is already frozen"); ensureNodeIndex(Math.max(nodeA, nodeB)); int edgeId = internalEdgeAdd(nextEdgeId(), nodeA, nodeB); EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(this); boolean valid = edge.init(edgeId, nodeB); assert valid; return edge; } /** * Writes a new edge to the array of edges and adds it to the linked list of edges at nodeA and nodeB */ final int internalEdgeAdd(int newEdgeId, int nodeA, int nodeB) { writeEdge(newEdgeId, nodeA, nodeB); long edgePointer = toPointer(newEdgeId); int edge = getEdgeRef(nodeA); if (edge > EdgeIterator.NO_EDGE) edges.setInt(E_LINKA + edgePointer, edge); setEdgeRef(nodeA, newEdgeId); if (nodeA != nodeB) { edge = getEdgeRef(nodeB); if (edge > EdgeIterator.NO_EDGE) edges.setInt(E_LINKB + edgePointer, edge); setEdgeRef(nodeB, newEdgeId); } return newEdgeId; } /** * Writes plain edge information to the edges index */ private long writeEdge(int edgeId, int nodeA, int nodeB) { if (!EdgeIterator.Edge.isValid(edgeId)) throw new IllegalStateException("Cannot write edge with illegal ID:" + edgeId + "; nodeA:" + nodeA + ", nodeB:" + nodeB); long edgePointer = toPointer(edgeId); edges.setInt(edgePointer + E_NODEA, nodeA); edges.setInt(edgePointer + E_NODEB, nodeB); edges.setInt(edgePointer + E_LINKA, EdgeIterator.NO_EDGE); edges.setInt(edgePointer + E_LINKB, EdgeIterator.NO_EDGE); return edgePointer; } // for test only void setEdgeCount(int cnt) { edgeCount = cnt; } /** * Determine next free edgeId and ensure byte capacity to store edge * * @return next free edgeId */ protected int nextEdgeId() { int nextEdge = edgeCount; edgeCount++; if (edgeCount < 0) throw new IllegalStateException("too many edges. new edge id would be negative. " + toString()); edges.ensureCapacity(((long) edgeCount + 1) * edgeEntryBytes); return nextEdge; } @Override public EdgeIteratorState getEdgeIteratorState(int edgeId, int adjNode) { if (!isInBounds(edgeId)) throw new IllegalStateException("edgeId " + edgeId + " out of bounds, edgeCount: " + edgeCount); checkAdjNodeBounds(adjNode); EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(this); if (edge.init(edgeId, adjNode)) return edge; // if edgeId exists but adjacent nodes do not match return null; } @Override public EdgeIteratorState getEdgeIteratorStateForKey(int edgeKey) { EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(this); edge.init(edgeKey); return edge; } final void checkAdjNodeBounds(int adjNode) { if (adjNode < 0 && adjNode != Integer.MIN_VALUE || adjNode >= nodeCount) throw new IllegalStateException("adjNode " + adjNode + " out of bounds [0," + nf(nodeCount) + ")"); } @Override public EdgeExplorer createEdgeExplorer(EdgeFilter filter) { return new EdgeIteratorImpl(this, filter); } @Override public EdgeExplorer createEdgeExplorer() { return createEdgeExplorer(EdgeFilter.ALL_EDGES); } @Override public AllEdgesIterator getAllEdges() { return new AllEdgeIterator(this); } @Override public Graph copyTo(Graph g) { initialized = true; if (g.getClass().equals(getClass())) { _copyTo((BaseGraph) g); return g; } else { return GHUtility.copyTo(this, g); } } void _copyTo(BaseGraph clonedG) { if (clonedG.edgeEntryBytes != edgeEntryBytes) throw new IllegalStateException("edgeEntryBytes cannot be different for cloned graph. " + "Cloned: " + clonedG.edgeEntryBytes + " vs " + edgeEntryBytes); if (clonedG.nodeEntryBytes != nodeEntryBytes) throw new IllegalStateException("nodeEntryBytes cannot be different for cloned graph. " + "Cloned: " + clonedG.nodeEntryBytes + " vs " + nodeEntryBytes); if (clonedG.nodeAccess.getDimension() != nodeAccess.getDimension()) throw new IllegalStateException("dimension cannot be different for cloned graph. " + "Cloned: " + clonedG.nodeAccess.getDimension() + " vs " + nodeAccess.getDimension()); // nodes setNodesHeader(); nodes.copyTo(clonedG.nodes); clonedG.loadNodesHeader(); // edges setEdgesHeader(); edges.copyTo(clonedG.edges); clonedG.loadEdgesHeader(); // name stringIndex.copyTo(clonedG.stringIndex); // geometry setWayGeometryHeader(); wayGeometry.copyTo(clonedG.wayGeometry); clonedG.loadWayGeometryHeader(); // turn cost storage if (supportsTurnCosts()) { turnCostStorage.copyTo(clonedG.turnCostStorage); } } @Override public TurnCostStorage getTurnCostStorage() { return turnCostStorage; } @Override public Weighting wrapWeighting(Weighting weighting) { return weighting; } @Override public int getOtherNode(int edge, int node) { long edgePointer = toPointer(edge); return getOtherNode(node, edgePointer); } @Override public boolean isAdjacentToNode(int edge, int node) { long edgePointer = toPointer(edge); return isAdjacentToNode(node, edgePointer); } private void readFlags(long edgePointer, IntsRef edgeFlags) { int size = edgeFlags.ints.length; for (int i = 0; i < size; i++) { edgeFlags.ints[i] = edges.getInt(edgePointer + E_FLAGS + i * 4); } } private void writeFlags(long edgePointer, IntsRef edgeFlags) { int size = edgeFlags.ints.length; for (int i = 0; i < size; i++) { edges.setInt(edgePointer + E_FLAGS + i * 4, edgeFlags.ints[i]); } } private void setDist(long edgePointer, double distance) { edges.setInt(edgePointer + E_DIST, distToInt(distance)); } /** * Translates double distance to integer in order to save it in a DataAccess object */ private int distToInt(double distance) { if (distance < 0) throw new IllegalArgumentException("Distance cannot be negative: " + distance); if (distance > MAX_DIST) { distance = MAX_DIST; } int integ = (int) Math.round(distance * INT_DIST_FACTOR); assert integ >= 0 : "distance out of range"; return integ; } /** * returns distance (already translated from integer to double) */ private double getDist(long pointer) { int val = edges.getInt(pointer + E_DIST); // do never return infinity even if INT MAX, see #435 return val / INT_DIST_FACTOR; } private void setWayGeometry_(PointList pillarNodes, long edgePointer, boolean reverse) { if (pillarNodes != null && !pillarNodes.isEmpty()) { if (pillarNodes.getDimension() != nodeAccess.getDimension()) throw new IllegalArgumentException("Cannot use pointlist which is " + pillarNodes.getDimension() + "D for graph which is " + nodeAccess.getDimension() + "D"); long existingGeoRef = Helper.toUnsignedLong(edges.getInt(edgePointer + E_GEO)); int len = pillarNodes.getSize(); int dim = nodeAccess.getDimension(); if (existingGeoRef > 0) { final int count = wayGeometry.getInt(existingGeoRef * 4L); if (len <= count) { setWayGeometryAtGeoRef(pillarNodes, edgePointer, reverse, existingGeoRef); return; } } long nextGeoRef = nextGeoRef(len * dim); setWayGeometryAtGeoRef(pillarNodes, edgePointer, reverse, nextGeoRef); } else { edges.setInt(edgePointer + E_GEO, 0); } } private void setWayGeometryAtGeoRef(PointList pillarNodes, long edgePointer, boolean reverse, long geoRef) { int len = pillarNodes.getSize(); int dim = nodeAccess.getDimension(); long geoRefPosition = geoRef * 4; int totalLen = len * dim * 4 + 4; ensureGeometry(geoRefPosition, totalLen); byte[] wayGeometryBytes = createWayGeometryBytes(pillarNodes, reverse); wayGeometry.setBytes(geoRefPosition, wayGeometryBytes, wayGeometryBytes.length); edges.setInt(edgePointer + E_GEO, Helper.toSignedInt(geoRef)); } private byte[] createWayGeometryBytes(PointList pillarNodes, boolean reverse) { int len = pillarNodes.getSize(); int dim = nodeAccess.getDimension(); int totalLen = len * dim * 4 + 4; byte[] bytes = new byte[totalLen]; bitUtil.fromInt(bytes, len, 0); if (reverse) pillarNodes.reverse(); int tmpOffset = 4; boolean is3D = nodeAccess.is3D(); for (int i = 0; i < len; i++) { double lat = pillarNodes.getLat(i); bitUtil.fromInt(bytes, Helper.degreeToInt(lat), tmpOffset); tmpOffset += 4; bitUtil.fromInt(bytes, Helper.degreeToInt(pillarNodes.getLon(i)), tmpOffset); tmpOffset += 4; if (is3D) { bitUtil.fromInt(bytes, Helper.eleToInt(pillarNodes.getEle(i)), tmpOffset); tmpOffset += 4; } } return bytes; } private PointList fetchWayGeometry_(long edgePointer, boolean reverse, FetchMode mode, int baseNode, int adjNode) { if (mode == FetchMode.TOWER_ONLY) { // no reverse handling required as adjNode and baseNode is already properly switched PointList pillarNodes = new PointList(2, nodeAccess.is3D()); pillarNodes.add(nodeAccess, baseNode); pillarNodes.add(nodeAccess, adjNode); return pillarNodes; } long geoRef = Helper.toUnsignedLong(edges.getInt(edgePointer + E_GEO)); int count = 0; byte[] bytes = null; if (geoRef > 0) { geoRef *= 4L; count = wayGeometry.getInt(geoRef); geoRef += 4L; bytes = new byte[count * nodeAccess.getDimension() * 4]; wayGeometry.getBytes(geoRef, bytes, bytes.length); } else if (mode == FetchMode.PILLAR_ONLY) return PointList.EMPTY; PointList pillarNodes = new PointList(getPointListLength(count, mode), nodeAccess.is3D()); if (reverse) { if (mode == FetchMode.ALL || mode == FetchMode.PILLAR_AND_ADJ) pillarNodes.add(nodeAccess, adjNode); } else if (mode == FetchMode.ALL || mode == FetchMode.BASE_AND_PILLAR) pillarNodes.add(nodeAccess, baseNode); int index = 0; for (int i = 0; i < count; i++) { double lat = Helper.intToDegree(bitUtil.toInt(bytes, index)); index += 4; double lon = Helper.intToDegree(bitUtil.toInt(bytes, index)); index += 4; if (nodeAccess.is3D()) { pillarNodes.add(lat, lon, Helper.intToEle(bitUtil.toInt(bytes, index))); index += 4; } else { pillarNodes.add(lat, lon); } } if (reverse) { if (mode == FetchMode.ALL || mode == FetchMode.BASE_AND_PILLAR) pillarNodes.add(nodeAccess, baseNode); pillarNodes.reverse(); } else if (mode == FetchMode.ALL || mode == FetchMode.PILLAR_AND_ADJ) pillarNodes.add(nodeAccess, adjNode); return pillarNodes; } static int getPointListLength(int pillarNodes, FetchMode mode) { switch (mode) { case TOWER_ONLY: return 2; case PILLAR_ONLY: return pillarNodes; case BASE_AND_PILLAR: case PILLAR_AND_ADJ: return pillarNodes + 1; case ALL: return pillarNodes + 2; } throw new IllegalArgumentException("Mode isn't handled " + mode); } private void setName(long edgePointer, String name) { int stringIndexRef = (int) stringIndex.add(Collections.singletonMap(STRING_IDX_NAME_KEY, name)); if (stringIndexRef < 0) throw new IllegalStateException("Too many names are stored, currently limited to int pointer"); edges.setInt(edgePointer + E_NAME, stringIndexRef); } private void ensureGeometry(long bytePos, int byteLength) { wayGeometry.ensureCapacity(bytePos + byteLength); } private long nextGeoRef(int arrayLength) { long tmp = maxGeoRef; maxGeoRef += arrayLength + 1L; if (maxGeoRef >= 0xFFFFffffL) throw new IllegalStateException("Geometry too large, does not fit in 32 bits " + maxGeoRef); return tmp; } protected static class EdgeIteratorImpl extends EdgeIteratorStateImpl implements EdgeExplorer, EdgeIterator { final EdgeFilter filter; int nextEdgeId; public EdgeIteratorImpl(BaseGraph baseGraph, EdgeFilter filter) { super(baseGraph); if (filter == null) throw new IllegalArgumentException("Instead null filter use EdgeFilter.ALL_EDGES"); this.filter = filter; } @Override public EdgeIterator setBaseNode(int baseNode) { nextEdgeId = edgeId = baseGraph.getEdgeRef(baseNode); this.baseNode = baseNode; return this; } @Override public final boolean next() { while (true) { if (!EdgeIterator.Edge.isValid(nextEdgeId)) return false; goToNext(); if (filter.accept(this)) { return true; } } } void goToNext() { edgePointer = baseGraph.toPointer(nextEdgeId); edgeId = nextEdgeId; int nodeA = baseGraph.getNodeA(edgePointer); boolean baseNodeIsNodeA = baseNode == nodeA; adjNode = baseNodeIsNodeA ? baseGraph.getNodeB(edgePointer) : nodeA; reverse = !baseNodeIsNodeA; freshFlags = false; // position to next edge nextEdgeId = baseNodeIsNodeA ? baseGraph.getLinkA(edgePointer) : baseGraph.getLinkB(edgePointer); assert nextEdgeId != edgeId : ("endless loop detected for base node: " + baseNode + ", adj node: " + adjNode + ", edge pointer: " + edgePointer + ", edge: " + edgeId); } @Override public EdgeIteratorState detach(boolean reverseArg) { if (edgeId == nextEdgeId) throw new IllegalStateException("call next before detaching (edgeId:" + edgeId + " vs. next " + nextEdgeId + ")"); return super.detach(reverseArg); } } /** * Include all edges of this storage in the iterator. */ protected static class AllEdgeIterator extends EdgeIteratorStateImpl implements AllEdgesIterator { public AllEdgeIterator(BaseGraph baseGraph) { super(baseGraph); } @Override public int length() { return baseGraph.edgeCount; } @Override public boolean next() { edgeId++; if (edgeId >= baseGraph.edgeCount) return false; edgePointer = baseGraph.toPointer(edgeId); baseNode = baseGraph.getNodeA(edgePointer); adjNode = baseGraph.getNodeB(edgePointer); freshFlags = false; reverse = false; return true; } @Override public final EdgeIteratorState detach(boolean reverseArg) { if (edgePointer < 0) throw new IllegalStateException("call next before detaching"); AllEdgeIterator iter = new AllEdgeIterator(baseGraph); iter.edgeId = edgeId; iter.edgePointer = edgePointer; if (reverseArg) { iter.reverse = !this.reverse; iter.baseNode = adjNode; iter.adjNode = baseNode; } else { iter.reverse = this.reverse; iter.baseNode = baseNode; iter.adjNode = adjNode; } return iter; } } static class EdgeIteratorStateImpl implements EdgeIteratorState { final BaseGraph baseGraph; long edgePointer = -1; int baseNode; int adjNode; // we need reverse if detach is called boolean reverse = false; boolean freshFlags; int edgeId = -1; private final IntsRef edgeFlags; public EdgeIteratorStateImpl(BaseGraph baseGraph) { this.baseGraph = baseGraph; this.edgeFlags = new IntsRef(baseGraph.intsForFlags); } /** * @return false if the edge has not a node equal to expectedAdjNode */ final boolean init(int edgeId, int expectedAdjNode) { if (!EdgeIterator.Edge.isValid(edgeId)) throw new IllegalArgumentException("fetching the edge requires a valid edgeId but was " + edgeId); this.edgeId = edgeId; edgePointer = baseGraph.toPointer(edgeId); baseNode = baseGraph.getNodeA(edgePointer); adjNode = baseGraph.getNodeB(edgePointer); freshFlags = false; if (expectedAdjNode == adjNode || expectedAdjNode == Integer.MIN_VALUE) { reverse = false; return true; } else if (expectedAdjNode == baseNode) { reverse = true; baseNode = adjNode; adjNode = expectedAdjNode; return true; } return false; } /** * Similar to {@link #init(int edgeId, int adjNode)}, but here we retrieve the edge in a certain direction * directly using an edge key. */ final void init(int edgeKey) { if (edgeKey < 0) throw new IllegalArgumentException("edge keys must not be negative, given: " + edgeKey); this.edgeId = GHUtility.getEdgeFromEdgeKey(edgeKey); edgePointer = baseGraph.toPointer(edgeId); baseNode = baseGraph.getNodeA(edgePointer); adjNode = baseGraph.getNodeB(edgePointer); freshFlags = false; if (edgeKey % 2 == 0 || baseNode == adjNode) { reverse = false; } else { reverse = true; int tmp = baseNode; baseNode = adjNode; adjNode = tmp; } } @Override public final int getBaseNode() { return baseNode; } @Override public final int getAdjNode() { return adjNode; } @Override public double getDistance() { return baseGraph.getDist(edgePointer); } @Override public EdgeIteratorState setDistance(double dist) { baseGraph.setDist(edgePointer, dist); return this; } @Override public IntsRef getFlags() { if (!freshFlags) { baseGraph.readFlags(edgePointer, edgeFlags); freshFlags = true; } return edgeFlags; } @Override public final EdgeIteratorState setFlags(IntsRef edgeFlags) { assert edgeId < baseGraph.edgeCount : "must be edge but was shortcut: " + edgeId + " >= " + baseGraph.edgeCount + ". Use setFlagsAndWeight"; baseGraph.writeFlags(edgePointer, edgeFlags); for (int i = 0; i < edgeFlags.ints.length; i++) { this.edgeFlags.ints[i] = edgeFlags.ints[i]; } freshFlags = true; return this; } @Override public boolean get(BooleanEncodedValue property) { return property.getBool(reverse, getFlags()); } @Override public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { property.setBool(reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public boolean getReverse(BooleanEncodedValue property) { return property.getBool(!reverse, getFlags()); } @Override public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) { property.setBool(!reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public EdgeIteratorState set(BooleanEncodedValue property, boolean fwd, boolean bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); property.setBool(reverse, getFlags(), fwd); property.setBool(!reverse, getFlags(), bwd); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public int get(IntEncodedValue property) { return property.getInt(reverse, getFlags()); } @Override public EdgeIteratorState set(IntEncodedValue property, int value) { property.setInt(reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public int getReverse(IntEncodedValue property) { return property.getInt(!reverse, getFlags()); } @Override public EdgeIteratorState setReverse(IntEncodedValue property, int value) { property.setInt(!reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public EdgeIteratorState set(IntEncodedValue property, int fwd, int bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); property.setInt(reverse, getFlags(), fwd); property.setInt(!reverse, getFlags(), bwd); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public double get(DecimalEncodedValue property) { return property.getDecimal(reverse, getFlags()); } @Override public EdgeIteratorState set(DecimalEncodedValue property, double value) { property.setDecimal(reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public double getReverse(DecimalEncodedValue property) { return property.getDecimal(!reverse, getFlags()); } @Override public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) { property.setDecimal(!reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public EdgeIteratorState set(DecimalEncodedValue property, double fwd, double bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); property.setDecimal(reverse, getFlags(), fwd); property.setDecimal(!reverse, getFlags(), bwd); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public > T get(EnumEncodedValue property) { return property.getEnum(reverse, getFlags()); } @Override public > EdgeIteratorState set(EnumEncodedValue property, T value) { property.setEnum(reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public > T getReverse(EnumEncodedValue property) { return property.getEnum(!reverse, getFlags()); } @Override public > EdgeIteratorState setReverse(EnumEncodedValue property, T value) { property.setEnum(!reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public > EdgeIteratorState set(EnumEncodedValue property, T fwd, T bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); property.setEnum(reverse, getFlags(), fwd); property.setEnum(!reverse, getFlags(), bwd); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public String get(StringEncodedValue property) { return property.getString(reverse, getFlags()); } @Override public EdgeIteratorState set(StringEncodedValue property, String value) { property.setString(reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public String getReverse(StringEncodedValue property) { return property.getString(!reverse, getFlags()); } @Override public EdgeIteratorState setReverse(StringEncodedValue property, String value) { property.setString(!reverse, getFlags(), value); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public EdgeIteratorState set(StringEncodedValue property, String fwd, String bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); property.setString(reverse, getFlags(), fwd); property.setString(!reverse, getFlags(), bwd); baseGraph.writeFlags(edgePointer, getFlags()); return this; } @Override public final EdgeIteratorState copyPropertiesFrom(EdgeIteratorState edge) { return baseGraph.copyProperties(edge, this); } @Override public EdgeIteratorState setWayGeometry(PointList pillarNodes) { baseGraph.setWayGeometry_(pillarNodes, edgePointer, reverse); return this; } @Override public PointList fetchWayGeometry(FetchMode mode) { return baseGraph.fetchWayGeometry_(edgePointer, reverse, mode, getBaseNode(), getAdjNode()); } @Override public int getEdge() { return edgeId; } @Override public int getEdgeKey() { return GHUtility.createEdgeKey(edgeId, reverse); } @Override public int getOrigEdgeFirst() { return getEdge(); } @Override public int getOrigEdgeLast() { return getEdge(); } @Override public String getName() { int stringIndexRef = baseGraph.edges.getInt(edgePointer + baseGraph.E_NAME); String name = baseGraph.stringIndex.get(stringIndexRef, STRING_IDX_NAME_KEY); // preserve backward compatibility (returns null if not explicitly set) return name == null ? "" : name; } @Override public EdgeIteratorState setName(String name) { baseGraph.setName(edgePointer, name); return this; } @Override public EdgeIteratorState detach(boolean reverseArg) { if (!EdgeIterator.Edge.isValid(edgeId)) throw new IllegalStateException("call setEdgeId before detaching (edgeId:" + edgeId + ")"); EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(baseGraph); boolean valid = edge.init(edgeId, reverseArg ? baseNode : adjNode); assert valid; if (reverseArg) { // for #162 edge.reverse = !reverse; } return edge; } @Override public final String toString() { return getEdge() + " " + getBaseNode() + "-" + getAdjNode(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy