org.opentripplanner.transit.raptor.api.path.PathBuilder Maven / Gradle / Ivy
Show all versions of otp Show documentation
package org.opentripplanner.transit.raptor.api.path;
import java.util.Objects;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.opentripplanner.transit.raptor.api.transit.CostCalculator;
import org.opentripplanner.transit.raptor.api.transit.RaptorConstrainedTransfer;
import org.opentripplanner.transit.raptor.api.transit.RaptorPathConstrainedTransferSearch;
import org.opentripplanner.transit.raptor.api.transit.RaptorSlackProvider;
import org.opentripplanner.transit.raptor.api.transit.RaptorStopNameResolver;
import org.opentripplanner.transit.raptor.api.transit.RaptorTransfer;
import org.opentripplanner.transit.raptor.api.transit.RaptorTripSchedule;
import org.opentripplanner.transit.raptor.api.view.BoardAndAlightTime;
import org.opentripplanner.transit.raptor.util.PathStringBuilder;
/**
* The path builder is a utility to build paths. The path builder is responsible for reconstructing
* information that was used in decision making inside Raptor, but not kept due to performance
* reasons. For example information about the transfer like transfer constraints.
*
* The path builder enforces the same logic as Raptor and generates information like the
* generalized-cost instead of getting it from the stop-arrivals. This is convenient if a path is
* created OUTSIDE Raptor, which is the case in the {@link org.opentripplanner.routing.algorithm.transferoptimization.OptimizeTransferService}.
*
* The path builder comes in two versions. One which adds new legs to the tail of the path,
* allowing us to add legs starting with the access leg and ending with the egress leg. The other
* adds legs in the opposite order, from egress to access. Hence the forward and reverse mappers
* are simplified using the head and tail builder respectively.
* See {@link #headPathBuilder(RaptorPathConstrainedTransferSearch, RaptorSlackProvider, CostCalculator)}
* and {@link #tailPathBuilder(RaptorPathConstrainedTransferSearch, RaptorSlackProvider, CostCalculator)}
*
* The builder is also used for creating test data in unit test.
*
* The {@code PathBuilder} can be extended to override specific things. The {@link org.opentripplanner.routing.algorithm.transferoptimization.model.OptimizedPathTail} does this to be able to create
* {@link org.opentripplanner.routing.algorithm.transferoptimization.api.OptimizedPath} instead of regular {@link Path} objects.
*/
public abstract class PathBuilder {
@Nullable
private final RaptorPathConstrainedTransferSearch transferConstraintsSearch;
protected final RaptorSlackProvider slackProvider;
@Nullable
protected final CostCalculator costCalculator;
private final RaptorStopNameResolver stopNameResolver;
// Path leg elements as a double linked list. This makes it easy to look at
// legs before and after in the logic and easy to fork, building alternative
// paths with the same path tail.
private PathBuilderLeg head = null;
private PathBuilderLeg tail = null;
protected PathBuilder(PathBuilder other) {
this(
other.transferConstraintsSearch,
other.slackProvider,
other.costCalculator,
other.stopNameResolver
);
this.head = other.head == null ? null : other.head.mutate();
this.tail = this.head == null ? null : last(this.head);
}
protected PathBuilder(
@Nullable RaptorPathConstrainedTransferSearch transferConstraintsSearch,
RaptorSlackProvider slackProvider,
@Nullable CostCalculator costCalculator,
@Nullable RaptorStopNameResolver stopNameResolver
) {
this.transferConstraintsSearch = transferConstraintsSearch;
this.slackProvider = slackProvider;
this.costCalculator = costCalculator;
this.stopNameResolver = stopNameResolver;
}
/**
* Create a new path builder to build path starting from the access and add elements
* in forward order until the last egress leg is added.
*
* This builder inserts transferConstraints, time-shifts access/transfers/egress and
* calculates generalized-cost in the build phase. (Insert new tail)
*/
public static PathBuilder headPathBuilder(
@Nullable RaptorPathConstrainedTransferSearch transferConstraintsSearch,
RaptorSlackProvider slackProvider,
@Nullable CostCalculator costCalculator,
@Nullable RaptorStopNameResolver stopNameResolver
) {
return new HeadPathBuilder<>(
transferConstraintsSearch,
slackProvider,
costCalculator,
stopNameResolver
);
}
/**
* Create a new path builder to build path starting from the egress and add elements
* in reverse order until the access leg is added last. (Insert new head)
*
* This builder inserts transferConstraints, time-shifts access/transfers/egress and
* calculates generalized-cost in the build phase.
*/
public static PathBuilder tailPathBuilder(
RaptorPathConstrainedTransferSearch transferConstraintsSearch,
RaptorSlackProvider slackProvider,
@Nullable CostCalculator costCalculator,
@Nullable RaptorStopNameResolver stopNameResolver
) {
return new TailPathBuilder<>(
transferConstraintsSearch,
slackProvider,
costCalculator,
stopNameResolver
);
}
public void access(RaptorTransfer access) {
add(PathBuilderLeg.accessLeg(access));
}
public void transit(T trip, BoardAndAlightTime times) {
add(PathBuilderLeg.transitLeg(trip, times));
}
public void transit(
T trip,
BoardAndAlightTime times,
RaptorConstrainedTransfer txConstrainedTransferAfter
) {
add(PathBuilderLeg.transitLeg(trip, times, txConstrainedTransferAfter));
}
public void transfer(RaptorTransfer transfer, int toStop) {
add(PathBuilderLeg.transferLeg(transfer, toStop));
}
public void egress(RaptorTransfer egress) {
add(PathBuilderLeg.egress(egress));
}
public Path build(int iterationDepartureTime) {
updateAggregatedFields();
return new Path<>(iterationDepartureTime, createPathLegs(costCalculator, slackProvider));
}
@Override
public String toString() {
final PathStringBuilder builder = new PathStringBuilder(stopNameResolver);
legsAsStream().forEach(it -> it.toString(builder));
return builder.toString();
}
public Stream> legsAsStream() {
return Stream.iterate(head, Objects::nonNull, PathBuilderLeg::next);
}
public PathBuilderLeg head() {
return head;
}
public PathBuilderLeg tail() {
return tail;
}
/* package local methods, accessible by private sub-classes */
protected abstract void add(PathBuilderLeg newLeg);
protected void updateAggregatedFields() {
timeShiftAllStreetLegs();
insertConstrainedTransfers();
}
protected void addTail(PathBuilderLeg newLeg) {
if(head == null) {
head = tail = newLeg;
}
else {
tail.setNext(newLeg);
newLeg.setPrev(tail);
tail = newLeg;
}
}
protected void addHead(PathBuilderLeg newLeg) {
if(head == null) {
head = tail = newLeg;
}
else {
newLeg.setNext(head);
head.setPrev(newLeg);
head = newLeg;
}
}
/* private methods */
private void timeShiftAllStreetLegs() {
legsAsStream().forEach(leg -> leg.timeShiftThisAndNextLeg(slackProvider));
}
private void insertConstrainedTransfers() {
// If constrained transfer is not in use
if(transferConstraintsSearch == null) { return; }
var prev = head.nextTransitLeg();
if(prev == null) { return; }
var curr = prev.nextTransitLeg();
while (curr != null) {
addTransferConstraints(prev, curr);
prev = curr;
curr = curr.nextTransitLeg();
}
}
private void addTransferConstraints(PathBuilderLeg from, PathBuilderLeg to) {
@SuppressWarnings("ConstantConditions")
var tx = transferConstraintsSearch.findConstrainedTransfer(
from.trip(), from.toStopPos(), to.trip(), to.fromStopPos()
);
if(tx != null) { from.setConstrainedTransferAfterLeg(tx); }
}
protected AccessPathLeg createPathLegs(
CostCalculator costCalculator,
RaptorSlackProvider slackProvider
) {
return head.createAccessPathLeg(costCalculator, slackProvider);
}
private PathBuilderLeg last(PathBuilderLeg head) {
return head.next() == null ? head : last(head.next());
}
protected boolean skipCostCalc() {
return costCalculator == null;
}
private static class HeadPathBuilder extends PathBuilder {
private HeadPathBuilder(
@Nullable RaptorPathConstrainedTransferSearch transferConstraintsSearch,
RaptorSlackProvider slackProvider,
CostCalculator costCalculator,
@Nullable RaptorStopNameResolver stopNameResolver
) {
super(transferConstraintsSearch, slackProvider, costCalculator, stopNameResolver);
}
@Override protected void add(PathBuilderLeg newLeg) { addHead(newLeg); }
}
private static class TailPathBuilder extends PathBuilder {
private TailPathBuilder(
@Nullable RaptorPathConstrainedTransferSearch transferConstraintsSearch,
RaptorSlackProvider slackProvider,
CostCalculator costCalculator,
@Nullable RaptorStopNameResolver stopNameResolver
) {
super(transferConstraintsSearch, slackProvider, costCalculator, stopNameResolver);
}
@Override protected void add(PathBuilderLeg newLeg) { addTail(newLeg); }
}
}