use of com.graphhopper.coll.GHBitSet in project graphhopper by graphhopper.
the class BaseGraph method inPlaceNodeRemove.
/**
* This methods disconnects all edges from removed nodes. It does no edge compaction. Then it
* moves the last nodes into the deleted nodes, where it needs to update the node ids in every
* edge.
*/
void inPlaceNodeRemove(int removeNodeCount) {
// Prepare edge-update of nodes which are connected to deleted nodes
int toMoveNodes = getNodes();
int itemsToMove = 0;
// sorted map when we access it via keyAt and valueAt - see below!
final SparseIntIntArray oldToNewMap = new SparseIntIntArray(removeNodeCount);
GHBitSet toRemoveSet = new GHBitSetImpl(removeNodeCount);
removedNodes.copyTo(toRemoveSet);
Logger logger = LoggerFactory.getLogger(getClass());
if (removeNodeCount > getNodes() / 2.0)
logger.warn("More than a half of the network should be removed!? " + "Nodes:" + getNodes() + ", remove:" + removeNodeCount);
EdgeExplorer delExplorer = createEdgeExplorer();
// create map of old node ids pointing to new ids
for (int removeNode = removedNodes.next(0); removeNode >= 0; removeNode = removedNodes.next(removeNode + 1)) {
EdgeIterator delEdgesIter = delExplorer.setBaseNode(removeNode);
while (delEdgesIter.next()) {
toRemoveSet.add(delEdgesIter.getAdjNode());
}
toMoveNodes--;
for (; toMoveNodes >= 0; toMoveNodes--) {
if (!removedNodes.contains(toMoveNodes))
break;
}
if (toMoveNodes >= removeNode)
oldToNewMap.put(toMoveNodes, removeNode);
itemsToMove++;
}
EdgeIterable adjNodesToDelIter = (EdgeIterable) createEdgeExplorer();
// all deleted nodes could be connected to existing. remove the connections
for (int removeNode = toRemoveSet.next(0); removeNode >= 0; removeNode = toRemoveSet.next(removeNode + 1)) {
// remove all edges connected to the deleted nodes
adjNodesToDelIter.setBaseNode(removeNode);
long prev = EdgeIterator.NO_EDGE;
while (adjNodesToDelIter.next()) {
int nodeId = adjNodesToDelIter.getAdjNode();
// already invalidated
if (nodeId != EdgeAccess.NO_NODE && removedNodes.contains(nodeId)) {
int edgeToRemove = adjNodesToDelIter.getEdge();
long edgeToRemovePointer = edgeAccess.toPointer(edgeToRemove);
edgeAccess.internalEdgeDisconnect(edgeToRemove, prev, removeNode, nodeId);
edgeAccess.invalidateEdge(edgeToRemovePointer);
} else {
prev = adjNodesToDelIter.edgePointer;
}
}
}
GHBitSet toMoveSet = new GHBitSetImpl(removeNodeCount * 3);
EdgeExplorer movedEdgeExplorer = createEdgeExplorer();
// marks connected nodes to rewrite the edges
for (int i = 0; i < itemsToMove; i++) {
int oldI = oldToNewMap.keyAt(i);
EdgeIterator movedEdgeIter = movedEdgeExplorer.setBaseNode(oldI);
while (movedEdgeIter.next()) {
int nodeId = movedEdgeIter.getAdjNode();
if (nodeId == EdgeAccess.NO_NODE)
continue;
if (removedNodes.contains(nodeId))
throw new IllegalStateException("shouldn't happen the edge to the node " + nodeId + " should be already deleted. " + oldI);
toMoveSet.add(nodeId);
}
}
// move nodes into deleted nodes
for (int i = 0; i < itemsToMove; i++) {
int oldI = oldToNewMap.keyAt(i);
int newI = oldToNewMap.valueAt(i);
long newOffset = (long) newI * nodeEntryBytes;
long oldOffset = (long) oldI * nodeEntryBytes;
for (long j = 0; j < nodeEntryBytes; j += 4) {
nodes.setInt(newOffset + j, nodes.getInt(oldOffset + j));
}
}
// *rewrites* all edges connected to moved nodes
// go through all edges and pick the necessary <- this is easier to implement than
// a more efficient (?) breadth-first search
EdgeIterator iter = getAllEdges();
while (iter.next()) {
int nodeA = iter.getBaseNode();
int nodeB = iter.getAdjNode();
if (!toMoveSet.contains(nodeA) && !toMoveSet.contains(nodeB))
continue;
// now overwrite exiting edge with new node ids
// also flags and links could have changed due to different node order
int updatedA = oldToNewMap.get(nodeA);
if (updatedA < 0)
updatedA = nodeA;
int updatedB = oldToNewMap.get(nodeB);
if (updatedB < 0)
updatedB = nodeB;
int edgeId = iter.getEdge();
long edgePointer = edgeAccess.toPointer(edgeId);
int linkA = edgeAccess.getEdgeRef(nodeA, nodeB, edgePointer);
int linkB = edgeAccess.getEdgeRef(nodeB, nodeA, edgePointer);
long flags = edgeAccess.getFlags_(edgePointer, false);
edgeAccess.writeEdge(edgeId, updatedA, updatedB, linkA, linkB);
edgeAccess.setFlags_(edgePointer, updatedA > updatedB, flags);
if (updatedA < updatedB != nodeA < nodeB)
setWayGeometry_(fetchWayGeometry_(edgePointer, true, 0, -1, -1), edgePointer, false);
}
// clear N_EDGE_REF
initNodeRefs((nodeCount - removeNodeCount) * nodeEntryBytes, nodeCount * nodeEntryBytes);
if (removeNodeCount >= nodeCount)
throw new IllegalStateException("graph is empty after in-place removal but was " + removeNodeCount);
// we do not remove the invalid edges => edgeCount stays the same!
nodeCount -= removeNodeCount;
// health check
if (isTestingEnabled()) {
EdgeExplorer explorer = createEdgeExplorer();
iter = getAllEdges();
while (iter.next()) {
int base = iter.getBaseNode();
int adj = iter.getAdjNode();
String str = iter.getEdge() + ", r.contains(" + base + "):" + removedNodes.contains(base) + ", r.contains(" + adj + "):" + removedNodes.contains(adj) + ", tr.contains(" + base + "):" + toRemoveSet.contains(base) + ", tr.contains(" + adj + "):" + toRemoveSet.contains(adj) + ", base:" + base + ", adj:" + adj + ", nodeCount:" + nodeCount;
if (adj >= nodeCount)
throw new RuntimeException("Adj.node problem with edge " + str);
if (base >= nodeCount)
throw new RuntimeException("Base node problem with edge " + str);
try {
explorer.setBaseNode(adj).toString();
} catch (Exception ex) {
org.slf4j.LoggerFactory.getLogger(getClass()).error("adj:" + adj);
}
try {
explorer.setBaseNode(base).toString();
} catch (Exception ex) {
org.slf4j.LoggerFactory.getLogger(getClass()).error("base:" + base);
}
}
// access last node -> no error
explorer.setBaseNode(nodeCount - 1).toString();
}
removedNodes = null;
}
use of com.graphhopper.coll.GHBitSet in project graphhopper by graphhopper.
the class Location2IDQuadtree method findClosest.
@Override
public QueryResult findClosest(final double queryLat, final double queryLon, final EdgeFilter edgeFilter) {
if (isClosed())
throw new IllegalStateException("You need to create a new LocationIndex instance as it is already closed");
if (edgeFilter != EdgeFilter.ALL_EDGES)
throw new UnsupportedOperationException("edge filters are not yet implemented for " + Location2IDQuadtree.class.getSimpleName());
// The following cases (e.g. dead ends or motorways crossing a normal way) could be problematic:
// | |
// | P |
// | | |< --- maxRasterWidth reached
// \-----/
/*
* Problem: use additionally the 8 surrounding quadrants: There an error due to the raster
* width. Because this index does not cover 100% of the graph you'll need to traverse the
* graph until you find the real matching point or if you reach the raster width limit. And
* there is a problem when using the raster limit as 'not found' indication and if you have
* arbitrary requests e.g. from other systems (where points do not match exactly): Although
* P is the closest point to the request one it could be that the raster limit is too short
* to reach it via graph traversal:
*/
long key = keyAlgo.encode(queryLat, queryLon);
final int id = index.getInt(key * 4);
double mainLat = nodeAccess.getLatitude(id);
double mainLon = nodeAccess.getLongitude(id);
final QueryResult res = new QueryResult(queryLat, queryLon);
res.setClosestNode(id);
res.setQueryDistance(distCalc.calcNormalizedDist(queryLat, queryLon, mainLat, mainLon));
goFurtherHook(id);
new BreadthFirstSearch() {
@Override
protected GHBitSet createBitSet() {
return new GHTBitSet(10);
}
@Override
protected boolean goFurther(int baseNode) {
if (baseNode == id)
return true;
goFurtherHook(baseNode);
double currLat = nodeAccess.getLatitude(baseNode);
double currLon = nodeAccess.getLongitude(baseNode);
double currNormedDist = distCalc.calcNormalizedDist(queryLat, queryLon, currLat, currLon);
if (currNormedDist < res.getQueryDistance()) {
res.setQueryDistance(currNormedDist);
res.setClosestNode(baseNode);
return true;
}
return currNormedDist < maxRasterWidth2InMeterNormed;
}
}.start(graph.createEdgeExplorer(), id);
// denormalize distance
res.setQueryDistance(distCalc.calcDenormalizedDist(res.getQueryDistance()));
return res;
}
use of com.graphhopper.coll.GHBitSet in project graphhopper by graphhopper.
the class Location2IDQuadtree method fillEmptyIndices.
private int fillEmptyIndices(GHBitSet filledIndices) {
int len = latSize * lonSize;
DataAccess indexCopy = new RAMDirectory().find("temp_index_copy");
indexCopy.setSegmentSize(index.getSegmentSize()).create(index.getCapacity());
GHBitSet indicesCopy = new GHBitSetImpl(len);
int initializedCounter = filledIndices.getCardinality();
// fan out initialized entries to avoid "nose-artifacts"
// we 1. do not use the same index for check and set and iterate until all indices are set
// and 2. use a taken-from array to decide which of the colliding should be preferred
int[] takenFrom = new int[len];
Arrays.fill(takenFrom, -1);
for (int i = filledIndices.next(0); i >= 0; i = filledIndices.next(i + 1)) {
takenFrom[i] = i;
}
if (initializedCounter == 0) {
throw new IllegalStateException("at least one entry has to be != null, which should have happened in initIndex");
}
int tmp = initializedCounter;
while (initializedCounter < len) {
index.copyTo(indexCopy);
filledIndices.copyTo(indicesCopy);
initializedCounter = filledIndices.getCardinality();
for (int i = 0; i < len; i++) {
int to = -1, from = -1;
if (indicesCopy.contains(i)) {
// check change "initialized to empty"
if ((i + 1) % lonSize != 0 && !indicesCopy.contains(i + 1)) {
// set right from current
from = i;
to = i + 1;
} else if (i + lonSize < len && !indicesCopy.contains(i + lonSize)) {
// set below from current
from = i;
to = i + lonSize;
}
} else // check change "empty to initialized"
if ((i + 1) % lonSize != 0 && indicesCopy.contains(i + 1)) {
// set from right
from = i + 1;
to = i;
} else if (i + lonSize < len && indicesCopy.contains(i + lonSize)) {
// set from below
from = i + lonSize;
to = i;
}
if (to >= 0) {
if (takenFrom[to] >= 0) {
// takenFrom[to] == to -> special case for normedDist == 0
if (takenFrom[to] == to || getNormedDist(from, to) >= getNormedDist(takenFrom[to], to)) {
continue;
}
}
index.setInt(to * 4, indexCopy.getInt(from * 4));
takenFrom[to] = takenFrom[from];
filledIndices.add(to);
initializedCounter++;
}
}
}
return initializedCounter - tmp;
}
use of com.graphhopper.coll.GHBitSet in project graphhopper by graphhopper.
the class LocationIndexTree method findNClosest.
/**
* Returns all edges that are within the specified radius around the queried position.
* Searches at most 9 cells to avoid performance problems. Hence, if the radius is larger than
* the cell width then not all edges might be returned.
*
* TODO: either clarify the method name and description (to only search e.g. 9 tiles) or
* refactor so it can handle a radius larger than 9 tiles. Also remove reference to 'NClosest',
* which is misleading, and don't always return at least one value. See map-matching #65.
* TODO: tidy up logic - see comments in graphhopper #994.
*
* @param radius in meters
*/
public List<QueryResult> findNClosest(final double queryLat, final double queryLon, final EdgeFilter edgeFilter, double radius) {
// Return ALL results which are very close and e.g. within the GPS signal accuracy.
// Also important to get all edges if GPS point is close to a junction.
final double returnAllResultsWithin = distCalc.calcNormalizedDist(radius);
// implement a cheap priority queue via List, sublist and Collections.sort
final List<QueryResult> queryResults = new ArrayList<QueryResult>();
GHIntHashSet set = new GHIntHashSet();
// Doing 2 iterations means searching 9 tiles.
for (int iteration = 0; iteration < 2; iteration++) {
// should we use the return value of earlyFinish?
findNetworkEntries(queryLat, queryLon, set, iteration);
final GHBitSet exploredNodes = new GHTBitSet(new GHIntHashSet(set));
final EdgeExplorer explorer = graph.createEdgeExplorer(edgeFilter);
set.forEach(new IntPredicate() {
@Override
public boolean apply(int node) {
new XFirstSearchCheck(queryLat, queryLon, exploredNodes, edgeFilter) {
@Override
protected double getQueryDistance() {
// do not skip search if distance is 0 or near zero (equalNormedDelta)
return Double.MAX_VALUE;
}
@Override
protected boolean check(int node, double normedDist, int wayIndex, EdgeIteratorState edge, QueryResult.Position pos) {
if (normedDist < returnAllResultsWithin || queryResults.isEmpty() || queryResults.get(0).getQueryDistance() > normedDist) {
// TODO: refactor below:
// - should only add edges within search radius (below allows the
// returning of a candidate outside search radius if it's the only
// one). Removing this test would simplify it a lot and probably
// match the expected behaviour (see method description)
// - create QueryResult first and the add/set as required - clean up
// the index tracking business.
int index = -1;
for (int qrIndex = 0; qrIndex < queryResults.size(); qrIndex++) {
QueryResult qr = queryResults.get(qrIndex);
// overwrite older queryResults which are potentially more far away than returnAllResultsWithin
if (qr.getQueryDistance() > returnAllResultsWithin) {
index = qrIndex;
break;
}
// avoid duplicate edges
if (qr.getClosestEdge().getEdge() == edge.getEdge()) {
if (qr.getQueryDistance() < normedDist) {
// do not add current edge
return true;
} else {
// overwrite old edge with current
index = qrIndex;
break;
}
}
}
QueryResult qr = new QueryResult(queryLat, queryLon);
qr.setQueryDistance(normedDist);
qr.setClosestNode(node);
qr.setClosestEdge(edge.detach(false));
qr.setWayIndex(wayIndex);
qr.setSnappedPosition(pos);
if (index < 0) {
queryResults.add(qr);
} else {
queryResults.set(index, qr);
}
}
return true;
}
}.start(explorer, node);
return true;
}
});
}
// TODO: pass boolean argument for whether or not to sort? Can be expensive if not required.
Collections.sort(queryResults, QR_COMPARATOR);
for (QueryResult qr : queryResults) {
if (qr.isValid()) {
// denormalize distance
qr.setQueryDistance(distCalc.calcDenormalizedDist(qr.getQueryDistance()));
qr.calcSnappedPoint(distCalc);
} else {
throw new IllegalStateException("Invalid QueryResult should not happen here: " + qr);
}
}
return queryResults;
}
use of com.graphhopper.coll.GHBitSet in project graphhopper by graphhopper.
the class DepthFirstSearch method start.
/**
* beginning with startNode add all following nodes to LIFO queue. If node has been already
* explored before, skip reexploration.
*/
@Override
public void start(EdgeExplorer explorer, int startNode) {
IntArrayDeque stack = new IntArrayDeque();
GHBitSet explored = createBitSet();
stack.addLast(startNode);
int current;
while (stack.size() > 0) {
current = stack.removeLast();
if (!explored.contains(current) && goFurther(current)) {
EdgeIterator iter = explorer.setBaseNode(current);
while (iter.next()) {
int connectedId = iter.getAdjNode();
if (checkAdjacent(iter)) {
stack.addLast(connectedId);
}
}
explored.add(current);
}
}
}
Aggregations