/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package org.opentripplanner.routing.algorithm;
import java.util.Collection;
import org.opentripplanner.common.pqueue.BinHeap;
import org.opentripplanner.common.pqueue.OTPPriorityQueue;
import org.opentripplanner.routing.algorithm.strategies.SearchTerminationStrategy;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.services.SPTService;
import org.opentripplanner.routing.spt.EarliestArrivalShortestPathTree;
import org.opentripplanner.routing.spt.ShortestPathTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Compute full SPT for earliest arrival problem.
* Always builds a full shortest path tree ("batch mode").
*
* Note that walk limiting must be turned off -- resource limiting is not algorithmically correct.
*/
public class EarliestArrivalSPTService implements SPTService {
private static final Logger LOG = LoggerFactory.getLogger(EarliestArrivalSPTService.class);
@Override
public ShortestPathTree getShortestPathTree(RoutingRequest req) {
return getShortestPathTree(req, -1, null); // negative timeout means no timeout
}
@Override
public ShortestPathTree getShortestPathTree(RoutingRequest req, double timeoutSeconds) {
return this.getShortestPathTree(req, timeoutSeconds, null);
}
public ShortestPathTree getShortestPathTree(RoutingRequest options, double relTimeout,
SearchTerminationStrategy terminationStrategy) {
// clone options before modifying, otherwise disabling resource limiting will cause
// SPT cache misses for subsequent requests.
options = options.clone();
// disable any resource limiting, which is algorithmically invalid here
options.setMaxTransfers(Integer.MAX_VALUE);
options.setMaxWalkDistance(Double.MAX_VALUE);
if (options.getClampInitialWait() < 0)
options.setClampInitialWait(60 * 30);
// impose search cutoff
final long maxt = (60 * 60 * 2) + options.getClampInitialWait();
options.worstTime = options.dateTime + (options.arriveBy ? -maxt : maxt);
// SPT cache does not look at routing request in SPT to perform lookup,
// so it's OK to construct with the local cloned one
ShortestPathTree spt = new EarliestArrivalShortestPathTree(options);
State initialState = new State(options);
spt.add(initialState);
OTPPriorityQueue<State> pq = new BinHeap<State>();
pq.insert(initialState, 0);
while (!pq.empty()) {
State u = pq.extract_min();
Vertex u_vertex = u.getVertex();
if (!spt.visit(u))
continue;
Collection<Edge> edges = options.isArriveBy() ? u_vertex.getIncoming() : u_vertex.getOutgoing();
for (Edge edge : edges) {
for (State v = edge.traverse(u); v != null; v = v.getNextResult()) {
if (isWorstTimeExceeded(v, options)) {
continue;
}
if (spt.add(v)) {
pq.insert(v, v.getActiveTime()); // activeTime?
}
}
}
}
return spt;
}
// Move this into State
private boolean isWorstTimeExceeded(State v, RoutingRequest opt) {
if (opt.isArriveBy())
return v.getTimeSeconds() < opt.worstTime;
else
return v.getTimeSeconds() > opt.worstTime;
}
}