
org.opentripplanner.ext.flex.template.FlexDirectPathFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
The newest version!
package org.opentripplanner.ext.flex.template;
import static org.opentripplanner.model.StopTime.MISSING_VALUE;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.street.model.vertex.Vertex;
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 {
private final FlexAccessEgressCallbackAdapter callbackService;
private final FlexPathCalculator accessPathCalculator;
private final FlexPathCalculator egressPathCalculator;
private final Duration maxTransferDuration;
public FlexDirectPathFactory(
FlexAccessEgressCallbackAdapter callbackService,
FlexPathCalculator accessPathCalculator,
FlexPathCalculator egressPathCalculator,
Duration maxTransferDuration
) {
this.callbackService = callbackService;
this.accessPathCalculator = accessPathCalculator;
this.egressPathCalculator = egressPathCalculator;
this.maxTransferDuration = maxTransferDuration;
}
public Collection calculateDirectFlexPaths(
Collection streetAccesses,
Collection streetEgresses,
List dates,
int requestTime,
boolean arriveBy
) {
Collection directFlexPaths = new ArrayList<>();
var flexAccessTemplates = new FlexAccessFactory(
callbackService,
accessPathCalculator,
maxTransferDuration
)
.calculateFlexAccessTemplates(streetAccesses, dates);
var flexEgressTemplates = new FlexEgressFactory(
callbackService,
egressPathCalculator,
maxTransferDuration
)
.calculateFlexEgressTemplates(streetEgresses, dates);
Multimap streetEgressByStop = HashMultimap.create();
streetEgresses.forEach(it -> streetEgressByStop.put(it.stop, it));
for (FlexAccessTemplate template : flexAccessTemplates) {
StopLocation transferStop = template.getTransferStop();
// TODO: Document or reimplement this. Why are we using the egress to see if the
// access-transfer-stop (last-stop) is used by at least one egress-template?
// Is it because:
// - of the group-stop expansion?
// - of the alight-restriction check?
// - nearest stop to trip match?
// Fix: Find out why and refactor out the business logic and reuse it.
// Problem: Any asymmetrical restriction witch apply/do not apply to the egress,
// but do not apply/apply to the access, like booking-notice.
if (
flexEgressTemplates.stream().anyMatch(t -> t.getAccessEgressStop().equals(transferStop))
) {
for (NearbyStop egress : streetEgressByStop.get(transferStop)) {
createDirectGraphPath(template, egress, arriveBy, requestTime)
.ifPresent(directFlexPaths::add);
}
}
}
return directFlexPaths;
}
private Optional createDirectGraphPath(
FlexAccessTemplate accessTemplate,
NearbyStop egress,
boolean arriveBy,
int requestTime
) {
var accessNearbyStop = accessTemplate.accessEgress;
var trip = accessTemplate.trip;
int accessBoardStopPosition = accessTemplate.boardStopPosition;
int accessAlightStopPosition = accessTemplate.alightStopPosition;
int requestedBookingTime = accessTemplate.requestedBookingTime;
var flexToVertex = egress.state.getVertex();
if (!isRouteable(accessTemplate, flexToVertex)) {
return Optional.empty();
}
var flexEdge = accessTemplate.getFlexEdge(flexToVertex, egress.stop);
if (flexEdge == null) {
return Optional.empty();
}
final State[] afterFlexState = flexEdge.traverse(accessNearbyStop.state);
var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState[0], egress.edges);
if (finalStateOpt.isEmpty()) {
return Optional.empty();
}
var finalState = finalStateOpt.get();
var flexDurations = accessTemplate.calculateFlexPathDurations(flexEdge, finalState);
int timeShift;
if (arriveBy) {
int lastStopArrivalTime = flexDurations.mapToFlexTripArrivalTime(requestTime);
int latestArrivalTime = trip.latestArrivalTime(
lastStopArrivalTime,
accessBoardStopPosition,
accessAlightStopPosition,
flexDurations.trip()
);
if (latestArrivalTime == MISSING_VALUE) {
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,
accessAlightStopPosition,
flexDurations.trip()
);
if (earliestDepartureTime == MISSING_VALUE) {
return Optional.empty();
}
timeShift = flexDurations.mapToRouterDepartureTime(earliestDepartureTime);
}
return Optional.of(new DirectFlexPath(timeShift, finalState));
}
protected boolean isRouteable(FlexAccessTemplate accessTemplate, Vertex flexVertex) {
if (accessTemplate.accessEgress.state.getVertex() == flexVertex) {
return false;
} else return (
accessTemplate.calculator.calculateFlexPath(
accessTemplate.accessEgress.state.getVertex(),
flexVertex,
accessTemplate.boardStopPosition,
accessTemplate.alightStopPosition
) !=
null
);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy