From 71efc46294f730a90fbf46c754f2266227584da6 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Wed, 14 Aug 2024 14:55:31 +0100 Subject: [PATCH] implement delay and skipped stops on added / replacement trips --- .../updater/trip/TimetableSnapshotSource.java | 35 +++++++++-- .../updater/trip/TripUpdateBuilder.java | 20 +++++++ .../trip/moduletests/addition/AddedTest.java | 60 +++++++++++++++++++ 3 files changed, 110 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 56e3c380b67..9069cb0879c 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -781,7 +781,11 @@ private Result addTripToGraphAndBuffer( stopTime.setStop(stop); // Set arrival time if (stopTimeUpdate.hasArrival() && stopTimeUpdate.getArrival().hasTime()) { - final long arrivalTime = stopTimeUpdate.getArrival().getTime() - midnightSecondsSinceEpoch; + final int delay = stopTimeUpdate.getArrival().hasDelay() + ? stopTimeUpdate.getArrival().getDelay() + : 0; + final long arrivalTime = + stopTimeUpdate.getArrival().getTime() - midnightSecondsSinceEpoch - delay; if (arrivalTime < 0 || arrivalTime > MAX_ARRIVAL_DEPARTURE_TIME) { debug( trip.getId(), @@ -794,8 +798,11 @@ private Result addTripToGraphAndBuffer( } // Set departure time if (stopTimeUpdate.hasDeparture() && stopTimeUpdate.getDeparture().hasTime()) { + final int delay = stopTimeUpdate.getDeparture().hasDelay() + ? stopTimeUpdate.getDeparture().getDelay() + : 0; final long departureTime = - stopTimeUpdate.getDeparture().getTime() - midnightSecondsSinceEpoch; + stopTimeUpdate.getDeparture().getTime() - midnightSecondsSinceEpoch - delay; if (departureTime < 0 || departureTime > MAX_ARRIVAL_DEPARTURE_TIME) { debug( trip.getId(), @@ -838,12 +845,30 @@ private Result addTripToGraphAndBuffer( ); // Update all times to mark trip times as realtime - // TODO: should we incorporate the delay field if present? + // TODO: This is based on the proposal at https://github.com/google/transit/issues/490 for (int stopIndex = 0; stopIndex < newTripTimes.getNumStops(); stopIndex++) { - newTripTimes.updateArrivalTime(stopIndex, newTripTimes.getScheduledArrivalTime(stopIndex)); + final StopTimeUpdate stopTimeUpdate = stopTimeUpdates.get(stopIndex); + + if ( + stopTimeUpdate.hasScheduleRelationship() && + stopTimeUpdate.getScheduleRelationship() == StopTimeUpdate.ScheduleRelationship.SKIPPED + ) { + newTripTimes.setCancelled(stopIndex); + } + + final int arrivalDelay = stopTimeUpdate.hasArrival() + ? stopTimeUpdate.getArrival().hasDelay() ? stopTimeUpdate.getArrival().getDelay() : 0 + : 0; + final int departureDelay = stopTimeUpdate.hasDeparture() + ? stopTimeUpdate.getDeparture().hasDelay() ? stopTimeUpdate.getDeparture().getDelay() : 0 + : 0; + newTripTimes.updateArrivalTime( + stopIndex, + newTripTimes.getScheduledArrivalTime(stopIndex) + arrivalDelay + ); newTripTimes.updateDepartureTime( stopIndex, - newTripTimes.getScheduledDepartureTime(stopIndex) + newTripTimes.getScheduledDepartureTime(stopIndex) + departureDelay ); } diff --git a/src/test/java/org/opentripplanner/updater/trip/TripUpdateBuilder.java b/src/test/java/org/opentripplanner/updater/trip/TripUpdateBuilder.java index 2960d92a9cd..eeebad4ff62 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TripUpdateBuilder.java +++ b/src/test/java/org/opentripplanner/updater/trip/TripUpdateBuilder.java @@ -49,6 +49,26 @@ public TripUpdateBuilder addStopTime(String stopId, int minutes) { ); } + public TripUpdateBuilder addStopTime(String stopId, int minutes, int delay) { + return addStopTime( + stopId, + minutes, + NO_VALUE, + delay, + delay, + DEFAULT_SCHEDULE_RELATIONSHIP, + null + ); + } + + public TripUpdateBuilder addStopTime( + String stopId, + int minutes, + StopTimeUpdate.ScheduleRelationship scheduleRelationship + ) { + return addStopTime(stopId, minutes, NO_VALUE, NO_DELAY, NO_DELAY, scheduleRelationship, null); + } + public TripUpdateBuilder addStopTime(String stopId, int minutes, DropOffPickupType pickDrop) { return addStopTime( stopId, diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 8371c5dda3a..61546ddd663 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -1,12 +1,15 @@ package org.opentripplanner.updater.trip.moduletests.addition; import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.ADDED; +import static com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate.ScheduleRelationship.SKIPPED; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.trip.BackwardsDelayPropagationType.REQUIRED_NO_DATA; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_A1_ID; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_B1_ID; @@ -16,9 +19,12 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.opentripplanner.model.PickDrop; +import org.opentripplanner.model.Timetable; +import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.spi.UpdateSuccess; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; @@ -120,6 +126,60 @@ void repeatedlyAddedTripWithNewRoute() { assertNotNull(env.getTransitService().getRouteForId(firstRoute.getId())); } + @Test + public void addedTripWithSkippedStop() { + var env = RealtimeTestEnvironment.gtfs(); + var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); + builder + .addStopTime(STOP_A1_ID, 30) + .addStopTime(STOP_B1_ID, 40, SKIPPED) + .addStopTime(STOP_C1_ID, 55); + var tripUpdate = builder.build(); + + env.applyTripUpdate(tripUpdate); + + // THEN + final TripPattern tripPattern = assertAddedTrip(ADDED_TRIP_ID, env); + assertEquals(PickDrop.SCHEDULED, tripPattern.getBoardType(0)); + assertEquals(PickDrop.CANCELLED, tripPattern.getAlightType(1)); + assertEquals(PickDrop.CANCELLED, tripPattern.getBoardType(1)); + assertEquals(PickDrop.SCHEDULED, tripPattern.getAlightType(2)); + final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + final Timetable forToday = snapshot.resolve(tripPattern, SERVICE_DATE); + final int forTodayAddedTripIndex = forToday.getTripIndex(ADDED_TRIP_ID); + final TripTimes tripTimes = forToday.getTripTimes(forTodayAddedTripIndex); + assertFalse(tripTimes.isCancelledStop(0)); + assertTrue(tripTimes.isCancelledStop(1)); + } + + @Test + public void addedTripWithDelay() { + var env = RealtimeTestEnvironment.gtfs(); + var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); + + // A1: scheduled 08:30:00 + // B1: scheduled 08:40:00, delay 300 seconds (actual 08:45:00) + // C1: scheduled 08:55:00 + builder + .addStopTime(STOP_A1_ID, 30) + .addStopTime(STOP_B1_ID, 45, 300) + .addStopTime(STOP_C1_ID, 55); + + var tripUpdate = builder.build(); + env.applyTripUpdate(tripUpdate); + + // THEN + final TripPattern tripPattern = assertAddedTrip(ADDED_TRIP_ID, env); + final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + final Timetable forToday = snapshot.resolve(tripPattern, SERVICE_DATE); + final int forTodayAddedTripIndex = forToday.getTripIndex(ADDED_TRIP_ID); + final TripTimes tripTimes = forToday.getTripTimes(forTodayAddedTripIndex); + assertEquals(0, tripTimes.getDepartureDelay(0)); + assertEquals(30600, tripTimes.getDepartureTime(0)); // 08:30:00 + assertEquals(300, tripTimes.getArrivalDelay(1)); + assertEquals(31500, tripTimes.getArrivalTime(1)); // 08:45:00 + } + private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) { var snapshot = env.getTimetableSnapshot(); var stopA = env.transitModel.getStopModel().getRegularStop(env.stopA1.getId());