org.opentripplanner.transit.raptor.rangeraptor.standard.stoparrivals.view.StopsCursor Maven / Gradle / Ivy
Show all versions of otp Show documentation
package org.opentripplanner.transit.raptor.rangeraptor.standard.stoparrivals.view;
import java.util.function.ToIntFunction;
import javax.validation.constraints.NotNull;
import org.opentripplanner.transit.raptor.api.transit.RaptorTransfer;
import org.opentripplanner.transit.raptor.api.transit.RaptorTripPattern;
import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule;
import org.opentripplanner.transit.raptor.api.view.ArrivalView;
import org.opentripplanner.transit.raptor.rangeraptor.standard.stoparrivals.StopArrivalState;
import org.opentripplanner.transit.raptor.rangeraptor.standard.stoparrivals.StopArrivals;
import org.opentripplanner.transit.raptor.rangeraptor.transit.TransitCalculator;
/**
* Used to create a view to the internal StdRangeRaptor model and to navigate
* between stop arrivals. Since view objects are only used for path and debugging
* operations, the view can create temporary objects for each StopArrival. These
* view objects are temporary objects and when the algorithm progress they might
* get invalid - so do not keep references to these objects bejond the scope of
* of a the callers method.
*
* The design was originally done to support the FLyweight design pattern.
*
* @param The TripSchedule type defined by the user of the raptor API.
*/
public class StopsCursor {
private final StopArrivals arrivals;
private final TransitCalculator transitCalculator;
private final ToIntFunction boardSlackProvider;
public StopsCursor(
StopArrivals arrivals,
TransitCalculator transitCalculator,
ToIntFunction boardSlackProvider
) {
this.arrivals = arrivals;
this.transitCalculator = transitCalculator;
this.boardSlackProvider = boardSlackProvider;
}
public boolean reachedOnBoard(int round, int stop) {
var a = arrivals.get(round, stop);
return a != null && a.reachedOnBoard();
}
public boolean reachedOnStreet(int round, int stop) {
var a = arrivals.get(round, stop);
if(a == null) { return false; }
return a.arrivedByAccessOnStreet() || a.arrivedByTransfer();
}
/** Return a fictive access stop arrival. */
public Access fictiveAccess(int round, RaptorTransfer accessPath, int arrivalTime) {
return new Access<>(round, arrivalTime, accessPath);
}
/**
* Return a fictive Transfer stop arrival view. The arrival does not exist in the state, but is
* linked with the previous arrival witch is a "real" arrival present in the state. This
* enables path generation.
*/
public Transfer fictiveTransfer(
int round, int fromStop, RaptorTransfer transfer, int toStop, int arrivalTime
) {
StopArrivalState arrival = StopArrivalState.create();
arrival.transferToStop(fromStop, arrivalTime, transfer);
return new Transfer<>(round, toStop, arrival, this);
}
/**
* Return a fictive Transit stop arrival view. The arrival does not exist in the state, but is
* linked with the previous arrival witch is a "real" arrival present in the state. This
* enables path generation.
*/
public Transit fictiveTransit(
int round, int alightStop, int alightTime, T trip, int boardStop, int boardTime
) {
StopArrivalState arrival = StopArrivalState.create();
arrival.arriveByTransit(alightTime, boardStop, boardTime, trip);
return new Transit<>(round, alightStop, arrival, this);
}
/**
* Return the stop-arrival for the given round, stop and given access. There is no
* check that the access exist.
*/
public ArrivalView access(int round, int stop, RaptorTransfer access) {
var arrival = arrivals.get(round, stop);
int time = access.stopReachedOnBoard() ? arrival.onBoardArrivalTime() : arrival.time();
return new Access<>(round, time, access);
}
/**
* Return the stop-arrival for the given round, stop and method of arrival(stopReachedOnBoard).
* The returned arrival can be access(including flex), transfer or transit.
*
* @param stopReachedOnBoard if {@code true} the arrival returned must arrive onboard a vehicle,
* if {@code false} the BEST arrival is returned on-street or on-board.
*/
public ArrivalView stop(int round, int stop, boolean stopReachedOnBoard) {
var arrival = arrivals.get(round, stop);
// We chack for on-street arrivals first, since on-street is only available if it is better
// than on-board arrivals
if(!stopReachedOnBoard) {
if (arrival.arrivedByAccessOnStreet()) {
return newAccessView(round, arrival.time(), arrival.accessPathOnStreet());
}
else if (arrival.arrivedByTransfer()) {
return new Transfer<>(round, stop, arrival, this);
}
}
// On on-board arrivals can always be used, we do not care what the *stopReachedOnBoard* is.
if(arrival.arrivedByAccessOnBoard()) {
return newAccessView(
round,
arrival.onBoardArrivalTime(),
arrival.accessPathOnBoard()
);
}
else if(arrival.arrivedByTransit()) {
return new Transit<>(round, stop, arrival, this);
}
// Should never get here...
throw new IllegalStateException("Unknown arrival: " + arrival);
}
/**
* Set cursor to stop followed by the give transit leg - this allows access to be time-shifted
* according to the next transit boarding/departure time.
*/
public ArrivalView stop(int round, int stop, @NotNull Transit nextTransitLeg) {
var arrival = arrivals.get(round, stop);
if(arrival.arrivedByAccessOnStreet()) {
return newAccessView(round, arrival.accessPathOnStreet(), nextTransitLeg);
}
else if(arrival.arrivedByTransfer()) {
return new Transfer<>(round, stop, arrival, this);
}
else if(arrival.arrivedByAccessOnBoard()) {
return newAccessView(round, arrival.accessPathOnBoard(), nextTransitLeg);
}
else if(arrival.arrivedByTransit()) {
return new Transit<>(round, stop, arrival, this);
}
// Should never get here...
throw new IllegalStateException("Unknown arrival: " + arrival);
}
/**
* A access stop arrival, time-shifted according to the first transit boarding/departure time
* and the possible restrictions in the access.
*
* If given transit is {@code null}, then use the iteration departure time without any
* time-shifted departure. This is used for logging and debugging, not for returned paths.
*/
private ArrivalView newAccessView(
int round,
RaptorTransfer accessPath,
Transit transit
) {
int transitDepartureTime = transit.boardTime();
int boardSlack = boardSlackProvider.applyAsInt(transit.trip().pattern());
// Preferred time-shifted access departure
int preferredDepartureTime = transitCalculator.minusDuration(
transitDepartureTime,
boardSlack + accessPath.durationInSeconds()
);
return newAccessView(round, preferredDepartureTime, accessPath);
}
/**
* A access stop arrival, time-shifted according to the {@code preferredDepartureTime} and the
* possible restrictions in the access.
*/
private ArrivalView newAccessView(
int round,
int preferredDepartureTime,
RaptorTransfer accessPath
) {
// Get the real 'departureTime' honoring the time-shift restriction in the access
int departureTime = transitCalculator.departureTime(
accessPath,
preferredDepartureTime
);
int arrivalTime = transitCalculator.plusDuration(
departureTime,
accessPath.durationInSeconds()
);
return new Access<>(round, arrivalTime, accessPath);
}
}