diff --git a/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java b/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java index abbb0d7d1bc..a59d9fc589a 100644 --- a/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java +++ b/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java @@ -5,6 +5,7 @@ import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.ScheduledTransitLeg; +import org.opentripplanner.model.plan.ScheduledTransitLegBuilder; import org.opentripplanner.transit.service.TransitService; public class RealtimeResolver { @@ -53,20 +54,12 @@ private static Leg combineReferenceWithOriginal( ScheduledTransitLeg reference, ScheduledTransitLeg original ) { - var leg = new ScheduledTransitLeg( - reference.getTripTimes(), - reference.getTripPattern(), - reference.getBoardStopPosInPattern(), - reference.getAlightStopPosInPattern(), - reference.getStartTime(), - reference.getEndTime(), - reference.getServiceDate(), - reference.getZoneId(), - original.getTransferFromPrevLeg(), - original.getTransferToNextLeg(), - original.getGeneralizedCost(), - original.accessibilityScore() - ); + var leg = new ScheduledTransitLegBuilder<>(reference) + .withTransferFromPreviousLeg(original.getTransferFromPrevLeg()) + .withTransferToNextLeg(original.getTransferToNextLeg()) + .withGeneralizedCost(original.getGeneralizedCost()) + .withAccessibilityScore(original.accessibilityScore()) + .build(); reference.getTransitAlerts().forEach(leg::addAlert); return leg; } diff --git a/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java b/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java index bef50ddd2fd..d2188fb1b0b 100644 --- a/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java +++ b/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java @@ -21,36 +21,9 @@ public class FrequencyTransitLeg extends ScheduledTransitLeg { private final int frequencyHeadwayInSeconds; - public FrequencyTransitLeg( - TripTimes tripTimes, - TripPattern tripPattern, - int boardStopIndexInPattern, - int alightStopIndexInPattern, - ZonedDateTime startTime, - ZonedDateTime endTime, - LocalDate serviceDate, - ZoneId zoneId, - ConstrainedTransfer transferFromPreviousLeg, - ConstrainedTransfer transferToNextLeg, - int generalizedCost, - int frequencyHeadwayInSeconds, - @Nullable Float accessibilityScore - ) { - super( - tripTimes, - tripPattern, - boardStopIndexInPattern, - alightStopIndexInPattern, - startTime, - endTime, - serviceDate, - zoneId, - transferFromPreviousLeg, - transferToNextLeg, - generalizedCost, - accessibilityScore - ); - this.frequencyHeadwayInSeconds = frequencyHeadwayInSeconds; + FrequencyTransitLeg(FrequencyTransitLegBuilder builder) { + super(builder); + this.frequencyHeadwayInSeconds = builder.frequencyHeadwayInSeconds(); } @Override @@ -101,20 +74,6 @@ public List getIntermediateStops() { @Override public ScheduledTransitLeg withAccessibilityScore(Float score) { - return new FrequencyTransitLeg( - tripTimes, - tripPattern, - boardStopPosInPattern, - alightStopPosInPattern, - getStartTime(), - getEndTime(), - serviceDate, - zoneId, - getTransferFromPrevLeg(), - getTransferToNextLeg(), - getGeneralizedCost(), - frequencyHeadwayInSeconds, - score - ); + return new FrequencyTransitLegBuilder(this).withAccessibilityScore(score).build(); } } diff --git a/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLegBuilder.java b/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLegBuilder.java new file mode 100644 index 00000000000..bbbc89393f0 --- /dev/null +++ b/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLegBuilder.java @@ -0,0 +1,28 @@ +package org.opentripplanner.model.plan; + +public class FrequencyTransitLegBuilder + extends ScheduledTransitLegBuilder { + + private int frequencyHeadwayInSeconds; + + public FrequencyTransitLegBuilder() {} + + public FrequencyTransitLegBuilder(FrequencyTransitLeg original) { + super(original); + frequencyHeadwayInSeconds = original.getHeadway(); + } + + public FrequencyTransitLegBuilder withFrequencyHeadwayInSeconds(int frequencyHeadwayInSeconds) { + this.frequencyHeadwayInSeconds = frequencyHeadwayInSeconds; + return instance(); + } + + public int frequencyHeadwayInSeconds() { + return frequencyHeadwayInSeconds; + } + + @Override + public FrequencyTransitLeg build() { + return new FrequencyTransitLeg(this); + } +} diff --git a/src/main/java/org/opentripplanner/model/plan/Leg.java b/src/main/java/org/opentripplanner/model/plan/Leg.java index f561f2713e1..05213dbdf5e 100644 --- a/src/main/java/org/opentripplanner/model/plan/Leg.java +++ b/src/main/java/org/opentripplanner/model/plan/Leg.java @@ -24,6 +24,7 @@ import org.opentripplanner.transit.model.organization.Operator; import org.opentripplanner.transit.model.site.FareZone; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; /** * One leg of a trip -- that is, a temporally continuous piece of the journey that takes place on a @@ -187,6 +188,14 @@ default Trip getTrip() { return null; } + /** + * For transit legs, the trip on service date, if it exists. For non-transit legs, null. + */ + @Nullable + default TripOnServiceDate getTripOnServiceDate() { + return null; + } + default Accessibility getTripWheelchairAccessibility() { return null; } diff --git a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index 10158cdc13b..1c535f631b9 100644 --- a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -35,6 +35,7 @@ import org.opentripplanner.transit.model.organization.Operator; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; /** @@ -57,47 +58,37 @@ public class ScheduledTransitLeg implements TransitLeg { private final int generalizedCost; protected final LocalDate serviceDate; protected final ZoneId zoneId; + private final TripOnServiceDate tripOnServiceDate; private double distanceMeters; private final double directDistanceMeters; private final Float accessibilityScore; private List fareProducts = List.of(); - public ScheduledTransitLeg( - TripTimes tripTimes, - TripPattern tripPattern, - int boardStopIndexInPattern, - int alightStopIndexInPattern, - ZonedDateTime startTime, - ZonedDateTime endTime, - LocalDate serviceDate, - ZoneId zoneId, - ConstrainedTransfer transferFromPreviousLeg, - ConstrainedTransfer transferToNextLeg, - int generalizedCost, - @Nullable Float accessibilityScore - ) { - this.tripTimes = tripTimes; - this.tripPattern = tripPattern; + ScheduledTransitLeg(ScheduledTransitLegBuilder builder) { + this.tripTimes = builder.tripTimes(); + this.tripPattern = builder.tripPattern(); - this.boardStopPosInPattern = boardStopIndexInPattern; - this.alightStopPosInPattern = alightStopIndexInPattern; + this.boardStopPosInPattern = builder.boardStopIndexInPattern(); + this.alightStopPosInPattern = builder.alightStopIndexInPattern(); - this.startTime = startTime; - this.endTime = endTime; + this.startTime = builder.startTime(); + this.endTime = builder.endTime(); - this.serviceDate = serviceDate; - this.zoneId = zoneId; + this.serviceDate = builder.serviceDate(); + this.zoneId = builder.zoneId(); - this.transferFromPrevLeg = transferFromPreviousLeg; - this.transferToNextLeg = transferToNextLeg; + this.tripOnServiceDate = builder.tripOnServiceDate(); - this.generalizedCost = generalizedCost; + this.transferFromPrevLeg = builder.transferFromPreviousLeg(); + this.transferToNextLeg = builder.transferToNextLeg(); - this.accessibilityScore = accessibilityScore; + this.generalizedCost = builder.generalizedCost(); + + this.accessibilityScore = builder.accessibilityScore(); List transitLegCoordinates = extractTransitLegCoordinates( tripPattern, - boardStopIndexInPattern, - alightStopIndexInPattern + builder.boardStopIndexInPattern(), + builder.alightStopIndexInPattern() ); this.legGeometry = GeometryUtils.makeLineString(transitLegCoordinates); @@ -127,10 +118,12 @@ public Instant getServiceDateMidnight() { return ServiceDateUtils.asStartOfService(serviceDate, zoneId).toInstant(); } + @Override public boolean isScheduledTransitLeg() { return true; } + @Override public ScheduledTransitLeg asScheduledTransitLeg() { return this; } @@ -244,6 +237,12 @@ public LocalDate getServiceDate() { return serviceDate; } + @Override + @Nullable + public TripOnServiceDate getTripOnServiceDate() { + return tripOnServiceDate; + } + @Override public Place getFrom() { return Place.forStop(tripPattern.getStop(boardStopPosInPattern)); @@ -344,18 +343,25 @@ public int getGeneralizedCost() { return generalizedCost; } + /** + * Construct a leg reference from this leg. + * If the trip is based on a TripOnServiceDate, the leg reference will contain the + * TripOnServiceDate id instead of the Trip id. + */ @Override public LegReference getLegReference() { return new ScheduledTransitLegReference( - tripTimes.getTrip().getId(), + tripOnServiceDate == null ? tripTimes.getTrip().getId() : null, serviceDate, boardStopPosInPattern, alightStopPosInPattern, tripPattern.getStops().get(boardStopPosInPattern).getId(), - tripPattern.getStops().get(alightStopPosInPattern).getId() + tripPattern.getStops().get(alightStopPosInPattern).getId(), + tripOnServiceDate == null ? null : tripOnServiceDate.getId() ); } + @Override public void addAlert(TransitAlert alert) { transitAlerts.add(alert); } @@ -377,20 +383,7 @@ public Float accessibilityScore() { } public ScheduledTransitLeg withAccessibilityScore(Float score) { - return new ScheduledTransitLeg( - tripTimes, - tripPattern, - boardStopPosInPattern, - alightStopPosInPattern, - startTime, - endTime, - serviceDate, - zoneId, - transferFromPrevLeg, - transferToNextLeg, - generalizedCost, - score - ); + return new ScheduledTransitLegBuilder<>(this).withAccessibilityScore(score).build(); } /** diff --git a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilder.java b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilder.java new file mode 100644 index 00000000000..de775eab263 --- /dev/null +++ b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilder.java @@ -0,0 +1,169 @@ +package org.opentripplanner.model.plan; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import org.opentripplanner.model.transfer.ConstrainedTransfer; +import org.opentripplanner.street.model.edge.StreetEdgeBuilder; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; +import org.opentripplanner.transit.model.timetable.TripTimes; + +public class ScheduledTransitLegBuilder> { + + private TripTimes tripTimes; + private TripPattern tripPattern; + private int boardStopIndexInPattern; + private int alightStopIndexInPattern; + private ZonedDateTime startTime; + private ZonedDateTime endTime; + private LocalDate serviceDate; + private ZoneId zoneId; + private TripOnServiceDate tripOnServiceDate; + private ConstrainedTransfer transferFromPreviousLeg; + private ConstrainedTransfer transferToNextLeg; + private int generalizedCost; + private Float accessibilityScore; + + public ScheduledTransitLegBuilder() {} + + public ScheduledTransitLegBuilder(ScheduledTransitLeg original) { + tripTimes = original.getTripTimes(); + tripPattern = original.getTripPattern(); + boardStopIndexInPattern = original.getBoardStopPosInPattern(); + alightStopIndexInPattern = original.getAlightStopPosInPattern(); + startTime = original.getStartTime(); + endTime = original.getEndTime(); + serviceDate = original.getServiceDate(); + tripOnServiceDate = original.getTripOnServiceDate(); + transferFromPreviousLeg = original.getTransferFromPrevLeg(); + transferToNextLeg = original.getTransferToNextLeg(); + generalizedCost = original.getGeneralizedCost(); + accessibilityScore = original.accessibilityScore(); + } + + public B withTripTimes(TripTimes tripTimes) { + this.tripTimes = tripTimes; + return instance(); + } + + public TripTimes tripTimes() { + return tripTimes; + } + + public B withTripPattern(TripPattern tripPattern) { + this.tripPattern = tripPattern; + return instance(); + } + + public TripPattern tripPattern() { + return tripPattern; + } + + public B withBoardStopIndexInPattern(int boardStopIndexInPattern) { + this.boardStopIndexInPattern = boardStopIndexInPattern; + return instance(); + } + + public int boardStopIndexInPattern() { + return boardStopIndexInPattern; + } + + public B withAlightStopIndexInPattern(int alightStopIndexInPattern) { + this.alightStopIndexInPattern = alightStopIndexInPattern; + return instance(); + } + + public int alightStopIndexInPattern() { + return alightStopIndexInPattern; + } + + public B withStartTime(ZonedDateTime startTime) { + this.startTime = startTime; + return instance(); + } + + public ZonedDateTime startTime() { + return startTime; + } + + public B withEndTime(ZonedDateTime endTime) { + this.endTime = endTime; + return instance(); + } + + public ZonedDateTime endTime() { + return endTime; + } + + public B withServiceDate(LocalDate serviceDate) { + this.serviceDate = serviceDate; + return instance(); + } + + public LocalDate serviceDate() { + return serviceDate; + } + + public B withZoneId(ZoneId zoneId) { + this.zoneId = zoneId; + return instance(); + } + + public ZoneId zoneId() { + return zoneId; + } + + public B withTripOnServiceDate(TripOnServiceDate tripOnServiceDate) { + this.tripOnServiceDate = tripOnServiceDate; + return instance(); + } + + public TripOnServiceDate tripOnServiceDate() { + return tripOnServiceDate; + } + + public B withTransferFromPreviousLeg(ConstrainedTransfer transferFromPreviousLeg) { + this.transferFromPreviousLeg = transferFromPreviousLeg; + return instance(); + } + + public ConstrainedTransfer transferFromPreviousLeg() { + return transferFromPreviousLeg; + } + + public B withTransferToNextLeg(ConstrainedTransfer transferToNextLeg) { + this.transferToNextLeg = transferToNextLeg; + return instance(); + } + + public ConstrainedTransfer transferToNextLeg() { + return transferToNextLeg; + } + + public B withGeneralizedCost(int generalizedCost) { + this.generalizedCost = generalizedCost; + return instance(); + } + + public int generalizedCost() { + return generalizedCost; + } + + public B withAccessibilityScore(Float accessibilityScore) { + this.accessibilityScore = accessibilityScore; + return instance(); + } + + public Float accessibilityScore() { + return accessibilityScore; + } + + public ScheduledTransitLeg build() { + return new ScheduledTransitLeg(this); + } + + final B instance() { + return (B) this; + } +} diff --git a/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java b/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java index 067037f54e1..d4d6eacb886 100644 --- a/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java +++ b/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java @@ -93,6 +93,21 @@ static void writeScheduledTransitLegV2(LegReference ref, ObjectOutputStream out) } } + static void writeScheduledTransitLegV3(LegReference ref, ObjectOutputStream out) + throws IOException { + if (ref instanceof ScheduledTransitLegReference s) { + out.writeUTF(s.tripOnServiceDateId() == null ? s.tripId().toString() : ""); + out.writeUTF(s.serviceDate().toString()); + out.writeInt(s.fromStopPositionInPattern()); + out.writeInt(s.toStopPositionInPattern()); + out.writeUTF(s.fromStopId().toString()); + out.writeUTF(s.toStopId().toString()); + out.writeUTF(s.tripOnServiceDateId() == null ? "" : s.tripOnServiceDateId().toString()); + } else { + throw new IllegalArgumentException("Invalid LegReference type"); + } + } + static LegReference readScheduledTransitLegV1(ObjectInputStream objectInputStream) throws IOException { return new ScheduledTransitLegReference( @@ -101,6 +116,7 @@ static LegReference readScheduledTransitLegV1(ObjectInputStream objectInputStrea objectInputStream.readInt(), objectInputStream.readInt(), null, + null, null ); } @@ -113,6 +129,20 @@ static LegReference readScheduledTransitLegV2(ObjectInputStream objectInputStrea objectInputStream.readInt(), objectInputStream.readInt(), FeedScopedId.parse(objectInputStream.readUTF()), + FeedScopedId.parse(objectInputStream.readUTF()), + null + ); + } + + static LegReference readScheduledTransitLegV3(ObjectInputStream objectInputStream) + throws IOException { + return new ScheduledTransitLegReference( + FeedScopedId.parse(objectInputStream.readUTF()), + LocalDate.parse(objectInputStream.readUTF(), DateTimeFormatter.ISO_LOCAL_DATE), + objectInputStream.readInt(), + objectInputStream.readInt(), + FeedScopedId.parse(objectInputStream.readUTF()), + FeedScopedId.parse(objectInputStream.readUTF()), FeedScopedId.parse(objectInputStream.readUTF()) ); } diff --git a/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceType.java b/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceType.java index 44cea2226d3..60e00f3861b 100644 --- a/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceType.java +++ b/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceType.java @@ -22,6 +22,13 @@ enum LegReferenceType { ScheduledTransitLegReference.class, LegReferenceSerializer::writeScheduledTransitLegV2, LegReferenceSerializer::readScheduledTransitLegV2 + ), + + SCHEDULED_TRANSIT_LEG_V3( + 3, + ScheduledTransitLegReference.class, + LegReferenceSerializer::writeScheduledTransitLegV3, + LegReferenceSerializer::readScheduledTransitLegV3 ); private final int version; diff --git a/src/main/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReference.java b/src/main/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReference.java index ae81a906d12..b008dea5737 100644 --- a/src/main/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReference.java +++ b/src/main/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReference.java @@ -6,11 +6,13 @@ import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.plan.ScheduledTransitLeg; +import org.opentripplanner.model.plan.ScheduledTransitLegBuilder; import org.opentripplanner.routing.algorithm.mapping.AlertToLegMapper; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.TransitService; import org.slf4j.Logger; @@ -27,11 +29,23 @@ public record ScheduledTransitLegReference( int toStopPositionInPattern, FeedScopedId fromStopId, - FeedScopedId toStopId + FeedScopedId toStopId, + FeedScopedId tripOnServiceDateId ) implements LegReference { private static final Logger LOG = LoggerFactory.getLogger(ScheduledTransitLegReference.class); + public ScheduledTransitLegReference { + if (tripId != null && tripOnServiceDateId != null) { + throw new IllegalArgumentException( + "ScheduledTransitLegReference cannot refer to both a Trip id " + + tripId + + " and a TripOnServiceDate id " + + tripOnServiceDateId + ); + } + } + /** * Reconstruct a scheduled transit leg from this scheduled transit leg reference. * Since the transit model could have been modified between the time the reference is created @@ -43,20 +57,47 @@ public record ScheduledTransitLegReference( * As an exception, the reference is still considered valid if the referenced stop is different * but belongs to the same parent station: this covers for example the case of a last-minute * platform change in a train station that typically does not affect the validity of the leg. + *

+ * If the referenced trip is based on a TripOnServiceDate (i.e. a TransModel dated service + * journey), the TripOnServiceDate id is stored in the leg reference instead of the Trip id: + * A TripOnServiceDate id is meant to be more stable than a Trip id across deliveries of planned + * data, using it gives a better guarantee to reconstruct correctly the original leg. */ @Override @Nullable public ScheduledTransitLeg getLeg(TransitService transitService) { - Trip trip = transitService.getTripForId(tripId); - if (trip == null) { - LOG.info("Invalid transit leg reference: trip {} not found", tripId); - return null; + Trip trip; + TripOnServiceDate tripOnServiceDate = null; + if (tripOnServiceDateId != null) { + tripOnServiceDate = transitService.getTripOnServiceDateById(tripOnServiceDateId); + if (tripOnServiceDate == null) { + LOG.info( + "Invalid transit leg reference: trip on service date '{}' not found", + tripOnServiceDateId + ); + return null; + } + if (!tripOnServiceDate.getServiceDate().equals(serviceDate)) { + LOG.info( + "Invalid transit leg reference: trip on service date '{}' does not run on service date {}", + tripOnServiceDateId, + serviceDate + ); + return null; + } + trip = tripOnServiceDate.getTrip(); + } else { + trip = transitService.getTripForId(tripId); + if (trip == null) { + LOG.info("Invalid transit leg reference: trip '{}' not found", tripId); + return null; + } } TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate); if (tripPattern == null) { LOG.info( - "Invalid transit leg reference: trip pattern not found for trip {} and service date {} ", + "Invalid transit leg reference: trip pattern not found for trip '{}' and service date {} ", tripId, serviceDate ); @@ -67,7 +108,7 @@ public ScheduledTransitLeg getLeg(TransitService transitService) { if (fromStopPositionInPattern >= numStops || toStopPositionInPattern >= numStops) { LOG.info( "Invalid transit leg reference: boarding stop {} or alighting stop {} is out of range" + - " in trip {} and service date {} ({} stops in trip pattern) ", + " in trip '{}' and service date {} ({} stops in trip pattern) ", fromStopPositionInPattern, toStopPositionInPattern, tripId, @@ -94,7 +135,7 @@ public ScheduledTransitLeg getLeg(TransitService transitService) { if (tripTimes == null) { LOG.info( - "Invalid transit leg reference: trip times not found for trip {} and service date {} ", + "Invalid transit leg reference: trip times not found for trip '{}' and service date {}", tripId, serviceDate ); @@ -107,7 +148,7 @@ public ScheduledTransitLeg getLeg(TransitService transitService) { .contains(tripTimes.getServiceCode()) ) { LOG.info( - "Invalid transit leg reference: the trip {} does not run on service date {} ", + "Invalid transit leg reference: the trip '{}' does not run on service date {}", tripId, serviceDate ); @@ -120,20 +161,19 @@ public ScheduledTransitLeg getLeg(TransitService transitService) { int boardingTime = tripTimes.getDepartureTime(fromStopPositionInPattern); int alightingTime = tripTimes.getArrivalTime(toStopPositionInPattern); - ScheduledTransitLeg leg = new ScheduledTransitLeg( - tripTimes, - tripPattern, - fromStopPositionInPattern, - toStopPositionInPattern, - ServiceDateUtils.toZonedDateTime(serviceDate, timeZone, boardingTime), - ServiceDateUtils.toZonedDateTime(serviceDate, timeZone, alightingTime), - serviceDate, - timeZone, - null, - null, - 0, // TODO: What should we have here - null - ); + ScheduledTransitLeg leg = new ScheduledTransitLegBuilder<>() + .withTripTimes(tripTimes) + .withTripPattern(tripPattern) + .withBoardStopIndexInPattern(fromStopPositionInPattern) + .withAlightStopIndexInPattern(toStopPositionInPattern) + .withStartTime(ServiceDateUtils.toZonedDateTime(serviceDate, timeZone, boardingTime)) + .withEndTime(ServiceDateUtils.toZonedDateTime(serviceDate, timeZone, alightingTime)) + .withServiceDate(serviceDate) + .withTripOnServiceDate(tripOnServiceDate) + .withZoneId(timeZone) + // TODO: What should we have here + .withGeneralizedCost(0) + .build(); new AlertToLegMapper( transitService.getTransitAlertService(), @@ -178,7 +218,7 @@ private boolean matchReferencedStopInPattern( LOG.info( "Invalid transit leg reference:" + " The referenced stop at position {} with id '{}' does not match" + - " the stop id '{}' in trip {} and service date {}", + " the stop id '{}' in trip '{}' and service date {}", stopPosition, stopId, stopLocationInPattern.getId(), @@ -189,8 +229,8 @@ private boolean matchReferencedStopInPattern( } LOG.info( "Transit leg reference with modified stop id within the same station: " + - "The referenced stop at position {} with id '{}' does not match\" +\n" + - " \" the stop id '{}' in trip {} and service date {}", + "The referenced stop at position {} with id '{}' does not match" + + " the stop id '{}' in trip {} and service date {}", stopPosition, stopId, stopLocationInPattern.getId(), diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java index 5f946b1d775..61ac1dd5cc1 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java @@ -10,11 +10,11 @@ import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.model.GenericLocation; -import org.opentripplanner.model.plan.FrequencyTransitLeg; +import org.opentripplanner.model.plan.FrequencyTransitLegBuilder; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.Place; -import org.opentripplanner.model.plan.ScheduledTransitLeg; +import org.opentripplanner.model.plan.ScheduledTransitLegBuilder; import org.opentripplanner.model.plan.StreetLeg; import org.opentripplanner.model.plan.UnknownTransitPathLeg; import org.opentripplanner.model.transfer.ConstrainedTransfer; @@ -39,6 +39,8 @@ import org.opentripplanner.street.search.request.StreetSearchRequestMapper; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.state.StateEditor; +import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.service.TransitService; /** @@ -57,6 +59,7 @@ public class RaptorPathToItineraryMapper { private final ZonedDateTime transitSearchTimeZero; private final GraphPathToItineraryMapper graphPathToItineraryMapper; + private final TransitService transitService; /** * Constructs an itinerary mapper for a request and a set of results @@ -84,6 +87,7 @@ public RaptorPathToItineraryMapper( graph.streetNotesService, graph.ellipsoidToGeoidDifference ); + this.transitService = transitService; } public Itinerary createItinerary(RaptorPath path) { @@ -204,36 +208,53 @@ private Leg mapTransitLeg(Leg prevTransitLeg, TransitPathLeg pathLeg) { if (tripSchedule.isFrequencyBasedTrip()) { int frequencyHeadwayInSeconds = tripSchedule.frequencyHeadwayInSeconds(); - return new FrequencyTransitLeg( - tripSchedule.getOriginalTripTimes(), - tripSchedule.getOriginalTripPattern(), - boardStopIndexInPattern, - alightStopIndexInPattern, - createZonedDateTime(pathLeg.fromTime() + frequencyHeadwayInSeconds), - createZonedDateTime(pathLeg.toTime()), - tripSchedule.getServiceDate(), - transitSearchTimeZero.getZone().normalized(), - (prevTransitLeg == null ? null : prevTransitLeg.getTransferToNextLeg()), - (ConstrainedTransfer) pathLeg.getConstrainedTransferAfterLeg(), - toOtpDomainCost(pathLeg.generalizedCost() + lastLegCost), - frequencyHeadwayInSeconds, - null - ); + return new FrequencyTransitLegBuilder() + .withTripTimes(tripSchedule.getOriginalTripTimes()) + .withTripPattern(tripSchedule.getOriginalTripPattern()) + .withBoardStopIndexInPattern(boardStopIndexInPattern) + .withAlightStopIndexInPattern(alightStopIndexInPattern) + .withStartTime(createZonedDateTime(pathLeg.fromTime() + frequencyHeadwayInSeconds)) + .withEndTime(createZonedDateTime(pathLeg.toTime())) + .withServiceDate(tripSchedule.getServiceDate()) + .withZoneId(transitSearchTimeZero.getZone().normalized()) + .withTransferFromPreviousLeg( + (prevTransitLeg == null ? null : prevTransitLeg.getTransferToNextLeg()) + ) + .withTransferToNextLeg((ConstrainedTransfer) pathLeg.getConstrainedTransferAfterLeg()) + .withGeneralizedCost(toOtpDomainCost(pathLeg.generalizedCost() + lastLegCost)) + .withFrequencyHeadwayInSeconds(frequencyHeadwayInSeconds) + .build(); + } + + TripOnServiceDate tripOnServiceDate = getTripOnServiceDate(tripSchedule); + + return new ScheduledTransitLegBuilder<>() + .withTripTimes(tripSchedule.getOriginalTripTimes()) + .withTripPattern(tripSchedule.getOriginalTripPattern()) + .withBoardStopIndexInPattern(boardStopIndexInPattern) + .withAlightStopIndexInPattern(alightStopIndexInPattern) + .withStartTime(createZonedDateTime(pathLeg.fromTime())) + .withEndTime(createZonedDateTime(pathLeg.toTime())) + .withServiceDate(tripSchedule.getServiceDate()) + .withZoneId(transitSearchTimeZero.getZone().normalized()) + .withTripOnServiceDate(tripOnServiceDate) + .withTransferFromPreviousLeg( + (prevTransitLeg == null ? null : prevTransitLeg.getTransferToNextLeg()) + ) + .withTransferToNextLeg((ConstrainedTransfer) pathLeg.getConstrainedTransferAfterLeg()) + .withGeneralizedCost(toOtpDomainCost(pathLeg.generalizedCost() + lastLegCost)) + .build(); + } + + private TripOnServiceDate getTripOnServiceDate(T tripSchedule) { + if (tripSchedule.getOriginalTripTimes() == null) { + return null; } - return new ScheduledTransitLeg( - tripSchedule.getOriginalTripTimes(), - tripSchedule.getOriginalTripPattern(), - boardStopIndexInPattern, - alightStopIndexInPattern, - createZonedDateTime(pathLeg.fromTime()), - createZonedDateTime(pathLeg.toTime()), - tripSchedule.getServiceDate(), - transitSearchTimeZero.getZone().normalized(), - (prevTransitLeg == null ? null : prevTransitLeg.getTransferToNextLeg()), - (ConstrainedTransfer) pathLeg.getConstrainedTransferAfterLeg(), - toOtpDomainCost(pathLeg.generalizedCost() + lastLegCost), - null + TripIdAndServiceDate tripIdAndServiceDate = new TripIdAndServiceDate( + tripSchedule.getOriginalTripTimes().getTrip().getId(), + tripSchedule.getServiceDate() ); + return transitService.getTripOnServiceDateForTripAndDay(tripIdAndServiceDate); } private boolean isFree(EgressPathLeg egressPathLeg) { diff --git a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java index cd4afd211af..6b51c0b1282 100644 --- a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java +++ b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java @@ -22,9 +22,12 @@ import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.ScheduledTransitLeg; +import org.opentripplanner.model.plan.ScheduledTransitLegBuilder; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.TransitService; @@ -206,7 +209,16 @@ private static Stream generateLegs( while (!pq.isEmpty()) { TripTimeOnDate tripTimeOnDate = pq.poll(); - res.add(mapToLeg(timeZone, pattern, boardingPosition, alightingPosition, tripTimeOnDate)); + res.add( + mapToLeg( + timeZone, + pattern, + boardingPosition, + alightingPosition, + tripTimeOnDate, + transitService + ) + ); } return res.stream(); @@ -218,7 +230,8 @@ private static ScheduledTransitLeg mapToLeg( TripPattern pattern, int boardingPosition, int alightingPosition, - TripTimeOnDate tripTimeOnDate + TripTimeOnDate tripTimeOnDate, + TransitService transitService ) { LocalDate serviceDay = tripTimeOnDate.getServiceDay(); TripTimes tripTimes = tripTimeOnDate.getTripTimes(); @@ -234,20 +247,21 @@ private static ScheduledTransitLeg mapToLeg( tripTimes.getArrivalTime(alightingPosition) ); - return new ScheduledTransitLeg( - tripTimes, - pattern, - boardingPosition, - alightingPosition, - boardingTime, - alightingTime, - serviceDay, - timeZone, - null, - null, - ZERO_COST, - null + TripOnServiceDate tripOnServiceDate = transitService.getTripOnServiceDateForTripAndDay( + new TripIdAndServiceDate(tripTimeOnDate.getTrip().getId(), tripTimeOnDate.getServiceDay()) ); + + return new ScheduledTransitLegBuilder<>() + .withTripTimes(tripTimes) + .withTripPattern(pattern) + .withBoardStopIndexInPattern(boardingPosition) + .withAlightStopIndexInPattern(alightingPosition) + .withStartTime(boardingTime) + .withEndTime(alightingTime) + .withServiceDate(serviceDay) + .withZoneId(timeZone) + .withTripOnServiceDate(tripOnServiceDate) + .build(); } @Nonnull diff --git a/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java b/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java index 71b110b0381..518fb528a22 100644 --- a/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java +++ b/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java @@ -23,20 +23,17 @@ void defaultFares() { .tripPattern("1", route) .withStopPattern(TransitModelForTest.stopPattern(3)) .build(); - var leg = new ScheduledTransitLeg( - null, - pattern, - 0, - 2, - TIME, - TIME.plusMinutes(10), - TIME.toLocalDate(), - ZoneIds.BERLIN, - null, - null, - 100, - null - ); + var leg = new ScheduledTransitLegBuilder() + .withTripTimes(null) + .withTripPattern(pattern) + .withBoardStopIndexInPattern(0) + .withAlightStopIndexInPattern(2) + .withStartTime(TIME) + .withEndTime(TIME.plusMinutes(10)) + .withServiceDate(TIME.toLocalDate()) + .withZoneId(ZoneIds.BERLIN) + .withGeneralizedCost(100) + .build(); assertEquals(List.of(), leg.fareProducts()); } diff --git a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java index ec243fca0e6..3eccdaaabd1 100644 --- a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java +++ b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java @@ -482,37 +482,33 @@ public TestItineraryBuilder transit( if (headwaySecs != null) { leg = - new FrequencyTransitLeg( - tripTimes, - tripPattern, - fromStopIndex, - toStopIndex, - newTime(start), - newTime(end), - serviceDate != null ? serviceDate : SERVICE_DAY, - UTC, - transferFromPreviousLeg, - null, - legCost, - headwaySecs, - null - ); + new FrequencyTransitLegBuilder() + .withTripTimes(tripTimes) + .withTripPattern(tripPattern) + .withBoardStopIndexInPattern(fromStopIndex) + .withAlightStopIndexInPattern(toStopIndex) + .withStartTime(newTime(start)) + .withEndTime(newTime(end)) + .withServiceDate(serviceDate != null ? serviceDate : SERVICE_DAY) + .withZoneId(UTC) + .withTransferFromPreviousLeg(transferFromPreviousLeg) + .withGeneralizedCost(legCost) + .withFrequencyHeadwayInSeconds(headwaySecs) + .build(); } else { leg = - new ScheduledTransitLeg( - tripTimes, - tripPattern, - fromStopIndex, - toStopIndex, - newTime(start), - newTime(end), - serviceDate != null ? serviceDate : SERVICE_DAY, - UTC, - transferFromPreviousLeg, - null, - legCost, - null - ); + new ScheduledTransitLegBuilder() + .withTripTimes(tripTimes) + .withTripPattern(tripPattern) + .withBoardStopIndexInPattern(fromStopIndex) + .withAlightStopIndexInPattern(toStopIndex) + .withStartTime(newTime(start)) + .withEndTime(newTime(end)) + .withServiceDate(serviceDate != null ? serviceDate : SERVICE_DAY) + .withZoneId(UTC) + .withTransferFromPreviousLeg(transferFromPreviousLeg) + .withGeneralizedCost(legCost) + .build(); } leg.setDistanceMeters(speed(leg.getMode()) * (end - start)); diff --git a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java index 655c2c24778..da295073e01 100644 --- a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java +++ b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java @@ -19,16 +19,22 @@ class LegReferenceSerializerTest { private static final FeedScopedId TO_STOP_ID = TransitModelForTest.id("Alighting Stop"); /** - * Token based on the latest format, including stop ids. + * Token based on the initial format, without stop ids. + */ + private static final String ENCODED_TOKEN_V1 = + "rO0ABXc2ABhTQ0hFRFVMRURfVFJBTlNJVF9MRUdfVjEABkY6VHJpcAAKMjAyMi0wMS0zMQAAAAEAAAAD"; + + /** + * Token based on the second version of the format, including stop ids. */ private static final String ENCODED_TOKEN_V2 = "rO0ABXdZABhTQ0hFRFVMRURfVFJBTlNJVF9MRUdfVjIABkY6VHJpcAAKMjAyMi0wMS0zMQAAAAEAAAADAA9GOkJvYXJkaW5nIFN0b3AAEEY6QWxpZ2h0aW5nIFN0b3A="; /** - * Token based on the previous format, without stop ids. + * Token based on the latest format, including stop ids and TripOnServiceDate id. */ - private static final String ENCODED_TOKEN_V1 = - "rO0ABXc2ABhTQ0hFRFVMRURfVFJBTlNJVF9MRUdfVjEABkY6VHJpcAAKMjAyMi0wMS0zMQAAAAEAAAAD"; + private static final String ENCODED_TOKEN_V3 = + "rO0ABXdbABhTQ0hFRFVMRURfVFJBTlNJVF9MRUdfVjMABkY6VHJpcAAKMjAyMi0wMS0zMQAAAAEAAAADAA9GOkJvYXJkaW5nIFN0b3AAEEY6QWxpZ2h0aW5nIFN0b3AAAA=="; @Test void testScheduledTransitLegReferenceRoundTrip() { @@ -38,12 +44,13 @@ void testScheduledTransitLegReferenceRoundTrip() { FROM_STOP_POS, TO_STOP_POS, FROM_STOP_ID, - TO_STOP_ID + TO_STOP_ID, + null ); var out = LegReferenceSerializer.encode(ref); - assertEquals(ENCODED_TOKEN_V2, out); + assertEquals(ENCODED_TOKEN_V3, out); var ref2 = LegReferenceSerializer.decode(out); @@ -52,7 +59,7 @@ void testScheduledTransitLegReferenceRoundTrip() { @Test void testScheduledTransitLegReferenceDeserialize() { - var ref = (ScheduledTransitLegReference) LegReferenceSerializer.decode(ENCODED_TOKEN_V2); + var ref = (ScheduledTransitLegReference) LegReferenceSerializer.decode(ENCODED_TOKEN_V3); assertNotNull(ref); assertEquals(TRIP_ID, ref.tripId()); assertEquals(SERVICE_DATE, ref.serviceDate()); @@ -61,7 +68,7 @@ void testScheduledTransitLegReferenceDeserialize() { } @Test - void testScheduledTransitLegReferenceLegacyDeserialize() { + void testScheduledTransitLegReferenceLegacyV1Deserialize() { var ref = (ScheduledTransitLegReference) LegReferenceSerializer.decode(ENCODED_TOKEN_V1); assertNotNull(ref); assertEquals(TRIP_ID, ref.tripId()); @@ -69,4 +76,14 @@ void testScheduledTransitLegReferenceLegacyDeserialize() { assertEquals(FROM_STOP_POS, ref.fromStopPositionInPattern()); assertEquals(TO_STOP_POS, ref.toStopPositionInPattern()); } + + @Test + void testScheduledTransitLegReferenceLegacyV2Deserialize() { + var ref = (ScheduledTransitLegReference) LegReferenceSerializer.decode(ENCODED_TOKEN_V2); + assertNotNull(ref); + assertEquals(TRIP_ID, ref.tripId()); + assertEquals(SERVICE_DATE, ref.serviceDate()); + assertEquals(FROM_STOP_POS, ref.fromStopPositionInPattern()); + assertEquals(TO_STOP_POS, ref.toStopPositionInPattern()); + } } diff --git a/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java b/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java index d44b2eb4857..4f7a485ff83 100644 --- a/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java +++ b/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import java.time.LocalDate; @@ -21,6 +22,7 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; @@ -32,6 +34,7 @@ class ScheduledTransitLegReferenceTest { private static final int SERVICE_CODE = 555; private static final LocalDate SERVICE_DATE = LocalDate.of(2023, 1, 1); private static final int NUMBER_OF_STOPS = 3; + private static final FeedScopedId TRIP_ON_SERVICE_DATE_ID = id("TRIP_ON_SERVICE_DATE_ID"); public static FeedScopedId stopIdAtPosition0; public static FeedScopedId stopIdAtPosition1; @@ -82,6 +85,18 @@ static void buildTransitService() { CalendarServiceData calendarServiceData = new CalendarServiceData(); calendarServiceData.putServiceDatesForServiceId(tripPattern.getId(), List.of(SERVICE_DATE)); transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); + + transitModel + .getTripOnServiceDates() + .put( + TRIP_ON_SERVICE_DATE_ID, + TripOnServiceDate + .of(TRIP_ON_SERVICE_DATE_ID) + .withTrip(trip) + .withServiceDate(SERVICE_DATE) + .build() + ); + transitModel.index(); // build transit service @@ -98,7 +113,8 @@ void getLegFromReference() { boardAtStopPos, alightAtStopPos, stopIdAtPosition0, - stopIdAtPosition1 + stopIdAtPosition1, + null ); ScheduledTransitLeg leg = scheduledTransitLegReference.getLeg(transitService); assertNotNull(leg); @@ -116,7 +132,8 @@ void getLegFromReferenceUnknownTrip() { 0, 1, stopIdAtPosition0, - stopIdAtPosition1 + stopIdAtPosition1, + null ); assertNull(scheduledTransitLegReference.getLeg(transitService)); } @@ -129,7 +146,8 @@ void getLegFromReferenceInvalidServiceDate() { 0, 1, stopIdAtPosition0, - stopIdAtPosition1 + stopIdAtPosition1, + null ); assertNull(scheduledTransitLegReference.getLeg(transitService)); } @@ -142,7 +160,8 @@ void getLegFromReferenceOutOfRangeBoardingStop() { NUMBER_OF_STOPS, 1, stopIdAtPosition0, - stopIdAtPosition1 + stopIdAtPosition1, + null ); assertNull(scheduledTransitLegReference.getLeg(transitService)); } @@ -155,7 +174,8 @@ void getLegFromReferenceMismatchOnBoardingStop() { 0, 1, TransitModelForTest.id("invalid stop id"), - stopIdAtPosition1 + stopIdAtPosition1, + null ); assertNull(scheduledTransitLegReference.getLeg(transitService)); } @@ -170,7 +190,8 @@ void getLegFromReferenceMismatchOnAlightingStopSameParentStation() { 0, 2, stopIdAtPosition0, - stop4.getId() + stop4.getId(), + null ); assertNotNull(scheduledTransitLegReference.getLeg(transitService)); } @@ -183,8 +204,71 @@ void getLegFromReferenceOutOfRangeAlightingStop() { 0, NUMBER_OF_STOPS, stopIdAtPosition0, - stopIdAtPosition1 + stopIdAtPosition1, + null + ); + assertNull(scheduledTransitLegReference.getLeg(transitService)); + } + + @Test + void legReferenceCannotReferToBothTripAndTripOnServiceDate() { + assertThrows( + IllegalArgumentException.class, + () -> + new ScheduledTransitLegReference( + tripId, + SERVICE_DATE, + 0, + NUMBER_OF_STOPS, + stopIdAtPosition0, + stopIdAtPosition1, + TransitModelForTest.id("trip on date id") + ) + ); + } + + @Test + void legReferenceCannotReferToInconsistentServiceDateAndTripOnServiceDate() { + ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference( + null, + SERVICE_DATE.plusDays(1), + 0, + 1, + stopIdAtPosition0, + stopIdAtPosition1, + TRIP_ON_SERVICE_DATE_ID + ); + assertNull(scheduledTransitLegReference.getLeg(transitService)); + } + + @Test + void getLegFromReferenceWithUnknownTripOnDate() { + ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference( + null, + SERVICE_DATE, + 0, + NUMBER_OF_STOPS, + stopIdAtPosition0, + stopIdAtPosition1, + TransitModelForTest.id("unknown trip on date id") ); assertNull(scheduledTransitLegReference.getLeg(transitService)); } + + @Test + void getLegFromReferenceWithValidTripOnDate() { + ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference( + null, + SERVICE_DATE, + 0, + 1, + stopIdAtPosition0, + stopIdAtPosition1, + TRIP_ON_SERVICE_DATE_ID + ); + ScheduledTransitLeg leg = scheduledTransitLegReference.getLeg(transitService); + assertNotNull(leg); + assertEquals(tripId, leg.getTrip().getId()); + assertEquals(SERVICE_DATE, leg.getServiceDate()); + } } diff --git a/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java b/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java index a7369a98a8c..96f49bb9c78 100644 --- a/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java +++ b/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java @@ -42,7 +42,8 @@ void testPreviousLegs() { 1, 2, STOP_ID_B, - STOP_ID_C + STOP_ID_C, + null ) .getLeg(transitService); @@ -80,7 +81,8 @@ void testNextLegs() { 0, 1, STOP_ID_B, - STOP_ID_C + STOP_ID_C, + null ) .getLeg(transitService); @@ -118,7 +120,8 @@ void testCircularRoutes() { 1, 2, STOP_ID_X, - STOP_ID_Y + STOP_ID_Y, + null ) .getLeg(transitService); @@ -151,7 +154,8 @@ void testComplexCircularRoutes() { 1, 7, STOP_ID_X, - STOP_ID_B + STOP_ID_B, + null ) .getLeg(transitService);