Commit a35562ea authored by DaGal's avatar DaGal

Implement real dijkstra one-to-many

parent 958378ea
......@@ -17,6 +17,8 @@
*/
package com.graphhopper.routing;
import java.util.PriorityQueue;
import com.carrotsearch.hppc.IntObjectMap;
import com.graphhopper.coll.GHIntObjectHashMap;
import com.graphhopper.routing.util.TraversalMode;
......@@ -27,8 +29,6 @@ import com.graphhopper.util.EdgeExplorer;
import com.graphhopper.util.EdgeIterator;
import com.graphhopper.util.Parameters;
import java.util.PriorityQueue;
/**
* Implements a single source shortest path algorithm
* http://en.wikipedia.org/wiki/Dijkstra's_algorithm
......@@ -37,101 +37,101 @@ import java.util.PriorityQueue;
* @author Peter Karich
*/
public class Dijkstra extends AbstractRoutingAlgorithm {
protected IntObjectMap<SPTEntry> fromMap;
protected PriorityQueue<SPTEntry> fromHeap;
protected SPTEntry currEdge;
private int visitedNodes;
private int to = -1;
public Dijkstra(Graph graph, Weighting weighting, TraversalMode tMode) {
super(graph, weighting, tMode);
int size = Math.min(Math.max(200, graph.getNodes() / 10), 2000);
initCollections(size);
}
protected void initCollections(int size) {
fromHeap = new PriorityQueue<SPTEntry>(size);
fromMap = new GHIntObjectHashMap<SPTEntry>(size);
}
@Override
public Path calcPath(int from, int to) {
checkAlreadyRun();
this.to = to;
currEdge = createSPTEntry(from, 0);
if (!traversalMode.isEdgeBased()) {
fromMap.put(from, currEdge);
}
runAlgo();
return extractPath();
}
protected void runAlgo() {
EdgeExplorer explorer = outEdgeExplorer;
while (true) {
visitedNodes++;
if (isMaxVisitedNodesExceeded() || finished())
break;
int startNode = currEdge.adjNode;
EdgeIterator iter = explorer.setBaseNode(startNode);
while (iter.next()) {
if (!accept(iter, currEdge.edge))
continue;
int traversalId = traversalMode.createTraversalId(iter, false);
double tmpWeight = weighting.calcWeight(iter, false, currEdge.edge) + currEdge.weight;
if (Double.isInfinite(tmpWeight))
continue;
SPTEntry nEdge = fromMap.get(traversalId);
if (nEdge == null) {
nEdge = new SPTEntry(iter.getEdge(), iter.getAdjNode(), tmpWeight);
nEdge.parent = currEdge;
fromMap.put(traversalId, nEdge);
fromHeap.add(nEdge);
} else if (nEdge.weight > tmpWeight) {
fromHeap.remove(nEdge);
nEdge.edge = iter.getEdge();
nEdge.weight = tmpWeight;
nEdge.parent = currEdge;
fromHeap.add(nEdge);
} else
continue;
updateBestPath(iter, nEdge, traversalId);
}
if (fromHeap.isEmpty())
break;
currEdge = fromHeap.poll();
if (currEdge == null)
throw new AssertionError("Empty edge cannot happen");
}
}
@Override
protected boolean finished() {
return currEdge.adjNode == to;
}
@Override
protected Path extractPath() {
if (currEdge == null || !finished())
return createEmptyPath();
return new Path(graph, weighting).
setWeight(currEdge.weight).setSPTEntry(currEdge).extract();
}
@Override
public int getVisitedNodes() {
return visitedNodes;
}
@Override
public String getName() {
return Parameters.Algorithms.DIJKSTRA;
}
protected IntObjectMap<SPTEntry> fromMap;
protected PriorityQueue<SPTEntry> fromHeap;
protected SPTEntry currEdge;
protected int visitedNodes;
protected int to = -1;
public Dijkstra(Graph graph, Weighting weighting, TraversalMode tMode) {
super(graph, weighting, tMode);
int size = Math.min(Math.max(200, graph.getNodes() / 10), 2000);
initCollections(size);
}
protected void initCollections(int size) {
fromHeap = new PriorityQueue<SPTEntry>(size);
fromMap = new GHIntObjectHashMap<SPTEntry>(size);
}
@Override
public Path calcPath(int from, int to) {
// checkAlreadyRun();
this.to = to;
currEdge = createSPTEntry(from, 0);
if (!traversalMode.isEdgeBased()) {
fromMap.put(from, currEdge);
}
runAlgo();
return extractPath();
}
protected void runAlgo() {
EdgeExplorer explorer = outEdgeExplorer;
while (true) {
visitedNodes++;
if (isMaxVisitedNodesExceeded() || finished())
break;
int startNode = currEdge.adjNode;
EdgeIterator iter = explorer.setBaseNode(startNode);
while (iter.next()) {
if (!accept(iter, currEdge.edge))
continue;
int traversalId = traversalMode.createTraversalId(iter, false);
double tmpWeight = weighting.calcWeight(iter, false, currEdge.edge) + currEdge.weight;
if (Double.isInfinite(tmpWeight))
continue;
SPTEntry nEdge = fromMap.get(traversalId);
if (nEdge == null) {
nEdge = new SPTEntry(iter.getEdge(), iter.getAdjNode(), tmpWeight);
nEdge.parent = currEdge;
fromMap.put(traversalId, nEdge);
fromHeap.add(nEdge);
} else if (nEdge.weight > tmpWeight) {
fromHeap.remove(nEdge);
nEdge.edge = iter.getEdge();
nEdge.weight = tmpWeight;
nEdge.parent = currEdge;
fromHeap.add(nEdge);
} else
continue;
updateBestPath(iter, nEdge, traversalId);
}
if (fromHeap.isEmpty())
break;
currEdge = fromHeap.poll();
if (currEdge == null)
throw new AssertionError("Empty edge cannot happen");
}
}
@Override
protected boolean finished() {
return currEdge.adjNode == to;
}
@Override
protected Path extractPath() {
if (currEdge == null || !finished())
return createEmptyPath();
return new Path(graph, weighting).
setWeight(currEdge.weight).setSPTEntry(currEdge).extract();
}
@Override
public int getVisitedNodes() {
return visitedNodes;
}
@Override
public String getName() {
return Parameters.Algorithms.DIJKSTRA;
}
}
/*
* 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.carrotsearch.hppc.IntObjectMap;
import com.graphhopper.coll.GHIntObjectHashMap;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.SPTEntry;
import com.graphhopper.util.Parameters;
/**
* A simple dijkstra tuned to perform one to many queries more efficient than Dijkstra. Old data
* structures are cached between requests and potentially reused.
* <p>
*
* @author Peter Karich
*/
public class RealDijkstraOneToMany extends Dijkstra {
protected IntObjectMap<SPTEntry> nodesMap;
public RealDijkstraOneToMany(Graph graph, Weighting weighting, TraversalMode tMode) {
super(graph, weighting, tMode);
}
@Override
protected void initCollections(int size) {
super.initCollections(size);
nodesMap = new GHIntObjectHashMap<SPTEntry>(size);
}
@Override
public Path calcPath(int from, int to) {
this.to = to;
currEdge = nodesMap.get(to);
if (currEdge == null) {
return super.calcPath(from, to);
}
return extractPath();
}
@Override
public String getName() {
return Parameters.Algorithms.REAL_DIJKSTRA_ONE_TO_MANY;
}
@Override
protected boolean isMaxVisitedNodesExceeded() {
nodesMap.put(currEdge.adjNode, currEdge);
return super.isMaxVisitedNodesExceeded();
}
}
......@@ -17,14 +17,22 @@
*/
package com.graphhopper.routing;
import static com.graphhopper.util.Parameters.Algorithms.ALT_ROUTE;
import static com.graphhopper.util.Parameters.Algorithms.ASTAR;
import static com.graphhopper.util.Parameters.Algorithms.ASTAR_BI;
import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA;
import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_ONE_TO_MANY;
import static com.graphhopper.util.Parameters.Algorithms.AltRoute.MAX_PATHS;
import static com.graphhopper.util.Parameters.Algorithms.AltRoute.MAX_SHARE;
import static com.graphhopper.util.Parameters.Algorithms.AltRoute.MAX_WEIGHT;
import com.graphhopper.routing.weighting.BeelineWeightApproximator;
import com.graphhopper.routing.weighting.WeightApproximator;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.NodeAccess;
import com.graphhopper.util.Helper;
import static com.graphhopper.util.Parameters.Algorithms.*;
import static com.graphhopper.util.Parameters.Algorithms.AltRoute.*;
import com.graphhopper.util.Parameters;
/**
* A simple factory creating normal algorithms (RoutingAlgorithm) without preparation.
......@@ -33,59 +41,62 @@ import static com.graphhopper.util.Parameters.Algorithms.AltRoute.*;
* @author Peter Karich
*/
public class RoutingAlgorithmFactorySimple implements RoutingAlgorithmFactory {
@Override
public RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts) {
RoutingAlgorithm ra;
String algoStr = opts.getAlgorithm();
if (DIJKSTRA_BI.equalsIgnoreCase(algoStr)) {
ra = new DijkstraBidirectionRef(g, opts.getWeighting(), opts.getTraversalMode());
} else if (DIJKSTRA.equalsIgnoreCase(algoStr)) {
ra = new Dijkstra(g, opts.getWeighting(), opts.getTraversalMode());
@Override
public RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts) {
RoutingAlgorithm ra;
String algoStr = opts.getAlgorithm();
if (DIJKSTRA_BI.equalsIgnoreCase(algoStr)) {
ra = new DijkstraBidirectionRef(g, opts.getWeighting(), opts.getTraversalMode());
} else if (DIJKSTRA.equalsIgnoreCase(algoStr)) {
ra = new Dijkstra(g, opts.getWeighting(), opts.getTraversalMode());
} else if (ASTAR_BI.equalsIgnoreCase(algoStr)) {
AStarBidirection aStarBi = new AStarBidirection(g, opts.getWeighting(),
opts.getTraversalMode());
aStarBi.setApproximation(getApproximation(ASTAR_BI, opts, g.getNodeAccess()));
ra = aStarBi;
} else if (ASTAR_BI.equalsIgnoreCase(algoStr)) {
AStarBidirection aStarBi = new AStarBidirection(g, opts.getWeighting(),
opts.getTraversalMode());
aStarBi.setApproximation(getApproximation(ASTAR_BI, opts, g.getNodeAccess()));
ra = aStarBi;
} else if (DIJKSTRA_ONE_TO_MANY.equalsIgnoreCase(algoStr)) {
ra = new DijkstraOneToMany(g, opts.getWeighting(), opts.getTraversalMode());
} else if (DIJKSTRA_ONE_TO_MANY.equalsIgnoreCase(algoStr)) {
ra = new DijkstraOneToMany(g, opts.getWeighting(), opts.getTraversalMode());
} else if (Parameters.Algorithms.REAL_DIJKSTRA_ONE_TO_MANY.equalsIgnoreCase(algoStr)) {
ra = new RealDijkstraOneToMany(g, opts.getWeighting(), opts.getTraversalMode());
} else if (ASTAR.equalsIgnoreCase(algoStr)) {
AStar aStar = new AStar(g, opts.getWeighting(), opts.getTraversalMode());
aStar.setApproximation(getApproximation(ASTAR, opts, g.getNodeAccess()));
ra = aStar;
} else if (ASTAR.equalsIgnoreCase(algoStr)) {
AStar aStar = new AStar(g, opts.getWeighting(), opts.getTraversalMode());
aStar.setApproximation(getApproximation(ASTAR, opts, g.getNodeAccess()));
ra = aStar;
} else if (ALT_ROUTE.equalsIgnoreCase(algoStr)) {
AlternativeRoute altRouteAlgo = new AlternativeRoute(g, opts.getWeighting(), opts.getTraversalMode());
altRouteAlgo.setMaxPaths(opts.getHints().getInt(MAX_PATHS, 2));
altRouteAlgo.setMaxWeightFactor(opts.getHints().getDouble(MAX_WEIGHT, 1.4));
altRouteAlgo.setMaxShareFactor(opts.getHints().getDouble(MAX_SHARE, 0.6));
altRouteAlgo.setMinPlateauFactor(opts.getHints().getDouble("alternative_route.min_plateau_factor", 0.2));
altRouteAlgo.setMaxExplorationFactor(opts.getHints().getDouble("alternative_route.max_exploration_factor", 1));
ra = altRouteAlgo;
} else if (ALT_ROUTE.equalsIgnoreCase(algoStr)) {
AlternativeRoute altRouteAlgo = new AlternativeRoute(g, opts.getWeighting(), opts.getTraversalMode());
altRouteAlgo.setMaxPaths(opts.getHints().getInt(MAX_PATHS, 2));
altRouteAlgo.setMaxWeightFactor(opts.getHints().getDouble(MAX_WEIGHT, 1.4));
altRouteAlgo.setMaxShareFactor(opts.getHints().getDouble(MAX_SHARE, 0.6));
altRouteAlgo.setMinPlateauFactor(opts.getHints().getDouble("alternative_route.min_plateau_factor", 0.2));
altRouteAlgo.setMaxExplorationFactor(opts.getHints().getDouble("alternative_route.max_exploration_factor", 1));
ra = altRouteAlgo;
} else {
throw new IllegalArgumentException("Algorithm " + algoStr + " not found in " + getClass().getName());
}
} else {
throw new IllegalArgumentException("Algorithm " + algoStr + " not found in " + getClass().getName());
}
ra.setMaxVisitedNodes(opts.getMaxVisitedNodes());
return ra;
}
ra.setMaxVisitedNodes(opts.getMaxVisitedNodes());
return ra;
}
public static WeightApproximator getApproximation(String prop, AlgorithmOptions opts, NodeAccess na) {
String approxAsStr = opts.getHints().get(prop + ".approximation", "BeelineSimplification");
double epsilon = opts.getHints().getDouble(prop + ".epsilon", 1);
public static WeightApproximator getApproximation(String prop, AlgorithmOptions opts, NodeAccess na) {
String approxAsStr = opts.getHints().get(prop + ".approximation", "BeelineSimplification");
double epsilon = opts.getHints().getDouble(prop + ".epsilon", 1);
BeelineWeightApproximator approx = new BeelineWeightApproximator(na, opts.getWeighting());
approx.setEpsilon(epsilon);
if ("BeelineSimplification".equals(approxAsStr))
approx.setDistanceCalc(Helper.DIST_PLANE);
else if ("BeelineAccurate".equals(approxAsStr))
approx.setDistanceCalc(Helper.DIST_EARTH);
else
throw new IllegalArgumentException("Approximation " + approxAsStr + " not found in " + RoutingAlgorithmFactorySimple.class.getName());
BeelineWeightApproximator approx = new BeelineWeightApproximator(na, opts.getWeighting());
approx.setEpsilon(epsilon);
if ("BeelineSimplification".equals(approxAsStr))
approx.setDistanceCalc(Helper.DIST_PLANE);
else if ("BeelineAccurate".equals(approxAsStr))
approx.setDistanceCalc(Helper.DIST_EARTH);
else
throw new IllegalArgumentException("Approximation " + approxAsStr + " not found in " + RoutingAlgorithmFactorySimple.class.getName());
return approx;
}
return approx;
}
}
......@@ -17,6 +17,11 @@
*/
package com.graphhopper.routing.template;
import static com.graphhopper.util.Parameters.Routing.PASS_THROUGH;
import java.util.Collections;
import java.util.List;
import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.PathWrapper;
......@@ -31,12 +36,6 @@ import com.graphhopper.util.Parameters.Routing;
import com.graphhopper.util.PathMerger;
import com.graphhopper.util.PointList;
import com.graphhopper.util.Translation;
import java.util.Collections;
import java.util.List;
import static com.graphhopper.util.Parameters.Routing.PASS_THROUGH;
import com.graphhopper.util.shapes.GHPoint;
/**
......@@ -44,44 +43,44 @@ import com.graphhopper.util.shapes.GHPoint;
*
* @author Peter Karich
*/
final public class AlternativeRoutingTemplate extends ViaRoutingTemplate {
public AlternativeRoutingTemplate(GHRequest ghRequest, GHResponse ghRsp, LocationIndex locationIndex) {
super(ghRequest, ghRsp, locationIndex);
}
public class AlternativeRoutingTemplate extends ViaRoutingTemplate {
public AlternativeRoutingTemplate(GHRequest ghRequest, GHResponse ghRsp, LocationIndex locationIndex) {
super(ghRequest, ghRsp, locationIndex);
}
@Override
public List<QueryResult> lookup(List<GHPoint> points, FlagEncoder encoder) {
if (points.size() > 2)
throw new IllegalArgumentException("Currently alternative routes work only with start and end point. You tried to use: " + points.size() + " points");
@Override
public List<QueryResult> lookup(List<GHPoint> points, FlagEncoder encoder) {
if (points.size() > 2)
throw new IllegalArgumentException("Currently alternative routes work only with start and end point. You tried to use: " + points.size() + " points");
return super.lookup(points, encoder);
}
return super.lookup(points, encoder);
}
@Override
public List<Path> calcPaths(QueryGraph queryGraph, RoutingAlgorithmFactory algoFactory, AlgorithmOptions algoOpts) {
boolean withViaTurnPenalty = ghRequest.getHints().getBool(Routing.PASS_THROUGH, false);
if (withViaTurnPenalty)
throw new IllegalArgumentException("Alternative paths and " + PASS_THROUGH + " at the same time is currently not supported");
@Override
public List<Path> calcPaths(QueryGraph queryGraph, RoutingAlgorithmFactory algoFactory, AlgorithmOptions algoOpts) {
boolean withViaTurnPenalty = ghRequest.getHints().getBool(Routing.PASS_THROUGH, false);
if (withViaTurnPenalty)
throw new IllegalArgumentException("Alternative paths and " + PASS_THROUGH + " at the same time is currently not supported");
return super.calcPaths(queryGraph, algoFactory, algoOpts);
}
return super.calcPaths(queryGraph, algoFactory, algoOpts);
}
@Override
public boolean isReady(PathMerger pathMerger, Translation tr) {
if (pathList.isEmpty())
throw new RuntimeException("Empty paths for alternative route calculation not expected");
@Override
public boolean isReady(PathMerger pathMerger, Translation tr) {
if (pathList.isEmpty())
throw new RuntimeException("Empty paths for alternative route calculation not expected");
// if alternative route calculation was done then create the responses from single paths
PointList wpList = getWaypoints();
altResponse.setWaypoints(wpList);
ghResponse.add(altResponse);
pathMerger.doWork(altResponse, Collections.singletonList(pathList.get(0)), tr);
for (int index = 1; index < pathList.size(); index++) {
PathWrapper tmpAltRsp = new PathWrapper();
tmpAltRsp.setWaypoints(wpList);
ghResponse.add(tmpAltRsp);
pathMerger.doWork(tmpAltRsp, Collections.singletonList(pathList.get(index)), tr);
}
return true;
}
// if alternative route calculation was done then create the responses from single paths
PointList wpList = getWaypoints();
altResponse.setWaypoints(wpList);
ghResponse.add(altResponse);
pathMerger.doWork(altResponse, Collections.singletonList(pathList.get(0)), tr);
for (int index = 1; index < pathList.size(); index++) {
PathWrapper tmpAltRsp = new PathWrapper();
tmpAltRsp.setWaypoints(wpList);
ghResponse.add(tmpAltRsp);
pathMerger.doWork(tmpAltRsp, Collections.singletonList(pathList.get(index)), tr);
}
return true;
}
}
/*
* 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.template;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.PathWrapper;
import com.graphhopper.routing.AlgorithmOptions;
import com.graphhopper.routing.Path;
import com.graphhopper.routing.QueryGraph;
import com.graphhopper.routing.RoutingAlgorithm;
import com.graphhopper.routing.RoutingAlgorithmFactory;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.storage.index.QueryResult;
import com.graphhopper.util.Parameters.Routing;
import com.graphhopper.util.PathMerger;
import com.graphhopper.util.PointList;
import com.graphhopper.util.StopWatch;
import com.graphhopper.util.Translation;