use of com.graphhopper.coll.GHIntObjectHashMap in project graphhopper by graphhopper.
the class QueryGraph method lookup.
/**
* For all specified query results calculate snapped point and if necessary set closest node
* to a virtual one and reverse closest edge. Additionally the wayIndex can change if an edge is
* swapped.
*
* @see QueryGraph
*/
public void lookup(List<QueryResult> resList) {
if (isInitialized())
throw new IllegalStateException("Call lookup only once. Otherwise you'll have problems for queries sharing the same edge.");
// initialize all none-final variables
virtualEdges = new ArrayList<VirtualEdgeIteratorState>(resList.size() * 2);
virtualNodes = new PointList(resList.size(), mainNodeAccess.is3D());
queryResults = new ArrayList<QueryResult>(resList.size());
baseGraph.virtualEdges = virtualEdges;
baseGraph.virtualNodes = virtualNodes;
baseGraph.queryResults = queryResults;
GHIntObjectHashMap<List<QueryResult>> edge2res = new GHIntObjectHashMap<List<QueryResult>>(resList.size());
// calculate snapped point and swap direction of closest edge if necessary
for (QueryResult res : resList) {
// Do not create virtual node for a query result if it is directly on a tower node or not found
if (res.getSnappedPosition() == QueryResult.Position.TOWER)
continue;
EdgeIteratorState closestEdge = res.getClosestEdge();
if (closestEdge == null)
throw new IllegalStateException("Do not call QueryGraph.lookup with invalid QueryResult " + res);
int base = closestEdge.getBaseNode();
// Force the identical direction for all closest edges.
// It is important to sort multiple results for the same edge by its wayIndex
boolean doReverse = base > closestEdge.getAdjNode();
if (base == closestEdge.getAdjNode()) {
// check for special case #162 where adj == base and force direction via latitude comparison
PointList pl = closestEdge.fetchWayGeometry(0);
if (pl.size() > 1)
doReverse = pl.getLatitude(0) > pl.getLatitude(pl.size() - 1);
}
if (doReverse) {
closestEdge = closestEdge.detach(true);
PointList fullPL = closestEdge.fetchWayGeometry(3);
res.setClosestEdge(closestEdge);
if (res.getSnappedPosition() == QueryResult.Position.PILLAR)
// ON pillar node
res.setWayIndex(fullPL.getSize() - res.getWayIndex() - 1);
else
// for case "OFF pillar node"
res.setWayIndex(fullPL.getSize() - res.getWayIndex() - 2);
if (res.getWayIndex() < 0)
throw new IllegalStateException("Problem with wayIndex while reversing closest edge:" + closestEdge + ", " + res);
}
// find multiple results on same edge
int edgeId = closestEdge.getEdge();
List<QueryResult> list = edge2res.get(edgeId);
if (list == null) {
list = new ArrayList<QueryResult>(5);
edge2res.put(edgeId, list);
}
list.add(res);
}
// Phase 2 - now it is clear which points cut one edge
// 1. create point lists
// 2. create virtual edges between virtual nodes and its neighbor (virtual or normal nodes)
edge2res.forEach(new IntObjectPredicate<List<QueryResult>>() {
@Override
public boolean apply(int edgeId, List<QueryResult> results) {
// we can expect at least one entry in the results
EdgeIteratorState closestEdge = results.get(0).getClosestEdge();
final PointList fullPL = closestEdge.fetchWayGeometry(3);
int baseNode = closestEdge.getBaseNode();
// sort results on the same edge by the wayIndex and if equal by distance to pillar node
Collections.sort(results, new Comparator<QueryResult>() {
@Override
public int compare(QueryResult o1, QueryResult o2) {
int diff = o1.getWayIndex() - o2.getWayIndex();
if (diff == 0) {
// sort by distance from snappedPoint to fullPL.get(wayIndex) if wayIndex is identical
GHPoint p1 = o1.getSnappedPoint();
GHPoint p2 = o2.getSnappedPoint();
if (p1.equals(p2))
return 0;
double fromLat = fullPL.getLatitude(o1.getWayIndex());
double fromLon = fullPL.getLongitude(o1.getWayIndex());
if (Helper.DIST_PLANE.calcNormalizedDist(fromLat, fromLon, p1.lat, p1.lon) > Helper.DIST_PLANE.calcNormalizedDist(fromLat, fromLon, p2.lat, p2.lon))
return 1;
return -1;
}
return diff;
}
});
GHPoint3D prevPoint = fullPL.toGHPoint(0);
int adjNode = closestEdge.getAdjNode();
int origTraversalKey = GHUtility.createEdgeKey(baseNode, adjNode, closestEdge.getEdge(), false);
int origRevTraversalKey = GHUtility.createEdgeKey(baseNode, adjNode, closestEdge.getEdge(), true);
long reverseFlags = closestEdge.detach(true).getFlags();
int prevWayIndex = 1;
int prevNodeId = baseNode;
int virtNodeId = virtualNodes.getSize() + mainNodes;
boolean addedEdges = false;
// fullPL into the right pieces.
for (int counter = 0; counter < results.size(); counter++) {
QueryResult res = results.get(counter);
if (res.getClosestEdge().getBaseNode() != baseNode)
throw new IllegalStateException("Base nodes have to be identical but were not: " + closestEdge + " vs " + res.getClosestEdge());
GHPoint3D currSnapped = res.getSnappedPoint();
// no new virtual nodes if exactly the same snapped point
if (prevPoint.equals(currSnapped)) {
res.setClosestNode(prevNodeId);
continue;
}
queryResults.add(res);
createEdges(origTraversalKey, origRevTraversalKey, prevPoint, prevWayIndex, res.getSnappedPoint(), res.getWayIndex(), fullPL, closestEdge, prevNodeId, virtNodeId, reverseFlags);
virtualNodes.add(currSnapped.lat, currSnapped.lon, currSnapped.ele);
// add edges again to set adjacent edges for newVirtNodeId
if (addedEdges) {
virtualEdges.add(virtualEdges.get(virtualEdges.size() - 2));
virtualEdges.add(virtualEdges.get(virtualEdges.size() - 2));
}
addedEdges = true;
res.setClosestNode(virtNodeId);
prevNodeId = virtNodeId;
prevWayIndex = res.getWayIndex() + 1;
prevPoint = currSnapped;
virtNodeId++;
}
// two edges between last result and adjacent node are still missing if not all points skipped
if (addedEdges)
createEdges(origTraversalKey, origRevTraversalKey, prevPoint, prevWayIndex, fullPL.toGHPoint(fullPL.getSize() - 1), fullPL.getSize() - 2, fullPL, closestEdge, virtNodeId - 1, adjNode, reverseFlags);
return true;
}
});
}
use of com.graphhopper.coll.GHIntObjectHashMap in project graphhopper by graphhopper.
the class QueryOverlayBuilder method buildVirtualEdges.
/**
* For all specified snaps calculate the snapped point and if necessary set the closest node
* to a virtual one and reverse the closest edge. Additionally the wayIndex can change if an edge is
* swapped.
*/
private void buildVirtualEdges(List<Snap> snaps) {
GHIntObjectHashMap<List<Snap>> edge2res = new GHIntObjectHashMap<>(snaps.size());
// calculate snapped point and swap direction of closest edge if necessary
for (Snap snap : snaps) {
// Do not create virtual node for a snap if it is directly on a tower node or not found
if (snap.getSnappedPosition() == Snap.Position.TOWER)
continue;
EdgeIteratorState closestEdge = snap.getClosestEdge();
if (closestEdge == null)
throw new IllegalStateException("Do not call QueryGraph.create with invalid Snap " + snap);
int base = closestEdge.getBaseNode();
// Force the identical direction for all closest edges.
// It is important to sort multiple results for the same edge by its wayIndex
boolean doReverse = base > closestEdge.getAdjNode();
if (base == closestEdge.getAdjNode()) {
// check for special case #162 where adj == base and force direction via latitude comparison
PointList pl = closestEdge.fetchWayGeometry(FetchMode.PILLAR_ONLY);
if (pl.size() > 1)
doReverse = pl.getLat(0) > pl.getLat(pl.size() - 1);
}
if (doReverse) {
closestEdge = closestEdge.detach(true);
PointList fullPL = closestEdge.fetchWayGeometry(FetchMode.ALL);
snap.setClosestEdge(closestEdge);
if (snap.getSnappedPosition() == Snap.Position.PILLAR)
// ON pillar node
snap.setWayIndex(fullPL.size() - snap.getWayIndex() - 1);
else
// for case "OFF pillar node"
snap.setWayIndex(fullPL.size() - snap.getWayIndex() - 2);
if (snap.getWayIndex() < 0)
throw new IllegalStateException("Problem with wayIndex while reversing closest edge:" + closestEdge + ", " + snap);
}
// find multiple results on same edge
int edgeId = closestEdge.getEdge();
List<Snap> list = edge2res.get(edgeId);
if (list == null) {
list = new ArrayList<>(5);
edge2res.put(edgeId, list);
}
list.add(snap);
}
// Phase 2 - now it is clear which points cut one edge
// 1. create point lists
// 2. create virtual edges between virtual nodes and its neighbor (virtual or normal nodes)
edge2res.forEach(new IntObjectPredicate<List<Snap>>() {
@Override
public boolean apply(int edgeId, List<Snap> results) {
// we can expect at least one entry in the results
EdgeIteratorState closestEdge = results.get(0).getClosestEdge();
final PointList fullPL = closestEdge.fetchWayGeometry(FetchMode.ALL);
int baseNode = closestEdge.getBaseNode();
Collections.sort(results, new Comparator<Snap>() {
@Override
public int compare(Snap o1, Snap o2) {
int diff = Integer.compare(o1.getWayIndex(), o2.getWayIndex());
if (diff == 0) {
return Double.compare(distanceOfSnappedPointToPillarNode(o1), distanceOfSnappedPointToPillarNode(o2));
} else {
return diff;
}
}
private double distanceOfSnappedPointToPillarNode(Snap o) {
GHPoint snappedPoint = o.getSnappedPoint();
double fromLat = fullPL.getLat(o.getWayIndex());
double fromLon = fullPL.getLon(o.getWayIndex());
return DistancePlaneProjection.DIST_PLANE.calcNormalizedDist(fromLat, fromLon, snappedPoint.lat, snappedPoint.lon);
}
});
GHPoint3D prevPoint = fullPL.get(0);
int adjNode = closestEdge.getAdjNode();
int origEdgeKey = closestEdge.getEdgeKey();
int origRevEdgeKey = GHUtility.reverseEdgeKey(origEdgeKey);
int prevWayIndex = 1;
int prevNodeId = baseNode;
int virtNodeId = queryOverlay.getVirtualNodes().size() + firstVirtualNodeId;
boolean addedEdges = false;
// fullPL into the right pieces.
for (Snap res : results) {
if (res.getClosestEdge().getBaseNode() != baseNode)
throw new IllegalStateException("Base nodes have to be identical but were not: " + closestEdge + " vs " + res.getClosestEdge());
GHPoint3D currSnapped = res.getSnappedPoint();
// no new virtual nodes if exactly the same snapped point
if (prevPoint.equals(currSnapped)) {
res.setClosestNode(prevNodeId);
continue;
}
queryOverlay.getClosestEdges().add(res.getClosestEdge().getEdge());
boolean isPillar = res.getSnappedPosition() == Snap.Position.PILLAR;
createEdges(origEdgeKey, origRevEdgeKey, prevPoint, prevWayIndex, isPillar, res.getSnappedPoint(), res.getWayIndex(), fullPL, closestEdge, prevNodeId, virtNodeId);
queryOverlay.getVirtualNodes().add(currSnapped.lat, currSnapped.lon, currSnapped.ele);
// add edges again to set adjacent edges for newVirtNodeId
if (addedEdges) {
queryOverlay.addVirtualEdge(queryOverlay.getVirtualEdge(queryOverlay.getNumVirtualEdges() - 2));
queryOverlay.addVirtualEdge(queryOverlay.getVirtualEdge(queryOverlay.getNumVirtualEdges() - 2));
}
addedEdges = true;
res.setClosestNode(virtNodeId);
prevNodeId = virtNodeId;
prevWayIndex = res.getWayIndex() + 1;
prevPoint = currSnapped;
virtNodeId++;
}
// two edges between last result and adjacent node are still missing if not all points skipped
if (addedEdges)
createEdges(origEdgeKey, origRevEdgeKey, prevPoint, prevWayIndex, false, fullPL.get(fullPL.size() - 1), fullPL.size() - 2, fullPL, closestEdge, virtNodeId - 1, adjNode);
return true;
}
});
}
use of com.graphhopper.coll.GHIntObjectHashMap in project graphhopper by graphhopper.
the class QueryGraph method createUncachedEdgeExplorer.
private EdgeExplorer createUncachedEdgeExplorer(EdgeFilter edgeFilter) {
// Iteration over virtual nodes needs to be thread safe if done from different explorer
// so we need to create the mapping on EVERY call!
// This needs to be a HashMap (and cannot be an array) as we also need to tweak edges for some mainNodes!
// The more query points we have the more inefficient this map could be. Hmmh.
final IntObjectMap<VirtualEdgeIterator> node2EdgeMap = new GHIntObjectHashMap<VirtualEdgeIterator>(queryResults.size() * 3);
final EdgeExplorer mainExplorer = mainGraph.createEdgeExplorer(edgeFilter);
final GHIntHashSet towerNodesToChange = new GHIntHashSet(queryResults.size());
// 1. virtualEdges should also get fresh EdgeIterators on every createEdgeExplorer call!
for (int i = 0; i < queryResults.size(); i++) {
// create outgoing edges
VirtualEdgeIterator virtEdgeIter = new VirtualEdgeIterator(2);
EdgeIteratorState baseRevEdge = virtualEdges.get(i * 4 + VE_BASE_REV);
if (edgeFilter.accept(baseRevEdge))
virtEdgeIter.add(baseRevEdge);
EdgeIteratorState adjEdge = virtualEdges.get(i * 4 + VE_ADJ);
if (edgeFilter.accept(adjEdge))
virtEdgeIter.add(adjEdge);
int virtNode = mainNodes + i;
node2EdgeMap.put(virtNode, virtEdgeIter);
// replace edge list of neighboring tower nodes:
// add virtual edges only and collect tower nodes where real edges will be added in step 2.
//
// base node
int towerNode = baseRevEdge.getAdjNode();
if (!isVirtualNode(towerNode)) {
towerNodesToChange.add(towerNode);
addVirtualEdges(node2EdgeMap, edgeFilter, true, towerNode, i);
}
// adj node
towerNode = adjEdge.getAdjNode();
if (!isVirtualNode(towerNode)) {
towerNodesToChange.add(towerNode);
addVirtualEdges(node2EdgeMap, edgeFilter, false, towerNode, i);
}
}
// 2. the connected tower nodes from mainGraph need fresh EdgeIterators with possible fakes
// where 'fresh' means independent of previous call and respecting the edgeFilter
// -> setup fake iterators of detected tower nodes (virtual edges are already added)
towerNodesToChange.forEach(new IntProcedure() {
@Override
public void apply(int value) {
fillVirtualEdges(node2EdgeMap, value, mainExplorer);
}
});
return new EdgeExplorer() {
@Override
public EdgeIterator setBaseNode(int baseNode) {
VirtualEdgeIterator iter = node2EdgeMap.get(baseNode);
if (iter != null)
return iter.reset();
return mainExplorer.setBaseNode(baseNode);
}
};
}
Aggregations