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

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

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 10.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.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.KVStorage;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.BBox;

import java.io.Closeable;
import java.util.List;

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 */ public class BaseGraph implements Graph, Closeable { final static long MAX_UNSIGNED_INT = 0xFFFF_FFFFL; final BaseGraphNodesAndEdges store; final NodeAccess nodeAccess; final KVStorage edgeKVStorage; // can be null if turn costs are not supported final TurnCostStorage turnCostStorage; final BitUtil bitUtil; // length | nodeA | nextNode | ... | nodeB private final DataAccess wayGeometry; private final Directory dir; private final int segmentSize; private boolean initialized = false; private long maxGeoRef; public BaseGraph(Directory dir, int intsForFlags, boolean withElevation, boolean withTurnCosts, int segmentSize) { this.dir = dir; this.bitUtil = BitUtil.LITTLE; this.wayGeometry = dir.create("geometry", segmentSize); this.edgeKVStorage = new KVStorage(dir, true); this.store = new BaseGraphNodesAndEdges(dir, intsForFlags, withElevation, withTurnCosts, segmentSize); this.nodeAccess = new GHNodeAccess(store); this.segmentSize = segmentSize; turnCostStorage = withTurnCosts ? new TurnCostStorage(this, dir.create("turn_costs", dir.getDefaultType("turn_costs", true), segmentSize)) : null; } private int getOtherNode(int nodeThis, long edgePointer) { int nodeA = store.getNodeA(edgePointer); return nodeThis == nodeA ? store.getNodeB(edgePointer) : nodeA; } private boolean isAdjacentToNode(int node, long edgePointer) { return store.getNodeA(edgePointer) == node || store.getNodeB(edgePointer) == node; } private static boolean isTestingEnabled() { boolean enableIfAssert = false; assert (enableIfAssert = true) : true; return enableIfAssert; } public void debugPrint() { store.debugPrint(); } @Override public BaseGraph getBaseGraph() { return this; } public boolean isInitialized() { return initialized; } void checkNotInitialized() { if (initialized) throw new IllegalStateException("You cannot configure this BaseGraph " + "after calling create or loadExisting. Calling one of the methods twice is also not allowed."); } private void loadWayGeometryHeader() { int geometryVersion = wayGeometry.getHeader(0); GHUtility.checkDAVersion(wayGeometry.getName(), Constants.VERSION_GEOMETRY, geometryVersion); maxGeoRef = bitUtil.toLong( wayGeometry.getHeader(4), wayGeometry.getHeader(8) ); } private void setWayGeometryHeader() { wayGeometry.setHeader(0, Constants.VERSION_GEOMETRY); wayGeometry.setHeader(4, bitUtil.getIntLow(maxGeoRef)); wayGeometry.setHeader(8, bitUtil.getIntHigh(maxGeoRef)); } private void setInitialized() { initialized = true; } boolean supportsTurnCosts() { return turnCostStorage != null; } @Override public int getNodes() { return store.getNodes(); } @Override public int getEdges() { return store.getEdges(); } @Override public NodeAccess getNodeAccess() { return nodeAccess; } @Override public BBox getBounds() { return store.getBounds(); } public synchronized void freeze() { if (isFrozen()) throw new IllegalStateException("base graph already frozen"); store.setFrozen(true); } public synchronized boolean isFrozen() { return store.getFrozen(); } public BaseGraph create(long initSize) { checkNotInitialized(); dir.create(); store.create(initSize); initSize = Math.min(initSize, 2000); wayGeometry.create(initSize); edgeKVStorage.create(initSize); if (supportsTurnCosts()) { turnCostStorage.create(initSize); } setInitialized(); // 0 stands for no separate geoRef maxGeoRef = 4; return this; } public int getIntsForFlags() { return store.getIntsForFlags(); } public String toDetailsString() { return store.toDetailsString() + ", " + "name:(" + edgeKVStorage.getCapacity() / Helper.MB + "MB), " + "geo:" + nf(maxGeoRef) + "(" + wayGeometry.getCapacity() / Helper.MB + "MB)"; } /** * Flush and free resources that are not needed for post-processing (way geometries and KVStorage for edges). */ public void flushAndCloseGeometryAndNameStorage() { setWayGeometryHeader(); wayGeometry.flush(); wayGeometry.close(); edgeKVStorage.flush(); edgeKVStorage.close(); } public void flush() { if (!wayGeometry.isClosed()) { setWayGeometryHeader(); wayGeometry.flush(); } if (!edgeKVStorage.isClosed()) edgeKVStorage.flush(); store.flush(); if (supportsTurnCosts()) { turnCostStorage.flush(); } } @Override public void close() { if (!wayGeometry.isClosed()) wayGeometry.close(); if (!edgeKVStorage.isClosed()) edgeKVStorage.close(); store.close(); if (supportsTurnCosts()) { turnCostStorage.close(); } } public long getCapacity() { return store.getCapacity() + edgeKVStorage.getCapacity() + wayGeometry.getCapacity() + (supportsTurnCosts() ? turnCostStorage.getCapacity() : 0); } long getMaxGeoRef() { return maxGeoRef; } public boolean loadExisting() { checkNotInitialized(); if (!store.loadExisting()) return false; if (!wayGeometry.loadExisting()) return false; if (!edgeKVStorage.loadExisting()) return false; if (supportsTurnCosts() && !turnCostStorage.loadExisting()) return false; setInitialized(); loadWayGeometryHeader(); return true; } /** * 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 = store.toEdgePointer(to.getEdge()); store.writeFlags(edgePointer, from.getFlags()); // copy the rest with higher level API to.setDistance(from.getDistance()). setKeyValues(from.getKeyValues()). 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"); if (nodeA == nodeB) // Loop edges would only make sense if their attributes were the same for both 'directions', // because for routing algorithms (which ignore the way geometry) loop edges do not even // have a well-defined 'direction'. So we either need to make sure the attributes // are the same for both directions, or reject loop edges altogether. Since we currently // don't know any use-case for loop edges in road networks (there is one for PT), // we reject them here. throw new IllegalArgumentException("Loop edges are not supported, got: " + nodeA + " - " + nodeB); int edgeId = store.edge(nodeA, nodeB); EdgeIteratorStateImpl edge = new EdgeIteratorStateImpl(this); boolean valid = edge.init(edgeId, nodeB); assert valid; return edge; } @Override public EdgeIteratorState getEdgeIteratorState(int edgeId, int 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; } @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 TurnCostStorage getTurnCostStorage() { return turnCostStorage; } @Override public Weighting wrapWeighting(Weighting weighting) { return weighting; } @Override public int getOtherNode(int edge, int node) { long edgePointer = store.toEdgePointer(edge); return getOtherNode(node, edgePointer); } @Override public boolean isAdjacentToNode(int edge, int node) { long edgePointer = store.toEdgePointer(edge); return isAdjacentToNode(node, edgePointer); } 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 = store.getGeoRef(edgePointer); int len = pillarNodes.size(); 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 { store.setGeoRef(edgePointer, 0L); } } public EdgeIntAccess createEdgeIntAccess() { return new EdgeIntAccess() { @Override public int getInt(int edgeId, int index) { long edgePointer = store.toEdgePointer(edgeId); return store.getFlagInt(edgePointer, index); } @Override public void setInt(int edgeId, int index, int value) { long edgePointer = store.toEdgePointer(edgeId); store.setFlagInt(edgePointer, index, value); } }; } private void setWayGeometryAtGeoRef(PointList pillarNodes, long edgePointer, boolean reverse, long geoRef) { long geoRefPosition = geoRef * 4; byte[] wayGeometryBytes = createWayGeometryBytes(pillarNodes, reverse); wayGeometry.ensureCapacity(geoRefPosition + wayGeometryBytes.length); wayGeometry.setBytes(geoRefPosition, wayGeometryBytes, wayGeometryBytes.length); store.setGeoRef(edgePointer, geoRef); } private byte[] createWayGeometryBytes(PointList pillarNodes, boolean reverse) { int len = pillarNodes.size(); 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 = store.getGeoRef(edgePointer); 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 long nextGeoRef(int arrayLength) { long tmp = maxGeoRef; maxGeoRef += arrayLength + 1L; return tmp; } public boolean isClosed() { return store.isClosed(); } public Directory getDirectory() { return dir; } public int getSegmentSize() { return segmentSize; } public static class Builder { private final int intsForFlags; private Directory directory = new RAMDirectory(); private boolean withElevation = false; private boolean withTurnCosts = false; private long bytes = 100; private int segmentSize = -1; public Builder(EncodingManager em) { this(em.getIntsForFlags()); withTurnCosts(em.needsTurnCostsSupport()); } public Builder(int intsForFlags) { this.intsForFlags = intsForFlags; } // todo: maybe rename later, but for now this makes it easier to replace GraphBuilder public Builder setDir(Directory directory) { this.directory = directory; return this; } // todo: maybe rename later, but for now this makes it easier to replace GraphBuilder public Builder set3D(boolean withElevation) { this.withElevation = withElevation; return this; } // todo: maybe rename later, but for now this makes it easier to replace GraphBuilder public Builder withTurnCosts(boolean withTurnCosts) { this.withTurnCosts = withTurnCosts; return this; } public Builder setSegmentSize(int segmentSize) { this.segmentSize = segmentSize; return this; } public Builder setBytes(long bytes) { this.bytes = bytes; return this; } public BaseGraph build() { return new BaseGraph(directory, intsForFlags, withElevation, withTurnCosts, segmentSize); } public BaseGraph create() { BaseGraph baseGraph = build(); baseGraph.create(bytes); return baseGraph; } } 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 = store.getEdgeRef(store.toNodePointer(baseNode)); this.baseNode = baseNode; return this; } @Override public final boolean next() { while (EdgeIterator.Edge.isValid(nextEdgeId)) { goToNext(); if (filter.accept(this)) return true; } return false; } void goToNext() { edgePointer = store.toEdgePointer(nextEdgeId); edgeId = nextEdgeId; int nodeA = store.getNodeA(edgePointer); boolean baseNodeIsNodeA = baseNode == nodeA; adjNode = baseNodeIsNodeA ? store.getNodeB(edgePointer) : nodeA; reverse = !baseNodeIsNodeA; // position to next edge nextEdgeId = baseNodeIsNodeA ? store.getLinkA(edgePointer) : store.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 store.getEdges(); } @Override public boolean next() { edgeId++; if (edgeId >= store.getEdges()) return false; edgePointer = store.toEdgePointer(edgeId); baseNode = store.getNodeA(edgePointer); adjNode = store.getNodeB(edgePointer); 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; final BaseGraphNodesAndEdges store; long edgePointer = -1; int baseNode; int adjNode; // we need reverse if detach is called boolean reverse = false; int edgeId = -1; private final EdgeIntAccess edgeIntAccess; public EdgeIteratorStateImpl(BaseGraph baseGraph) { this.baseGraph = baseGraph; edgeIntAccess = baseGraph.createEdgeIntAccess(); store = baseGraph.store; } /** * @return false if the edge has not a node equal to expectedAdjNode */ final boolean init(int edgeId, int expectedAdjNode) { if (edgeId < 0 || edgeId >= store.getEdges()) throw new IllegalArgumentException("edge: " + edgeId + " out of bounds: [0," + store.getEdges() + "["); this.edgeId = edgeId; edgePointer = store.toEdgePointer(edgeId); baseNode = store.getNodeA(edgePointer); adjNode = store.getNodeB(edgePointer); 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 = store.toEdgePointer(edgeId); baseNode = store.getNodeA(edgePointer); adjNode = store.getNodeB(edgePointer); if (edgeKey % 2 == 0) { 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 store.getDist(edgePointer); } @Override public EdgeIteratorState setDistance(double dist) { store.setDist(edgePointer, dist); return this; } @Override public IntsRef getFlags() { IntsRef edgeFlags = new IntsRef(store.getIntsForFlags()); store.readFlags(edgePointer, edgeFlags); return edgeFlags; } @Override public final EdgeIteratorState setFlags(IntsRef edgeFlags) { assert edgeId < store.getEdges() : "must be edge but was shortcut: " + edgeId + " >= " + store.getEdges() + ". Use setFlagsAndWeight"; store.writeFlags(edgePointer, edgeFlags); return this; } @Override public boolean get(BooleanEncodedValue property) { return property.getBool(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { property.setBool(reverse, edgeId, edgeIntAccess, value); return this; } @Override public boolean getReverse(BooleanEncodedValue property) { return property.getBool(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) { property.setBool(!reverse, edgeId, edgeIntAccess, value); 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, edgeId, edgeIntAccess, fwd); property.setBool(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public int get(IntEncodedValue property) { return property.getInt(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(IntEncodedValue property, int value) { property.setInt(reverse, edgeId, edgeIntAccess, value); return this; } @Override public int getReverse(IntEncodedValue property) { return property.getInt(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(IntEncodedValue property, int value) { property.setInt(!reverse, edgeId, edgeIntAccess, value); 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, edgeId, edgeIntAccess, fwd); property.setInt(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public double get(DecimalEncodedValue property) { return property.getDecimal(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(DecimalEncodedValue property, double value) { property.setDecimal(reverse, edgeId, edgeIntAccess, value); return this; } @Override public double getReverse(DecimalEncodedValue property) { return property.getDecimal(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) { property.setDecimal(!reverse, edgeId, edgeIntAccess, value); 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, edgeId, edgeIntAccess, fwd); property.setDecimal(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public > T get(EnumEncodedValue property) { return property.getEnum(reverse, edgeId, edgeIntAccess); } @Override public > EdgeIteratorState set(EnumEncodedValue property, T value) { property.setEnum(reverse, edgeId, edgeIntAccess, value); return this; } @Override public > T getReverse(EnumEncodedValue property) { return property.getEnum(!reverse, edgeId, edgeIntAccess); } @Override public > EdgeIteratorState setReverse(EnumEncodedValue property, T value) { property.setEnum(!reverse, edgeId, edgeIntAccess, value); 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, edgeId, edgeIntAccess, fwd); property.setEnum(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public String get(StringEncodedValue property) { return property.getString(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(StringEncodedValue property, String value) { property.setString(reverse, edgeId, edgeIntAccess, value); return this; } @Override public String getReverse(StringEncodedValue property) { return property.getString(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(StringEncodedValue property, String value) { property.setString(!reverse, edgeId, edgeIntAccess, value); 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, edgeId, edgeIntAccess, fwd); property.setString(!reverse, edgeId, edgeIntAccess, bwd); 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 getReverseEdgeKey() { return GHUtility.reverseEdgeKey(getEdgeKey()); } @Override public EdgeIteratorState setKeyValues(List entries) { long pointer = baseGraph.edgeKVStorage.add(entries); if (pointer > MAX_UNSIGNED_INT) throw new IllegalStateException("Too many key value pairs are stored, currently limited to " + MAX_UNSIGNED_INT + " was " + pointer); store.setKeyValuesRef(edgePointer, BitUtil.toSignedInt(pointer)); return this; } @Override public List getKeyValues() { long kvEntryRef = Integer.toUnsignedLong(store.getKeyValuesRef(edgePointer)); return baseGraph.edgeKVStorage.getAll(kvEntryRef); } @Override public Object getValue(String key) { long kvEntryRef = Integer.toUnsignedLong(store.getKeyValuesRef(edgePointer)); return baseGraph.edgeKVStorage.get(kvEntryRef, key, reverse); } @Override public String getName() { String name = (String) getValue(KVStorage.KeyValue.STREET_NAME); // preserve backward compatibility (returns empty string if name tag missing) return name == null ? "" : name; } @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 - 2024 Weber Informatics LLC | Privacy Policy