/**
*
*/
package edu.kit.pse.ws2013.routekit.routecalculation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import edu.kit.pse.ws2013.routekit.map.EdgeBasedGraph;
import edu.kit.pse.ws2013.routekit.map.Graph;
import edu.kit.pse.ws2013.routekit.map.StreetMap;
import edu.kit.pse.ws2013.routekit.models.ProfileMapCombination;
import edu.kit.pse.ws2013.routekit.models.Weights;
import edu.kit.pse.ws2013.routekit.util.PointOnEdge;
/**
* @author Kevin
*
*/
public class Dijkstra implements RouteCalculator {
int destinationPartition;
int destinationCorrespondingPartition;
@Override
public Route calculateRoute(final PointOnEdge start,
final PointOnEdge destination, final ProfileMapCombination data) {
final StreetMap streetMap = data.getStreetMap();
final Graph graph = streetMap.getGraph();
final EdgeBasedGraph edgeBasedGraph = streetMap.getEdgeBasedGraph();
final Weights weights = data.getWeights();
final int edgeCount = edgeBasedGraph.getNumberOfEdges();
final int[] distance = new int[edgeCount];
final int[] previous = new int[edgeCount];
final FibonacciHeap fh = new FibonacciHeap();
final int startEdge = start.getEdge();
final int destinationEdge = destination.getEdge();
final int startCorrespondingEdge = graph
.getCorrespondingEdge(startEdge);
final int destinationCorrespondingEdge = graph
.getCorrespondingEdge(destinationEdge);
boolean otherDestination = false;
final LinkedList<Integer> turns = new LinkedList<>();
final Map<Integer, FibonacciHeapEntry> fhList = new HashMap<>();
final Set<Integer> ignore = new HashSet<>();
// destination edge’s partition
destinationPartition = edgeBasedGraph.getPartition(destinationEdge);
if (destinationCorrespondingEdge != -1) {
destinationCorrespondingPartition = edgeBasedGraph
.getPartition(destinationCorrespondingEdge);
} else {
destinationCorrespondingPartition = -1;
}
// initialization
boolean foundRoute = false;
final Set<Integer> startIncomingTurns = edgeBasedGraph
.getIncomingTurns(startEdge);
Set<Integer> startCorrespondingIncomingTurns = null;
if (startCorrespondingEdge != -1) {
startCorrespondingIncomingTurns = edgeBasedGraph
.getIncomingTurns(startCorrespondingEdge);
}
if (startEdge == destinationEdge
|| startEdge == destinationCorrespondingEdge) {
// Corner case: route on one edge.
// If we can take this edge, that’s our route and we skip Dijkstra.
// Note that checking this isn’t quite straightforward, since
// Arc-flags record the restrictions of the *target* edge;
// therefore, we need to check the Arc-flags of all *incoming* turns
// to verify that we can take this edge.
if (start.getPosition() < (startEdge == destinationEdge ? destination
.getPosition() : 1 - destination.getPosition())) {
boolean allowed = false;
for (int turn : startIncomingTurns) {
if (allowsTurn(turn)) {
allowed = true;
break;
}
}
if (startIncomingTurns.isEmpty()) {
// dead-end; it’s allowed if any outgoing turn is allowed
for (int turn : edgeBasedGraph.getOutgoingTurns(startEdge)) {
if (allowsTurn(turn)) {
allowed = true;
break;
}
}
}
if (allowed) {
foundRoute = true;
previous[startEdge] = startEdge;
if (startEdge == destinationCorrespondingEdge) {
otherDestination = true;
}
}
} else {
if (startCorrespondingEdge != -1) {
boolean allowed = false;
for (int turn : startCorrespondingIncomingTurns) {
if (allowsTurn(turn)) {
allowed = true;
break;
}
}
if (startCorrespondingIncomingTurns.isEmpty()) {
// dead-end; it’s allowed if any outgoing turn is
// allowed
for (int turn : edgeBasedGraph
.getOutgoingTurns(startCorrespondingEdge)) {
if (allowsTurn(turn)) {
allowed = true;
break;
}
}
}
if (allowed) {
foundRoute = true;
otherDestination = true;
previous[destinationCorrespondingEdge] = destinationCorrespondingEdge;
}
}
}
if (!foundRoute) {
// no turn in this direction, use regular Dijkstra –
// emulate first round: insert all reachable edges into heap
Set<Integer> startTurns = new HashSet<>(
edgeBasedGraph.getOutgoingTurns(startEdge));
if (startCorrespondingEdge != -1) {
startTurns.addAll(edgeBasedGraph
.getOutgoingTurns(startCorrespondingEdge));
}
for (int currentTurn : startTurns) {
if (allowsTurn(currentTurn)) {
boolean reallyAllowed = false;
// reallyAllowed checks if we can start on the start
// edge, i. e. if we can take the start edge, which is
// (as above) determined via the incoming edges
// note that this is only necessary for the
for (int incoming : edgeBasedGraph
.getIncomingTurns(edgeBasedGraph
.getStartEdge(currentTurn))) {
if (allowsTurn(incoming)) {
reallyAllowed = true;
break;
}
}
if (reallyAllowed) {
final int weight = weights.getWeight(currentTurn);
if (weight == Integer.MAX_VALUE) {
continue;
}
final int alt = 1 + weight;
final int targetEdge = edgeBasedGraph
.getTargetEdge(currentTurn);
distance[targetEdge] = alt;
previous[targetEdge] = startEdge;
fhList.put(targetEdge, fh.add(targetEdge, alt));
}
}
}
}
} else {
// regular route
boolean startEdgeAllowed = false;
for (int incoming : startIncomingTurns) {
if (allowsTurn(incoming)) {
startEdgeAllowed = true;
break;
}
}
if (startIncomingTurns.isEmpty()) {
// corner case: dead-end.
// we have no way of knowing if a route is allowed here; we just
// always allow it, assuming that this is the more common case.
startEdgeAllowed = true;
}
if (startEdgeAllowed) {
distance[startEdge] = 1;
fhList.put(startEdge, fh.add(startEdge, distance[startEdge]));
}
if (startCorrespondingEdge != -1) {
boolean startCorrespondingEdgeAllowed = false;
for (int incoming : startCorrespondingIncomingTurns) {
if (allowsTurn(incoming)) {
startCorrespondingEdgeAllowed = true;
break;
}
}
if (startCorrespondingIncomingTurns.isEmpty()) {
// same as above
startCorrespondingEdgeAllowed = true;
}
if (startCorrespondingEdgeAllowed) {
distance[startCorrespondingEdge] = 1;
fhList.put(startCorrespondingEdge, fh.add(
startCorrespondingEdge,
distance[startCorrespondingEdge]));
}
}
}
// calculation
while (!fh.isEmpty()) {
final int u = fh.deleteMin().getValue();
fhList.remove(u);
ignore.add(u);
if (u == destinationEdge) {
// found it!
foundRoute = true;
break;
}
if (u == destinationCorrespondingEdge) {
// found it!
otherDestination = true;
foundRoute = true;
break;
}
final Set<Integer> outgoingTurns = edgeBasedGraph
.getOutgoingTurns(u);
for (final Integer currentTurn : outgoingTurns) {
final int targetEdge = edgeBasedGraph
.getTargetEdge(currentTurn);
if (!ignore.contains(targetEdge)) {
if (allowsTurn(currentTurn)) {
final int weight = weights.getWeight(currentTurn);
if (weight == Integer.MAX_VALUE) {
continue;
}
final int alt = distance[u] + weight;
if (alt < distance[targetEdge]
|| distance[targetEdge] == 0) {
distance[targetEdge] = alt;
previous[targetEdge] = u;
final FibonacciHeapEntry toDecrease = fhList
.get(targetEdge);
if (toDecrease == null) {
fhList.put(targetEdge, fh.add(targetEdge, alt));
} else {
fh.decreaseKey(toDecrease, alt);
}
}
}
}
}
}
// reconstruct way
int x;
int newStartEdge = start.getEdge();
int newDestinationEdge = destination.getEdge();
if (otherDestination) {
x = destinationCorrespondingEdge;
newDestinationEdge = x;
} else {
x = destinationEdge;
}
if (!foundRoute) {
// no route found
return null;
} else {
do {
final Set<Integer> outgoingTurns = edgeBasedGraph
.getOutgoingTurns(previous[x]);
for (final Integer turn : outgoingTurns) {
if (edgeBasedGraph.getTargetEdge(turn) == x) {
turns.addFirst(turn);
break;
}
}
x = previous[x];
} while (x != startEdge && x != startCorrespondingEdge);
}
newStartEdge = x;
final PointOnEdge newStart = new PointOnEdge(newStartEdge,
(newStartEdge != startEdge) ? (1 - start.getPosition())
: start.getPosition());
final PointOnEdge newDestination = new PointOnEdge(newDestinationEdge,
(newDestinationEdge != destinationEdge) ? (1 - destination
.getPosition()) : destination.getPosition());
final Route route = new Route(data, newStart, newDestination, turns);
return route;
}
protected boolean allowsTurn(int turn) {
return true;
}
}