Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.opentripplanner.raptor.api.model;

/**
* An 'access' that happens on-board, meaning that one is already on the vehicle when the path
* starts. This access is defined by a specific trip within a specific route, starting at a specific
* stop in the pattern.
*/
public interface RaptorOnBoardAccess extends RaptorAccessEgress {
/**
* The index of the boarded route
*/
int routeIndex();

/**
* The index of the boarded trip within the route
*/
int tripScheduleIndex();

/**
* The position in the route pattern of the first stop in the journey, where the access path just
* arrived at. Since this is an on-board access, this stop represents the most recently visited
* stop on the currently boarded trip.
* The next stop position after this is the first you can alight.
*/
int stopPositionInPattern();

/**
* The first stop in the journey, where the access path just arrived at. Since this is an on-board
* access, this stop represents the most recently visited stop on the currently boarded trip.
* The next stop position after this is the first you can alight.
* {@inheritDoc}
*/
@Override
int stop();

/**
* Since this is an on-board access, the duration until ({@link #stop}) is 0 seconds.
* {@inheritDoc}
*/
@Override
default int durationInSeconds() {
return 0;
}

@Override
default int earliestDepartureTime(int requestedDepartureTime) {
return requestedDepartureTime;
}

@Override
default int latestArrivalTime(int requestedArrivalTime) {
return requestedArrivalTime;
}

@Override
default boolean hasOpeningHours() {
return false;
}

@Override
default boolean stopReachedByWalking() {
return false;
}

/**
* An on-board access does not support riding other transit before the specified boarding
*/
@Override
default int numberOfRides() {
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ default EgressPathView egressPath() {

boolean arrivedOnBoard();

default OnBoardTripConstraint onBoardTripConstraint() {
throw new UnsupportedOperationException();
}

/** Use this to create a {@code toString()} implementation. */
default String asString() {
String arrival =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.opentripplanner.raptor.api.view;

/**
*
* @param routeIndex The index of the boarded route
* @param tripScheduleIndex The index of the boarded trip within the route
* @param stopPositionInPattern The position in the route pattern of the first stop in the journey,
* where the access path just arrived at. Since this is an on-board
* access, this stop represents the most recently visited stop on the
* currently boarded trip. The next stop position after this is the
* first you can alight.
*/
public record OnBoardTripConstraint(
int routeIndex,
int tripScheduleIndex,
int stopPositionInPattern
) {}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.opentripplanner.raptor.rangeraptor;

import java.util.Collection;
import javax.annotation.Nullable;
import org.opentripplanner.raptor.api.debug.RaptorTimers;
import org.opentripplanner.raptor.api.model.RaptorAccessEgress;
import org.opentripplanner.raptor.api.model.RaptorConstants;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
import org.opentripplanner.raptor.api.view.ArrivalView;
import org.opentripplanner.raptor.rangeraptor.internalapi.RangeRaptorWorker;
import org.opentripplanner.raptor.rangeraptor.internalapi.RaptorRouterResult;
import org.opentripplanner.raptor.rangeraptor.internalapi.RaptorWorkerState;
Expand All @@ -15,6 +15,7 @@
import org.opentripplanner.raptor.rangeraptor.transit.AccessPaths;
import org.opentripplanner.raptor.rangeraptor.transit.RaptorTransitCalculator;
import org.opentripplanner.raptor.spi.IntIterator;
import org.opentripplanner.raptor.spi.RaptorRoute;
import org.opentripplanner.raptor.spi.RaptorTransitDataProvider;

/**
Expand Down Expand Up @@ -71,7 +72,6 @@ public final class DefaultRangeRaptorWorker<T extends RaptorTripSchedule>

private final RaptorTimers timers;

@Nullable
private final AccessPaths accessPaths;

private final boolean enableTransferConstraints;
Expand All @@ -89,7 +89,7 @@ public DefaultRangeRaptorWorker(
RoutingStrategy<T> transitWorker,
RaptorTransitDataProvider<T> transitData,
SlackProvider slackProvider,
@Nullable AccessPaths accessPaths,
AccessPaths accessPaths,
RaptorTransitCalculator<T> calculator,
WorkerLifeCycle lifeCycle,
RaptorTimers timers,
Expand Down Expand Up @@ -143,10 +143,12 @@ public void findTransitForRound() {

transitWorker.prepareForTransitWith(route);

IntIterator stop = calculator.patternStopIterator(pattern.numberOfStopsInPattern());
IntIterator stopPositions = calculator.patternStopIterator(
pattern.numberOfStopsInPattern()
);

while (stop.hasNext()) {
int stopPos = stop.next();
while (stopPositions.hasNext()) {
int stopPos = stopPositions.next();
int stopIndex = pattern.stopIndex(stopPos);

transitWorker.prepareForNextStop(stopIndex, stopPos);
Expand Down Expand Up @@ -183,6 +185,39 @@ public void findTransitForRound() {
});
}

@Override
public void findOnBoardAccessForRound(int iterationDepartureTime) {
for (var accessPath : accessPaths.onBoardAccessPaths()) {
var route = transitData.getRouteForIndex(accessPath.routeIndex());
var trip = route.timetable().getTripSchedule(accessPath.tripScheduleIndex());

var boardTime = trip.departure(accessPath.stopPositionInPattern());
if (calculator.isInIteration(boardTime, iterationDepartureTime)) {
transitWorker.registerOnBoardAccessStopArrival(accessPath, boardTime, trip);
}
}
}

@Override
public void findOnBoardAccessTransitForRound() {
var onBoardStopArrivals = transitWorker.consumeOnBoardStopArrivals();
while (onBoardStopArrivals.hasNext()) {
var onBoardStopArrival = onBoardStopArrivals.next();
var route = transitData.getRouteForIndex(
onBoardStopArrival.onBoardTripConstraint().routeIndex()
);

transitWorker.prepareForTransitWith(route);

var boarded = tryBoardOnBoardAccess(onBoardStopArrival, route);

if (boarded) {
alightOnBoardAccess(onBoardStopArrival, route);
onBoardStopArrivals.remove();
}
}
}

@Override
public void findTransfersForRound() {
timers.findTransfersForRound(() -> {
Expand Down Expand Up @@ -226,4 +261,50 @@ private void addAccessPaths(Collection<RaptorAccessEgress> accessPaths) {
}
}
}

private boolean tryBoardOnBoardAccess(ArrivalView<T> onBoardStopArrival, RaptorRoute<T> route) {
var onBoardTripConstraint = onBoardStopArrival.onBoardTripConstraint();
var trip = route.timetable().getTripSchedule(onBoardTripConstraint.tripScheduleIndex());

return transitWorker.boardAsOnBoardAccess(
onBoardStopArrival,
onBoardTripConstraint.stopPositionInPattern(),
trip
);
}

private void alightOnBoardAccess(ArrivalView<T> onBoardStopArrival, RaptorRoute<T> route) {
var onBoardTripConstraint = onBoardStopArrival.onBoardTripConstraint();

var pattern = route.pattern();
IntIterator stopPositions = calculator.patternStopIterator(pattern.numberOfStopsInPattern());

int alightSlack = slackProvider.alightSlack(pattern.slackIndex());

while (
stopPositions.hasNext() &&
stopPositions.next() != onBoardTripConstraint.stopPositionInPattern()
) {
// Skip past the initial on-board access stop
// We will only consider alighting on stops after this one
}

var txSearch = enableTransferConstraints
? calculator.transferConstraintsSearch(transitData, onBoardTripConstraint.routeIndex())
: null;

while (stopPositions.hasNext()) {
int stopPos = stopPositions.next();
int stopIndex = pattern.stopIndex(stopPos);

// attempt to alight if we're on board
if (calculator.alightingPossibleAt(pattern, stopPos)) {
if (enableTransferConstraints && txSearch.transferExistSourceStop(stopPos)) {
transitWorker.alightConstrainedTransferExist(stopIndex, stopPos, alightSlack);
} else {
transitWorker.alightOnlyRegularTransferExist(stopIndex, stopPos, alightSlack);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,15 @@ public RaptorRouterResult<T> route() {
private void runRaptorForMinute(int iterationDepartureTime) {
setupIteration(iterationDepartureTime);
worker.findAccessOnStreetForRound();
worker.findOnBoardAccessForRound(iterationDepartureTime);

while (hasMoreRounds()) {
lifeCycle.prepareForNextRound(roundTracker.nextRound());

// NB since we have transfer limiting not bothering to cut off search when there are no
// more transfers as that will be rare and complicates the code
worker.findTransitForRound();
worker.findOnBoardAccessTransitForRound();
lifeCycle.transitsForRoundComplete();

worker.findAccessOnBoardForRound();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ public void findTransitForRound() {
}
}

@Override
public void findOnBoardAccessForRound(int iterationDepartureTime) {
for (RangeRaptorWorker<T> child : children) {
child.findOnBoardAccessForRound(iterationDepartureTime);
}
}

@Override
public void findOnBoardAccessTransitForRound() {
for (RangeRaptorWorker<T> child : children) {
child.findOnBoardAccessTransitForRound();
}
}

@Override
public void findTransfersForRound() {
for (RangeRaptorWorker<T> child : children) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ public interface RangeRaptorWorker<T extends RaptorTripSchedule> {
*/
void findTransitForRound();

/**
* Find on-board access for round (accesses on-board an already started trip)
*/
void findOnBoardAccessForRound(int iterationDepartureTime);

/**
* Perform on-board (accesses on-board an already started trip) transit search for boardings and
* alight events for the current round.
*/
void findOnBoardAccessTransitForRound();
Comment on lines +30 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The names are a bit confusing, maybe we should make a follow up PR and try to improve some of the naming. The method names follow the naming conventions as existing methods so we should change all methods at the same time.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed to do this, along with rewording the other methods in this worker for consistency in a follow-up PR. A follow up PR is preferred to keep that refactor isolated and easy to review.


/**
* Apply transfers for the current round.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.opentripplanner.raptor.rangeraptor.internalapi;

import java.util.Collections;
import java.util.Iterator;
import org.opentripplanner.raptor.api.model.RaptorAccessEgress;
import org.opentripplanner.raptor.api.model.RaptorOnBoardAccess;
import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
import org.opentripplanner.raptor.api.view.ArrivalView;
import org.opentripplanner.raptor.rangeraptor.RangeRaptor;
import org.opentripplanner.raptor.spi.RaptorConstrainedBoardingSearch;
import org.opentripplanner.raptor.spi.RaptorRoute;
Expand Down Expand Up @@ -71,4 +75,32 @@ void boardWithConstrainedTransfer(
int boardSlack,
RaptorConstrainedBoardingSearch<T> txSearch
);

default void registerOnBoardAccessStopArrival(RaptorOnBoardAccess access, int boardTime, T trip) {
throw new UnsupportedOperationException(
"On-board access is not yet supported for this routing strategy"
);
}

/**
* @return an iterator over all on-board stop-arrivals currently in the state.
* The iterator allows removal and this is the expected way to remove or a stop-arrival. When
* removed is when we consider a stop-arrival to be "consumed".
*/
default Iterator<? extends ArrivalView<T>> consumeOnBoardStopArrivals() {
return Collections.emptyIterator();
}

/**
* @return true if boarding successful, false otherwise
*/
default boolean boardAsOnBoardAccess(
ArrivalView<T> prevArrival,
int stopPositionInPattern,
T trip
) {
throw new UnsupportedOperationException(
"On-board access is not yet supported for this routing strategy"
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.opentripplanner.raptor.rangeraptor.multicriteria;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.opentripplanner.raptor.api.model.RaptorAccessEgress;
Expand Down Expand Up @@ -39,6 +40,7 @@ public final class McRangeRaptorWorkerState<T extends RaptorTripSchedule>
private final List<McStopArrival<T>> arrivalsCache = new ArrayList<>();
private final RaptorCostCalculator<T> calculatorGeneralizedCost;
private final RaptorTransitCalculator<T> transitCalculator;
private final Collection<McStopArrival<T>> onBoardAccessStopArrivals;

/**
* create a RaptorState for a network with a particular number of stops, and a given maximum
Expand All @@ -59,6 +61,7 @@ public McRangeRaptorWorkerState(
this.stopArrivalFactory = stopArrivalFactory;
this.calculatorGeneralizedCost = calculatorGeneralizedCost;
this.transitCalculator = transitCalculator;
this.onBoardAccessStopArrivals = new ArrayList<>();

// Attach to the RR life cycle
lifeCycle.onSetupIteration(ignore -> setupIteration());
Expand All @@ -71,7 +74,7 @@ public McRangeRaptorWorkerState(

@Override
public boolean isNewRoundAvailable() {
return arrivals.updateExist();
return arrivals.updateExist() || !onBoardAccessStopArrivals.isEmpty();
}

@Override
Expand Down Expand Up @@ -120,6 +123,15 @@ Iterable<? extends McStopArrival<T>> listStopArrivalsPreviousRound(int stop) {
return arrivals.listArrivalsAfterMarker(stop);
}

Iterable<? extends McStopArrival<T>> listOnBoardStopArrivals() {
return onBoardAccessStopArrivals;
}

public void addOnBoardAccessStopArrival(RaptorAccessEgress accessPath, int departureTime) {
var arrival = stopArrivalFactory.createAccessStopArrival(departureTime, accessPath);
onBoardAccessStopArrivals.add(arrival);
}

/**
* Set the time at a transit stop iff it is optimal.
*/
Expand Down
Loading
Loading