Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.opentripplanner.routing.spt;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import org.opentripplanner.routing.api.request.RoutingRequest;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.graph.Vertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* This class keeps track which graph vertices have been visited and their associated states,
* so that decisions can be made about whether new states should be enqueued for later exploration.
* It also allows states to be retrieved for a given target vertex.
*
* We no longer have different implementations of ShortestPathTree because the label-setting (multi-state) approach
* used in public transit routing, turn restrictions, bike rental, etc. is a generalization of the basic Dijkstra
* (single-state) approach. It is much more straightforward to use the more general SPT implementation in all cases.
*
* Note that turn restrictions make all searches multi-state; however turn restrictions do not apply when walking.
* The turn restriction handling is done in the base dominance function implementation, and applies to all subclasses.
* It essentially splits each vertex into N vertices depending on the incoming edge being taken.
*/
public class ShortestPathTree {
private static final Logger LOG = LoggerFactory.getLogger(ShortestPathTree.class);
public final RoutingRequest options;
public final DominanceFunction dominanceFunction;
private Map> stateSets;
public ShortestPathTree (RoutingRequest options, DominanceFunction dominanceFunction) {
this.options = options;
this.dominanceFunction = dominanceFunction;
stateSets = new IdentityHashMap>();
}
/** @return a list of GraphPaths, sometimes empty but never null. */
public List getPaths(Vertex dest, boolean optimize) {
List extends State> stateList = getStates(dest);
if (stateList == null)
return Collections.emptyList();
List ret = new LinkedList();
for (State s : stateList) {
if (s.isFinal()) {
ret.add(new GraphPath(s, optimize));
}
}
return ret;
}
/** @return a default set of back-optimized paths to the target vertex. */
public List getPaths() {
List graphPaths = new ArrayList<>();
for (Vertex vertex : options.getRoutingContext().toVertices) {
graphPaths.addAll(getPaths(vertex, true));
}
return graphPaths;
}
/** @return a single optimal, optionally back-optimized path to the given vertex. */
public GraphPath getPath(Vertex dest, boolean optimize) {
State s = getState(dest);
if (s == null) {
return null;
} else {
return new GraphPath(s, optimize);
}
}
/** @return the routing context for the search that produced this tree */
public RoutingRequest getOptions() {
return options;
}
/** Print out a summary of the number of states and vertices. */
public void dump() {
Multiset histogram = HashMultiset.create();
int statesCount = 0;
int maxSize = 0;
for (Map.Entry> kv : stateSets.entrySet()) {
List states = kv.getValue();
int size = states.size();
histogram.add(size);
statesCount += size;
if (size > maxSize) {
maxSize = size;
}
}
LOG.info("SPT: vertices: " + stateSets.size() + " states: total: "
+ statesCount + " per vertex max: " + maxSize + " avg: "
+ (statesCount * 1.0 / stateSets.size()));
List nStates = new ArrayList(histogram.elementSet());
Collections.sort(nStates);
for (Integer nState : nStates) {
LOG.info(nState + " states: " + histogram.count(nState) + " vertices.");
}
}
public Set getVertices() {
return stateSets.keySet();
}
/**
* The add method checks a new State to see if it is non-dominated and thus worth visiting
* later. If so, the method returns 'true' indicating that the state is deemed useful and should
* be enqueued for later exploration. The method will also perform implementation-specific
* actions that track dominant or optimal states.
*
* @param newState the State to add to the SPT, if it is deemed non-dominated
* @return a boolean value indicating whether the state was added to the tree and should
* therefore be enqueued
*/
public boolean add(State newState) {
Vertex vertex = newState.getVertex();
List states = stateSets.get(vertex);
// if the vertex has no states, add one and return
if (states == null) {
states = new ArrayList<>();
stateSets.put(vertex, states);
states.add(newState);
return true;
}
// if the vertex has any states that dominate the new state, don't add the state
// if the new state dominates any old states, remove them
Iterator it = states.iterator();
while (it.hasNext()) {
State oldState = it.next();
// order is important, because in the case of a tie
// we want to reject the new state
if (dominanceFunction.betterOrEqualAndComparable(oldState, newState))
return false;
if (dominanceFunction.betterOrEqualAndComparable(newState, oldState))
it.remove();
}
// any states remaining are co-dominant with the new state
states.add(newState);
return true;
}
/**
* Returns the 'best' state for the given Vertex, where 'best' depends on the implementation.
*
* @param dest the vertex of interest
* @return a 'best' state at that vertex
*/
public State getState(Vertex dest) {
Collection states = stateSets.get(dest);
if (states == null)
return null;
State ret = null;
// TODO are we only checking path parser acceptance when we fetch states via this specific method?
for (State s : states) {
if ((ret == null || s.weight < ret.weight) && s.isFinal()) {
ret = s;
}
}
return ret;
}
/**
* Returns a collection of 'interesting' states for the given Vertex. Depending on the
* implementation, this could contain a single optimal state, a set of Pareto-optimal states, or
* even states that are not known to be optimal but are judged interesting by some other
* criteria.
*
* @param dest the vertex of interest
* @return a collection of 'interesting' states at that vertex
*/
public List getStates(Vertex dest) {
return stateSets.get(dest);
}
/** @return number of vertices referenced in this SPT */
public int getVertexCount() {
return stateSets.keySet().size();
}
/**
* The visit method should be called upon extracting a State from a priority queue. It
* checks whether the State is still worth visiting (i.e. whether it has been dominated since it
* was enqueued) and informs the ShortestPathTree that this State's outgoing edges have been
* relaxed. A state may remain in the priority queue after being dominated, and such sub-optimal
* states must be caught as they come out of the queue to avoid unnecessary branching.
*
* So this function checks that a state coming out of the queue is still in the Pareto-optimal set for this vertex,
* which indicates that it has not been ruled out as a state on an optimal path. Many shortest
* path algorithms will decrease the key of a vertex in the priority queue when it is updated, but we store states
* in the queue rather than vertices, and states do not get updated or change their weight.
* TODO consider just removing states from the priority queue.
*
* When the Fibonacci heap was replaced with a binary heap, the decrease-key operation was
* removed for the same reason: both improve theoretical run time complexity, at the cost of
* high constant factors and more complex code.
*
* So there can be dominated (useless) states in the queue. When they come out we want to
* ignore them rather than spend time branching out from them.
*
* @param state - the state about to be visited
* @return - whether this state is still considered worth visiting.
*/
public boolean visit(State state) {
boolean ret = false;
for (State s : stateSets.get(state.getVertex())) {
if (s == state) {
ret = true;
break;
}
}
return ret;
}
/** @return every state in this tree */
public Collection getAllStates() {
ArrayList allStates = new ArrayList();
for (List stateSet : stateSets.values()) {
allStates.addAll(stateSet);
}
return allStates;
}
public String toString() {
return "ShortestPathTree(" + this.stateSets.size() + " vertices)";
}
}