All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.routing.algorithm.transferoptimization.services.TransferGenerator Maven / Gradle / Ivy

There is a newer version: 2.5.0
Show newest version
package org.opentripplanner.routing.algorithm.transferoptimization.services;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opentripplanner.model.transfer.ConstrainedTransfer;
import org.opentripplanner.routing.algorithm.transferoptimization.model.StopTime;
import org.opentripplanner.routing.algorithm.transferoptimization.model.TripStopTime;
import org.opentripplanner.routing.algorithm.transferoptimization.model.TripToTripTransfer;
import org.opentripplanner.transit.raptor.api.path.TransitPathLeg;
import org.opentripplanner.transit.raptor.api.transit.RaptorSlackProvider;
import org.opentripplanner.transit.raptor.api.transit.RaptorTransfer;
import org.opentripplanner.transit.raptor.api.transit.RaptorTransitDataProvider;
import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule;
import org.opentripplanner.util.OTPFeature;


/**
 * This class is responsible for finding all possible transfers between each pair of transit legs
 * passed in. The configured slack should be respected, and the transfers found here should be
 * equivalent to the transfers explored during routing (in Raptor).
 * 

* This class also filters away transfers which cannot be used due to time constraints. For example, * if a transfer point is before the earliest possible boarding or after the latest possible * arrival. Transfer constraints should also be respected. *

* This service does NOT combine transfers between various trips to form full paths. There are * potentially millions of permutations, so we do that later when we can prune the result. * * @param The TripSchedule type defined by the user of the raptor API. */ public class TransferGenerator { private static final int SAME_STOP_TRANSFER_TIME = 0; private final TransferServiceAdaptor transferServiceAdaptor; private final RaptorSlackProvider slackProvider; private final RaptorTransitDataProvider stdTransfers; private T fromTrip; private T toTrip; public TransferGenerator( TransferServiceAdaptor transferServiceAdaptor, RaptorSlackProvider slackProvider, RaptorTransitDataProvider stdTransfers ) { this.transferServiceAdaptor = transferServiceAdaptor; this.slackProvider = slackProvider; this.stdTransfers = stdTransfers; } public List>> findAllPossibleTransfers( List> transitLegs ) { List>> result = new ArrayList<>(); var fromLeg = transitLegs.get(0); TransitPathLeg toLeg; var earliestDeparture = getFromStopTime(fromLeg); for (int i = 1; i < transitLegs.size(); i++) { toLeg = transitLegs.get(i); var transfers = findTransfers( fromLeg.trip(), earliestDeparture, toLeg.trip() ); result.add(transfers); // Setup next iteration earliestDeparture = findMinimumToStopTime(transfers); fromLeg = toLeg; } removeTransfersAfterLatestStopArrival(last(transitLegs).getToStopPosition(), result); return result; } /* private methods */ private List> findTransfers( T fromTrip, StopTime fromTripDeparture, T toTrip ) { this.fromTrip = fromTrip; this.toTrip = toTrip; int firstStopPos = firstPossibleArrivalStopPos(fromTrip, fromTripDeparture); return findAllTransfers(firstStopPos); } /** Given the trip and departure, find the first possible stop position to alight. */ private int firstPossibleArrivalStopPos(T trip, StopTime departure) { return 1 + trip.findDepartureStopPosition(departure.time(), departure.stop()); } private List> findAllTransfers(int stopPos) { final List> result = new ArrayList<>(); while (stopPos < fromTrip.pattern().numberOfStopsInPattern()) { boolean alightingPossible = fromTrip.pattern().alightingPossibleAt(stopPos); if (alightingPossible) { var from = TripStopTime.arrival(fromTrip, stopPos); // First add high priority transfers result.addAll(transferFromSameStop(from)); result.addAll(findStandardTransfers(from)); } ++stopPos; } return result; } private Collection> transferFromSameStop(TripStopTime from) { final int stop = from.stop(); var tx = transferServiceAdaptor.findTransfer(from, toTrip, stop); if(tx != null && tx.getTransferConstraint().isNotAllowed()) { return List.of(); } final int earliestDepartureTime = earliestDepartureTime( from.time(), SAME_STOP_TRANSFER_TIME, tx ); final int toTripStopPos = toTrip.findDepartureStopPosition(earliestDepartureTime, stop); if(toTripStopPos < 0) { return List.of(); } boolean boardingPossible = toTrip.pattern().boardingPossibleAt(toTripStopPos); if (!boardingPossible) { return List.of(); } return List.of( new TripToTripTransfer<>( from, TripStopTime.departure(toTrip, toTripStopPos), null, tx ) ); } private Collection> findStandardTransfers(TripStopTime from) { final List> result = new ArrayList<>(); Iterator transfers = stdTransfers.getTransfersFromStop(from.stop()); while (transfers.hasNext()) { var it = transfers.next(); int toStop = it.stop(); ConstrainedTransfer tx = transferServiceAdaptor.findTransfer(from, toTrip, toStop); if(tx != null && tx.getTransferConstraint().isNotAllowed()) { continue; } int earliestDepartureTime = earliestDepartureTime(from.time(), it.durationInSeconds(), tx); int toTripStopPos = toTrip.findDepartureStopPosition(earliestDepartureTime, toStop); if(toTripStopPos < 0) { continue; } var to = TripStopTime.departure(toTrip, toTripStopPos); boolean boardingPossible = to.trip().pattern().boardingPossibleAt(to.stopPosition()); if (boardingPossible) { result.add(new TripToTripTransfer<>(from, to, it, tx)); } } return result; } private int earliestDepartureTime( int fromTime, int transferDurationInSeconds, @Nullable ConstrainedTransfer tx ) { // Ignore slack and walking-time for guaranteed and stay-seated transfers if(tx != null && tx.getTransferConstraint().isFacilitated()) { return fromTime; // Ignore board and alight slack for min transfer time, but keep transfer slack } else if (tx != null && tx.getTransferConstraint().isMinTransferTimeSet()) { var minTransferTime = tx.getTransferConstraint().getMinTransferTime(); int transferDuration; if(OTPFeature.MinimumTransferTimeIsDefinitive.isOn()) { transferDuration = minTransferTime; } else { transferDuration = Math.max(minTransferTime, transferDurationInSeconds); } return fromTime + transferDuration + slackProvider.transferSlack(); } else { return fromTime + slackProvider.alightSlack(fromTrip.pattern()) + transferDurationInSeconds + slackProvider.transferSlack() + slackProvider.boardSlack(toTrip.pattern()); } } @Nonnull private StopTime getFromStopTime(final TransitPathLeg leg) { return StopTime.stopTime(leg.fromStop(), leg.fromTime()); } @Nonnull private TripStopTime findMinimumToStopTime(List> transfers) { return transfers.stream() .map(TripToTripTransfer::to) .min(Comparator.comparingInt(TripStopTime::time)) .orElseThrow(); } private void removeTransfersAfterLatestStopArrival( int latestArrivalStopPos, List>> result ) { int nextLatestArrivalStopPos = 0; for (int i = result.size()-1; i >=0; --i) { List> filteredTransfers = new ArrayList<>(); for (TripToTripTransfer tx : result.get(i)) { if(tx.to().stopPosition() < latestArrivalStopPos) { filteredTransfers.add(tx); nextLatestArrivalStopPos = Math.max(tx.from().stopPosition(), nextLatestArrivalStopPos); } } latestArrivalStopPos = nextLatestArrivalStopPos; result.set(i, filteredTransfers); } } private static T last(List list) { return list.get(list.size() - 1); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy