Skip to content
This repository was archived by the owner on Mar 3, 2025. It is now read-only.

Commit

Permalink
feature: Support for min-booking-notice and latest-booking-time
Browse files Browse the repository at this point in the history
  • Loading branch information
t2gran committed May 28, 2024
1 parent 26e9446 commit ae7a20e
Show file tree
Hide file tree
Showing 15 changed files with 449 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.test.support.ResourceLoader;
import org.opentripplanner.transit.model.framework.Deduplicator;
import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo;
import org.opentripplanner.transit.service.StopModel;
import org.opentripplanner.transit.service.TransitModel;

Expand All @@ -39,6 +40,7 @@ public final class FlexIntegrationTestData {
public static final FlexServiceDate FLEX_DATE = new FlexServiceDate(
SERVICE_DATE,
SECONDS_SINCE_MIDNIGHT,
RoutingBookingInfo.NOT_SET,
new TIntHashSet()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo;

class FlexTemplateFactoryTest {

Expand Down Expand Up @@ -61,6 +62,7 @@ class FlexTemplateFactoryTest {
private static final FlexServiceDate DATE = new FlexServiceDate(
LocalDate.of(2024, Month.MAY, 17),
SERVICE_TIME_OFFSET,
RoutingBookingInfo.NOT_SET,
new TIntHashSet()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ void calculateDirectFare() {
new DefaultTransitService(transitModel),
FlexParameters.defaultValues(),
OffsetDateTime.parse("2021-11-12T10:15:24-05:00").toInstant(),
null,
1,
1,
List.of(from),
Expand Down
14 changes: 13 additions & 1 deletion src/ext/java/org/opentripplanner/ext/flex/FlexAccessEgress.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.opentripplanner.framework.tostring.ToStringBuilder;
import org.opentripplanner.street.search.state.State;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo;

public final class FlexAccessEgress {

Expand All @@ -17,6 +18,7 @@ public final class FlexAccessEgress {
private final FlexTrip<?, ?> trip;
private final State lastState;
private final boolean stopReachedOnBoard;
private final RoutingBookingInfo routingBookingInfo;

public FlexAccessEgress(
RegularStop stop,
Expand All @@ -25,7 +27,8 @@ public FlexAccessEgress(
int alightStopPosition,
FlexTrip<?, ?> trip,
State lastState,
boolean stopReachedOnBoard
boolean stopReachedOnBoard,
int requestedBookingTime
) {
this.stop = stop;
this.pathDurations = pathDurations;
Expand All @@ -34,6 +37,8 @@ public FlexAccessEgress(
this.trip = Objects.requireNonNull(trip);
this.lastState = lastState;
this.stopReachedOnBoard = stopReachedOnBoard;
this.routingBookingInfo =
RoutingBookingInfo.of(requestedBookingTime, trip.getPickupBookingInfo(boardStopPosition));
}

public RegularStop stop() {
Expand All @@ -50,6 +55,10 @@ public boolean stopReachedOnBoard() {

public int earliestDepartureTime(int departureTime) {
int tripDepartureTime = pathDurations.mapToFlexTripDepartureTime(departureTime);

// Apply minimum-booking-notice
tripDepartureTime = routingBookingInfo.earliestDepartureTime(tripDepartureTime);

int earliestDepartureTime = trip.earliestDepartureTime(
tripDepartureTime,
boardStopPosition,
Expand All @@ -73,6 +82,9 @@ public int latestArrivalTime(int arrivalTime) {
if (latestArrivalTime == MISSING_VALUE) {
return MISSING_VALUE;
}
if (routingBookingInfo.exceedsMinimumBookingNotice(latestArrivalTime - pathDurations.trip())) {
return MISSING_VALUE;
}
return pathDurations.mapToRouterArrivalTime(latestArrivalTime);
}

Expand Down
9 changes: 9 additions & 0 deletions src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import org.opentripplanner.astar.model.GraphPath;
import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator;
import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator;
Expand All @@ -28,6 +29,7 @@
import org.opentripplanner.street.model.vertex.TransitStopVertex;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo;
import org.opentripplanner.transit.service.TransitService;

public class FlexRouter {
Expand All @@ -48,13 +50,15 @@ public class FlexRouter {
/* Request data */
private final ZonedDateTime startOfTime;
private final int requestedTime;
private final int requestedBookingTime;
private final List<FlexServiceDate> dates;

public FlexRouter(
Graph graph,
TransitService transitService,
FlexParameters flexParameters,
Instant requestedTime,
@Nullable Instant requestedBookingTime,
int additionalPastSearchDays,
int additionalFutureSearchDays,
Collection<NearbyStop> streetAccesses,
Expand Down Expand Up @@ -90,6 +94,10 @@ public FlexRouter(
LocalDate searchDate = LocalDate.ofInstant(requestedTime, tz);
this.startOfTime = ServiceDateUtils.asStartOfService(searchDate, tz);
this.requestedTime = ServiceDateUtils.secondsSinceStartOfTime(startOfTime, requestedTime);
this.requestedBookingTime =
requestedBookingTime == null
? RoutingBookingInfo.NOT_SET
: ServiceDateUtils.secondsSinceStartOfTime(startOfTime, requestedBookingTime);
this.dates =
createFlexServiceDates(
transitService,
Expand Down Expand Up @@ -161,6 +169,7 @@ private List<FlexServiceDate> createFlexServiceDates(
new FlexServiceDate(
date,
ServiceDateUtils.secondsSinceStartOfTime(startOfTime, date),
requestedBookingTime,
transitService.getServiceCodesRunningForDate(date)
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ abstract class AbstractFlexTemplate {
protected final StopLocation transferStop;
protected final int secondsFromStartOfTime;
protected final LocalDate serviceDate;
protected final int requestedBookingTime;
protected final FlexPathCalculator calculator;
private final Duration maxTransferDuration;

Expand Down Expand Up @@ -79,6 +80,7 @@ abstract class AbstractFlexTemplate {
this.transferStop = transferStop;
this.secondsFromStartOfTime = date.secondsFromStartOfTime();
this.serviceDate = date.serviceDate();
this.requestedBookingTime = date.requestedBookingTime();
this.calculator = calculator;
this.maxTransferDuration = maxTransferDuration;
}
Expand Down Expand Up @@ -210,7 +212,8 @@ private FlexAccessEgress createFlexAccessEgress(
alightStopPosition,
trip,
finalState,
transferEdges.isEmpty()
transferEdges.isEmpty(),
requestedBookingTime
);
})
.orElse(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.framework.lang.IntUtils;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo;

/**
* The combination of the closest stop, trip and trip active date.
Expand Down Expand Up @@ -54,7 +55,7 @@ static Collection<ClosestTrip> of(
boolean pickup
) {
var closestTrips = findAllTripsReachableFromNearbyStop(callbackService, nearbyStops, pickup);
return findActiveDatesForTripAndDecorateResult(callbackService, dates, closestTrips);
return findActiveDatesForTripAndDecorateResult(callbackService, dates, closestTrips, true);
}

@Override
Expand Down Expand Up @@ -90,7 +91,8 @@ public FlexServiceDate activeDate() {
private static ArrayList<ClosestTrip> findActiveDatesForTripAndDecorateResult(
FlexAccessEgressCallbackAdapter callbackService,
List<FlexServiceDate> dates,
Map<FlexTrip<?, ?>, ClosestTrip> map
Map<FlexTrip<?, ?>, ClosestTrip> map,
boolean pickup
) {
var result = new ArrayList<ClosestTrip>();
// Add active dates
Expand All @@ -99,6 +101,11 @@ private static ArrayList<ClosestTrip> findActiveDatesForTripAndDecorateResult(
var closestTrip = e.getValue();
// Include dates where the service is running
for (FlexServiceDate date : dates) {
// Filter away boardings early. This needs to be done for egress as well when the
// board stop is known (not known here).
if (pickup && exceedsLatestBookingTime(trip, date, closestTrip.stopPos())) {
continue;
}
if (callbackService.isDateActive(date, trip)) {
result.add(closestTrip.withDate(date));
}
Expand All @@ -111,4 +118,17 @@ private ClosestTrip withDate(FlexServiceDate date) {
Objects.requireNonNull(date);
return new ClosestTrip(this, date);
}

/**
* Check if the trip can be booked at the given date and boarding stop position.
*/
private static boolean exceedsLatestBookingTime(
FlexTrip<?, ?> trip,
FlexServiceDate date,
int stopPos
) {
return RoutingBookingInfo
.of(date.requestedBookingTime(), trip.getPickupBookingInfo(stopPos))
.exceedsLatestBookingTime();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.opentripplanner.street.search.state.EdgeTraverser;
import org.opentripplanner.street.search.state.State;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo;

public class FlexDirectPathFactory {

Expand Down Expand Up @@ -96,6 +97,7 @@ private Optional<DirectFlexPath> createDirectGraphPath(
var trip = accessTemplate.trip;
int accessBoardStopPosition = accessTemplate.boardStopPosition;
int accessAlightStopPosition = accessTemplate.alightStopPosition;
int requestedBookingTime = accessTemplate.requestedBookingTime;

var flexToVertex = egress.state.getVertex();

Expand Down Expand Up @@ -135,10 +137,28 @@ private Optional<DirectFlexPath> createDirectGraphPath(
return Optional.empty();
}

// No need to time-shift latestArrivalTime for meeting the min-booking notice restriction,
// the time is already as-late-as-possible
var bookingInfo = RoutingBookingInfo.of(
requestedBookingTime,
trip.getPickupBookingInfo(accessTemplate.boardStopPosition)
);
if (bookingInfo.exceedsMinimumBookingNotice(latestArrivalTime)) {
return Optional.empty();
}

// Shift from departing at departureTime to arriving at departureTime
timeShift = flexDurations.mapToRouterArrivalTime(latestArrivalTime) - flexDurations.total();
} else {
int firstStopDepartureTime = flexDurations.mapToFlexTripDepartureTime(requestTime);

// Time-shift departure so the minimum-booking-notice restriction is honored.
var bookingInfo = trip.getPickupBookingInfo(accessBoardStopPosition);
firstStopDepartureTime =
RoutingBookingInfo
.of(requestedBookingTime, bookingInfo)
.earliestDepartureTime(firstStopDepartureTime);

int earliestDepartureTime = trip.earliestDepartureTime(
firstStopDepartureTime,
accessBoardStopPosition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ public class FlexServiceDate {
/** Which services are running on the date. */
private final TIntSet servicesRunning;

private final int requestedBookingTime;

public FlexServiceDate(
LocalDate serviceDate,
int secondsFromStartOfTime,
int requestedBookingTime,
TIntSet servicesRunning
) {
this.serviceDate = serviceDate;
this.secondsFromStartOfTime = secondsFromStartOfTime;
this.requestedBookingTime = requestedBookingTime;
this.servicesRunning = servicesRunning;
}

Expand All @@ -39,7 +43,14 @@ int secondsFromStartOfTime() {
return secondsFromStartOfTime;
}

int requestedBookingTime() {
return requestedBookingTime;
}

/**
* Return true if the given {@code serviceCode} is active and running.
*/
public boolean isTripServiceRunning(int serviceCode) {
return (servicesRunning != null && servicesRunning.contains(serviceCode));
return servicesRunning != null && servicesRunning.contains(serviceCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.transit.model.site.GroupStop;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo;

/**
* The factory is used to create flex trip templates.
Expand Down Expand Up @@ -75,7 +76,7 @@ private List<FlexEgressTemplate> createEgressTemplates() {
int end = isBoardingAndAlightingAtSameStopPositionAllowed() ? alightStopPos : alightStopPos - 1;

for (int boardStopPos = 0; boardStopPos <= end; boardStopPos++) {
if (trip.getBoardRule(boardStopPos).isRoutable()) {
if (isAllowedToBoardAt(boardStopPos)) {
for (var stop : expandStopsAt(trip, boardStopPos)) {
result.add(createEgressTemplate(trip, stop, boardStopPos, alightStopPos));
}
Expand All @@ -84,6 +85,18 @@ private List<FlexEgressTemplate> createEgressTemplates() {
return result;
}

/**
* Check if stop position is routable and that the latest-booking time criteria is met.
*/
private boolean isAllowedToBoardAt(int boardStopPosition) {
return (
trip.getBoardRule(boardStopPosition).isRoutable() &&
!RoutingBookingInfo
.of(date.requestedBookingTime(), trip.getPickupBookingInfo(boardStopPosition))
.exceedsLatestBookingTime()
);
}

/**
* With respect to one journey/itinerary this method retuns {@code true} if a passenger can
* board and alight at the same stop in the journey pattern. This is not allowed for regular
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static List<Itinerary> route(
serverContext.transitService(),
serverContext.flexParameters(),
request.dateTime(),
request.bookingTime(),
additionalSearchDays.additionalSearchDaysInPast(),
additionalSearchDays.additionalSearchDaysInFuture(),
accessStops,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public static Collection<FlexAccessEgress> routeAccessEgress(
transitService,
config,
request.dateTime(),
request.bookingTime(),
searchDays.additionalSearchDaysInPast(),
searchDays.additionalSearchDaysInFuture(),
accessStops,
Expand Down
Loading

0 comments on commit ae7a20e

Please sign in to comment.