use of com.graphhopper.storage.index.QueryResult in project graphhopper by graphhopper.
the class QueryGraphTest method testTurnCostsProperlyPropagated_Issue282.
@Test
public void testTurnCostsProperlyPropagated_Issue282() {
TurnCostExtension turnExt = new TurnCostExtension();
FlagEncoder encoder = new CarFlagEncoder(5, 5, 15);
GraphHopperStorage graphWithTurnCosts = new GraphHopperStorage(new RAMDirectory(), new EncodingManager(encoder), false, turnExt).create(100);
NodeAccess na = graphWithTurnCosts.getNodeAccess();
na.setNode(0, .00, .00);
na.setNode(1, .00, .01);
na.setNode(2, .01, .01);
EdgeIteratorState edge0 = graphWithTurnCosts.edge(0, 1, 10, true);
EdgeIteratorState edge1 = graphWithTurnCosts.edge(2, 1, 10, true);
QueryGraph qGraph = new QueryGraph(graphWithTurnCosts);
FastestWeighting weighting = new FastestWeighting(encoder);
TurnWeighting turnWeighting = new TurnWeighting(weighting, (TurnCostExtension) qGraph.getExtension());
assertEquals(0, turnWeighting.calcTurnWeight(edge0.getEdge(), 1, edge1.getEdge()), .1);
// now use turn costs and QueryGraph
turnExt.addTurnInfo(edge0.getEdge(), 1, edge1.getEdge(), encoder.getTurnFlags(false, 10));
assertEquals(10, turnWeighting.calcTurnWeight(edge0.getEdge(), 1, edge1.getEdge()), .1);
QueryResult res1 = createLocationResult(0.000, 0.005, edge0, 0, QueryResult.Position.EDGE);
QueryResult res2 = createLocationResult(0.005, 0.010, edge1, 0, QueryResult.Position.EDGE);
qGraph.lookup(Arrays.asList(res1, res2));
int fromQueryEdge = GHUtility.getEdge(qGraph, res1.getClosestNode(), 1).getEdge();
int toQueryEdge = GHUtility.getEdge(qGraph, res2.getClosestNode(), 1).getEdge();
assertEquals(10, turnWeighting.calcTurnWeight(fromQueryEdge, 1, toQueryEdge), .1);
graphWithTurnCosts.close();
}
use of com.graphhopper.storage.index.QueryResult in project graphhopper by graphhopper.
the class GraphHopperStorageCHTest method testQueryGraph.
@Test
public void testQueryGraph() {
graph = createGHStorage();
CHGraph chGraph = getGraph(graph);
NodeAccess na = chGraph.getNodeAccess();
na.setNode(0, 1.00, 1.00);
na.setNode(1, 1.02, 1.00);
na.setNode(2, 1.04, 1.00);
EdgeIteratorState edge1 = chGraph.edge(0, 1);
chGraph.edge(1, 2);
graph.freeze();
chGraph.shortcut(0, 1);
QueryGraph qGraph = new QueryGraph(chGraph);
QueryResult fromRes = createQR(1.004, 1.01, 0, edge1);
QueryResult toRes = createQR(1.019, 1.00, 0, edge1);
qGraph.lookup(fromRes, toRes);
Graph baseGraph = qGraph.getBaseGraph();
EdgeExplorer explorer = baseGraph.createEdgeExplorer();
assertTrue(chGraph.getNodes() < qGraph.getNodes());
assertTrue(baseGraph.getNodes() == qGraph.getNodes());
// traverse virtual edges and normal edges but no shortcuts!
assertEquals(GHUtility.asSet(fromRes.getClosestNode()), GHUtility.getNeighbors(explorer.setBaseNode(0)));
assertEquals(GHUtility.asSet(toRes.getClosestNode(), 2), GHUtility.getNeighbors(explorer.setBaseNode(1)));
// get neighbors from virtual nodes
assertEquals(GHUtility.asSet(0, toRes.getClosestNode()), GHUtility.getNeighbors(explorer.setBaseNode(fromRes.getClosestNode())));
assertEquals(GHUtility.asSet(1, fromRes.getClosestNode()), GHUtility.getNeighbors(explorer.setBaseNode(toRes.getClosestNode())));
}
use of com.graphhopper.storage.index.QueryResult in project graphhopper by graphhopper.
the class GraphHopper method calcPaths.
/**
* This method calculates the alternative path list using the low level Path objects.
*/
public List<Path> calcPaths(GHRequest request, GHResponse ghRsp) {
if (ghStorage == null || !fullyLoaded)
throw new IllegalStateException("Do a successful call to load or importOrLoad before routing");
if (ghStorage.isClosed())
throw new IllegalStateException("You need to create a new GraphHopper instance as it is already closed");
// default handling
String vehicle = request.getVehicle();
if (vehicle.isEmpty()) {
vehicle = getDefaultVehicle().toString();
request.setVehicle(vehicle);
}
Lock readLock = readWriteLock.readLock();
readLock.lock();
try {
if (!encodingManager.supports(vehicle))
throw new IllegalArgumentException("Vehicle " + vehicle + " unsupported. " + "Supported are: " + getEncodingManager());
HintsMap hints = request.getHints();
String tModeStr = hints.get("traversal_mode", traversalMode.toString());
TraversalMode tMode = TraversalMode.fromString(tModeStr);
if (hints.has(Routing.EDGE_BASED))
tMode = hints.getBool(Routing.EDGE_BASED, false) ? TraversalMode.EDGE_BASED_2DIR : TraversalMode.NODE_BASED;
FlagEncoder encoder = encodingManager.getEncoder(vehicle);
boolean disableCH = hints.getBool(CH.DISABLE, false);
if (!chFactoryDecorator.isDisablingAllowed() && disableCH)
throw new IllegalArgumentException("Disabling CH not allowed on the server-side");
boolean disableLM = hints.getBool(Landmark.DISABLE, false);
if (!lmFactoryDecorator.isDisablingAllowed() && disableLM)
throw new IllegalArgumentException("Disabling LM not allowed on the server-side");
String algoStr = request.getAlgorithm();
if (algoStr.isEmpty())
algoStr = chFactoryDecorator.isEnabled() && !disableCH ? DIJKSTRA_BI : ASTAR_BI;
List<GHPoint> points = request.getPoints();
// TODO Maybe we should think about a isRequestValid method that checks all that stuff that we could do to fail fast
// For example see #734
checkIfPointsAreInBounds(points);
RoutingTemplate routingTemplate;
if (ROUND_TRIP.equalsIgnoreCase(algoStr))
routingTemplate = new RoundTripRoutingTemplate(request, ghRsp, locationIndex, maxRoundTripRetries);
else if (ALT_ROUTE.equalsIgnoreCase(algoStr))
routingTemplate = new AlternativeRoutingTemplate(request, ghRsp, locationIndex);
else
routingTemplate = new ViaRoutingTemplate(request, ghRsp, locationIndex);
List<Path> altPaths = null;
int maxRetries = routingTemplate.getMaxRetries();
Locale locale = request.getLocale();
Translation tr = trMap.getWithFallBack(locale);
for (int i = 0; i < maxRetries; i++) {
StopWatch sw = new StopWatch().start();
List<QueryResult> qResults = routingTemplate.lookup(points, encoder);
ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
if (ghRsp.hasErrors())
return Collections.emptyList();
RoutingAlgorithmFactory tmpAlgoFactory = getAlgorithmFactory(hints);
Weighting weighting;
QueryGraph queryGraph;
if (chFactoryDecorator.isEnabled() && !disableCH) {
boolean forceCHHeading = hints.getBool(CH.FORCE_HEADING, false);
if (!forceCHHeading && request.hasFavoredHeading(0))
throw new IllegalArgumentException("Heading is not (fully) supported for CHGraph. See issue #483");
// if LM is enabled we have the LMFactory with the CH algo!
RoutingAlgorithmFactory chAlgoFactory = tmpAlgoFactory;
if (tmpAlgoFactory instanceof LMAlgoFactoryDecorator.LMRAFactory)
chAlgoFactory = ((LMAlgoFactoryDecorator.LMRAFactory) tmpAlgoFactory).getDefaultAlgoFactory();
if (chAlgoFactory instanceof PrepareContractionHierarchies)
weighting = ((PrepareContractionHierarchies) chAlgoFactory).getWeighting();
else
throw new IllegalStateException("Although CH was enabled a non-CH algorithm factory was returned " + tmpAlgoFactory);
tMode = getCHFactoryDecorator().getNodeBase();
queryGraph = new QueryGraph(ghStorage.getGraph(CHGraph.class, weighting));
queryGraph.lookup(qResults);
} else {
checkNonChMaxWaypointDistance(points);
queryGraph = new QueryGraph(ghStorage);
queryGraph.lookup(qResults);
weighting = createWeighting(hints, encoder, queryGraph);
ghRsp.addDebugInfo("tmode:" + tMode.toString());
}
int maxVisitedNodesForRequest = hints.getInt(Routing.MAX_VISITED_NODES, maxVisitedNodes);
if (maxVisitedNodesForRequest > maxVisitedNodes)
throw new IllegalArgumentException("The max_visited_nodes parameter has to be below or equal to:" + maxVisitedNodes);
weighting = createTurnWeighting(queryGraph, weighting, tMode);
AlgorithmOptions algoOpts = AlgorithmOptions.start().algorithm(algoStr).traversalMode(tMode).weighting(weighting).maxVisitedNodes(maxVisitedNodesForRequest).hints(hints).build();
altPaths = routingTemplate.calcPaths(queryGraph, tmpAlgoFactory, algoOpts);
boolean tmpEnableInstructions = hints.getBool(Routing.INSTRUCTIONS, enableInstructions);
boolean tmpCalcPoints = hints.getBool(Routing.CALC_POINTS, calcPoints);
double wayPointMaxDistance = hints.getDouble(Routing.WAY_POINT_MAX_DISTANCE, 1d);
DouglasPeucker peucker = new DouglasPeucker().setMaxDistance(wayPointMaxDistance);
PathMerger pathMerger = new PathMerger().setCalcPoints(tmpCalcPoints).setDouglasPeucker(peucker).setEnableInstructions(tmpEnableInstructions).setPathDetailsBuilders(pathBuilderFactory, request.getPathDetails()).setSimplifyResponse(simplifyResponse && wayPointMaxDistance > 0);
if (request.hasFavoredHeading(0))
pathMerger.setFavoredHeading(request.getFavoredHeading(0));
if (routingTemplate.isReady(pathMerger, tr))
break;
}
return altPaths;
} catch (IllegalArgumentException ex) {
ghRsp.addError(ex);
return Collections.emptyList();
} finally {
readLock.unlock();
}
}
use of com.graphhopper.storage.index.QueryResult 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.storage.index.QueryResult in project graphhopper by graphhopper.
the class ViaRoutingTemplate method calcPaths.
@Override
public List<Path> calcPaths(QueryGraph queryGraph, RoutingAlgorithmFactory algoFactory, AlgorithmOptions algoOpts) {
long visitedNodesSum = 0L;
boolean viaTurnPenalty = ghRequest.getHints().getBool(Routing.PASS_THROUGH, false);
int pointCounts = ghRequest.getPoints().size();
pathList = new ArrayList<>(pointCounts - 1);
QueryResult fromQResult = queryResults.get(0);
StopWatch sw;
for (int placeIndex = 1; placeIndex < pointCounts; placeIndex++) {
if (placeIndex == 1) {
// enforce start direction
queryGraph.enforceHeading(fromQResult.getClosestNode(), ghRequest.getFavoredHeading(0), false);
} else if (viaTurnPenalty) {
// enforce straight start after via stop
Path prevRoute = pathList.get(placeIndex - 2);
if (prevRoute.getEdgeCount() > 0) {
EdgeIteratorState incomingVirtualEdge = prevRoute.getFinalEdge();
queryGraph.unfavorVirtualEdgePair(fromQResult.getClosestNode(), incomingVirtualEdge.getEdge());
}
}
QueryResult toQResult = queryResults.get(placeIndex);
// enforce end direction
queryGraph.enforceHeading(toQResult.getClosestNode(), ghRequest.getFavoredHeading(placeIndex), true);
sw = new StopWatch().start();
RoutingAlgorithm algo = algoFactory.createAlgo(queryGraph, algoOpts);
String debug = ", algoInit:" + sw.stop().getSeconds() + "s";
sw = new StopWatch().start();
List<Path> tmpPathList = algo.calcPaths(fromQResult.getClosestNode(), toQResult.getClosestNode());
debug += ", " + algo.getName() + "-routing:" + sw.stop().getSeconds() + "s";
if (tmpPathList.isEmpty())
throw new IllegalStateException("At least one path has to be returned for " + fromQResult + " -> " + toQResult);
int idx = 0;
for (Path path : tmpPathList) {
if (path.getTime() < 0)
throw new RuntimeException("Time was negative " + path.getTime() + " for index " + idx + ". Please report as bug and include:" + ghRequest);
pathList.add(path);
debug += ", " + path.getDebugInfo();
idx++;
}
altResponse.addDebugInfo(debug);
// reset all direction enforcements in queryGraph to avoid influencing next path
queryGraph.clearUnfavoredStatus();
if (algo.getVisitedNodes() >= algoOpts.getMaxVisitedNodes())
throw new IllegalArgumentException("No path found due to maximum nodes exceeded " + algoOpts.getMaxVisitedNodes());
visitedNodesSum += algo.getVisitedNodes();
fromQResult = toQResult;
}
ghResponse.getHints().put("visited_nodes.sum", visitedNodesSum);
ghResponse.getHints().put("visited_nodes.average", (float) visitedNodesSum / (pointCounts - 1));
return pathList;
}
Aggregations