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

com.graphhopper.routing.AStarBidirection Maven / Gradle / Ivy

Go to download

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

The 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.routing;

import com.graphhopper.routing.util.*;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;

import java.util.PriorityQueue;

import com.graphhopper.routing.AStar.AStarEntry;
import com.graphhopper.storage.SPTEntry;
import com.graphhopper.storage.Graph;
import com.graphhopper.util.*;

/**
 * This class implements a bidirectional A* algorithm. It is interesting to note that a
 * bidirectional dijkstra is far more efficient than a single direction one. The same does not hold
 * for a bidirectional A* as the heuristic can not be as tight.
 * 

* See http://research.microsoft.com/apps/pubs/default.aspx?id=64511 * http://i11www.iti.uni-karlsruhe.de/_media/teaching/sommer2012/routenplanung/vorlesung4.pdf * http://research.microsoft.com/pubs/64504/goldberg-sofsem07.pdf * http://www.cs.princeton.edu/courses/archive/spr06/cos423/Handouts/EPP%20shortest%20path%20algorithms.pdf *

* and *

* 1. Ikeda, T., Hsu, M.-Y., Imai, H., Nishimura, S., Shimoura, H., Hashimoto, T., Tenmoku, K., and * Mitoh, K. (1994). A fast algorithm for finding better routes by ai search techniques. In VNIS, * pages 291–296. *

* 2. Whangbo, T. K. (2007). Efficient modified bidirectional a* algorithm for optimal route- * finding. In IEA/AIE, volume 4570, pages 344–353. Springer. *

* or could we even use this three phase approach? * www.lix.polytechnique.fr/~giacomon/papers/bidirtimedep.pdf *

* @author Peter Karich * @author jansoe */ public class AStarBidirection extends AbstractBidirAlgo { private ConsistentWeightApproximator weightApprox; private PriorityQueue prioQueueOpenSetFrom; protected TIntObjectMap bestWeightMapFrom; private PriorityQueue prioQueueOpenSetTo; protected TIntObjectMap bestWeightMapTo; private TIntObjectMap bestWeightMapOther; protected AStarEntry currFrom; protected AStarEntry currTo; protected PathBidirRef bestPath; public AStarBidirection( Graph graph, FlagEncoder encoder, Weighting weighting, TraversalMode tMode ) { super(graph, encoder, weighting, tMode); int size = Math.min(Math.max(200, graph.getNodes() / 10), 2000); initCollections(size); BeelineWeightApproximator defaultApprox = new BeelineWeightApproximator(nodeAccess, weighting); defaultApprox.setDistanceCalc(new DistancePlaneProjection()); setApproximation(defaultApprox); } protected void initCollections( int size ) { prioQueueOpenSetFrom = new PriorityQueue(size); bestWeightMapFrom = new TIntObjectHashMap(size); prioQueueOpenSetTo = new PriorityQueue(size); bestWeightMapTo = new TIntObjectHashMap(size); } /** * @param approx if true it enables approximative distance calculation from lat,lon values */ public AStarBidirection setApproximation( WeightApproximator approx ) { weightApprox = new ConsistentWeightApproximator(approx); return this; } @Override protected SPTEntry createSPTEntry( int node, double weight ) { throw new IllegalStateException("use AStarEdge constructor directly"); } @Override public void initFrom( int from, double weight ) { currFrom = new AStarEntry(EdgeIterator.NO_EDGE, from, weight, weight); weightApprox.setSourceNode(from); prioQueueOpenSetFrom.add(currFrom); if (currTo != null) { currFrom.weight += weightApprox.approximate(currFrom.adjNode, false); currTo.weight += weightApprox.approximate(currTo.adjNode, true); } if (!traversalMode.isEdgeBased()) { bestWeightMapFrom.put(from, currFrom); if (currTo != null) { bestWeightMapOther = bestWeightMapTo; updateBestPath(GHUtility.getEdge(graph, from, currTo.adjNode), currTo, from); } } else { if (currTo != null && currTo.adjNode == from) { // special case of identical start and end bestPath.sptEntry = currFrom; bestPath.edgeTo = currTo; finishedFrom = true; finishedTo = true; } } } @Override public void initTo( int to, double weight ) { currTo = new AStarEntry(EdgeIterator.NO_EDGE, to, weight, weight); weightApprox.setGoalNode(to); prioQueueOpenSetTo.add(currTo); if (currFrom != null) { currFrom.weight += weightApprox.approximate(currFrom.adjNode, false); currTo.weight += weightApprox.approximate(currTo.adjNode, true); } if (!traversalMode.isEdgeBased()) { bestWeightMapTo.put(to, currTo); if (currFrom != null) { bestWeightMapOther = bestWeightMapFrom; updateBestPath(GHUtility.getEdge(graph, currFrom.adjNode, to), currFrom, to); } } else { if (currFrom != null && currFrom.adjNode == to) { // special case of identical start and end bestPath.sptEntry = currFrom; bestPath.edgeTo = currTo; finishedFrom = true; finishedTo = true; } } } @Override protected Path createAndInitPath() { bestPath = new PathBidirRef(graph, flagEncoder); return bestPath; } @Override protected Path extractPath() { if (finished()) return bestPath.extract(); return bestPath; } @Override protected double getCurrentFromWeight() { return currFrom.weight; } @Override protected double getCurrentToWeight() { return currTo.weight; } @Override protected boolean finished() { if (finishedFrom || finishedTo) return true; // using 'weight' is important and correct here e.g. approximation can get negative and smaller than 'weightOfVisitedPath' return currFrom.weight + currTo.weight >= bestPath.getWeight(); } @Override boolean fillEdgesFrom() { if (prioQueueOpenSetFrom.isEmpty()) return false; currFrom = prioQueueOpenSetFrom.poll(); bestWeightMapOther = bestWeightMapTo; fillEdges(currFrom, prioQueueOpenSetFrom, bestWeightMapFrom, outEdgeExplorer, false); visitedCountFrom++; return true; } @Override boolean fillEdgesTo() { if (prioQueueOpenSetTo.isEmpty()) return false; currTo = prioQueueOpenSetTo.poll(); bestWeightMapOther = bestWeightMapFrom; fillEdges(currTo, prioQueueOpenSetTo, bestWeightMapTo, inEdgeExplorer, true); visitedCountTo++; return true; } private void fillEdges( AStarEntry currEdge, PriorityQueue prioQueueOpenSet, TIntObjectMap bestWeightMap, EdgeExplorer explorer, boolean reverse ) { int currNode = currEdge.adjNode; EdgeIterator iter = explorer.setBaseNode(currNode); while (iter.next()) { if (!accept(iter, currEdge.edge)) continue; int neighborNode = iter.getAdjNode(); int traversalId = traversalMode.createTraversalId(iter, reverse); // TODO performance: check if the node is already existent in the opposite direction // then we could avoid the approximation as we already know the exact complete path! double alreadyVisitedWeight = weighting.calcWeight(iter, reverse, currEdge.edge) + currEdge.getWeightOfVisitedPath(); if (Double.isInfinite(alreadyVisitedWeight)) continue; AStarEntry ase = bestWeightMap.get(traversalId); if (ase == null || ase.getWeightOfVisitedPath() > alreadyVisitedWeight) { double currWeightToGoal = weightApprox.approximate(neighborNode, reverse); double estimationFullWeight = alreadyVisitedWeight + currWeightToGoal; if (ase == null) { ase = new AStarEntry(iter.getEdge(), neighborNode, estimationFullWeight, alreadyVisitedWeight); bestWeightMap.put(traversalId, ase); } else { assert (ase.weight > 0.999999 * estimationFullWeight) : "Inconsistent distance estimate " + ase.weight + " vs " + estimationFullWeight + " (" + ase.weight / estimationFullWeight + "), and:" + ase.getWeightOfVisitedPath() + " vs " + alreadyVisitedWeight + " (" + ase.getWeightOfVisitedPath() / alreadyVisitedWeight + ")"; prioQueueOpenSet.remove(ase); ase.edge = iter.getEdge(); ase.weight = estimationFullWeight; ase.weightOfVisitedPath = alreadyVisitedWeight; } ase.parent = currEdge; prioQueueOpenSet.add(ase); updateBestPath(iter, ase, traversalId); } } } public void updateBestPath( EdgeIteratorState edgeState, AStarEntry entryCurrent, int currLoc ) { AStarEntry entryOther = bestWeightMapOther.get(currLoc); if (entryOther == null) return; boolean reverse = bestWeightMapFrom == bestWeightMapOther; // update μ double newWeight = entryCurrent.weightOfVisitedPath + entryOther.weightOfVisitedPath; if (traversalMode.isEdgeBased()) { if (entryOther.edge != entryCurrent.edge) throw new IllegalStateException("cannot happen for edge based execution of " + getName()); // see DijkstraBidirectionRef if (entryOther.adjNode != entryCurrent.adjNode) { entryCurrent = (AStar.AStarEntry) entryCurrent.parent; newWeight -= weighting.calcWeight(edgeState, reverse, EdgeIterator.NO_EDGE); } else { // we detected a u-turn at meeting point, skip if not supported if (!traversalMode.hasUTurnSupport()) return; } } if (newWeight < bestPath.getWeight()) { bestPath.setSwitchToFrom(reverse); bestPath.sptEntry = entryCurrent; bestPath.edgeTo = entryOther; bestPath.setWeight(newWeight); } } @Override public String getName() { return Parameters.Algorithms.ASTAR_BI; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy