From df412960be4b778d12e80e0264d045da7cb8c7dd Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 22 Nov 2023 14:06:16 +0100 Subject: [PATCH 01/39] feature: Add validation to ScheduledTripTimes (not enabled, FLEX fails) --- .../model/timetable/ScheduledTripTimes.java | 119 ++++++++----- .../timetable/ScheduledTripTimesBuilder.java | 80 +++++++-- .../model/timetable/TripTimesFactory.java | 4 +- ...rRoutingRequestTransitDataCreatorTest.java | 22 +-- .../timetable/ScheduledTripTimesTest.java | 160 ++++++++++++++++++ .../model/timetable/TripTimesTest.java | 76 +++------ 6 files changed, 340 insertions(+), 121 deletions(-) create mode 100644 src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index c5a5d8f097c..39d4e58c7b6 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -1,23 +1,30 @@ package org.opentripplanner.transit.model.timetable; -import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_DWELL_TIME; -import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_HOP_TIME; - import java.io.Serializable; import java.util.Arrays; import java.util.BitSet; import java.util.List; -import java.util.Optional; +import java.util.Objects; import java.util.OptionalInt; import java.util.function.Supplier; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.IntUtils; +import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.framework.Deduplicator; -final class ScheduledTripTimes implements Serializable, Comparable { +public final class ScheduledTripTimes implements Serializable, Comparable { + + /** + * When time-shifting from one time-zone to another negative times may occur. + */ + private static final int MIN_TIME = DurationUtils.durationInSeconds("-12h"); + /** + * We allow a trip to last for maximum 10 days. In Norway the longest trip is 6 days. + */ + private static final int MAX_TIME = DurationUtils.durationInSeconds("10d"); /** * Implementation notes: This allows re-using the same scheduled arrival and departure time @@ -48,17 +55,22 @@ final class ScheduledTripTimes implements Serializable, Comparable validateNonIncreasingTimes() { - final int nStops = arrivalTimes.length; - int prevDep = -9_999_999; - for (int s = 0; s < nStops; s++) { - final int arr = getArrivalTime(s); - final int dep = getDepartureTime(s); - - if (dep < arr) { - return Optional.of(new ValidationError(NEGATIVE_DWELL_TIME, s)); - } - if (prevDep > arr) { - return Optional.of(new ValidationError(NEGATIVE_HOP_TIME, s)); - } - prevDep = dep; - } - return Optional.empty(); - } - /** Sort trips based on first departure time. */ @Override public int compareTo(final ScheduledTripTimes other) { @@ -343,4 +330,52 @@ int[] copyDepartureTimes() { I18NString[] copyHeadsigns(Supplier defaultValue) { return headsigns == null ? defaultValue.get() : Arrays.copyOf(headsigns, headsigns.length); } + + /* private methods */ + + private void validate() { + int lastStop = departureTimes.length - 1; + IntUtils.requireInRange(departureTimes[0], MIN_TIME, MAX_TIME); + IntUtils.requireInRange(arrivalTimes[lastStop], MIN_TIME, MAX_TIME); + // TODO: This class is used by FLEX, so we can not validate increasing TripTimes + // validateNonIncreasingTimes(); + } + + /** + * When creating scheduled trip times we could potentially imply negative running or dwell times. + * We really don't want those being used in routing. This method checks that all times are + * increasing. The first stop arrival time and the last stops depature time is NOT checked - + * these should be ignored by raptor. + */ + private void validateNonIncreasingTimes() { + final int lastStop = arrivalTimes.length - 1; + + // This check is currently used since Flex trips may have only one stop. This class should + // not be used to represent FLEX, so remove this check and create new data classes for FLEX + // trips. + if (lastStop < 1) { + return; + } + int prevDep = getDepartureTime(0); + + for (int i = 1; true; ++i) { + final int arr = getArrivalTime(i); + final int dep = getDepartureTime(i); + + if (prevDep > arr) { + throw new IllegalArgumentException( + "Negative hop time for stop position " + i + " for " + trip + "." + ); + } + if (i == lastStop) { + return; + } + if (dep < arr) { + throw new IllegalArgumentException( + "Negative dwell time for stop position " + i + " for " + trip + "." + ); + } + prevDep = dep; + } + } } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java index d6515321f74..6fc5f968826 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java @@ -4,6 +4,7 @@ import java.util.List; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.DeduplicatorService; @@ -11,18 +12,19 @@ public class ScheduledTripTimesBuilder { private final int NOT_SET = -1; - - int timeShift; - int serviceCode = NOT_SET; - int[] arrivalTimes; - int[] departureTimes; - BitSet timepoints; - Trip trip; - List dropOffBookingInfos; - List pickupBookingInfos; - I18NString[] headsigns; - String[][] headsignVias; - int[] originalGtfsStopSequence; + private final BitSet EMPTY_BIT_SET = new BitSet(0); + + private int timeShift; + private int serviceCode = NOT_SET; + private int[] arrivalTimes; + private int[] departureTimes; + private BitSet timepoints; + private Trip trip; + private List dropOffBookingInfos; + private List pickupBookingInfos; + private I18NString[] headsigns; + private String[][] headsignVias; + private int[] originalGtfsStopSequence; private final DeduplicatorService deduplicator; ScheduledTripTimesBuilder(@Nullable DeduplicatorService deduplicator) { @@ -57,6 +59,10 @@ public class ScheduledTripTimesBuilder { this.originalGtfsStopSequence = originalGtfsStopSequence; } + public int timeShift() { + return timeShift; + } + public ScheduledTripTimesBuilder withTimeShift(int timeShift) { this.timeShift = timeShift; return this; @@ -71,53 +77,103 @@ public ScheduledTripTimesBuilder plusTimeShift(int delta) { return this; } + public int serviceCode() { + return serviceCode; + } + public ScheduledTripTimesBuilder withServiceCode(int serviceCode) { this.serviceCode = serviceCode; return this; } + public int[] arrivalTimes() { + return arrivalTimes == null ? departureTimes : arrivalTimes; + } + public ScheduledTripTimesBuilder withArrivalTimes(int[] arrivalTimes) { this.arrivalTimes = deduplicator.deduplicateIntArray(arrivalTimes); return this; } + /** For unit testing, uses {@link TimeUtils#time(java.lang.String)}. */ + public ScheduledTripTimesBuilder withArrivalTimes(String arrivalTimes) { + return withArrivalTimes(TimeUtils.times(arrivalTimes)); + } + + public int[] departureTimes() { + return departureTimes == null ? arrivalTimes : departureTimes; + } + public ScheduledTripTimesBuilder withDepartureTimes(int[] departureTimes) { this.departureTimes = deduplicator.deduplicateIntArray(departureTimes); return this; } + /** For unit testing, uses {@link TimeUtils#time(java.lang.String)}. */ + public ScheduledTripTimesBuilder withDepartureTimes(String departureTimes) { + return withDepartureTimes(TimeUtils.times(departureTimes)); + } + + public BitSet timepoints() { + return timepoints == null ? EMPTY_BIT_SET : timepoints; + } + public ScheduledTripTimesBuilder withTimepoints(BitSet timepoints) { this.timepoints = deduplicator.deduplicateBitSet(timepoints); return this; } + public Trip trip() { + return trip; + } + public ScheduledTripTimesBuilder withTrip(Trip trip) { this.trip = trip; return this; } + public List dropOffBookingInfos() { + return dropOffBookingInfos == null ? List.of() : dropOffBookingInfos; + } + public ScheduledTripTimesBuilder withDropOffBookingInfos(List dropOffBookingInfos) { this.dropOffBookingInfos = deduplicator.deduplicateImmutableList(BookingInfo.class, dropOffBookingInfos); return this; } + public List pickupBookingInfos() { + return pickupBookingInfos == null ? List.of() : pickupBookingInfos; + } + public ScheduledTripTimesBuilder withPickupBookingInfos(List pickupBookingInfos) { this.pickupBookingInfos = deduplicator.deduplicateImmutableList(BookingInfo.class, pickupBookingInfos); return this; } + public I18NString[] headsigns() { + return headsigns; + } + public ScheduledTripTimesBuilder withHeadsigns(I18NString[] headsigns) { this.headsigns = deduplicator.deduplicateObjectArray(I18NString.class, headsigns); return this; } + public String[][] headsignVias() { + return headsignVias; + } + public ScheduledTripTimesBuilder withHeadsignVias(String[][] headsignVias) { this.headsignVias = deduplicator.deduplicateString2DArray(headsignVias); return this; } + public int[] originalGtfsStopSequence() { + return originalGtfsStopSequence; + } + public ScheduledTripTimesBuilder withOriginalGtfsStopSequence(int[] originalGtfsStopSequence) { this.originalGtfsStopSequence = deduplicator.deduplicateIntArray(originalGtfsStopSequence); return this; diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java index 1bb953b81c5..c868fda6582 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java @@ -1,6 +1,6 @@ package org.opentripplanner.transit.model.timetable; -import java.util.Collection; +import java.util.List; import org.opentripplanner.model.StopTime; import org.opentripplanner.transit.model.framework.Deduplicator; @@ -20,7 +20,7 @@ public class TripTimesFactory { */ public static TripTimes tripTimes( Trip trip, - Collection stopTimes, + List stopTimes, Deduplicator deduplicator ) { return new TripTimes(StopTimeToScheduledTripTimesMapper.map(trip, stopTimes, deduplicator)); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java index 20ca21a50a6..e59a5360808 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java @@ -6,7 +6,6 @@ import java.time.LocalDate; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.BitSet; import java.util.List; import org.junit.jupiter.api.Test; @@ -16,13 +15,12 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripPatternForDate; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.basic.TransitMode; -import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.RoutingTripPattern; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; import org.opentripplanner.transit.model.timetable.TripTimes; -import org.opentripplanner.transit.model.timetable.TripTimesFactory; public class RaptorRoutingRequestTransitDataCreatorTest { @@ -98,21 +96,17 @@ private static TripPatternForDates findTripPatternForDate( } private TripTimes createTripTimesForTest() { - StopTime stopTime1 = new StopTime(); - StopTime stopTime2 = new StopTime(); - - stopTime1.setDepartureTime(0); - stopTime2.setArrivalTime(7200); - - return TripTimesFactory.tripTimes( - TransitModelForTest.trip("Test").build(), - Arrays.asList(stopTime1, stopTime2), - new Deduplicator() + return TripTimes.of( + ScheduledTripTimes + .of() + .withTrip(TransitModelForTest.trip("Test").build()) + .withDepartureTimes("00:00 02:00") + .build() ); } /** - * Utility function to create bare minimum of valid StopTime with no interesting attributes + * Utility function to create bare minimum of valid StopTime * * @return StopTime instance */ diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java new file mode 100644 index 00000000000..d548be2309b --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java @@ -0,0 +1,160 @@ +package org.opentripplanner.transit.model.timetable; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.util.BitSet; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.time.TimeUtils; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.framework.FeedScopedId; + +class ScheduledTripTimesTest { + + private static final Trip TRIP = TransitModelForTest.trip("Trip-1").build(); + + private static final List STOP_IDS = List.of(id("A"), id("B"), id("C")); + private static final int SERVICE_CODE = 5; + private static final BitSet TIMEPOINTS = new BitSet(3); + private static final int T10_00 = TimeUtils.time("10:00"); + private static final int T10_01 = T10_00 + 60; + private static final int T11_00 = TimeUtils.time("11:00"); + private static final int T11_02 = T11_00 + 120; + private static final int T12_00 = TimeUtils.time("12:00"); + private static final int T12_03 = T12_00 + 180; + public static final int STOP_POS_0 = 0; + public static final int STOP_POS_1 = 1; + public static final int STOP_POS_2 = 2; + + static { + TIMEPOINTS.set(1); + } + + private final ScheduledTripTimes subject = ScheduledTripTimes + .of(null) + .withArrivalTimes("10:00 11:00 12:00") + .withDepartureTimes("10:01 11:02 12:03") + .withServiceCode(SERVICE_CODE) + .withTrip(TRIP) + .withTimepoints(TIMEPOINTS) + .build(); + + @Test + void getServiceCode() { + assertEquals(SERVICE_CODE, subject.getServiceCode()); + } + + @Test + void getScheduledArrivalTime() { + assertEquals(T10_00, subject.getScheduledArrivalTime(STOP_POS_0)); + assertEquals(T11_00, subject.getScheduledArrivalTime(STOP_POS_1)); + assertEquals(T12_00, subject.getScheduledArrivalTime(STOP_POS_2)); + } + + @Test + void getArrivalTime() { + assertEquals(T10_00, subject.getArrivalTime(STOP_POS_0)); + assertEquals(T11_00, subject.getArrivalTime(STOP_POS_1)); + assertEquals(T12_00, subject.getArrivalTime(STOP_POS_2)); + } + + @Test + void getArrivalDelay() { + assertEquals(0, subject.getArrivalDelay(STOP_POS_0)); + assertEquals(0, subject.getArrivalDelay(STOP_POS_1)); + assertEquals(0, subject.getArrivalDelay(STOP_POS_2)); + } + + @Test + void getScheduledDepartureTime() { + assertEquals(T10_01, subject.getScheduledDepartureTime(STOP_POS_0)); + assertEquals(T11_02, subject.getScheduledDepartureTime(STOP_POS_1)); + assertEquals(T12_03, subject.getScheduledDepartureTime(STOP_POS_2)); + } + + @Test + void getDepartureTime() { + assertEquals(T10_01, subject.getDepartureTime(STOP_POS_0)); + assertEquals(T11_02, subject.getDepartureTime(STOP_POS_1)); + assertEquals(T12_03, subject.getDepartureTime(STOP_POS_2)); + } + + @Test + void getDepartureDelay() { + assertEquals(0, subject.getDepartureDelay(STOP_POS_0)); + assertEquals(0, subject.getDepartureDelay(STOP_POS_1)); + assertEquals(0, subject.getDepartureDelay(STOP_POS_2)); + } + + @Test + void isTimepoint() { + assertFalse(subject.isTimepoint(STOP_POS_0)); + assertTrue(subject.isTimepoint(STOP_POS_1)); + assertFalse(subject.isTimepoint(STOP_POS_2)); + } + + @Test + void getTrip() { + assertEquals(TRIP, subject.getTrip()); + } + + @Test + void sortIndex() { + assertEquals(T10_01, subject.sortIndex()); + } + + @Test + void isScheduled() { + assertTrue(subject.isScheduled()); + } + + @Test + void isCanceledOrDeleted() { + assertFalse(subject.isCanceledOrDeleted()); + } + + @Test + void isCanceled() { + assertFalse(subject.isCanceled()); + } + + @Test + void isDeleted() { + assertFalse(subject.isDeleted()); + } + + @Test + void getRealTimeState() { + assertEquals(RealTimeState.SCHEDULED, subject.getRealTimeState()); + } + + @Test + void getNumStops() { + assertEquals(3, subject.getNumStops()); + } + + @Test + void getWheelchairAccessibility() { + assertEquals(Accessibility.NO_INFORMATION, subject.getWheelchairAccessibility()); + } + + @Test + void getOccupancyStatus() { + assertEquals(OccupancyStatus.NO_DATA_AVAILABLE, subject.getOccupancyStatus(0)); + } + + @Test + void copyArrivalTimes() { + assertArrayEquals(new int[] { T10_00, T11_00, T12_00 }, subject.copyArrivalTimes()); + } + + @Test + void copyDepartureTimes() { + assertArrayEquals(new int[] { T10_01, T11_02, T12_03 }, subject.copyDepartureTimes()); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java index 4c2cc41cef2..af37c812667 100644 --- a/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java +++ b/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java @@ -8,7 +8,6 @@ import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_DWELL_TIME; import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_HOP_TIME; -import java.util.Collection; import java.util.LinkedList; import java.util.List; import org.junit.jupiter.api.Nested; @@ -27,7 +26,7 @@ class TripTimesTest { private static final String TRIP_ID = "testTripId"; - private static final List stops = List.of( + private static final List stopIds = List.of( id("A"), id("B"), id("C"), @@ -43,10 +42,10 @@ static TripTimes createInitialTripTimes() { List stopTimes = new LinkedList<>(); - for (int i = 0; i < stops.size(); ++i) { + for (int i = 0; i < stopIds.size(); ++i) { StopTime stopTime = new StopTime(); - RegularStop stop = TEST_MODEL.stop(stops.get(i).getId(), 0.0, 0.0).build(); + RegularStop stop = TEST_MODEL.stop(stopIds.get(i).getId(), 0.0, 0.0).build(); stopTime.setStop(stop); stopTime.setArrivalTime(i * 60); stopTime.setDepartureTime(i * 60); @@ -69,7 +68,7 @@ class Headsign { @Test void shouldHandleBothNullScenario() { Trip trip = TransitModelForTest.trip("TRIP").build(); - Collection stopTimes = List.of(EMPTY_STOPPOINT, EMPTY_STOPPOINT, EMPTY_STOPPOINT); + List stopTimes = List.of(EMPTY_STOPPOINT, EMPTY_STOPPOINT, EMPTY_STOPPOINT); TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); @@ -80,7 +79,7 @@ void shouldHandleBothNullScenario() { @Test void shouldHandleTripOnlyHeadSignScenario() { Trip trip = TransitModelForTest.trip("TRIP").withHeadsign(DIRECTION).build(); - Collection stopTimes = List.of(EMPTY_STOPPOINT, EMPTY_STOPPOINT, EMPTY_STOPPOINT); + List stopTimes = List.of(EMPTY_STOPPOINT, EMPTY_STOPPOINT, EMPTY_STOPPOINT); TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); @@ -93,11 +92,7 @@ void shouldHandleStopsOnlyHeadSignScenario() { Trip trip = TransitModelForTest.trip("TRIP").build(); StopTime stopWithHeadsign = new StopTime(); stopWithHeadsign.setStopHeadsign(STOP_TEST_DIRECTION); - Collection stopTimes = List.of( - stopWithHeadsign, - stopWithHeadsign, - stopWithHeadsign - ); + List stopTimes = List.of(stopWithHeadsign, stopWithHeadsign, stopWithHeadsign); TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); @@ -110,11 +105,7 @@ void shouldHandleStopsEqualToTripHeadSignScenario() { Trip trip = TransitModelForTest.trip("TRIP").withHeadsign(DIRECTION).build(); StopTime stopWithHeadsign = new StopTime(); stopWithHeadsign.setStopHeadsign(DIRECTION); - Collection stopTimes = List.of( - stopWithHeadsign, - stopWithHeadsign, - stopWithHeadsign - ); + List stopTimes = List.of(stopWithHeadsign, stopWithHeadsign, stopWithHeadsign); TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); @@ -127,7 +118,7 @@ void shouldHandleDifferingTripAndStopHeadSignScenario() { Trip trip = TransitModelForTest.trip("TRIP").withHeadsign(DIRECTION).build(); StopTime stopWithHeadsign = new StopTime(); stopWithHeadsign.setStopHeadsign(STOP_TEST_DIRECTION); - Collection stopTimes = List.of(stopWithHeadsign, EMPTY_STOPPOINT, EMPTY_STOPPOINT); + List stopTimes = List.of(stopWithHeadsign, EMPTY_STOPPOINT, EMPTY_STOPPOINT); TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); @@ -433,46 +424,29 @@ void unknownGtfsSequence() { } @Test - public void testApply() { - Trip trip = TransitModelForTest.trip(TRIP_ID).build(); - - List stopTimes = new LinkedList<>(); - - StopTime stopTime0 = new StopTime(); - StopTime stopTime1 = new StopTime(); - StopTime stopTime2 = new StopTime(); - - RegularStop stop0 = TEST_MODEL.stop(stops.get(0).getId(), 0.0, 0.0).build(); - RegularStop stop1 = TEST_MODEL.stop(stops.get(1).getId(), 0.0, 0.0).build(); - RegularStop stop2 = TEST_MODEL.stop(stops.get(2).getId(), 0.0, 0.0).build(); - - stopTime0.setStop(stop0); - stopTime0.setDepartureTime(0); - stopTime0.setStopSequence(0); - - stopTime1.setStop(stop1); - stopTime1.setArrivalTime(30); - stopTime1.setDepartureTime(60); - stopTime1.setStopSequence(1); + public void validateNegativeDwellTime() { + var tt = createInitialTripTimes(); + var updatedTt = tt.copyOfScheduledTimes(); - stopTime2.setStop(stop2); - stopTime2.setArrivalTime(90); - stopTime2.setStopSequence(2); + updatedTt.updateArrivalTime(2, 69); + updatedTt.updateDepartureTime(2, 68); - stopTimes.add(stopTime0); - stopTimes.add(stopTime1); - stopTimes.add(stopTime2); - - TripTimes differingTripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); + var validationResult = updatedTt.validateNonIncreasingTimes(); + assertTrue(validationResult.isPresent()); + assertEquals(2, validationResult.get().stopIndex()); + assertEquals(NEGATIVE_DWELL_TIME, validationResult.get().code()); + } - TripTimes updatedTripTimesA = differingTripTimes.copyOfScheduledTimes(); + @Test + public void validateNegativeHopTime() { + var tt = createInitialTripTimes(); + var updatedTt = tt.copyOfScheduledTimes(); - updatedTripTimesA.updateArrivalTime(1, 89); - updatedTripTimesA.updateDepartureTime(1, 98); + updatedTt.updateArrivalTime(2, 59); - var validationResult = updatedTripTimesA.validateNonIncreasingTimes(); + var validationResult = updatedTt.validateNonIncreasingTimes(); assertTrue(validationResult.isPresent()); assertEquals(2, validationResult.get().stopIndex()); - assertEquals(NEGATIVE_DWELL_TIME, validationResult.get().code()); + assertEquals(NEGATIVE_HOP_TIME, validationResult.get().code()); } } From 7ff79bf2c3890146707e48ac9d4773b3122e545d Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Mon, 2 Oct 2023 12:10:24 +0200 Subject: [PATCH 02/39] refactor: Move normalization of trip-times from mapper to builder. This ensures the normalization is performed even if the mapper is no used. --- .../model/timetable/ScheduledTripTimes.java | 16 +++--- .../timetable/ScheduledTripTimesBuilder.java | 51 +++++++++++++++---- .../StopTimeToScheduledTripTimesMapper.java | 7 +-- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index 39d4e58c7b6..310397ba7ba 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -69,22 +69,20 @@ public final class ScheduledTripTimes implements Serializable, Comparable - * Always provide a deduplicator when building the graph. No deduplication is ok when changing - * simple fields like {@code timeShift} and {@code serviceCode} or even the prefered way in a - * unittest. - */ - public ScheduledTripTimesBuilder copyOf(@Nullable Deduplicator deduplicator) { + public ScheduledTripTimesBuilder copyOf(Deduplicator deduplicator) { return new ScheduledTripTimesBuilder( timeShift, serviceCode, diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java index 6fc5f968826..0eee5d3e183 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java @@ -6,16 +6,15 @@ import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.model.BookingInfo; -import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.DeduplicatorService; public class ScheduledTripTimesBuilder { - private final int NOT_SET = -1; - private final BitSet EMPTY_BIT_SET = new BitSet(0); + private static final int NOT_SET = -1; + private static final BitSet EMPTY_BIT_SET = new BitSet(0); private int timeShift; - private int serviceCode = NOT_SET; + private int serviceCode; private int[] arrivalTimes; private int[] departureTimes; private BitSet timepoints; @@ -28,7 +27,7 @@ public class ScheduledTripTimesBuilder { private final DeduplicatorService deduplicator; ScheduledTripTimesBuilder(@Nullable DeduplicatorService deduplicator) { - this.deduplicator = deduplicator == null ? DeduplicatorService.NOOP : deduplicator; + this(0, NOT_SET, null, null, null, null, null, null, null, null, null, deduplicator); } ScheduledTripTimesBuilder( @@ -43,9 +42,8 @@ public class ScheduledTripTimesBuilder { I18NString[] headsigns, String[][] headsignVias, int[] originalGtfsStopSequence, - Deduplicator deduplicator + DeduplicatorService deduplicator ) { - this(deduplicator); this.timeShift = timeShift; this.serviceCode = serviceCode; this.arrivalTimes = arrivalTimes; @@ -57,6 +55,7 @@ public class ScheduledTripTimesBuilder { this.headsigns = headsigns; this.headsignVias = headsignVias; this.originalGtfsStopSequence = originalGtfsStopSequence; + this.deduplicator = deduplicator == null ? DeduplicatorService.NOOP : deduplicator; } public int timeShift() { @@ -87,7 +86,7 @@ public ScheduledTripTimesBuilder withServiceCode(int serviceCode) { } public int[] arrivalTimes() { - return arrivalTimes == null ? departureTimes : arrivalTimes; + return arrivalTimes; } public ScheduledTripTimesBuilder withArrivalTimes(int[] arrivalTimes) { @@ -101,7 +100,7 @@ public ScheduledTripTimesBuilder withArrivalTimes(String arrivalTimes) { } public int[] departureTimes() { - return departureTimes == null ? arrivalTimes : departureTimes; + return departureTimes; } public ScheduledTripTimesBuilder withDepartureTimes(int[] departureTimes) { @@ -180,6 +179,40 @@ public ScheduledTripTimesBuilder withOriginalGtfsStopSequence(int[] originalGtfs } public ScheduledTripTimes build() { + normalizeTimes(); return new ScheduledTripTimes(this); } + + /** + * Times are always shifted to zero based on the first departure time. This is essential for + * frequencies and deduplication. + */ + private void normalizeTimes() { + if (departureTimes == null) { + this.departureTimes = arrivalTimes; + } + if (arrivalTimes == null) { + this.arrivalTimes = departureTimes; + } + + int shift = departureTimes[0]; + if (shift == 0) { + return; + } + this.departureTimes = timeShift(departureTimes, shift); + if (arrivalTimes != departureTimes) { + this.arrivalTimes = timeShift(arrivalTimes, shift); + } + this.timeShift += shift; + } + + int[] timeShift(int[] a, int shift) { + if (shift == 0) { + return a; + } + for (int i = 0; i < a.length; i++) { + a[i] -= shift; + } + return a; + } } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java index bf98c6e77b8..22bb77b073e 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java @@ -40,16 +40,13 @@ private ScheduledTripTimes doMap(Collection stopTimes) { final int[] arrivals = new int[nStops]; final int[] sequences = new int[nStops]; final BitSet timepoints = new BitSet(nStops); - // Times are always shifted to zero. This is essential for frequencies and deduplication. - int timeShift = stopTimes.iterator().next().getArrivalTime(); - builder.withTimeShift(timeShift); final List dropOffBookingInfos = new ArrayList<>(); final List pickupBookingInfos = new ArrayList<>(); int s = 0; for (final StopTime st : stopTimes) { - departures[s] = st.getDepartureTime() - timeShift; - arrivals[s] = st.getArrivalTime() - timeShift; + departures[s] = st.getDepartureTime(); + arrivals[s] = st.getArrivalTime(); sequences[s] = st.getStopSequence(); timepoints.set(s, st.getTimepoint() == 1); From 1728c29de73b706fb6c640e28200c4bdb2a776df Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 16 Nov 2023 19:44:44 +0100 Subject: [PATCH 03/39] refactor: Use DataValidationException to signal TripTime errors. --- .../ext/siri/AddedTripBuilder.java | 10 +- .../ext/siri/ModifiedTripBuilder.java | 22 +-- .../ext/siri/SiriTimetableSnapshotSource.java | 4 + .../framework/error/OtpError.java | 37 ++++ .../issue/api/DataImportIssueStore.java | 4 + .../issue/api/NoopDataImportIssueStore.java | 4 + .../service/DefaultDataImportIssueStore.java | 6 + .../gtfs/GenerateTripPatternsOperation.java | 17 +- .../org/opentripplanner/model/Timetable.java | 15 +- .../netex/mapping/TripPatternMapper.java | 17 +- .../framework/DataValidationException.java | 35 ++++ .../transit/model/framework/Result.java | 15 ++ .../model/timetable/ScheduledTripTimes.java | 15 +- .../StopTimeToScheduledTripTimesMapper.java | 6 +- .../timetable/TimetableValidationError.java | 29 ++++ .../transit/model/timetable/TripTimes.java | 26 ++- .../model/timetable/ValidationError.java | 11 -- .../spi/DataValidationExceptionMapper.java | 36 ++++ .../spi/TripTimesValidationMapper.java | 25 --- .../updater/trip/TimetableSnapshotSource.java | 58 ++++--- .../OtpArchitectureModules.java | 1 + .../TimetableValidationErrorTest.java | 24 +++ .../model/timetable/TripTimesTest.java | 163 ++++++++++-------- 23 files changed, 382 insertions(+), 198 deletions(-) create mode 100644 src/main/java/org/opentripplanner/framework/error/OtpError.java create mode 100644 src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java create mode 100644 src/main/java/org/opentripplanner/transit/model/timetable/TimetableValidationError.java delete mode 100644 src/main/java/org/opentripplanner/transit/model/timetable/ValidationError.java create mode 100644 src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java delete mode 100644 src/main/java/org/opentripplanner/updater/spi/TripTimesValidationMapper.java create mode 100644 src/test/java/org/opentripplanner/transit/model/timetable/TimetableValidationErrorTest.java diff --git a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java index 2d7789422ea..74d84ee397e 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java @@ -18,6 +18,7 @@ import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.StopTime; import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.Route; @@ -31,7 +32,7 @@ import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.updater.spi.TripTimesValidationMapper; +import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.UpdateError; import org.rutebanken.netex.model.BusSubmodeEnumeration; import org.rutebanken.netex.model.RailSubmodeEnumeration; @@ -231,9 +232,10 @@ Result build() { } /* Validate */ - var validityResult = updatedTripTimes.validateNonIncreasingTimes(); - if (validityResult.isPresent()) { - return TripTimesValidationMapper.toResult(tripId, validityResult.get()); + try { + updatedTripTimes.validateNonIncreasingTimes(); + } catch (DataValidationException e) { + return DataValidationExceptionMapper.toResult(e); } // Adding trip to index necessary to include values in graphql-queries diff --git a/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java index f56d3c6a75f..ce21823b9b3 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java @@ -12,7 +12,7 @@ import java.util.Set; import org.opentripplanner.ext.siri.mapper.PickDropMapper; import org.opentripplanner.framework.time.ServiceDateUtils; -import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; @@ -20,7 +20,7 @@ import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.TripTimes; -import org.opentripplanner.updater.spi.TripTimesValidationMapper; +import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.UpdateError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,16 +114,16 @@ public Result build() { newTimes.setRealTimeState(RealTimeState.MODIFIED); } - var error = newTimes.validateNonIncreasingTimes(); - final FeedScopedId id = newTimes.getTrip().getId(); - if (error.isPresent()) { - var updateError = error.get(); + // TODO - Handle DataValidationException at the outemost level(pr trip) + try { + newTimes.validateNonIncreasingTimes(); + } catch (DataValidationException e) { LOG.info( - "Invalid SIRI-ET data for trip {} - TripTimes are non-increasing after applying SIRI delay propagation at stop index {}", - id, - updateError.stopIndex() + "Invalid SIRI-ET data for trip {} - TripTimes failed to validate after applying SIRI delay propagation. {}", + newTimes.getTrip().getId(), + e.getMessage() ); - return TripTimesValidationMapper.toResult(id, updateError); + return DataValidationExceptionMapper.toResult(e); } int numStopsInUpdate = newTimes.getNumStops(); @@ -131,7 +131,7 @@ public Result build() { if (numStopsInUpdate != numStopsInPattern) { LOG.info( "Invalid SIRI-ET data for trip {} - Inconsistent number of updated stops ({}) and stops in pattern ({})", - id, + newTimes.getTrip().getId(), numStopsInUpdate, numStopsInPattern ); diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index 0cedc491f79..e6efb3ec3d9 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -18,6 +18,7 @@ import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.model.TimetableSnapshotProvider; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.Trip; @@ -26,6 +27,7 @@ import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.TimetableSnapshotSourceParameters; +import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.UpdateSuccess; @@ -215,6 +217,8 @@ private Result apply( /* commit */ return addTripToGraphAndBuffer(result.successValue()); + } catch (DataValidationException e) { + return DataValidationExceptionMapper.toResult(e); } catch (Exception e) { LOG.warn( "{} EstimatedJourney {} failed.", diff --git a/src/main/java/org/opentripplanner/framework/error/OtpError.java b/src/main/java/org/opentripplanner/framework/error/OtpError.java new file mode 100644 index 00000000000..6b2ff37945e --- /dev/null +++ b/src/main/java/org/opentripplanner/framework/error/OtpError.java @@ -0,0 +1,37 @@ +package org.opentripplanner.framework.error; + +import java.util.Locale; + +/** + * A generic representation of an error. The error should have a code used to group the same + * type of errors together. To avoid filling up memory with error strings during graph build + * we store errors in memory "decomposed". The {@link #messageTemplate()} and + * {@link #messageArguments()} is used to construct the message. Use the {@link Locale#ROOT} + * when constructing the message - we only support english with SI formatting. + */ +public interface OtpError { + /** + * An error code used to identify the error type. This is NOT an enum, but feel free + * to use an inum in the implementation, then use the enum NAME as the code or + * enum TYPE:NAME. Then name should be unique within OTP. + */ + String errorCode(); + + /** + * The string template used to create a human-readable error message. Use the + * {@link String#format(Locale, String, Object...)} format. + */ + String messageTemplate(); + + /** + * The arguments to inject into the message. + */ + Object[] messageArguments(); + + /** + * Construct a message. + */ + default String message() { + return String.format(Locale.ROOT, messageTemplate(), messageArguments()); + } +} diff --git a/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueStore.java b/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueStore.java index 1e28cec72f4..bd158436747 100644 --- a/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueStore.java +++ b/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueStore.java @@ -1,6 +1,7 @@ package org.opentripplanner.graph_builder.issue.api; import java.util.List; +import org.opentripplanner.framework.error.OtpError; /** * This service is used to store issued during data import. When the import is complete @@ -19,6 +20,9 @@ public interface DataImportIssueStore { /** Add an issue to the issue report. */ void add(DataImportIssue issue); + /** Add an issue to the issue report. */ + void add(OtpError issue); + /** Add an issue to the issue report without the need of creating an issue class. */ void add(String type, String message); diff --git a/src/main/java/org/opentripplanner/graph_builder/issue/api/NoopDataImportIssueStore.java b/src/main/java/org/opentripplanner/graph_builder/issue/api/NoopDataImportIssueStore.java index 504980d74a5..a3adfe7127c 100644 --- a/src/main/java/org/opentripplanner/graph_builder/issue/api/NoopDataImportIssueStore.java +++ b/src/main/java/org/opentripplanner/graph_builder/issue/api/NoopDataImportIssueStore.java @@ -1,6 +1,7 @@ package org.opentripplanner.graph_builder.issue.api; import java.util.List; +import org.opentripplanner.framework.error.OtpError; /** * A no-op implementation of the issue store, convenient for unit testing. No issues are @@ -11,6 +12,9 @@ class NoopDataImportIssueStore implements DataImportIssueStore { @Override public void add(DataImportIssue issue) {} + @Override + public void add(OtpError issue) {} + @Override public void add(String type, String message) {} diff --git a/src/main/java/org/opentripplanner/graph_builder/issue/service/DefaultDataImportIssueStore.java b/src/main/java/org/opentripplanner/graph_builder/issue/service/DefaultDataImportIssueStore.java index 53f8c65d10c..3cdcf9d0db6 100644 --- a/src/main/java/org/opentripplanner/graph_builder/issue/service/DefaultDataImportIssueStore.java +++ b/src/main/java/org/opentripplanner/graph_builder/issue/service/DefaultDataImportIssueStore.java @@ -3,6 +3,7 @@ import jakarta.inject.Singleton; import java.util.ArrayList; import java.util.List; +import org.opentripplanner.framework.error.OtpError; import org.opentripplanner.graph_builder.issue.api.DataImportIssue; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issue.api.Issue; @@ -30,6 +31,11 @@ public void add(DataImportIssue issue) { } } + @Override + public void add(OtpError issue) { + add(issue.errorCode(), issue.messageTemplate(), issue.messageArguments()); + } + @Override public void add(String type, String message) { add(Issue.issue(type, message)); diff --git a/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java b/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java index 1cb3c4c0186..bf26f9c6d7b 100644 --- a/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java +++ b/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Set; import org.opentripplanner.ext.flex.trip.FlexTrip; +import org.opentripplanner.framework.logging.ProgressTracker; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issues.TripDegenerate; import org.opentripplanner.graph_builder.issues.TripUndefinedService; @@ -16,6 +17,7 @@ import org.opentripplanner.model.Frequency; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.impl.OtpTransitServiceBuilder; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -47,7 +49,6 @@ public class GenerateTripPatternsOperation { private final Multimap tripPatterns; private final ListMultimap frequenciesForTrip = ArrayListMultimap.create(); - private int tripCount = 0; private int freqCount = 0; private int scheduledCount = 0; @@ -70,17 +71,21 @@ public void run() { collectFrequencyByTrip(); final Collection trips = transitDaoBuilder.getTripsById().values(); - final int tripsSize = trips.size(); + var progressLogger = ProgressTracker.track("build trip patterns", 50_000, trips.size()); + LOG.info(progressLogger.startMessage()); /* Loop over all trips, handling each one as a frequency-based or scheduled trip. */ for (Trip trip : trips) { - if (++tripCount % 100000 == 0) { - LOG.debug("build trip patterns {}/{}", tripCount, tripsSize); + try { + buildTripPatternForTrip(trip); + //noinspection Convert2MethodRef + progressLogger.step(m -> LOG.info(m)); + } catch (DataValidationException e) { + issueStore.add(e.error()); } - - buildTripPatternForTrip(trip); } + LOG.info(progressLogger.completeMessage()); LOG.info( "Added {} frequency-based and {} single-trip timetable entries.", freqCount, diff --git a/src/main/java/org/opentripplanner/model/Timetable.java b/src/main/java/org/opentripplanner/model/Timetable.java index 170ed49092d..0bcdb9fd1ab 100644 --- a/src/main/java/org/opentripplanner/model/Timetable.java +++ b/src/main/java/org/opentripplanner/model/Timetable.java @@ -25,6 +25,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.time.ServiceDateUtils; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; @@ -33,7 +34,7 @@ import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.GtfsRealtimeMapper; -import org.opentripplanner.updater.spi.TripTimesValidationMapper; +import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.trip.BackwardsDelayPropagationType; import org.slf4j.Logger; @@ -360,14 +361,10 @@ public Result createUpdatedTripTimesFromGTFSRT( } // Validate for non-increasing times. Log error if present. - var error = newTimes.validateNonIncreasingTimes(); - if (error.isPresent()) { - LOG.debug( - "TripTimes are non-increasing after applying GTFS-RT delay propagation to trip {} after stop index {}.", - tripId, - error.get().stopIndex() - ); - return TripTimesValidationMapper.toResult(newTimes.getTrip().getId(), error.get()); + try { + newTimes.validateNonIncreasingTimes(); + } catch (DataValidationException e) { + return DataValidationExceptionMapper.toResult(e); } if (tripUpdate.hasVehicle()) { diff --git a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java index 675b9eab5c9..a00912264ea 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java @@ -16,6 +16,7 @@ import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; import org.opentripplanner.transit.model.basic.SubMode; import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.EntityById; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -367,12 +368,16 @@ private void createTripTimes(List trips, TripPattern tripPattern) { trip.getId() ); } else { - TripTimes tripTimes = TripTimesFactory.tripTimes( - trip, - result.tripStopTimes.get(trip), - deduplicator - ); - tripPattern.add(tripTimes); + try { + TripTimes tripTimes = TripTimesFactory.tripTimes( + trip, + result.tripStopTimes.get(trip), + deduplicator + ); + tripPattern.add(tripTimes); + } catch (DataValidationException e) { + issueStore.add(e.error()); + } } } } diff --git a/src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java b/src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java new file mode 100644 index 00000000000..d1c985e6db0 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java @@ -0,0 +1,35 @@ +package org.opentripplanner.transit.model.framework; + +import org.opentripplanner.framework.error.OtpError; + +/** + * This class is used to throw a data validation exception. It holds an error witch can be + * inserted into build issue store, into the updater logs or returned to the APIs. The framework + * to catch and handle this is NOT IN PLACE, see + * Error code design #5070. + *

+ * MORE WORK ON THIS IS NEEDED! + */ +public class DataValidationException extends RuntimeException { + + private final OtpError error; + + public DataValidationException(OtpError error) { + super(); + this.error = error; + } + + public OtpError error() { + return error; + } + + @Override + public String getMessage() { + return error.message(); + } + + @Override + public String toString() { + return getMessage(); + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/framework/Result.java b/src/main/java/org/opentripplanner/transit/model/framework/Result.java index e23bdca83ca..621ce7569f2 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/Result.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/Result.java @@ -9,7 +9,22 @@ * A type for containing either a success or a failure type as the result of a computation. *

* It's very similar to the Either or Validation type found in functional programming languages. + * + * @deprecated This not possible to use inside a constructor - can only return one thing. Witch, + * then makes encapsulation harder and leaves the door open to forget. Also, having to create + * error codes, mapping and handling code for hundreds of different possible validation error + * types make changes harder, and cleanup impossible. This is a nice way to design APIs, but + * faced with hundreds or even thousands of different validation error types and the same + * amount of code branches this breaks. + *

+ * I will use the {@link DataValidationException} for now, but we need to make an error + * handling strategy witch take all use-cases and goals into account, also pragmatic goals + * like maintainability. The {@link DataValidationException} is not a solution, but it + * at least it allows me to omit returning all possible error on every method ... + *

+ * See https://github.com/opentripplanner/OpenTripPlanner/issues/5070 */ +@Deprecated public abstract sealed class Result { private Result() {} diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index 310397ba7ba..1f6aecc3ac5 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -1,5 +1,8 @@ package org.opentripplanner.transit.model.timetable; +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_DWELL_TIME; +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_HOP_TIME; + import java.io.Serializable; import java.util.Arrays; import java.util.BitSet; @@ -13,7 +16,9 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.framework.DeduplicatorService; public final class ScheduledTripTimes implements Serializable, Comparable { @@ -78,7 +83,7 @@ public static ScheduledTripTimesBuilder of() { return new ScheduledTripTimesBuilder(null); } - public static ScheduledTripTimesBuilder of(Deduplicator deduplicator) { + public static ScheduledTripTimesBuilder of(DeduplicatorService deduplicator) { return new ScheduledTripTimesBuilder(deduplicator); } @@ -361,16 +366,14 @@ private void validateNonIncreasingTimes() { final int dep = getDepartureTime(i); if (prevDep > arr) { - throw new IllegalArgumentException( - "Negative hop time for stop position " + i + " for " + trip + "." - ); + throw new DataValidationException(new TimetableValidationError(NEGATIVE_HOP_TIME, i, trip)); } if (i == lastStop) { return; } if (dep < arr) { - throw new IllegalArgumentException( - "Negative dwell time for stop position " + i + " for " + trip + "." + throw new DataValidationException( + new TimetableValidationError(NEGATIVE_DWELL_TIME, i, trip) ); } prevDep = dep; diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java index 22bb77b073e..bd21905f7d1 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java @@ -7,7 +7,7 @@ import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.model.StopTime; -import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.framework.DeduplicatorService; class StopTimeToScheduledTripTimesMapper { @@ -16,7 +16,7 @@ class StopTimeToScheduledTripTimesMapper { private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private StopTimeToScheduledTripTimesMapper(Trip trip, Deduplicator deduplicator) { + private StopTimeToScheduledTripTimesMapper(Trip trip, DeduplicatorService deduplicator) { this.trip = trip; this.builder = ScheduledTripTimes.of(deduplicator).withTrip(trip); } @@ -29,7 +29,7 @@ private StopTimeToScheduledTripTimesMapper(Trip trip, Deduplicator deduplicator) public static ScheduledTripTimes map( Trip trip, Collection stopTimes, - Deduplicator deduplicator + DeduplicatorService deduplicator ) { return new StopTimeToScheduledTripTimesMapper(trip, deduplicator).doMap(stopTimes); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TimetableValidationError.java b/src/main/java/org/opentripplanner/transit/model/timetable/TimetableValidationError.java new file mode 100644 index 00000000000..a69fd87e93b --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TimetableValidationError.java @@ -0,0 +1,29 @@ +package org.opentripplanner.transit.model.timetable; + +import org.opentripplanner.framework.error.OtpError; + +/** + * Details about why a {@link TripTimes} instance is invalid. + */ +public record TimetableValidationError(ErrorCode code, int stopIndex, Trip trip) + implements OtpError { + @Override + public String errorCode() { + return code.name(); + } + + @Override + public String messageTemplate() { + return "%s for stop position %d in trip %s."; + } + + @Override + public Object[] messageArguments() { + return new Object[] { code, stopIndex, trip }; + } + + public enum ErrorCode { + NEGATIVE_DWELL_TIME, + NEGATIVE_HOP_TIME, + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java index b4ca4850a18..ecb0df38127 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java @@ -1,19 +1,19 @@ package org.opentripplanner.transit.model.timetable; -import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_DWELL_TIME; -import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_HOP_TIME; +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_DWELL_TIME; +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_HOP_TIME; import java.io.Serializable; import java.time.Duration; import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.OptionalInt; import java.util.function.IntUnaryOperator; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.framework.DataValidationException; /** * A TripTimes represents the arrival and departure times for a single trip in an Timetable. It is @@ -33,13 +33,6 @@ public final class TripTimes implements Serializable, Comparable { private OccupancyStatus[] occupancyStatus; private Accessibility wheelchairAccessibility; - private TripTimes(final TripTimes original, int timeShiftDelta) { - this( - original, - original.scheduledTripTimes.copyOfNoDuplication().plusTimeShift(timeShiftDelta).build() - ); - } - TripTimes(ScheduledTripTimes scheduledTripTimes) { this( scheduledTripTimes, @@ -278,9 +271,9 @@ public void setRealTimeState(final RealTimeState realTimeState) { * checks that all internal times are increasing. Thus, this check should be used at the end of * updating trip times, after any propagating or interpolating delay operations. * - * @return empty if times were found to be increasing, the first validation error otherwise + * @throws org.opentripplanner.transit.model.framework.DataValidationException on the first validation error found. */ - public Optional validateNonIncreasingTimes() { + public void validateNonIncreasingTimes() { final int nStops = arrivalTimes.length; int prevDep = -9_999_999; for (int s = 0; s < nStops; s++) { @@ -288,14 +281,17 @@ public Optional validateNonIncreasingTimes() { final int dep = getDepartureTime(s); if (dep < arr) { - return Optional.of(new ValidationError(NEGATIVE_DWELL_TIME, s)); + throw new DataValidationException( + new TimetableValidationError(NEGATIVE_DWELL_TIME, s, getTrip()) + ); } if (prevDep > arr) { - return Optional.of(new ValidationError(NEGATIVE_HOP_TIME, s)); + throw new DataValidationException( + new TimetableValidationError(NEGATIVE_HOP_TIME, s, getTrip()) + ); } prevDep = dep; } - return Optional.empty(); } /** diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ValidationError.java b/src/main/java/org/opentripplanner/transit/model/timetable/ValidationError.java deleted file mode 100644 index 1e150218dc5..00000000000 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ValidationError.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.opentripplanner.transit.model.timetable; - -/** - * Details about why a {@link TripTimes} instance is invalid. - */ -public record ValidationError(ErrorCode code, int stopIndex) { - public enum ErrorCode { - NEGATIVE_DWELL_TIME, - NEGATIVE_HOP_TIME, - } -} diff --git a/src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java b/src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java new file mode 100644 index 00000000000..dee103bc385 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java @@ -0,0 +1,36 @@ +package org.opentripplanner.updater.spi; + +import org.opentripplanner.transit.model.framework.DataValidationException; +import org.opentripplanner.transit.model.framework.Result; +import org.opentripplanner.transit.model.timetable.TimetableValidationError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Converts a {@link TimetableValidationError} to the model of the updater ready to be consumed + * by the metrics APIS and logs. + */ +public class DataValidationExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(DataValidationExceptionMapper.class); + + public static Result toResult(DataValidationException error) { + if (error.error() instanceof TimetableValidationError tt) { + return Result.failure( + new UpdateError(tt.trip().getId(), mapTimeTableError(tt.code()), tt.stopIndex()) + ); + } + // The mapper should handle all possible errors + LOG.error("Unhandled error: " + error.getMessage(), error); + return Result.failure(UpdateError.noTripId(UpdateError.UpdateErrorType.UNKNOWN)); + } + + private static UpdateError.UpdateErrorType mapTimeTableError( + TimetableValidationError.ErrorCode code + ) { + return switch (code) { + case NEGATIVE_DWELL_TIME -> UpdateError.UpdateErrorType.NEGATIVE_DWELL_TIME; + case NEGATIVE_HOP_TIME -> UpdateError.UpdateErrorType.NEGATIVE_HOP_TIME; + }; + } +} diff --git a/src/main/java/org/opentripplanner/updater/spi/TripTimesValidationMapper.java b/src/main/java/org/opentripplanner/updater/spi/TripTimesValidationMapper.java deleted file mode 100644 index 127eddeabeb..00000000000 --- a/src/main/java/org/opentripplanner/updater/spi/TripTimesValidationMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.opentripplanner.updater.spi; - -import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.framework.Result; -import org.opentripplanner.transit.model.timetable.ValidationError; - -/** - * Converts a {@link ValidationError} to the model of the updater ready to be consumed - * by the metrics APIS and logs. - */ -public class TripTimesValidationMapper { - - public static Result toResult( - FeedScopedId tripId, - ValidationError validationError - ) { - var type = - switch (validationError.code()) { - case NEGATIVE_DWELL_TIME -> UpdateError.UpdateErrorType.NEGATIVE_DWELL_TIME; - case NEGATIVE_HOP_TIME -> UpdateError.UpdateErrorType.NEGATIVE_HOP_TIME; - }; - - return Result.failure(new UpdateError(tripId, type, validationError.stopIndex())); - } -} diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 6cda95eb983..8e782ebe934 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -44,6 +44,7 @@ import org.opentripplanner.model.TimetableSnapshotProvider; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; @@ -62,6 +63,7 @@ import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; import org.opentripplanner.updater.GtfsRealtimeMapper; import org.opentripplanner.updater.TimetableSnapshotSourceParameters; +import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.ResultLogger; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.spi.UpdateResult; @@ -279,31 +281,36 @@ public UpdateResult applyTripUpdates( tripDescriptor ); - Result result = - switch (tripScheduleRelationship) { - case SCHEDULED -> handleScheduledTrip( - tripUpdate, - tripId, - serviceDate, - backwardsDelayPropagationType - ); - case ADDED -> validateAndHandleAddedTrip( - tripUpdate, - tripDescriptor, - tripId, - serviceDate - ); - case CANCELED -> handleCanceledTrip(tripId, serviceDate, CancelationType.CANCEL); - case DELETED -> handleCanceledTrip(tripId, serviceDate, CancelationType.DELETE); - case REPLACEMENT -> validateAndHandleModifiedTrip( - tripUpdate, - tripDescriptor, - tripId, - serviceDate - ); - case UNSCHEDULED -> UpdateError.result(tripId, NOT_IMPLEMENTED_UNSCHEDULED); - case DUPLICATED -> UpdateError.result(tripId, NOT_IMPLEMENTED_DUPLICATED); - }; + Result result; + try { + result = + switch (tripScheduleRelationship) { + case SCHEDULED -> handleScheduledTrip( + tripUpdate, + tripId, + serviceDate, + backwardsDelayPropagationType + ); + case ADDED -> validateAndHandleAddedTrip( + tripUpdate, + tripDescriptor, + tripId, + serviceDate + ); + case CANCELED -> handleCanceledTrip(tripId, serviceDate, CancelationType.CANCEL); + case DELETED -> handleCanceledTrip(tripId, serviceDate, CancelationType.DELETE); + case REPLACEMENT -> validateAndHandleModifiedTrip( + tripUpdate, + tripDescriptor, + tripId, + serviceDate + ); + case UNSCHEDULED -> UpdateError.result(tripId, NOT_IMPLEMENTED_UNSCHEDULED); + case DUPLICATED -> UpdateError.result(tripId, NOT_IMPLEMENTED_DUPLICATED); + }; + } catch (DataValidationException e) { + result = DataValidationExceptionMapper.toResult(e); + } results.add(result); if (result.isFailure()) { @@ -708,7 +715,6 @@ private Result handleAddedTrip( // Just use first service id of set tripBuilder.withServiceId(serviceIds.iterator().next()); } - return addTripToGraphAndBuffer( tripBuilder.build(), tripUpdate.getVehicle(), diff --git a/src/test/java/org/opentripplanner/OtpArchitectureModules.java b/src/test/java/org/opentripplanner/OtpArchitectureModules.java index e4d08427c71..72520acaa1c 100644 --- a/src/test/java/org/opentripplanner/OtpArchitectureModules.java +++ b/src/test/java/org/opentripplanner/OtpArchitectureModules.java @@ -39,6 +39,7 @@ public interface OtpArchitectureModules { */ Module FRAMEWORK_UTILS = Module.of( FRAMEWORK.subPackage("application"), + FRAMEWORK.subPackage("error"), FRAMEWORK.subPackage("i18n"), FRAMEWORK.subPackage("lang"), FRAMEWORK.subPackage("logging"), diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/TimetableValidationErrorTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/TimetableValidationErrorTest.java new file mode 100644 index 00000000000..7dd67665e92 --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/timetable/TimetableValidationErrorTest.java @@ -0,0 +1,24 @@ +package org.opentripplanner.transit.model.timetable; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.basic.TransitMode; + +class TimetableValidationErrorTest { + + private TimetableValidationError subject = new TimetableValidationError( + TimetableValidationError.ErrorCode.NEGATIVE_HOP_TIME, + 3, + TransitModelForTest.trip("A").withMode(TransitMode.BUS).withShortName("Line A").build() + ); + + @Test + void message() { + assertEquals( + "NEGATIVE_HOP_TIME for stop position 3 in trip Trip{F:A Line A}.", + subject.message() + ); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java index af37c812667..ee73bd6856c 100644 --- a/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java +++ b/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java @@ -3,10 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.transit.model._data.TransitModelForTest.id; -import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_DWELL_TIME; -import static org.opentripplanner.transit.model.timetable.ValidationError.ErrorCode.NEGATIVE_HOP_TIME; +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_DWELL_TIME; +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_HOP_TIME; import java.util.LinkedList; import java.util.List; @@ -16,6 +17,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.model.StopTime; import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.RegularStop; @@ -155,32 +157,6 @@ public void testPassedUpdate() { assertEquals(60, updatedTripTimesA.getArrivalTime(1)); } - @Test - public void testNegativeDwellTime() { - TripTimes updatedTripTimesA = createInitialTripTimes().copyOfScheduledTimes(); - - updatedTripTimesA.updateArrivalTime(1, 60); - updatedTripTimesA.updateDepartureTime(1, 59); - - var error = updatedTripTimesA.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(1, error.get().stopIndex()); - assertEquals(NEGATIVE_DWELL_TIME, error.get().code()); - } - - @Test - public void testNegativeHopTime() { - TripTimes updatedTripTimesB = createInitialTripTimes().copyOfScheduledTimes(); - - updatedTripTimesB.updateDepartureTime(6, 421); - updatedTripTimesB.updateArrivalTime(7, 420); - - var error = updatedTripTimesB.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(7, error.get().stopIndex()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); - } - /** * Test negative hop time with stop cancellations. * Scheduled: 5 at 300, 6 at 360, 7 at 420 @@ -199,14 +175,14 @@ public void testNegativeHopTimeWithStopCancellations() { updatedTripTimes.setCancelled(6); updatedTripTimes.updateArrivalTime(7, 420); - var error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); - - assertTrue(updatedTripTimes.interpolateMissingTimes()); - error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); + var error = assertThrows( + DataValidationException.class, + updatedTripTimes::validateNonIncreasingTimes + ); + assertEquals( + "NEGATIVE_HOP_TIME for stop position 7 in trip Trip{F:testTripId RRtestTripId}.", + error.error().message() + ); } /** @@ -226,13 +202,18 @@ public void testPositiveHopTimeWithStopCancellationsLate() { updatedTripTimes.setCancelled(6); updatedTripTimes.updateArrivalTime(7, 420); - var error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); + var error = assertThrows( + DataValidationException.class, + updatedTripTimes::validateNonIncreasingTimes + ); + assertEquals( + "NEGATIVE_HOP_TIME for stop position 7 in trip Trip{F:testTripId RRtestTripId}.", + error.error().message() + ); assertTrue(updatedTripTimes.interpolateMissingTimes()); - error = updatedTripTimes.validateNonIncreasingTimes(); - assertFalse(error.isPresent()); + + updatedTripTimes.validateNonIncreasingTimes(); } /** @@ -250,13 +231,17 @@ public void testPositiveHopTimeWithStopCancellationsEarly() { updatedTripTimes.setCancelled(6); updatedTripTimes.updateArrivalTime(7, 320); - var error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); + var error = assertThrows( + DataValidationException.class, + updatedTripTimes::validateNonIncreasingTimes + ); + assertEquals( + "NEGATIVE_HOP_TIME for stop position 7 in trip Trip{F:testTripId RRtestTripId}.", + error.error().message() + ); assertTrue(updatedTripTimes.interpolateMissingTimes()); - error = updatedTripTimes.validateNonIncreasingTimes(); - assertFalse(error.isPresent()); + updatedTripTimes.validateNonIncreasingTimes(); } /** @@ -276,14 +261,22 @@ public void testPositiveHopTimeWithTerminalCancellation() { updatedTripTimes.updateArrivalTime(2, 0); updatedTripTimes.updateDepartureTime(2, 10); - var error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); + var error = assertThrows( + DataValidationException.class, + updatedTripTimes::validateNonIncreasingTimes + ); + assertEquals( + "NEGATIVE_HOP_TIME for stop position 2 in trip Trip{F:testTripId RRtestTripId}.", + error.error().message() + ); assertFalse(updatedTripTimes.interpolateMissingTimes()); - error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); + error = + assertThrows(DataValidationException.class, updatedTripTimes::validateNonIncreasingTimes); + assertEquals( + "NEGATIVE_HOP_TIME for stop position 2 in trip Trip{F:testTripId RRtestTripId}.", + error.error().message() + ); } /** @@ -301,8 +294,8 @@ public void testInterpolationWithTerminalCancellation() { updatedTripTimes.setCancelled(7); assertFalse(updatedTripTimes.interpolateMissingTimes()); - var error = updatedTripTimes.validateNonIncreasingTimes(); - assertFalse(error.isPresent()); + + updatedTripTimes.validateNonIncreasingTimes(); } /** @@ -325,13 +318,18 @@ public void testInterpolationWithMultipleStopCancellations() { updatedTripTimes.updateArrivalTime(7, 350); updatedTripTimes.updateDepartureTime(7, 350); - var error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); + var error = assertThrows( + DataValidationException.class, + updatedTripTimes::validateNonIncreasingTimes + ); + assertEquals( + "NEGATIVE_HOP_TIME for stop position 7 in trip Trip{F:testTripId RRtestTripId}.", + error.error().message() + ); assertTrue(updatedTripTimes.interpolateMissingTimes()); - error = updatedTripTimes.validateNonIncreasingTimes(); - assertFalse(error.isPresent()); + + updatedTripTimes.validateNonIncreasingTimes(); } /** @@ -355,13 +353,17 @@ public void testInterpolationWithMultipleStopCancellations2() { updatedTripTimes.updateArrivalTime(7, 240); updatedTripTimes.updateDepartureTime(7, 240); - var error = updatedTripTimes.validateNonIncreasingTimes(); - assertTrue(error.isPresent()); - assertEquals(NEGATIVE_HOP_TIME, error.get().code()); + var error = assertThrows( + DataValidationException.class, + updatedTripTimes::validateNonIncreasingTimes + ); + assertEquals( + "NEGATIVE_HOP_TIME for stop position 3 in trip Trip{F:testTripId RRtestTripId}.", + error.error().message() + ); assertTrue(updatedTripTimes.interpolateMissingTimes()); - error = updatedTripTimes.validateNonIncreasingTimes(); - assertFalse(error.isPresent()); + updatedTripTimes.validateNonIncreasingTimes(); } @Test @@ -371,7 +373,7 @@ public void testNonIncreasingUpdateCrossingMidnight() { updatedTripTimesA.updateArrivalTime(0, -300); //"Yesterday" updatedTripTimesA.updateDepartureTime(0, 50); - assertTrue(updatedTripTimesA.validateNonIncreasingTimes().isEmpty()); + updatedTripTimesA.validateNonIncreasingTimes(); } @Test @@ -425,28 +427,37 @@ void unknownGtfsSequence() { @Test public void validateNegativeDwellTime() { + var expMsg = "NEGATIVE_DWELL_TIME for stop position 3 in trip Trip{F:testTripId RRtestTripId}."; var tt = createInitialTripTimes(); var updatedTt = tt.copyOfScheduledTimes(); - updatedTt.updateArrivalTime(2, 69); - updatedTt.updateDepartureTime(2, 68); + updatedTt.updateArrivalTime(3, 69); + updatedTt.updateDepartureTime(3, 68); + + var ex = assertThrows(DataValidationException.class, updatedTt::validateNonIncreasingTimes); + var error = (TimetableValidationError) ex.error(); - var validationResult = updatedTt.validateNonIncreasingTimes(); - assertTrue(validationResult.isPresent()); - assertEquals(2, validationResult.get().stopIndex()); - assertEquals(NEGATIVE_DWELL_TIME, validationResult.get().code()); + assertEquals(3, error.stopIndex()); + assertEquals(NEGATIVE_DWELL_TIME, error.code()); + assertEquals(expMsg, error.message()); + assertEquals(expMsg, ex.getMessage()); } @Test public void validateNegativeHopTime() { + var expMsg = "NEGATIVE_HOP_TIME for stop position 2 in trip Trip{F:testTripId RRtestTripId}."; var tt = createInitialTripTimes(); var updatedTt = tt.copyOfScheduledTimes(); - updatedTt.updateArrivalTime(2, 59); + updatedTt.updateDepartureTime(1, 100); + updatedTt.updateArrivalTime(2, 99); + + var ex = assertThrows(DataValidationException.class, updatedTt::validateNonIncreasingTimes); + var error = (TimetableValidationError) ex.error(); - var validationResult = updatedTt.validateNonIncreasingTimes(); - assertTrue(validationResult.isPresent()); - assertEquals(2, validationResult.get().stopIndex()); - assertEquals(NEGATIVE_HOP_TIME, validationResult.get().code()); + assertEquals(2, error.stopIndex()); + assertEquals(NEGATIVE_HOP_TIME, error.code()); + assertEquals(expMsg, error.message()); + assertEquals(expMsg, ex.getMessage()); } } From ebbe2fd4798d0fe65adc34439ea5aa92200b224c Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 16 Nov 2023 20:33:23 +0100 Subject: [PATCH 04/39] refactor: Extract TripTimes interface. This creates a new interface TripTimes and rename the old class to RealTimeTripTimes and let the ScheduledTripTimes also implement this interface. This will allow us to wait for an update before an accusal RealTime object is created. --- .../RealtimeResolverTest.java | 2 +- .../ext/siri/TimetableHelperTest.java | 4 +- .../ext/siri/AddedTripBuilder.java | 6 +- .../ext/siri/ModifiedTripBuilder.java | 5 +- .../ext/siri/SiriTimetableSnapshotSource.java | 3 +- .../ext/siri/TimetableHelper.java | 4 +- .../ext/siri/TripTimesAndStopPattern.java | 6 - .../org/opentripplanner/model/Timetable.java | 7 +- .../opentripplanner/model/TripTimesPatch.java | 7 +- .../netex/mapping/TripPatternMapper.java | 10 +- .../model/timetable/FrequencyEntry.java | 8 +- .../model/timetable/RealTimeTripTimes.java | 584 +++++++++++++++++ .../model/timetable/ScheduledTripTimes.java | 141 ++-- .../transit/model/timetable/TripTimes.java | 600 +++--------------- .../model/timetable/TripTimesFactory.java | 6 +- .../updater/trip/TimetableSnapshotSource.java | 16 +- ...rRoutingRequestTransitDataCreatorTest.java | 12 +- ...eRequestTransitDataProviderFilterTest.java | 7 +- .../stoptimes/StopTimesHelperTest.java | 4 +- ...esTest.java => RealTimeTripTimesTest.java} | 32 +- 20 files changed, 794 insertions(+), 670 deletions(-) delete mode 100644 src/ext/java/org/opentripplanner/ext/siri/TripTimesAndStopPattern.java create mode 100644 src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java rename src/test/java/org/opentripplanner/transit/model/timetable/{TripTimesTest.java => RealTimeTripTimesTest.java} (93%) diff --git a/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java b/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java index 43be9c6d735..af415b5e883 100644 --- a/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java @@ -137,7 +137,7 @@ private static TripPattern delay(TripPattern pattern1, int seconds) { } private static TripTimes delay(TripTimes tt, int seconds) { - var delayed = tt.copyOfScheduledTimes(); + var delayed = tt.copyScheduledTimes(); IntStream .range(0, delayed.getNumStops()) .forEach(i -> { diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/TimetableHelperTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/TimetableHelperTest.java index 97bd3be2398..dde2677a952 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/TimetableHelperTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/TimetableHelperTest.java @@ -19,8 +19,8 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.timetable.OccupancyStatus; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.StopModel; import uk.org.siri.siri20.OccupancyEnumeration; @@ -46,7 +46,7 @@ public class TimetableHelperTest { ZoneIds.CET ); - private TripTimes tripTimes; + private RealTimeTripTimes tripTimes; @BeforeEach public void setUp() { diff --git a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java index 74d84ee397e..cb49c6ea50e 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java @@ -28,8 +28,8 @@ import org.opentripplanner.transit.model.organization.Operator; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.spi.DataValidationExceptionMapper; @@ -203,14 +203,14 @@ Result build() { .withStopPattern(stopPattern) .build(); - TripTimes tripTimes = TripTimesFactory.tripTimes( + RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes( trip, aimedStopTimes, transitModel.getDeduplicator() ); tripTimes.setServiceCode(transitModel.getServiceCodes().get(trip.getServiceId())); pattern.add(tripTimes); - TripTimes updatedTripTimes = tripTimes.copyOfScheduledTimes(); + RealTimeTripTimes updatedTripTimes = tripTimes.copyScheduledTimes(); // Loop through calls again and apply updates for (int stopSequence = 0; stopSequence < calls.size(); stopSequence++) { diff --git a/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java index ce21823b9b3..56cc28d82f4 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java @@ -19,6 +19,7 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.UpdateError; @@ -94,7 +95,7 @@ public ModifiedTripBuilder( * in form the SIRI-ET update. */ public Result build() { - TripTimes newTimes = existingTripTimes.copyOfScheduledTimes(); + RealTimeTripTimes newTimes = existingTripTimes.copyScheduledTimes(); StopPattern stopPattern = createStopPattern(pattern, calls, entityResolver); @@ -145,7 +146,7 @@ public Result build() { /** * Applies real-time updates from the calls into newTimes. */ - private void applyUpdates(TripTimes newTimes) { + private void applyUpdates(RealTimeTripTimes newTimes) { ZonedDateTime startOfService = ServiceDateUtils.asStartOfService(serviceDate, zoneId); Set alreadyVisited = new HashSet<>(); diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index e6efb3ec3d9..0dd935361d6 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -21,6 +21,7 @@ import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.DefaultTransitService; @@ -399,7 +400,7 @@ private boolean markScheduledTripAsDeleted(Trip trip, final LocalDate serviceDat if (tripTimes == null) { LOG.warn("Could not mark scheduled trip as deleted {}", trip.getId()); } else { - final TripTimes newTripTimes = tripTimes.copyOfScheduledTimes(); + final RealTimeTripTimes newTripTimes = tripTimes.copyScheduledTimes(); newTripTimes.deleteTrip(); buffer.update(pattern, newTripTimes, serviceDate); success = true; diff --git a/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java b/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java index ade320db298..e7063730e87 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java +++ b/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java @@ -7,7 +7,7 @@ import org.opentripplanner.ext.siri.mapper.OccupancyMapper; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.time.ServiceDateUtils; -import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import uk.org.siri.siri20.NaturalLanguageStringStructure; import uk.org.siri.siri20.OccupancyEnumeration; @@ -52,7 +52,7 @@ private static int handleMissingRealtime(int... times) { public static void applyUpdates( ZonedDateTime departureDate, - TripTimes tripTimes, + RealTimeTripTimes tripTimes, int index, boolean isLastStop, boolean isJourneyPredictionInaccurate, diff --git a/src/ext/java/org/opentripplanner/ext/siri/TripTimesAndStopPattern.java b/src/ext/java/org/opentripplanner/ext/siri/TripTimesAndStopPattern.java deleted file mode 100644 index 70d9e034188..00000000000 --- a/src/ext/java/org/opentripplanner/ext/siri/TripTimesAndStopPattern.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.opentripplanner.ext.siri; - -import org.opentripplanner.transit.model.network.StopPattern; -import org.opentripplanner.transit.model.timetable.TripTimes; - -public record TripTimesAndStopPattern(TripTimes times, StopPattern pattern) {} diff --git a/src/main/java/org/opentripplanner/model/Timetable.java b/src/main/java/org/opentripplanner/model/Timetable.java index 0bcdb9fd1ab..a8f0f1bbf44 100644 --- a/src/main/java/org/opentripplanner/model/Timetable.java +++ b/src/main/java/org/opentripplanner/model/Timetable.java @@ -31,6 +31,7 @@ import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.Direction; import org.opentripplanner.transit.model.timetable.FrequencyEntry; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.GtfsRealtimeMapper; @@ -204,7 +205,7 @@ public Result createUpdatedTripTimesFromGTFSRT( LOG.trace("tripId {} found at index {} in timetable.", tripId, tripIndex); } - TripTimes newTimes = getTripTimes(tripIndex).copyOfScheduledTimes(); + RealTimeTripTimes newTimes = getTripTimes(tripIndex).copyScheduledTimes(); List skippedStopIndices = new ArrayList<>(); // The GTFS-RT reference specifies that StopTimeUpdates are sorted by stop_sequence. @@ -429,12 +430,12 @@ public boolean isValidFor(LocalDate serviceDate) { // TODO maybe put this is a more appropriate place public void setServiceCodes(Map serviceCodes) { for (TripTimes tt : this.tripTimes) { - tt.setServiceCode(serviceCodes.get(tt.getTrip().getServiceId())); + ((RealTimeTripTimes) tt).setServiceCode(serviceCodes.get(tt.getTrip().getServiceId())); } // Repeated code... bad sign... for (FrequencyEntry freq : this.frequencyEntries) { TripTimes tt = freq.tripTimes; - tt.setServiceCode(serviceCodes.get(tt.getTrip().getServiceId())); + ((RealTimeTripTimes) tt).setServiceCode(serviceCodes.get(tt.getTrip().getServiceId())); } } diff --git a/src/main/java/org/opentripplanner/model/TripTimesPatch.java b/src/main/java/org/opentripplanner/model/TripTimesPatch.java index f804a502d51..25afaf81eae 100644 --- a/src/main/java/org/opentripplanner/model/TripTimesPatch.java +++ b/src/main/java/org/opentripplanner/model/TripTimesPatch.java @@ -1,6 +1,7 @@ package org.opentripplanner.model; import java.util.List; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.TripTimes; /** @@ -9,15 +10,15 @@ */ public class TripTimesPatch { - private final TripTimes tripTimes; + private final RealTimeTripTimes tripTimes; private final List skippedStopIndices; - public TripTimesPatch(TripTimes tripTimes, List skippedStopIndices) { + public TripTimesPatch(RealTimeTripTimes tripTimes, List skippedStopIndices) { this.tripTimes = tripTimes; this.skippedStopIndices = skippedStopIndices; } - public TripTimes getTripTimes() { + public RealTimeTripTimes getTripTimes() { return this.tripTimes; } diff --git a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java index a00912264ea..4babac12e03 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java @@ -11,6 +11,7 @@ import java.util.Objects; import java.util.stream.Collectors; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.model.StopTime; import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMap; import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMapById; import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; @@ -361,7 +362,8 @@ private org.opentripplanner.transit.model.network.Route lookupRoute( private void createTripTimes(List trips, TripPattern tripPattern) { for (Trip trip : trips) { - if (result.tripStopTimes.get(trip).size() == 0) { + List stopTimes = result.tripStopTimes.get(trip); + if (stopTimes.size() == 0) { issueStore.add( "TripWithoutTripTimes", "Trip %s does not contain any trip times.", @@ -369,11 +371,7 @@ private void createTripTimes(List trips, TripPattern tripPattern) { ); } else { try { - TripTimes tripTimes = TripTimesFactory.tripTimes( - trip, - result.tripStopTimes.get(trip), - deduplicator - ); + TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, deduplicator); tripPattern.add(tripTimes); } catch (DataValidationException e) { issueStore.add(e.error()); diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/FrequencyEntry.java b/src/main/java/org/opentripplanner/transit/model/timetable/FrequencyEntry.java index fc34b08e0b2..61658f6b3e0 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/FrequencyEntry.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/FrequencyEntry.java @@ -6,6 +6,8 @@ /** * Uses a TripTimes to represent multiple trips following the same template at regular intervals. * (see GTFS frequencies.txt) + *

+ * Refactor this to inherit {@link TripTimes} */ public class FrequencyEntry implements Serializable { @@ -116,11 +118,13 @@ public int prevArrivalTime(int stop, int t) { * Returns a disposable TripTimes for this frequency entry in which the vehicle passes the given * stop index (not stop sequence number) at the given time. This allows us to separate the * departure/arrival search process from actually instantiating a TripTimes, to avoid making too - * many short-lived clones. This delegation is a sign that maybe FrequencyEntry should subclass + * many short-lived clones. This delegation is a sign that maybe FrequencyEntry should implement * TripTimes. */ public TripTimes materialize(int stop, int time, boolean depart) { - return tripTimes.timeShift(stop, time, depart); + // TODO: The cast should be changed, the internal type should be scheduledTripTimes and the + // shift method should partially be inlined here + return ((RealTimeTripTimes) tripTimes).timeShift(stop, time, depart); } /** Used in debugging / dumping times. */ diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java new file mode 100644 index 00000000000..a62da95e79a --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java @@ -0,0 +1,584 @@ +package org.opentripplanner.transit.model.timetable; + +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_DWELL_TIME; +import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_HOP_TIME; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.OptionalInt; +import java.util.function.IntUnaryOperator; +import javax.annotation.Nullable; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.model.BookingInfo; +import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.framework.DataValidationException; + +/** + * A TripTimes represents the arrival and departure times for a single trip in an Timetable. It is + * carried along by States when routing to ensure that they have a consistent, fast view of the trip + * when realtime updates have been applied. All times are expressed as seconds since midnight (as in + * GTFS). + */ +public final class RealTimeTripTimes implements TripTimes { + + private ScheduledTripTimes scheduledTripTimes; + + private int[] arrivalTimes; + private int[] departureTimes; + private RealTimeState realTimeState; + private StopRealTimeState[] stopRealTimeStates; + private I18NString[] headsigns; + private OccupancyStatus[] occupancyStatus; + private Accessibility wheelchairAccessibility; + + RealTimeTripTimes(ScheduledTripTimes scheduledTripTimes) { + this( + scheduledTripTimes, + scheduledTripTimes.getRealTimeState(), + null, + null, + null, + scheduledTripTimes.getWheelchairAccessibility() + ); + } + + private RealTimeTripTimes(RealTimeTripTimes original, ScheduledTripTimes scheduledTripTimes) { + this( + scheduledTripTimes, + original.realTimeState, + original.stopRealTimeStates, + original.headsigns, + original.occupancyStatus, + original.wheelchairAccessibility + ); + } + + private RealTimeTripTimes( + ScheduledTripTimes scheduledTripTimes, + RealTimeState realTimeState, + StopRealTimeState[] stopRealTimeStates, + I18NString[] headsigns, + OccupancyStatus[] occupancyStatus, + Accessibility wheelchairAccessibility + ) { + this.scheduledTripTimes = scheduledTripTimes; + this.realTimeState = realTimeState; + this.stopRealTimeStates = stopRealTimeStates; + this.headsigns = headsigns; + this.occupancyStatus = occupancyStatus; + this.wheelchairAccessibility = wheelchairAccessibility; + + // We set these to null to indicate that this is a non-updated/scheduled TripTimes. + // We cannot point to the scheduled times because we do not want to make an unnecessary copy. + this.arrivalTimes = null; + this.departureTimes = null; + } + + public static RealTimeTripTimes of(ScheduledTripTimes scheduledTripTimes) { + return new RealTimeTripTimes(scheduledTripTimes); + } + + @Override + public RealTimeTripTimes copyScheduledTimes() { + return new RealTimeTripTimes(this, scheduledTripTimes); + } + + /** + * Both trip_headsign and stop_headsign (per stop on a particular trip) are optional GTFS fields. + * A trip may not have a headsign, in which case we should fall back on a Timetable or + * Pattern-level headsign. Such a string will be available when we give TripPatterns or + * StopPatterns unique human readable route variant names, but a TripTimes currently does not have + * a pointer to its enclosing timetable or pattern. + */ + @Nullable + public I18NString getHeadsign(final int stop) { + return (headsigns != null && headsigns[stop] != null) + ? headsigns[stop] + : scheduledTripTimes.getHeadsign(stop); + } + + /** + * Return list of via names per particular stop. This field provides info about intermediate stops + * between current stop and final trip destination. Mapped from NeTEx DestinationDisplay.vias. No + * GTFS mapping at the moment. + * + * @return Empty list if there are no vias registered for a stop. + */ + @Override + public List getHeadsignVias(final int stop) { + return scheduledTripTimes.getHeadsignVias(stop); + } + + /** + * @return the whole trip's headsign. Individual stops can have different headsigns. + */ + @Override + public I18NString getTripHeadsign() { + return scheduledTripTimes.getTripHeadsign(); + } + + /** + * The time in seconds after midnight at which the vehicle should arrive at the given stop + * according to the original schedule. + */ + @Override + public int getScheduledArrivalTime(final int stop) { + return scheduledTripTimes.getScheduledArrivalTime(stop); + } + + /** + * The time in seconds after midnight at which the vehicle should leave the given stop according + * to the original schedule. + */ + @Override + public int getScheduledDepartureTime(final int stop) { + return scheduledTripTimes.getScheduledDepartureTime(stop); + } + + /** + * The time in seconds after midnight at which the vehicle arrives at each stop, accounting for + * any real-time updates. + */ + @Override + public int getArrivalTime(final int stop) { + return getOrElse(stop, arrivalTimes, scheduledTripTimes::getScheduledArrivalTime); + } + + /** + * The time in seconds after midnight at which the vehicle leaves each stop, accounting for any + * real-time updates. + */ + @Override + public int getDepartureTime(final int stop) { + return getOrElse(stop, departureTimes, scheduledTripTimes::getScheduledDepartureTime); + } + + /** @return the difference between the scheduled and actual arrival times at this stop. */ + @Override + public int getArrivalDelay(final int stop) { + return getArrivalTime(stop) - scheduledTripTimes.getScheduledArrivalTime(stop); + } + + /** @return the difference between the scheduled and actual departure times at this stop. */ + @Override + public int getDepartureDelay(final int stop) { + return getDepartureTime(stop) - scheduledTripTimes.getScheduledDepartureTime(stop); + } + + public void setRecorded(int stop) { + setStopRealTimeStates(stop, StopRealTimeState.RECORDED); + } + + public void setCancelled(int stop) { + setStopRealTimeStates(stop, StopRealTimeState.CANCELLED); + } + + public void setNoData(int stop) { + setStopRealTimeStates(stop, StopRealTimeState.NO_DATA); + } + + public void setPredictionInaccurate(int stop) { + setStopRealTimeStates(stop, StopRealTimeState.INACCURATE_PREDICTIONS); + } + + public boolean isCancelledStop(int stop) { + return isStopRealTimeStates(stop, StopRealTimeState.CANCELLED); + } + + public boolean isRecordedStop(int stop) { + return isStopRealTimeStates(stop, StopRealTimeState.RECORDED); + } + + public boolean isNoDataStop(int stop) { + return isStopRealTimeStates(stop, StopRealTimeState.NO_DATA); + } + + public boolean isPredictionInaccurate(int stop) { + return isStopRealTimeStates(stop, StopRealTimeState.INACCURATE_PREDICTIONS); + } + + public void setOccupancyStatus(int stop, OccupancyStatus occupancyStatus) { + prepareForRealTimeUpdates(); + this.occupancyStatus[stop] = occupancyStatus; + } + + /** + * This is only for API-purposes (does not affect routing). + */ + @Override + public OccupancyStatus getOccupancyStatus(int stop) { + if (this.occupancyStatus == null) { + return OccupancyStatus.NO_DATA_AVAILABLE; + } + return this.occupancyStatus[stop]; + } + + @Override + public BookingInfo getDropOffBookingInfo(int stop) { + return scheduledTripTimes.getDropOffBookingInfo(stop); + } + + @Override + public BookingInfo getPickupBookingInfo(int stop) { + return scheduledTripTimes.getPickupBookingInfo(stop); + } + + @Override + public boolean isScheduled() { + return realTimeState == RealTimeState.SCHEDULED; + } + + @Override + public boolean isCanceledOrDeleted() { + return isCanceled() || isDeleted(); + } + + @Override + public boolean isCanceled() { + return realTimeState == RealTimeState.CANCELED; + } + + @Override + public boolean isDeleted() { + return realTimeState == RealTimeState.DELETED; + } + + @Override + public RealTimeState getRealTimeState() { + return realTimeState; + } + + public void setRealTimeState(final RealTimeState realTimeState) { + this.realTimeState = realTimeState; + } + + /** + * When creating a scheduled TripTimes or wrapping it in updates, we could potentially imply + * negative running or dwell times. We really don't want those being used in routing. This method + * checks that all internal times are increasing. Thus, this check should be used at the end of + * updating trip times, after any propagating or interpolating delay operations. + * + * @throws org.opentripplanner.transit.model.framework.DataValidationException of the first error + * found. + */ + public void validateNonIncreasingTimes() { + final int nStops = arrivalTimes.length; + int prevDep = -9_999_999; + for (int s = 0; s < nStops; s++) { + final int arr = getArrivalTime(s); + final int dep = getDepartureTime(s); + + if (dep < arr) { + throw new DataValidationException( + new TimetableValidationError(NEGATIVE_DWELL_TIME, s, getTrip()) + ); + } + if (prevDep > arr) { + throw new DataValidationException( + new TimetableValidationError(NEGATIVE_HOP_TIME, s, getTrip()) + ); + } + prevDep = dep; + } + } + + /** Cancel this entire trip */ + public void cancelTrip() { + realTimeState = RealTimeState.CANCELED; + } + + /** Soft delete the entire trip */ + public void deleteTrip() { + realTimeState = RealTimeState.DELETED; + } + + public void updateDepartureTime(final int stop, final int time) { + prepareForRealTimeUpdates(); + departureTimes[stop] = time; + } + + public void updateDepartureDelay(final int stop, final int delay) { + prepareForRealTimeUpdates(); + departureTimes[stop] = scheduledTripTimes.getScheduledDepartureTime(stop) + delay; + } + + public void updateArrivalTime(final int stop, final int time) { + prepareForRealTimeUpdates(); + arrivalTimes[stop] = time; + } + + public void updateArrivalDelay(final int stop, final int delay) { + prepareForRealTimeUpdates(); + arrivalTimes[stop] = scheduledTripTimes.getScheduledArrivalTime(stop) + delay; + } + + @Nullable + public Accessibility getWheelchairAccessibility() { + // No need to fall back to scheduled state, since it is copied over in the constructor + return wheelchairAccessibility; + } + + public void updateWheelchairAccessibility(Accessibility wheelchairAccessibility) { + this.wheelchairAccessibility = wheelchairAccessibility; + } + + public int getNumStops() { + return scheduledTripTimes.getNumStops(); + } + + /** + * Returns a time-shifted copy of this TripTimes in which the vehicle passes the given stop index + * (not stop sequence number) at the given time. We only have a mechanism to shift the scheduled + * stoptimes, not the real-time stoptimes. Therefore, this only works on trips without updates for + * now (frequency trips don't have updates). + */ + public TripTimes timeShift(final int stop, final int time, final boolean depart) { + if (arrivalTimes != null || departureTimes != null) { + return null; + } + // Adjust 0-based times to match desired stoptime. + final int shift = time - (depart ? getDepartureTime(stop) : getArrivalTime(stop)); + + return new RealTimeTripTimes( + this, + scheduledTripTimes.copyOfNoDuplication().plusTimeShift(shift).build() + ); + } + + /** + * Time-shift all times on this trip. This is used when updating the time zone for the trip. + */ + @Override + public TripTimes adjustTimesToGraphTimeZone(Duration shiftDelta) { + return new RealTimeTripTimes( + this, + scheduledTripTimes.copyOfNoDuplication().plusTimeShift((int) shiftDelta.toSeconds()).build() + ); + } + + @Override + public int gtfsSequenceOfStopIndex(final int stop) { + return scheduledTripTimes.gtfsSequenceOfStopIndex(stop); + } + + @Override + public OptionalInt stopIndexOfGtfsSequence(int stopSequence) { + return scheduledTripTimes.stopIndexOfGtfsSequence(stopSequence); + } + + @Override + public boolean isTimepoint(final int stopIndex) { + return scheduledTripTimes.isTimepoint(stopIndex); + } + + @Override + public int getServiceCode() { + return scheduledTripTimes.getServiceCode(); + } + + public void setServiceCode(int serviceCode) { + this.scheduledTripTimes = + scheduledTripTimes.copyOfNoDuplication().withServiceCode(serviceCode).build(); + } + + @Override + public Trip getTrip() { + return scheduledTripTimes.getTrip(); + } + + /** + * Note: This method only applies for GTFS, not SIRI! + * This method interpolates the times for SKIPPED stops in between regular stops since GTFS-RT + * does not require arrival and departure times for these stops. This method ensures the internal + * time representations in OTP for SKIPPED stops are between the regular stop times immediately + * before and after the cancellation in GTFS-RT. This is to meet the OTP requirement that stop + * times should be increasing and to support the trip search flag `includeRealtimeCancellations`. + * Terminal stop cancellations can be handled by backward and forward propagations, and are + * outside the scope of this method. + * + * @return true if there is interpolated times, false if there is no interpolation. + */ + public boolean interpolateMissingTimes() { + boolean hasInterpolatedTimes = false; + final int numStops = getNumStops(); + boolean startInterpolate = false; + boolean hasPrevTimes = false; + int prevDeparture = 0; + int prevScheduledDeparture = 0; + int prevStopIndex = -1; + + // Loop through all stops + for (int s = 0; s < numStops; s++) { + final boolean isCancelledStop = isCancelledStop(s); + final int scheduledArrival = getScheduledArrivalTime(s); + final int scheduledDeparture = getScheduledDepartureTime(s); + final int arrival = getArrivalTime(s); + final int departure = getDepartureTime(s); + + if (!isCancelledStop && !startInterpolate) { + // Regular stop, could be used for interpolation for future cancellation, keep track. + prevDeparture = departure; + prevScheduledDeparture = scheduledDeparture; + prevStopIndex = s; + hasPrevTimes = true; + } else if (isCancelledStop && !startInterpolate && hasPrevTimes) { + // First cancelled stop, keep track. + startInterpolate = true; + } else if (!isCancelledStop && startInterpolate && hasPrevTimes) { + // First regular stop after cancelled stops, interpolate. + // Calculate necessary info for interpolation. + int numCancelledStops = s - prevStopIndex - 1; + int scheduledTravelTime = scheduledArrival - prevScheduledDeparture; + int realTimeTravelTime = arrival - prevDeparture; + double travelTimeRatio = (double) realTimeTravelTime / scheduledTravelTime; + + // Fill out interpolated time for cancelled stops, using the calculated ratio. + for (int cancelledIndex = prevStopIndex + 1; cancelledIndex < s; cancelledIndex++) { + final int scheduledArrivalCancelled = getScheduledArrivalTime(cancelledIndex); + final int scheduledDepartureCancelled = getScheduledDepartureTime(cancelledIndex); + + // Interpolate + int scheduledArrivalDiff = scheduledArrivalCancelled - prevScheduledDeparture; + double interpolatedArrival = prevDeparture + travelTimeRatio * scheduledArrivalDiff; + int scheduledDepartureDiff = scheduledDepartureCancelled - prevScheduledDeparture; + double interpolatedDeparture = prevDeparture + travelTimeRatio * scheduledDepartureDiff; + + // Set Interpolated Times + updateArrivalTime(cancelledIndex, (int) interpolatedArrival); + updateDepartureTime(cancelledIndex, (int) interpolatedDeparture); + } + + // Set tracking variables + prevDeparture = departure; + prevScheduledDeparture = scheduledDeparture; + prevStopIndex = s; + startInterpolate = false; + hasPrevTimes = true; + + // Set return variable + hasInterpolatedTimes = true; + } + } + + return hasInterpolatedTimes; + } + + /** + * Adjusts arrival time for the stop at the firstUpdatedIndex if no update was given for it and + * arrival/departure times for the stops before that stop. Returns {@code true} if times have been + * adjusted. + */ + public boolean adjustTimesBeforeAlways(int firstUpdatedIndex) { + boolean hasAdjustedTimes = false; + int delay = getDepartureDelay(firstUpdatedIndex); + if (getArrivalDelay(firstUpdatedIndex) == 0) { + updateArrivalDelay(firstUpdatedIndex, delay); + hasAdjustedTimes = true; + } + delay = getArrivalDelay(firstUpdatedIndex); + if (delay == 0) { + return false; + } + for (int i = firstUpdatedIndex - 1; i >= 0; i--) { + hasAdjustedTimes = true; + updateDepartureDelay(i, delay); + updateArrivalDelay(i, delay); + } + return hasAdjustedTimes; + } + + /** + * Adjusts arrival and departure times for the stops before the stop at firstUpdatedIndex when + * required to ensure that the times are increasing. Can set NO_DATA flag on the updated previous + * stops. Returns {@code true} if times have been adjusted. + */ + public boolean adjustTimesBeforeWhenRequired(int firstUpdatedIndex, boolean setNoData) { + if (getArrivalTime(firstUpdatedIndex) > getDepartureTime(firstUpdatedIndex)) { + // The given trip update has arrival time after departure time for the first updated stop. + // This method doesn't try to fix issues in the given data, only for the missing part + return false; + } + int nextStopArrivalTime = getArrivalTime(firstUpdatedIndex); + int delay = getArrivalDelay(firstUpdatedIndex); + boolean hasAdjustedTimes = false; + boolean adjustTimes = true; + for (int i = firstUpdatedIndex - 1; i >= 0; i--) { + if (setNoData && !isCancelledStop(i)) { + setNoData(i); + } + if (adjustTimes) { + if (getDepartureTime(i) < nextStopArrivalTime) { + adjustTimes = false; + continue; + } else { + hasAdjustedTimes = true; + updateDepartureDelay(i, delay); + } + if (getArrivalTime(i) < getDepartureTime(i)) { + adjustTimes = false; + } else { + updateArrivalDelay(i, delay); + nextStopArrivalTime = getArrivalTime(i); + } + } + } + return hasAdjustedTimes; + } + + /* private member methods */ + + private void setStopRealTimeStates(int stop, StopRealTimeState state) { + prepareForRealTimeUpdates(); + this.stopRealTimeStates[stop] = state; + } + + /** + * The real-time states for a given stops. If the state is DEFAULT for a stop, + * the {@link #getRealTimeState()} should determine the realtime state of the stop. + *

+ * This is only for API-purposes (does not affect routing). + */ + private boolean isStopRealTimeStates(int stop, StopRealTimeState state) { + return stopRealTimeStates != null && stopRealTimeStates[stop] == state; + } + + public void setHeadsign(int index, I18NString headsign) { + if (headsigns == null) { + if (headsign.equals(getTrip().getHeadsign())) { + return; + } + this.headsigns = scheduledTripTimes.copyHeadsigns(() -> new I18NString[getNumStops()]); + this.headsigns[index] = headsign; + return; + } + + prepareForRealTimeUpdates(); + headsigns[index] = headsign; + } + + private static int getOrElse(int index, int[] array, IntUnaryOperator defaultValue) { + return array != null ? array[index] : defaultValue.applyAsInt(index); + } + + /** + * If they don't already exist, create arrays for updated arrival and departure times that are + * just time-shifted copies of the zero-based scheduled departure times. + *

+ * Also sets the realtime state to UPDATED. + */ + private void prepareForRealTimeUpdates() { + if (arrivalTimes == null) { + this.arrivalTimes = scheduledTripTimes.copyArrivalTimes(); + this.departureTimes = scheduledTripTimes.copyDepartureTimes(); + // Update the real-time state + this.realTimeState = RealTimeState.UPDATED; + this.stopRealTimeStates = new StopRealTimeState[arrivalTimes.length]; + Arrays.fill(stopRealTimeStates, StopRealTimeState.DEFAULT); + this.headsigns = scheduledTripTimes.copyHeadsigns(() -> null); + this.occupancyStatus = new OccupancyStatus[arrivalTimes.length]; + Arrays.fill(occupancyStatus, OccupancyStatus.NO_DATA_AVAILABLE); + // skip immutable types: scheduledTripTimes & wheelchairAccessibility + } + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index 1f6aecc3ac5..0d1efeea7c7 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -3,7 +3,7 @@ import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_DWELL_TIME; import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_HOP_TIME; -import java.io.Serializable; +import java.time.Duration; import java.util.Arrays; import java.util.BitSet; import java.util.List; @@ -20,7 +20,7 @@ import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.DeduplicatorService; -public final class ScheduledTripTimes implements Serializable, Comparable { +public final class ScheduledTripTimes implements TripTimes { /** * When time-shifting from one time-zone to another negative times may occur. @@ -111,132 +111,127 @@ public ScheduledTripTimesBuilder copyOfNoDuplication() { return copyOf(null); } - /** The code for the service on which this trip runs. For departure search optimizations. */ + @Override + public RealTimeTripTimes copyScheduledTimes() { + return RealTimeTripTimes.of(this); + } + + @Override + public TripTimes adjustTimesToGraphTimeZone(Duration shiftDelta) { + return copyOfNoDuplication().plusTimeShift((int) shiftDelta.toSeconds()).build(); + } + + @Override public int getServiceCode() { return serviceCode; } - /** - * The time in seconds after midnight at which the vehicle should arrive at the given stop - * according to the original schedule. - */ + @Override public int getScheduledArrivalTime(final int stop) { return arrivalTimes[stop] + timeShift; } - /** - * The time in seconds after midnight at which the vehicle arrives at each stop, accounting for - * any real-time updates. - */ + @Override public int getArrivalTime(final int stop) { return getScheduledArrivalTime(stop); } - /** @return the difference between the scheduled and actual arrival times at this stop. */ + @Override public int getArrivalDelay(final int stop) { return getArrivalTime(stop) - (arrivalTimes[stop] + timeShift); } - /** - * The time in seconds after midnight at which the vehicle should leave the given stop according - * to the original schedule. - */ + @Override public int getScheduledDepartureTime(final int stop) { return departureTimes[stop] + timeShift; } - /** - * The time in seconds after midnight at which the vehicle leaves each stop, accounting for any - * real-time updates. - */ + @Override public int getDepartureTime(final int stop) { return getScheduledDepartureTime(stop); } - /** @return the difference between the scheduled and actual departure times at this stop. */ + @Override public int getDepartureDelay(final int stop) { return getDepartureTime(stop) - (departureTimes[stop] + timeShift); } - /** - * Whether or not stopIndex is considered a GTFS timepoint. - */ + @Override public boolean isTimepoint(final int stopIndex) { return timepoints.get(stopIndex); } - /** The trips whose arrivals and departures are represented by this class */ + @Override public Trip getTrip() { return trip; } - /** - * Return an integer which can be used to sort TripTimes in order of departure/arrivals. - *

- * This sorted trip times is used to search for trips. OTP assume one trip do NOT pass another - * trip down the line. - */ + @Override public int sortIndex() { return getDepartureTime(0); } + @Override public BookingInfo getDropOffBookingInfo(int stop) { return dropOffBookingInfos.get(stop); } + @Override public BookingInfo getPickupBookingInfo(int stop) { return pickupBookingInfos.get(stop); } - /** - * Return {@code true} if the trip is unmodified, a scheduled trip from a published timetable. - * Return {@code false} if the trip is an updated, cancelled, or otherwise modified one. This - * method differs from {@link #getRealTimeState()} in that it checks whether real-time - * information is actually available. - */ + @Override public boolean isScheduled() { return true; } - /** - * Return {@code true} if canceled or soft-deleted - */ + @Override public boolean isCanceledOrDeleted() { return false; } - /** - * Return {@code true} if canceled - */ + @Override public boolean isCanceled() { return false; } - /** - * Return true if trip is soft-deleted, and should not be visible to the user - */ + @Override public boolean isDeleted() { return false; } + @Override public RealTimeState getRealTimeState() { return RealTimeState.SCHEDULED; } - /** - * @return the whole trip's headsign. Individual stops can have different headsigns. - */ + @Override + public boolean isCancelledStop(int stop) { + return false; + } + + @Override + public boolean isRecordedStop(int stop) { + return false; + } + + @Override + public boolean isNoDataStop(int stop) { + return false; + } + + @Override + public boolean isPredictionInaccurate(int stop) { + return false; + } + + @Override public I18NString getTripHeadsign() { return trip.getHeadsign(); } - /** - * Both trip_headsign and stop_headsign (per stop on a particular trip) are optional GTFS fields. - * A trip may not have a headsign, in which case we should fall back on a Timetable or - * Pattern-level headsign. Such a string will be available when we give TripPatterns or - * StopPatterns unique human-readable route variant names, but a ScheduledTripTimes currently - * does not have a pointer to its enclosing timetable or pattern. - */ + @Override @Nullable public I18NString getHeadsign(final int stop) { return (headsigns != null && headsigns[stop] != null) @@ -244,13 +239,7 @@ public I18NString getHeadsign(final int stop) { : getTrip().getHeadsign(); } - /** - * Return list of via names per particular stop. This field provides info about intermediate stops - * between current stop and final trip destination. Mapped from NeTEx DestinationDisplay.vias. No - * GTFS mapping at the moment. - * - * @return Empty list if there are no vias registered for a stop. - */ + @Override public List getHeadsignVias(final int stop) { if (headsignVias == null || headsignVias[stop] == null) { return List.of(); @@ -258,45 +247,27 @@ public List getHeadsignVias(final int stop) { return List.of(headsignVias[stop]); } + @Override public int getNumStops() { return arrivalTimes.length; } + @Override public Accessibility getWheelchairAccessibility() { return trip.getWheelchairBoarding(); } - /** - * This is only for API-purposes (does not affect routing). - */ + @Override public OccupancyStatus getOccupancyStatus(int ignore) { return OccupancyStatus.NO_DATA_AVAILABLE; } - /** Sort trips based on first departure time. */ @Override - public int compareTo(final ScheduledTripTimes other) { - return this.getDepartureTime(0) - other.getDepartureTime(0); - } - - /** - * Returns the GTFS sequence number of the given 0-based stop position. - * - * These are the GTFS stop sequence numbers, which show the order in which the vehicle visits the - * stops. Despite the fact that the StopPattern or TripPattern enclosing this class provides - * an ordered list of Stops, the original stop sequence numbers may still be needed for matching - * with GTFS-RT update messages. Unfortunately, each individual trip can have totally different - * sequence numbers for the same stops, so we need to store them at the individual trip level. An - * effort is made to re-use the sequence number arrays when they are the same across different - * trips in the same pattern. - */ public int gtfsSequenceOfStopIndex(final int stop) { return originalGtfsStopSequence[stop]; } - /** - * Returns the 0-based stop index of the given GTFS sequence number. - */ + @Override public OptionalInt stopIndexOfGtfsSequence(int stopSequence) { if (originalGtfsStopSequence == null) { return OptionalInt.empty(); diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java index ecb0df38127..a7a5b34e7c2 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java @@ -1,614 +1,172 @@ package org.opentripplanner.transit.model.timetable; -import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_DWELL_TIME; -import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.NEGATIVE_HOP_TIME; - import java.io.Serializable; import java.time.Duration; -import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.OptionalInt; -import java.util.function.IntUnaryOperator; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.transit.model.basic.Accessibility; -import org.opentripplanner.transit.model.framework.DataValidationException; - -/** - * A TripTimes represents the arrival and departure times for a single trip in an Timetable. It is - * carried along by States when routing to ensure that they have a consistent, fast view of the trip - * when realtime updates have been applied. All times are expressed as seconds since midnight (as in - * GTFS). - */ -public final class TripTimes implements Serializable, Comparable { - - private ScheduledTripTimes scheduledTripTimes; - - private int[] arrivalTimes; - private int[] departureTimes; - private RealTimeState realTimeState; - private StopRealTimeState[] stopRealTimeStates; - private I18NString[] headsigns; - private OccupancyStatus[] occupancyStatus; - private Accessibility wheelchairAccessibility; - - TripTimes(ScheduledTripTimes scheduledTripTimes) { - this( - scheduledTripTimes, - scheduledTripTimes.getRealTimeState(), - null, - null, - null, - scheduledTripTimes.getWheelchairAccessibility() - ); - } - - private TripTimes(TripTimes original, ScheduledTripTimes scheduledTripTimes) { - this( - scheduledTripTimes, - original.realTimeState, - original.stopRealTimeStates, - original.headsigns, - original.occupancyStatus, - original.wheelchairAccessibility - ); - } - - private TripTimes( - ScheduledTripTimes scheduledTripTimes, - RealTimeState realTimeState, - StopRealTimeState[] stopRealTimeStates, - I18NString[] headsigns, - OccupancyStatus[] occupancyStatus, - Accessibility wheelchairAccessibility - ) { - this.scheduledTripTimes = scheduledTripTimes; - this.realTimeState = realTimeState; - this.stopRealTimeStates = stopRealTimeStates; - this.headsigns = headsigns; - this.occupancyStatus = occupancyStatus; - this.wheelchairAccessibility = wheelchairAccessibility; - - // We set these to null to indicate that this is a non-updated/scheduled TripTimes. - // We cannot point to the scheduled times because we do not want to make an unnecessary copy. - this.arrivalTimes = null; - this.departureTimes = null; - } - - public static TripTimes of(ScheduledTripTimes scheduledTripTimes) { - return new TripTimes(scheduledTripTimes); - } +public interface TripTimes extends Serializable, Comparable { /** * Copy scheduled times, but not the actual times. */ - public TripTimes copyOfScheduledTimes() { - return new TripTimes(this, scheduledTripTimes); - } - - /** - * Both trip_headsign and stop_headsign (per stop on a particular trip) are optional GTFS fields. - * A trip may not have a headsign, in which case we should fall back on a Timetable or - * Pattern-level headsign. Such a string will be available when we give TripPatterns or - * StopPatterns unique human readable route variant names, but a TripTimes currently does not have - * a pointer to its enclosing timetable or pattern. - */ - @Nullable - public I18NString getHeadsign(final int stop) { - return (headsigns != null && headsigns[stop] != null) - ? headsigns[stop] - : scheduledTripTimes.getHeadsign(stop); - } - - /** - * Return list of via names per particular stop. This field provides info about intermediate stops - * between current stop and final trip destination. Mapped from NeTEx DestinationDisplay.vias. No - * GTFS mapping at the moment. - * - * @return Empty list if there are no vias registered for a stop. - */ - public List getHeadsignVias(final int stop) { - return scheduledTripTimes.getHeadsignVias(stop); - } + public RealTimeTripTimes copyScheduledTimes(); - /** - * @return the whole trip's headsign. Individual stops can have different headsigns. - */ - public I18NString getTripHeadsign() { - return scheduledTripTimes.getTripHeadsign(); - } + /** The code for the service on which this trip runs. For departure search optimizations. */ + int getServiceCode(); /** * The time in seconds after midnight at which the vehicle should arrive at the given stop * according to the original schedule. */ - public int getScheduledArrivalTime(final int stop) { - return scheduledTripTimes.getScheduledArrivalTime(stop); - } + int getScheduledArrivalTime(int stop); /** - * The time in seconds after midnight at which the vehicle should leave the given stop according - * to the original schedule. + * The time in seconds after midnight at which the vehicle arrives at each stop, accounting for + * any real-time updates. */ - public int getScheduledDepartureTime(final int stop) { - return scheduledTripTimes.getScheduledDepartureTime(stop); - } + int getArrivalTime(int stop); - /** - * Return an integer which can be used to sort TripTimes in order of departure/arrivals. - *

- * This sorted trip times is used to search for trips. OTP assume one trip do NOT pass another - * trip down the line. - */ - public int sortIndex() { - return getDepartureTime(0); - } + /** @return the difference between the scheduled and actual arrival times at this stop. */ + int getArrivalDelay(int stop); /** - * The time in seconds after midnight at which the vehicle arrives at each stop, accounting for - * any real-time updates. + * The time in seconds after midnight at which the vehicle should leave the given stop according + * to the original schedule. */ - public int getArrivalTime(final int stop) { - return getOrElse(stop, arrivalTimes, scheduledTripTimes::getScheduledArrivalTime); - } + int getScheduledDepartureTime(int stop); /** * The time in seconds after midnight at which the vehicle leaves each stop, accounting for any * real-time updates. */ - public int getDepartureTime(final int stop) { - return getOrElse(stop, departureTimes, scheduledTripTimes::getScheduledDepartureTime); - } - - /** @return the difference between the scheduled and actual arrival times at this stop. */ - public int getArrivalDelay(final int stop) { - return getArrivalTime(stop) - scheduledTripTimes.getScheduledArrivalTime(stop); - } + int getDepartureTime(int stop); /** @return the difference between the scheduled and actual departure times at this stop. */ - public int getDepartureDelay(final int stop) { - return getDepartureTime(stop) - scheduledTripTimes.getScheduledDepartureTime(stop); - } - - public void setRecorded(int stop) { - setStopRealTimeStates(stop, StopRealTimeState.RECORDED); - } - - public void setCancelled(int stop) { - setStopRealTimeStates(stop, StopRealTimeState.CANCELLED); - } - - public void setNoData(int stop) { - setStopRealTimeStates(stop, StopRealTimeState.NO_DATA); - } - - public void setPredictionInaccurate(int stop) { - setStopRealTimeStates(stop, StopRealTimeState.INACCURATE_PREDICTIONS); - } - - public boolean isCancelledStop(int stop) { - return isStopRealTimeStates(stop, StopRealTimeState.CANCELLED); - } + int getDepartureDelay(int stop); - public boolean isRecordedStop(int stop) { - return isStopRealTimeStates(stop, StopRealTimeState.RECORDED); - } - - public boolean isNoDataStop(int stop) { - return isStopRealTimeStates(stop, StopRealTimeState.NO_DATA); - } - - public boolean isPredictionInaccurate(int stop) { - return isStopRealTimeStates(stop, StopRealTimeState.INACCURATE_PREDICTIONS); - } + /** + * Whether or not stopIndex is considered a GTFS timepoint. + */ + boolean isTimepoint(int stopIndex); - public void setOccupancyStatus(int stop, OccupancyStatus occupancyStatus) { - prepareForRealTimeUpdates(); - this.occupancyStatus[stop] = occupancyStatus; - } + /** The trips whose arrivals and departures are represented by this class */ + Trip getTrip(); /** - * This is only for API-purposes (does not affect routing). + * Return an integer which can be used to sort TripTimes in order of departure/arrivals. + *

+ * This sorted trip times is used to search for trips. OTP assume one trip do NOT pass another + * trip down the line. */ - public OccupancyStatus getOccupancyStatus(int stop) { - if (this.occupancyStatus == null) { - return OccupancyStatus.NO_DATA_AVAILABLE; - } - return this.occupancyStatus[stop]; + default int sortIndex() { + return getDepartureTime(0); } - public BookingInfo getDropOffBookingInfo(int stop) { - return scheduledTripTimes.getDropOffBookingInfo(stop); + /** Sort trips based on first departure time. */ + default Comparator compare() { + return Comparator.comparingInt(TripTimes::sortIndex); } - public BookingInfo getPickupBookingInfo(int stop) { - return scheduledTripTimes.getPickupBookingInfo(stop); + /** Sort trips based on first departure time. */ + default int compareTo(TripTimes other) { + return sortIndex() - other.sortIndex(); } + BookingInfo getDropOffBookingInfo(int stop); + + BookingInfo getPickupBookingInfo(int stop); + /** * Return {@code true} if the trip is unmodified, a scheduled trip from a published timetable. * Return {@code false} if the trip is an updated, cancelled, or otherwise modified one. This - * method differs from {@link #getRealTimeState()} in that it checks whether real-time - * information is actually available. + * method differs from {@link #getRealTimeState()} in that it checks whether real-time information + * is actually available. */ - public boolean isScheduled() { - return realTimeState == RealTimeState.SCHEDULED; - } + boolean isScheduled(); /** * Return {@code true} if canceled or soft-deleted */ - public boolean isCanceledOrDeleted() { - return isCanceled() || isDeleted(); - } + boolean isCanceledOrDeleted(); /** * Return {@code true} if canceled */ - public boolean isCanceled() { - return realTimeState == RealTimeState.CANCELED; - } + boolean isCanceled(); /** * Return true if trip is soft-deleted, and should not be visible to the user */ - public boolean isDeleted() { - return realTimeState == RealTimeState.DELETED; - } + boolean isDeleted(); - public RealTimeState getRealTimeState() { - return realTimeState; - } + RealTimeState getRealTimeState(); - public void setRealTimeState(final RealTimeState realTimeState) { - this.realTimeState = realTimeState; - } + boolean isCancelledStop(int stop); + + boolean isRecordedStop(int stop); + + boolean isNoDataStop(int stop); + + boolean isPredictionInaccurate(int stop); /** - * When creating a scheduled TripTimes or wrapping it in updates, we could potentially imply - * negative running or dwell times. We really don't want those being used in routing. This method - * checks that all internal times are increasing. Thus, this check should be used at the end of - * updating trip times, after any propagating or interpolating delay operations. - * - * @throws org.opentripplanner.transit.model.framework.DataValidationException on the first validation error found. + * @return the whole trip's headsign. Individual stops can have different headsigns. */ - public void validateNonIncreasingTimes() { - final int nStops = arrivalTimes.length; - int prevDep = -9_999_999; - for (int s = 0; s < nStops; s++) { - final int arr = getArrivalTime(s); - final int dep = getDepartureTime(s); - - if (dep < arr) { - throw new DataValidationException( - new TimetableValidationError(NEGATIVE_DWELL_TIME, s, getTrip()) - ); - } - if (prevDep > arr) { - throw new DataValidationException( - new TimetableValidationError(NEGATIVE_HOP_TIME, s, getTrip()) - ); - } - prevDep = dep; - } - } + I18NString getTripHeadsign(); /** - * Note: This method only applies for GTFS, not SIRI! - * This method interpolates the times for SKIPPED stops in between regular stops since GTFS-RT - * does not require arrival and departure times for these stops. This method ensures the internal - * time representations in OTP for SKIPPED stops are between the regular stop times immediately - * before and after the cancellation in GTFS-RT. This is to meet the OTP requirement that stop - * times should be increasing and to support the trip search flag `includeRealtimeCancellations`. - * Terminal stop cancellations can be handled by backward and forward propagations, and are - * outside the scope of this method. - * - * @return true if there is interpolated times, false if there is no interpolation. + * Both trip_headsign and stop_headsign (per stop on a particular trip) are optional GTFS fields. + * A trip may not have a headsign, in which case we should fall back on a Timetable or + * Pattern-level headsign. Such a string will be available when we give TripPatterns or + * StopPatterns unique human-readable route variant names, but a ScheduledTripTimes currently does + * not have a pointer to its enclosing timetable or pattern. */ - public boolean interpolateMissingTimes() { - boolean hasInterpolatedTimes = false; - final int numStops = getNumStops(); - boolean startInterpolate = false; - boolean hasPrevTimes = false; - int prevDeparture = 0; - int prevScheduledDeparture = 0; - int prevStopIndex = -1; - - // Loop through all stops - for (int s = 0; s < numStops; s++) { - final boolean isCancelledStop = isCancelledStop(s); - final int scheduledArrival = getScheduledArrivalTime(s); - final int scheduledDeparture = getScheduledDepartureTime(s); - final int arrival = getArrivalTime(s); - final int departure = getDepartureTime(s); - - if (!isCancelledStop && !startInterpolate) { - // Regular stop, could be used for interpolation for future cancellation, keep track. - prevDeparture = departure; - prevScheduledDeparture = scheduledDeparture; - prevStopIndex = s; - hasPrevTimes = true; - } else if (isCancelledStop && !startInterpolate && hasPrevTimes) { - // First cancelled stop, keep track. - startInterpolate = true; - } else if (!isCancelledStop && startInterpolate && hasPrevTimes) { - // First regular stop after cancelled stops, interpolate. - // Calculate necessary info for interpolation. - int numCancelledStops = s - prevStopIndex - 1; - int scheduledTravelTime = scheduledArrival - prevScheduledDeparture; - int realTimeTravelTime = arrival - prevDeparture; - double travelTimeRatio = (double) realTimeTravelTime / scheduledTravelTime; - - // Fill out interpolated time for cancelled stops, using the calculated ratio. - for (int cancelledIndex = prevStopIndex + 1; cancelledIndex < s; cancelledIndex++) { - final int scheduledArrivalCancelled = getScheduledArrivalTime(cancelledIndex); - final int scheduledDepartureCancelled = getScheduledDepartureTime(cancelledIndex); - - // Interpolate - int scheduledArrivalDiff = scheduledArrivalCancelled - prevScheduledDeparture; - double interpolatedArrival = prevDeparture + travelTimeRatio * scheduledArrivalDiff; - int scheduledDepartureDiff = scheduledDepartureCancelled - prevScheduledDeparture; - double interpolatedDeparture = prevDeparture + travelTimeRatio * scheduledDepartureDiff; - - // Set Interpolated Times - updateArrivalTime(cancelledIndex, (int) interpolatedArrival); - updateDepartureTime(cancelledIndex, (int) interpolatedDeparture); - } - - // Set tracking variables - prevDeparture = departure; - prevScheduledDeparture = scheduledDeparture; - prevStopIndex = s; - startInterpolate = false; - hasPrevTimes = true; - - // Set return variable - hasInterpolatedTimes = true; - } - } - - return hasInterpolatedTimes; - } - - /** Cancel this entire trip */ - public void cancelTrip() { - realTimeState = RealTimeState.CANCELED; - } - - /** Soft delete the entire trip */ - public void deleteTrip() { - realTimeState = RealTimeState.DELETED; - } - - public void updateDepartureTime(final int stop, final int time) { - prepareForRealTimeUpdates(); - departureTimes[stop] = time; - } - - public void updateDepartureDelay(final int stop, final int delay) { - prepareForRealTimeUpdates(); - departureTimes[stop] = scheduledTripTimes.getScheduledDepartureTime(stop) + delay; - } - - public void updateArrivalTime(final int stop, final int time) { - prepareForRealTimeUpdates(); - arrivalTimes[stop] = time; - } - - public void updateArrivalDelay(final int stop, final int delay) { - prepareForRealTimeUpdates(); - arrivalTimes[stop] = scheduledTripTimes.getScheduledArrivalTime(stop) + delay; - } - @Nullable - public Accessibility getWheelchairAccessibility() { - // No need to fall back to scheduled state, since it is copied over in the constructor - return wheelchairAccessibility; - } - - public void updateWheelchairAccessibility(Accessibility wheelchairAccessibility) { - this.wheelchairAccessibility = wheelchairAccessibility; - } - - public int getNumStops() { - return scheduledTripTimes.getNumStops(); - } - - /** Sort trips based on first departure time. */ - @Override - public int compareTo(final TripTimes other) { - return this.getDepartureTime(0) - other.getDepartureTime(0); - } + I18NString getHeadsign(int stop); /** - * Returns a time-shifted copy of this TripTimes in which the vehicle passes the given stop index - * (not stop sequence number) at the given time. We only have a mechanism to shift the scheduled - * stoptimes, not the real-time stoptimes. Therefore, this only works on trips without updates for - * now (frequency trips don't have updates). + * Return list of via names per particular stop. This field provides info about intermediate stops + * between current stop and final trip destination. Mapped from NeTEx DestinationDisplay.vias. No + * GTFS mapping at the moment. + * + * @return Empty list if there are no vias registered for a stop. */ - public TripTimes timeShift(final int stop, final int time, final boolean depart) { - if (arrivalTimes != null || departureTimes != null) { - return null; - } - // Adjust 0-based times to match desired stoptime. - final int shift = time - (depart ? getDepartureTime(stop) : getArrivalTime(stop)); - - return new TripTimes( - this, - scheduledTripTimes.copyOfNoDuplication().plusTimeShift(shift).build() - ); - } + List getHeadsignVias(int stop); + + int getNumStops(); + + Accessibility getWheelchairAccessibility(); /** - * Time-shift all times on this trip. This is used when updating the time zone for the trip. + * This is only for API-purposes (does not affect routing). */ - public TripTimes adjustTimesToGraphTimeZone(Duration shiftDelta) { - return new TripTimes( - this, - scheduledTripTimes.copyOfNoDuplication().plusTimeShift((int) shiftDelta.toSeconds()).build() - ); - } + OccupancyStatus getOccupancyStatus(int stop); /** * Returns the GTFS sequence number of the given 0-based stop position. - * + *

* These are the GTFS stop sequence numbers, which show the order in which the vehicle visits the - * stops. Despite the fact that the StopPattern or TripPattern enclosing this TripTimes provides - * an ordered list of Stops, the original stop sequence numbers may still be needed for matching + * stops. Despite the fact that the StopPattern or TripPattern enclosing this class provides an + * ordered list of Stops, the original stop sequence numbers may still be needed for matching * with GTFS-RT update messages. Unfortunately, each individual trip can have totally different * sequence numbers for the same stops, so we need to store them at the individual trip level. An * effort is made to re-use the sequence number arrays when they are the same across different * trips in the same pattern. */ - public int gtfsSequenceOfStopIndex(final int stop) { - return scheduledTripTimes.gtfsSequenceOfStopIndex(stop); - } + int gtfsSequenceOfStopIndex(int stop); /** * Returns the 0-based stop index of the given GTFS sequence number. */ - public OptionalInt stopIndexOfGtfsSequence(int stopSequence) { - return scheduledTripTimes.stopIndexOfGtfsSequence(stopSequence); - } - - /** - * Whether or not stopIndex is considered a GTFS timepoint. - */ - public boolean isTimepoint(final int stopIndex) { - return scheduledTripTimes.isTimepoint(stopIndex); - } - - /** The code for the service on which this trip runs. For departure search optimizations. */ - public int getServiceCode() { - return scheduledTripTimes.getServiceCode(); - } - - public void setServiceCode(int serviceCode) { - this.scheduledTripTimes = - scheduledTripTimes.copyOfNoDuplication().withServiceCode(serviceCode).build(); - } - - /** The trips whose arrivals and departures are represented by this class */ - public Trip getTrip() { - return scheduledTripTimes.getTrip(); - } - - /** - * Adjusts arrival time for the stop at the firstUpdatedIndex if no update was given for it and - * arrival/departure times for the stops before that stop. Returns {@code true} if times have been - * adjusted. - */ - public boolean adjustTimesBeforeAlways(int firstUpdatedIndex) { - boolean hasAdjustedTimes = false; - int delay = getDepartureDelay(firstUpdatedIndex); - if (getArrivalDelay(firstUpdatedIndex) == 0) { - updateArrivalDelay(firstUpdatedIndex, delay); - hasAdjustedTimes = true; - } - delay = getArrivalDelay(firstUpdatedIndex); - if (delay == 0) { - return false; - } - for (int i = firstUpdatedIndex - 1; i >= 0; i--) { - hasAdjustedTimes = true; - updateDepartureDelay(i, delay); - updateArrivalDelay(i, delay); - } - return hasAdjustedTimes; - } - - /** - * Adjusts arrival and departure times for the stops before the stop at firstUpdatedIndex when - * required to ensure that the times are increasing. Can set NO_DATA flag on the updated previous - * stops. Returns {@code true} if times have been adjusted. - */ - public boolean adjustTimesBeforeWhenRequired(int firstUpdatedIndex, boolean setNoData) { - if (getArrivalTime(firstUpdatedIndex) > getDepartureTime(firstUpdatedIndex)) { - // The given trip update has arrival time after departure time for the first updated stop. - // This method doesn't try to fix issues in the given data, only for the missing part - return false; - } - int nextStopArrivalTime = getArrivalTime(firstUpdatedIndex); - int delay = getArrivalDelay(firstUpdatedIndex); - boolean hasAdjustedTimes = false; - boolean adjustTimes = true; - for (int i = firstUpdatedIndex - 1; i >= 0; i--) { - if (setNoData && !isCancelledStop(i)) { - setNoData(i); - } - if (adjustTimes) { - if (getDepartureTime(i) < nextStopArrivalTime) { - adjustTimes = false; - continue; - } else { - hasAdjustedTimes = true; - updateDepartureDelay(i, delay); - } - if (getArrivalTime(i) < getDepartureTime(i)) { - adjustTimes = false; - } else { - updateArrivalDelay(i, delay); - nextStopArrivalTime = getArrivalTime(i); - } - } - } - return hasAdjustedTimes; - } - - /* private member methods */ - - private void setStopRealTimeStates(int stop, StopRealTimeState state) { - prepareForRealTimeUpdates(); - this.stopRealTimeStates[stop] = state; - } - - /** - * The real-time states for a given stops. If the state is DEFAULT for a stop, - * the {@link #getRealTimeState()} should determine the realtime state of the stop. - *

- * This is only for API-purposes (does not affect routing). - */ - private boolean isStopRealTimeStates(int stop, StopRealTimeState state) { - return stopRealTimeStates != null && stopRealTimeStates[stop] == state; - } - - public void setHeadsign(int index, I18NString headsign) { - if (headsigns == null) { - if (headsign.equals(getTrip().getHeadsign())) { - return; - } - this.headsigns = scheduledTripTimes.copyHeadsigns(() -> new I18NString[getNumStops()]); - this.headsigns[index] = headsign; - return; - } - - prepareForRealTimeUpdates(); - headsigns[index] = headsign; - } - - private static int getOrElse(int index, int[] array, IntUnaryOperator defaultValue) { - return array != null ? array[index] : defaultValue.applyAsInt(index); - } + OptionalInt stopIndexOfGtfsSequence(int stopSequence); /** - * If they don't already exist, create arrays for updated arrival and departure times that are - * just time-shifted copies of the zero-based scheduled departure times. - *

- * Also sets the realtime state to UPDATED. + * Time-shift all times on this trip. This is used when updating the time zone for the trip. */ - private void prepareForRealTimeUpdates() { - if (arrivalTimes == null) { - this.arrivalTimes = scheduledTripTimes.copyArrivalTimes(); - this.departureTimes = scheduledTripTimes.copyDepartureTimes(); - // Update the real-time state - this.realTimeState = RealTimeState.UPDATED; - this.stopRealTimeStates = new StopRealTimeState[arrivalTimes.length]; - Arrays.fill(stopRealTimeStates, StopRealTimeState.DEFAULT); - this.headsigns = scheduledTripTimes.copyHeadsigns(() -> null); - this.occupancyStatus = new OccupancyStatus[arrivalTimes.length]; - Arrays.fill(occupancyStatus, OccupancyStatus.NO_DATA_AVAILABLE); - // skip immutable types: scheduledTripTimes & wheelchairAccessibility - } - } + TripTimes adjustTimesToGraphTimeZone(Duration shiftDelta); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java index c868fda6582..611974904f0 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesFactory.java @@ -18,11 +18,13 @@ public class TripTimesFactory { * non-interpolated stoptimes should already be marked at timepoints by a previous filtering * step. */ - public static TripTimes tripTimes( + public static RealTimeTripTimes tripTimes( Trip trip, List stopTimes, Deduplicator deduplicator ) { - return new TripTimes(StopTimeToScheduledTripTimesMapper.map(trip, stopTimes, deduplicator)); + return new RealTimeTripTimes( + StopTimeToScheduledTripTimesMapper.map(trip, stopTimes, deduplicator) + ); } } diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 8e782ebe934..d8954298123 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -54,8 +54,8 @@ import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitEditorService; @@ -892,7 +892,11 @@ private Result addTripToGraphAndBuffer( ); // Create new trip times - final TripTimes newTripTimes = TripTimesFactory.tripTimes(trip, stopTimes, deduplicator); + final RealTimeTripTimes newTripTimes = TripTimesFactory.tripTimes( + trip, + stopTimes, + deduplicator + ); // Update all times to mark trip times as realtime // TODO: should we incorporate the delay field if present? @@ -952,7 +956,9 @@ private boolean cancelScheduledTrip( if (tripIndex == -1) { debug(tripId, "Could not cancel scheduled trip because it's not in the timetable"); } else { - final TripTimes newTripTimes = timetable.getTripTimes(tripIndex).copyOfScheduledTimes(); + final RealTimeTripTimes newTripTimes = timetable + .getTripTimes(tripIndex) + .copyScheduledTimes(); switch (cancelationType) { case CANCEL -> newTripTimes.cancelTrip(); case DELETE -> newTripTimes.deleteTrip(); @@ -992,7 +998,9 @@ private boolean cancelPreviouslyAddedTrip( if (tripIndex == -1) { debug(tripId, "Could not cancel previously added trip on {}", serviceDate); } else { - final TripTimes newTripTimes = timetable.getTripTimes(tripIndex).copyOfScheduledTimes(); + final RealTimeTripTimes newTripTimes = timetable + .getTripTimes(tripIndex) + .copyScheduledTimes(); switch (cancelationType) { case CANCEL -> newTripTimes.cancelTrip(); case DELETE -> newTripTimes.deleteTrip(); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java index e59a5360808..5dd1175cd1c 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java @@ -96,13 +96,11 @@ private static TripPatternForDates findTripPatternForDate( } private TripTimes createTripTimesForTest() { - return TripTimes.of( - ScheduledTripTimes - .of() - .withTrip(TransitModelForTest.trip("Test").build()) - .withDepartureTimes("00:00 02:00") - .build() - ); + return ScheduledTripTimes + .of() + .withTrip(TransitModelForTest.trip("Test").build()) + .withDepartureTimes("00:00 02:00") + .build(); } /** diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java index 86d5f9404ed..a45c3f2b7a6 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java @@ -40,6 +40,7 @@ import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripAlteration; import org.opentripplanner.transit.model.timetable.TripBuilder; @@ -513,7 +514,7 @@ void keepAccessibleTrip() { @Test void keepRealTimeAccessibleTrip() { - TripTimes realTimeWheelchairAccessibleTrip = createTestTripTimes( + RealTimeTripTimes realTimeWheelchairAccessibleTrip = createTestTripTimes( TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, @@ -616,7 +617,7 @@ void includeRealtimeCancellationsTest() { TripAlteration.PLANNED ); - TripTimes tripTimesWithCancellation = createTestTripTimes( + RealTimeTripTimes tripTimesWithCancellation = createTestTripTimes( TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, @@ -860,7 +861,7 @@ private List combinedFilterForModesAndBannedAgencies( ); } - private TripTimes createTestTripTimes( + private RealTimeTripTimes createTestTripTimes( FeedScopedId tripId, Route route, BikeAccess bikeAccess, diff --git a/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java b/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java index 54194696e71..1432c68fd49 100644 --- a/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java +++ b/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java @@ -38,7 +38,9 @@ public static void setUp() throws Exception { transitService.getTripForId(new FeedScopedId(feedId, "5.1")) ); var tt = transitService.getTimetableForTripPattern(pattern, LocalDate.now()); - tt.getTripTimes(0).cancelTrip(); + var newTripTimes = tt.getTripTimes(0).copyScheduledTimes(); + newTripTimes.cancelTrip(); + tt.setTripTimes(0, newTripTimes); } /** diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesTest.java similarity index 93% rename from src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java rename to src/test/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesTest.java index ee73bd6856c..5b3a8f76052 100644 --- a/src/test/java/org/opentripplanner/transit/model/timetable/TripTimesTest.java +++ b/src/test/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesTest.java @@ -22,7 +22,7 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.RegularStop; -class TripTimesTest { +class RealTimeTripTimesTest { private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); @@ -134,7 +134,7 @@ void shouldHandleDifferingTripAndStopHeadSignScenario() { @Test public void testStopUpdate() { - TripTimes updatedTripTimesA = createInitialTripTimes().copyOfScheduledTimes(); + RealTimeTripTimes updatedTripTimesA = createInitialTripTimes().copyScheduledTimes(); updatedTripTimesA.updateArrivalTime(3, 190); updatedTripTimesA.updateDepartureTime(3, 190); @@ -149,7 +149,7 @@ public void testStopUpdate() { @Test public void testPassedUpdate() { - TripTimes updatedTripTimesA = createInitialTripTimes().copyOfScheduledTimes(); + RealTimeTripTimes updatedTripTimesA = createInitialTripTimes().copyScheduledTimes(); updatedTripTimesA.updateDepartureTime(0, 30); @@ -167,7 +167,7 @@ public void testPassedUpdate() { */ @Test public void testNegativeHopTimeWithStopCancellations() { - TripTimes updatedTripTimes = createInitialTripTimes(); + var updatedTripTimes = createInitialTripTimes().copyScheduledTimes(); updatedTripTimes.updateDepartureTime(5, 421); updatedTripTimes.updateArrivalTime(6, 481); @@ -194,7 +194,7 @@ public void testNegativeHopTimeWithStopCancellations() { */ @Test public void testPositiveHopTimeWithStopCancellationsLate() { - TripTimes updatedTripTimes = createInitialTripTimes(); + var updatedTripTimes = createInitialTripTimes().copyScheduledTimes(); updatedTripTimes.updateDepartureTime(5, 400); updatedTripTimes.updateArrivalTime(6, 460); @@ -225,7 +225,7 @@ public void testPositiveHopTimeWithStopCancellationsLate() { */ @Test public void testPositiveHopTimeWithStopCancellationsEarly() { - TripTimes updatedTripTimes = createInitialTripTimes(); + var updatedTripTimes = createInitialTripTimes().copyScheduledTimes(); updatedTripTimes.updateDepartureTime(5, 300); updatedTripTimes.setCancelled(6); @@ -254,7 +254,7 @@ public void testPositiveHopTimeWithStopCancellationsEarly() { */ @Test public void testPositiveHopTimeWithTerminalCancellation() { - TripTimes updatedTripTimes = createInitialTripTimes(); + var updatedTripTimes = createInitialTripTimes().copyScheduledTimes(); updatedTripTimes.setCancelled(0); updatedTripTimes.setCancelled(1); @@ -288,7 +288,7 @@ public void testPositiveHopTimeWithTerminalCancellation() { */ @Test public void testInterpolationWithTerminalCancellation() { - TripTimes updatedTripTimes = createInitialTripTimes(); + var updatedTripTimes = createInitialTripTimes().copyScheduledTimes(); updatedTripTimes.setCancelled(6); updatedTripTimes.setCancelled(7); @@ -307,7 +307,7 @@ public void testInterpolationWithTerminalCancellation() { */ @Test public void testInterpolationWithMultipleStopCancellations() { - TripTimes updatedTripTimes = createInitialTripTimes(); + var updatedTripTimes = createInitialTripTimes().copyScheduledTimes(); updatedTripTimes.setCancelled(1); updatedTripTimes.setCancelled(2); @@ -341,7 +341,7 @@ public void testInterpolationWithMultipleStopCancellations() { */ @Test public void testInterpolationWithMultipleStopCancellations2() { - TripTimes updatedTripTimes = createInitialTripTimes(); + var updatedTripTimes = createInitialTripTimes().copyScheduledTimes(); updatedTripTimes.setCancelled(1); updatedTripTimes.setCancelled(2); @@ -368,7 +368,7 @@ public void testInterpolationWithMultipleStopCancellations2() { @Test public void testNonIncreasingUpdateCrossingMidnight() { - TripTimes updatedTripTimesA = createInitialTripTimes().copyOfScheduledTimes(); + RealTimeTripTimes updatedTripTimesA = createInitialTripTimes().copyScheduledTimes(); updatedTripTimesA.updateArrivalTime(0, -300); //"Yesterday" updatedTripTimesA.updateDepartureTime(0, 50); @@ -378,7 +378,7 @@ public void testNonIncreasingUpdateCrossingMidnight() { @Test public void testDelay() { - TripTimes updatedTripTimesA = createInitialTripTimes().copyOfScheduledTimes(); + RealTimeTripTimes updatedTripTimesA = createInitialTripTimes().copyScheduledTimes(); updatedTripTimesA.updateDepartureDelay(0, 10); updatedTripTimesA.updateArrivalDelay(6, 13); @@ -388,14 +388,14 @@ public void testDelay() { @Test public void testCancel() { - TripTimes updatedTripTimesA = createInitialTripTimes().copyOfScheduledTimes(); + RealTimeTripTimes updatedTripTimesA = createInitialTripTimes().copyScheduledTimes(); updatedTripTimesA.cancelTrip(); assertEquals(RealTimeState.CANCELED, updatedTripTimesA.getRealTimeState()); } @Test public void testNoData() { - TripTimes updatedTripTimesA = createInitialTripTimes().copyOfScheduledTimes(); + RealTimeTripTimes updatedTripTimesA = createInitialTripTimes().copyScheduledTimes(); updatedTripTimesA.setNoData(1); assertFalse(updatedTripTimesA.isNoDataStop(0)); assertTrue(updatedTripTimesA.isNoDataStop(1)); @@ -429,7 +429,7 @@ void unknownGtfsSequence() { public void validateNegativeDwellTime() { var expMsg = "NEGATIVE_DWELL_TIME for stop position 3 in trip Trip{F:testTripId RRtestTripId}."; var tt = createInitialTripTimes(); - var updatedTt = tt.copyOfScheduledTimes(); + var updatedTt = tt.copyScheduledTimes(); updatedTt.updateArrivalTime(3, 69); updatedTt.updateDepartureTime(3, 68); @@ -447,7 +447,7 @@ public void validateNegativeDwellTime() { public void validateNegativeHopTime() { var expMsg = "NEGATIVE_HOP_TIME for stop position 2 in trip Trip{F:testTripId RRtestTripId}."; var tt = createInitialTripTimes(); - var updatedTt = tt.copyOfScheduledTimes(); + var updatedTt = tt.copyScheduledTimes(); updatedTt.updateDepartureTime(1, 100); updatedTt.updateArrivalTime(2, 99); From 5cc33549156571775bf3e607c6feea5094de250e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 22 Nov 2023 11:28:20 +0100 Subject: [PATCH 05/39] Remove BusRouteStreetMatcher --- docs/BuildConfiguration.md | 1 - .../graph_builder/GraphBuilder.java | 3 - .../module/configure/GraphBuilderFactory.java | 2 - .../module/map/BusRouteStreetMatcher.java | 99 ------- .../module/map/EndMatchState.java | 21 -- .../module/map/LinearIterator.java | 229 --------------- .../graph_builder/module/map/MatchState.java | 119 -------- .../module/map/MidblockMatchState.java | 265 ------------------ .../module/map/StreetMatcher.java | 168 ----------- .../standalone/config/BuildConfig.java | 10 - .../transit/model/network/TripPattern.java | 9 - .../module/map/StreetMatcherTest.java | 157 ----------- 12 files changed, 1083 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java delete mode 100644 src/main/java/org/opentripplanner/graph_builder/module/map/EndMatchState.java delete mode 100644 src/main/java/org/opentripplanner/graph_builder/module/map/LinearIterator.java delete mode 100644 src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java delete mode 100644 src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java delete mode 100644 src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java delete mode 100644 src/test/java/org/opentripplanner/graph_builder/module/map/StreetMatcherTest.java diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index b32f782aa32..05da4cf4d0a 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -28,7 +28,6 @@ Sections follow that describe particular settings in more depth. | [graph](#graph) | `uri` | URI to the graph object file for reading and writing. | *Optional* | | 2.0 | | [gsCredentials](#gsCredentials) | `string` | Local file system path to Google Cloud Platform service accounts credentials file. | *Optional* | | 2.0 | | [includeEllipsoidToGeoidDifference](#includeEllipsoidToGeoidDifference) | `boolean` | Include the Ellipsoid to Geoid difference in the calculations of every point along every StreetWithElevationEdge. | *Optional* | `false` | 2.0 | -| matchBusRoutesToStreets | `boolean` | Based on GTFS shape data, guess which OSM streets each bus runs on to improve stop linking. | *Optional* | `false` | 1.5 | | maxAreaNodes | `integer` | Visibility calculations for an area will not be done if there are more nodes than this limit. | *Optional* | `150` | 2.1 | | [maxDataImportIssuesPerFile](#maxDataImportIssuesPerFile) | `integer` | When to split the import report. | *Optional* | `1000` | 2.0 | | maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index e6aba29278b..3a2bb777f04 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -121,9 +121,6 @@ public static GraphBuilder create( } if (hasTransitData && (hasOsm || graphBuilder.graph.hasStreets)) { - if (config.matchBusRoutesToStreets) { - graphBuilder.addModule(factory.busRouteStreetMatcher()); - } graphBuilder.addModule(factory.osmBoardingLocationsModule()); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java index 1f50c7a6327..9fd99d35ea0 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java @@ -24,7 +24,6 @@ import org.opentripplanner.graph_builder.module.TripPatternNamer; import org.opentripplanner.graph_builder.module.geometry.CalculateWorldEnvelopeModule; import org.opentripplanner.graph_builder.module.islandpruning.PruneIslands; -import org.opentripplanner.graph_builder.module.map.BusRouteStreetMatcher; import org.opentripplanner.graph_builder.module.ned.ElevationModule; import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.gtfs.graphbuilder.GtfsModule; @@ -45,7 +44,6 @@ public interface GraphBuilderFactory { NetexModule netexModule(); TimeZoneAdjusterModule timeZoneAdjusterModule(); TripPatternNamer tripPatternNamer(); - BusRouteStreetMatcher busRouteStreetMatcher(); OsmBoardingLocationsModule osmBoardingLocationsModule(); StreetLinkerModule streetLinkerModule(); PruneIslands pruneIslands(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java b/src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java deleted file mode 100644 index 8fc77bb7662..00000000000 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/BusRouteStreetMatcher.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.opentripplanner.graph_builder.module.map; - -import jakarta.inject.Inject; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.LineString; -import org.opentripplanner.framework.geometry.GeometryUtils; -import org.opentripplanner.framework.logging.ProgressTracker; -import org.opentripplanner.graph_builder.model.GraphBuilderModule; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.transit.model.network.Route; -import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.service.TransitModel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Uses the shapes from GTFS to determine which streets buses drive on. This is used to improve the - * quality of the shapes shown for the user. - *

- * GTFS provides a mapping from trips→shapes. This module provides a mapping from stops→trips and - * shapes→edges. Then transitively we get a mapping from stop→edges. - */ -public class BusRouteStreetMatcher implements GraphBuilderModule { - - private static final Logger log = LoggerFactory.getLogger(BusRouteStreetMatcher.class); - - private final Graph graph; - private final TransitModel transitModel; - - @Inject - public BusRouteStreetMatcher(Graph graph, TransitModel transitModel) { - this.graph = graph; - this.transitModel = transitModel; - } - - public void buildGraph() { - // Mapbuilder needs transit index - transitModel.index(); - graph.index(transitModel.getStopModel()); - - StreetMatcher matcher = new StreetMatcher(graph); - log.info("Finding corresponding street edges for trip patterns..."); - // Why do we need to iterate over the routes? Why not just patterns? - Collection allRoutes = transitModel.getTransitModelIndex().getAllRoutes(); - - // Track progress - ProgressTracker progress = ProgressTracker.track( - "Match route to street edges", - 10, - allRoutes.size() - ); - log.info(progress.startMessage()); - - for (Route route : allRoutes) { - for (TripPattern pattern : transitModel - .getTransitModelIndex() - .getPatternsForRoute() - .get(route)) { - if (pattern.getMode().onStreet()) { - /* we can only match geometry to streets on bus routes */ - log.debug("Matching {}", pattern); - //If there are no shapes in GTFS pattern geometry is generated - //generated geometry is useless for street matching - //that is why pattern.geometry is null in that case - if (pattern.getGeometry() == null) { - continue; - } - - for (int i = 0; i < pattern.numHopGeometries(); i++) { - LineString hopGeometry = pattern.getHopGeometry(i); - - List edges = matcher.match(hopGeometry); - if (edges == null || edges.isEmpty()) { - log.warn("Could not match to street network: {}", pattern); - continue; - } - List coordinates = new ArrayList<>(); - for (Edge e : edges) { - coordinates.addAll(Arrays.asList(e.getGeometry().getCoordinates())); - } - Coordinate[] coordinateArray = new Coordinate[coordinates.size()]; - LineString ls = GeometryUtils - .getGeometryFactory() - .createLineString(coordinates.toArray(coordinateArray)); - // Replace the hop's geometry from GTFS with that of the equivalent OSM edges. - pattern.setHopGeometry(i, ls); - } - } - } - progress.step(log::info); - } - log.info(progress.completeMessage()); - } -} diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/EndMatchState.java b/src/main/java/org/opentripplanner/graph_builder/module/map/EndMatchState.java deleted file mode 100644 index ccb90cf3b00..00000000000 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/EndMatchState.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.opentripplanner.graph_builder.module.map; - -import java.util.List; - -/** - * The end of a route's geometry, meaning that the search can quit - * - * @author novalis - */ -public class EndMatchState extends MatchState { - - public EndMatchState(MatchState parent, double error, double distance) { - super(parent, null, distance); - this.currentError = error; - } - - @Override - public List getNextStates() { - return null; - } -} diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/LinearIterator.java b/src/main/java/org/opentripplanner/graph_builder/module/map/LinearIterator.java deleted file mode 100644 index 562e65e4757..00000000000 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/LinearIterator.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.opentripplanner.graph_builder.module.map; - -import java.util.Iterator; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.Lineal; -import org.locationtech.jts.linearref.LinearLocation; - -/** - * I copied this class from JTS but made a few changes. - *

- * The JTS version of this class has several design decisions that don't work for me. In particular, - * hasNext() in the original should be "isValid", and if we start mid-segment, we should continue at - * the end of this segment rather than the end of the next segment. - */ -public class LinearIterator implements Iterable { - - private final Geometry linearGeom; - - private final int numLines; - - /** - * Invariant: currentLine <> null if the iterator is pointing at a valid coordinate - * - * @throws IllegalArgumentException if linearGeom is not lineal - */ - private LineString currentLine; - - private int componentIndex = 0; - - private int vertexIndex = 0; - - private double segmentFraction; - - /** - * Creates an iterator initialized to the start of a linear {@link Geometry} - * - * @param linear the linear geometry to iterate over - * @throws IllegalArgumentException if linearGeom is not lineal - */ - public LinearIterator(Geometry linear) { - this(linear, 0, 0); - } - - /** - * Creates an iterator starting at a {@link LinearLocation} on a linear {@link Geometry} - * - * @param linear the linear geometry to iterate over - * @param start the location to start at - * @throws IllegalArgumentException if linearGeom is not lineal - */ - public LinearIterator(Geometry linear, LinearLocation start) { - this(linear, start.getComponentIndex(), start.getSegmentIndex()); - this.segmentFraction = start.getSegmentFraction(); - } - - /** - * Creates an iterator starting at a specified component and vertex in a linear {@link Geometry} - * - * @param linearGeom the linear geometry to iterate over - * @param componentIndex the component to start at - * @param vertexIndex the vertex to start at - * @throws IllegalArgumentException if linearGeom is not lineal - */ - public LinearIterator(Geometry linearGeom, int componentIndex, int vertexIndex) { - if (!(linearGeom instanceof Lineal)) throw new IllegalArgumentException( - "Lineal geometry is required" - ); - this.linearGeom = linearGeom; - numLines = linearGeom.getNumGeometries(); - this.componentIndex = componentIndex; - this.vertexIndex = vertexIndex; - loadCurrentLine(); - } - - public static LinearLocation getEndLocation(Geometry linear) { - //the version in LinearLocation is broken - - int lastComponentIndex = linear.getNumGeometries() - 1; - LineString lastLine = (LineString) linear.getGeometryN(lastComponentIndex); - int lastSegmentIndex = lastLine.getNumPoints() - 1; - return new LinearLocation(lastComponentIndex, lastSegmentIndex, 0.0); - } - - /** - * Tests whether there are any vertices left to iterator over. - * - * @return true if there are more vertices to scan - */ - public boolean hasNext() { - if (componentIndex >= numLines) { - return false; - } - if (componentIndex == numLines - 1 && vertexIndex >= currentLine.getNumPoints() - 1) { - return false; - } - return true; - } - - public boolean isValidIndex() { - if (componentIndex >= numLines) { - return false; - } - if (componentIndex == numLines - 1 && vertexIndex >= currentLine.getNumPoints()) { - return false; - } - return true; - } - - /** - * Moves the iterator ahead to the next vertex and (possibly) linear component. - */ - public void next() { - if (!hasNext()) return; - segmentFraction = 0.0; - vertexIndex++; - if (vertexIndex >= currentLine.getNumPoints()) { - componentIndex++; - if (componentIndex < linearGeom.getNumGeometries() - 1) { - loadCurrentLine(); - vertexIndex = 0; - } - } - } - - /** - * Checks whether the iterator cursor is pointing to the endpoint of a linestring. - * - * @return true if the iterator is at an endpoint - */ - public boolean isEndOfLine() { - if (componentIndex >= numLines) { - return false; - } - // LineString currentLine = (LineString) linear.getGeometryN(componentIndex); - if (vertexIndex < currentLine.getNumPoints() - 1) { - return false; - } - return true; - } - - /** - * The component index of the vertex the iterator is currently at. - * - * @return the current component index - */ - public int getComponentIndex() { - return componentIndex; - } - - /** - * The vertex index of the vertex the iterator is currently at. - * - * @return the current vertex index - */ - public int getVertexIndex() { - return vertexIndex; - } - - /** - * Gets the {@link LineString} component the iterator is current at. - * - * @return a linestring - */ - public LineString getLine() { - return currentLine; - } - - /** - * Gets the first {@link Coordinate} of the current segment. (the coordinate of the current - * vertex). - * - * @return a {@link Coordinate} - */ - public Coordinate getSegmentStart() { - return currentLine.getCoordinateN(vertexIndex); - } - - /** - * Gets the second {@link Coordinate} of the current segment. (the coordinate of the next vertex). - * If the iterator is at the end of a line, null is returned. - * - * @return a {@link Coordinate} or null - */ - public Coordinate getSegmentEnd() { - if (vertexIndex < getLine().getNumPoints() - 1) { - return currentLine.getCoordinateN(vertexIndex + 1); - } - return null; - } - - public LinearLocation getLocation() { - return new LinearLocation(componentIndex, vertexIndex, segmentFraction); - } - - @Override - public Iterator iterator() { - return new LinearIteratorIterator(); - } - - private void loadCurrentLine() { - if (componentIndex >= numLines) { - currentLine = null; - return; - } - currentLine = (LineString) linearGeom.getGeometryN(componentIndex); - } - - class LinearIteratorIterator implements Iterator { - - @Override - public boolean hasNext() { - return LinearIterator.this.hasNext(); - } - - @Override - public LinearLocation next() { - LinearLocation result = getLocation(); - LinearIterator.this.next(); - return result; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } -} diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java b/src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java deleted file mode 100644 index 424655eb377..00000000000 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/MatchState.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.opentripplanner.graph_builder.module.map; - -import java.util.ArrayList; -import java.util.List; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.linearref.LinearLocation; -import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; -import org.opentripplanner.routing.api.request.StreetMode; -import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.street.model.edge.StreetEdge; -import org.opentripplanner.street.model.vertex.Vertex; -import org.opentripplanner.street.search.request.StreetSearchRequest; -import org.opentripplanner.street.search.state.State; - -public abstract class MatchState { - - private static final StreetSearchRequest REQUEST = StreetSearchRequest - .of() - .withMode(StreetMode.CAR) - .build(); - - protected static final double NEW_SEGMENT_PENALTY = 0.1; - - protected static final double NO_TRAVERSE_PENALTY = 20; - - public double currentError; - - public double accumulatedError; - - public MatchState parent; - - protected Edge edge; - - private double distanceAlongRoute = 0; - - public MatchState(MatchState parent, Edge edge, double distanceAlongRoute) { - this.distanceAlongRoute = distanceAlongRoute; - this.parent = parent; - this.edge = edge; - if (parent != null) { - this.accumulatedError = parent.accumulatedError + parent.currentError; - this.distanceAlongRoute += parent.distanceAlongRoute; - } - } - - public abstract List getNextStates(); - - public Edge getEdge() { - return edge; - } - - public double getTotalError() { - return accumulatedError + currentError; - } - - public double getDistanceAlongRoute() { - return distanceAlongRoute; - } - - /* computes the distance, in meters, along a geometry */ - protected static double distanceAlongGeometry( - Geometry geometry, - LinearLocation startIndex, - LinearLocation endIndex - ) { - if (endIndex == null) { - endIndex = LinearLocation.getEndLocation(geometry); - } - double total = 0; - LinearIterator it = new LinearIterator(geometry, startIndex); - LinearLocation index = startIndex; - Coordinate previousCoordinate = startIndex.getCoordinate(geometry); - - it.next(); - index = it.getLocation(); - while (index.compareTo(endIndex) < 0) { - Coordinate thisCoordinate = index.getCoordinate(geometry); - double distance = SphericalDistanceLibrary.fastDistance(previousCoordinate, thisCoordinate); - total += distance; - previousCoordinate = thisCoordinate; - if (!it.hasNext()) { - break; - } - it.next(); - index = it.getLocation(); - } - //now, last bit of last segment - Coordinate finalCoordinate = endIndex.getCoordinate(geometry); - total += SphericalDistanceLibrary.distance(previousCoordinate, finalCoordinate); - - return total; - } - - protected static double distance(Coordinate from, Coordinate to) { - return SphericalDistanceLibrary.fastDistance(from, to); - } - - protected boolean carsCanTraverse(Edge edge) { - // should be done with a method on edge (canTraverse already exists on turnEdge) - State s0 = new State(edge.getFromVertex(), REQUEST); - var states = edge.traverse(s0); - return !State.isEmpty(states); - } - - protected List getOutgoingMatchableEdges(Vertex vertex) { - List edges = new ArrayList<>(); - for (Edge e : vertex.getOutgoing()) { - if (!(e instanceof StreetEdge)) { - continue; - } - if (e.getGeometry() == null) { - continue; - } - edges.add(e); - } - return edges; - } -} diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java b/src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java deleted file mode 100644 index 111817e5ed8..00000000000 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/MidblockMatchState.java +++ /dev/null @@ -1,265 +0,0 @@ -package org.opentripplanner.graph_builder.module.map; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.linearref.LinearLocation; -import org.locationtech.jts.linearref.LocationIndexedLine; -import org.locationtech.jts.util.AssertionFailedException; -import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.street.model.vertex.Vertex; - -public class MidblockMatchState extends MatchState { - - private static final double MAX_ERROR = 1000; - - private final LinearLocation edgeIndex; - private final Geometry edgeGeometry; - private final LocationIndexedLine indexedEdge; - public LinearLocation routeIndex; - Geometry routeGeometry; - - public MidblockMatchState( - MatchState parent, - Geometry routeGeometry, - Edge edge, - LinearLocation routeIndex, - LinearLocation edgeIndex, - double error, - double distanceAlongRoute - ) { - super(parent, edge, distanceAlongRoute); - this.routeGeometry = routeGeometry; - this.routeIndex = routeIndex; - this.edgeIndex = edgeIndex; - - edgeGeometry = edge.getGeometry(); - indexedEdge = new LocationIndexedLine(edgeGeometry); - currentError = error; - } - - @Override - public List getNextStates() { - ArrayList nextStates = new ArrayList<>(); - if (routeIndex.getSegmentIndex() == routeGeometry.getNumPoints() - 1) { - // this has either hit the end, or gone off the end. It's not real clear which. - // for now, let's assume it means that the ending is somewhere along this edge, - // so we return an end state - Coordinate pt = routeIndex.getCoordinate(routeGeometry); - double error = distance(pt, edgeIndex.getCoordinate(edgeGeometry)); - nextStates.add(new EndMatchState(this, error, 0)); - return nextStates; - } - - LinearIterator it = new LinearIterator(routeGeometry, routeIndex); - if (it.hasNext()) { - it.next(); - LinearLocation routeSuccessor = it.getLocation(); - - // now we want to see where this new point is in terms of the edge's geometry - Coordinate newRouteCoord = routeSuccessor.getCoordinate(routeGeometry); - LinearLocation newEdgeIndex = indexedEdge.project(newRouteCoord); - - Coordinate edgeCoord = newEdgeIndex.getCoordinate(edgeGeometry); - if (newEdgeIndex.compareTo(edgeIndex) <= 0) { - // we must make forward progress along the edge... or go to the next edge - /* this should not require the try/catch, but there is a bug in JTS */ - try { - LinearLocation projected2 = indexedEdge.indexOfAfter(edgeCoord, edgeIndex); - //another bug in JTS - if (Double.isNaN(projected2.getSegmentFraction())) { - // we are probably moving backwards - return Collections.emptyList(); - } else { - newEdgeIndex = projected2; - if (newEdgeIndex.equals(edgeIndex)) { - return Collections.emptyList(); - } - } - edgeCoord = newEdgeIndex.getCoordinate(edgeGeometry); - } catch (AssertionFailedException e) { - // we are not making progress, so just return an empty list - return Collections.emptyList(); - } - } - - if (newEdgeIndex.getSegmentIndex() == edgeGeometry.getNumPoints() - 1) { - // we might choose to continue from the end of the edge and a point mid-way - // along this route segment - - // find nearest point that makes progress along the route - Vertex toVertex = edge.getToVertex(); - Coordinate endCoord = toVertex.getCoordinate(); - LocationIndexedLine indexedRoute = new LocationIndexedLine(routeGeometry); - - // FIXME: it would be better to do this project/indexOfAfter in one step - // as the two-step version could snap to a bad place and be unable to escape. - - LinearLocation routeProjectedEndIndex = indexedRoute.project(endCoord); - Coordinate routeProjectedEndCoord = routeProjectedEndIndex.getCoordinate(routeGeometry); - - if (routeProjectedEndIndex.compareTo(routeIndex) <= 0) { - try { - routeProjectedEndIndex = indexedRoute.indexOfAfter(routeProjectedEndCoord, routeIndex); - if (Double.isNaN(routeProjectedEndIndex.getSegmentFraction())) { - // can't go forward - routeProjectedEndIndex = routeIndex; // this is bad, but not terrible - // since we are advancing along the edge - } - } catch (AssertionFailedException e) { - routeProjectedEndIndex = routeIndex; - } - routeProjectedEndCoord = routeProjectedEndIndex.getCoordinate(routeGeometry); - } - - double positionError = distance(routeProjectedEndCoord, endCoord); - double travelAlongRoute = distanceAlongGeometry( - routeGeometry, - routeIndex, - routeProjectedEndIndex - ); - double travelAlongEdge = distanceAlongGeometry(edgeGeometry, edgeIndex, newEdgeIndex); - double travelError = Math.abs(travelAlongEdge - travelAlongRoute); - - double error = positionError + travelError; - - if (error > MAX_ERROR) { - // we're not going to bother with states which are - // totally wrong - return nextStates; - } - - for (Edge e : getOutgoingMatchableEdges(toVertex)) { - double cost = error + NEW_SEGMENT_PENALTY; - if (!carsCanTraverse(e)) { - cost += NO_TRAVERSE_PENALTY; - } - MatchState nextState = new MidblockMatchState( - this, - routeGeometry, - e, - routeProjectedEndIndex, - new LinearLocation(), - cost, - travelAlongRoute - ); - nextStates.add(nextState); - } - } else { - double travelAlongEdge = distanceAlongGeometry(edgeGeometry, edgeIndex, newEdgeIndex); - double travelAlongRoute = distanceAlongGeometry(routeGeometry, routeIndex, routeSuccessor); - double travelError = Math.abs(travelAlongRoute - travelAlongEdge); - - double positionError = distance(edgeCoord, newRouteCoord); - - double error = travelError + positionError; - - MatchState nextState = new MidblockMatchState( - this, - routeGeometry, - edge, - routeSuccessor, - newEdgeIndex, - error, - travelAlongRoute - ); - nextStates.add(nextState); - - // it's also possible that, although we have not yet reached the end of this edge, - // we are going to turn, because the route turns earlier than the edge. In that - // case, we jump to the corner, and our error is the distance from the route point - // and the corner - - Vertex toVertex = edge.getToVertex(); - double travelAlongOldEdge = distanceAlongGeometry(edgeGeometry, edgeIndex, null); - - for (Edge e : getOutgoingMatchableEdges(toVertex)) { - Geometry newEdgeGeometry = e.getGeometry(); - LocationIndexedLine newIndexedEdge = new LocationIndexedLine(newEdgeGeometry); - newEdgeIndex = newIndexedEdge.project(newRouteCoord); - Coordinate newEdgeCoord = newEdgeIndex.getCoordinate(newEdgeGeometry); - positionError = distance(newEdgeCoord, newRouteCoord); - travelAlongEdge = - travelAlongOldEdge + - distanceAlongGeometry(newEdgeGeometry, new LinearLocation(), newEdgeIndex); - travelError = Math.abs(travelAlongRoute - travelAlongEdge); - - error = travelError + positionError; - - if (error > MAX_ERROR) { - // we're not going to bother with states which are - // totally wrong - return nextStates; - } - - double cost = error + NEW_SEGMENT_PENALTY; - if (!carsCanTraverse(e)) { - cost += NO_TRAVERSE_PENALTY; - } - - nextState = - new MidblockMatchState( - this, - routeGeometry, - e, - routeSuccessor, - new LinearLocation(), - cost, - travelAlongRoute - ); - nextStates.add(nextState); - } - } - return nextStates; - } else { - Coordinate routeCoord = routeIndex.getCoordinate(routeGeometry); - LinearLocation projected = indexedEdge.project(routeCoord); - double locationError = distance(projected.getCoordinate(edgeGeometry), routeCoord); - - MatchState end = new EndMatchState(this, locationError, 0); - return Arrays.asList(end); - } - } - - public int hashCode() { - return (edge.hashCode() * 1337 + hashCode(edgeIndex)) * 1337 + hashCode(routeIndex); - } - - public boolean equals(Object o) { - if (!(o instanceof MidblockMatchState)) { - return false; - } - MidblockMatchState other = (MidblockMatchState) o; - return ( - other.edge == edge && - other.edgeIndex.compareTo(edgeIndex) == 0 && - other.routeIndex.compareTo(routeIndex) == 0 - ); - } - - public String toString() { - return ( - "MidblockMatchState(" + - edge + - ", " + - edgeIndex.getSegmentIndex() + - ", " + - edgeIndex.getSegmentFraction() + - ") - " + - currentError - ); - } - - private int hashCode(LinearLocation location) { - return ( - location.getComponentIndex() * - 1000000 + - location.getSegmentIndex() * - 37 + - Double.valueOf(location.getSegmentFraction()).hashCode() - ); - } -} diff --git a/src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java b/src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java deleted file mode 100644 index e8bc92cc46f..00000000000 --- a/src/main/java/org/opentripplanner/graph_builder/module/map/StreetMatcher.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.opentripplanner.graph_builder.module.map; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.index.strtree.STRtree; -import org.locationtech.jts.linearref.LinearLocation; -import org.locationtech.jts.linearref.LocationIndexedLine; -import org.locationtech.jts.simplify.DouglasPeuckerSimplifier; -import org.opentripplanner.astar.model.BinHeap; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.street.model.edge.StreetEdge; -import org.opentripplanner.street.model.vertex.Vertex; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This Performs most of the work for the MapBuilder graph builder module. It determines which - * sequence of graph edges a GTFS shape probably corresponds to. Note that GTFS shapes are not in - * any way constrained to OSM edges or even roads. - */ -public class StreetMatcher { - - private static final Logger log = LoggerFactory.getLogger(StreetMatcher.class); - private static final double DISTANCE_THRESHOLD = 0.0002; - private final STRtree index; - Graph graph; - - public StreetMatcher(Graph graph) { - this.graph = graph; - index = createIndex(); - index.build(); - } - - @SuppressWarnings("unchecked") - public List match(Geometry routeGeometry) { - routeGeometry = removeDuplicatePoints(routeGeometry); - - if (routeGeometry == null) { - return null; - } - - routeGeometry = DouglasPeuckerSimplifier.simplify(routeGeometry, 0.00001); - - // initial state: start midway along a block. - LocationIndexedLine indexedLine = new LocationIndexedLine(routeGeometry); - - LinearLocation startIndex = indexedLine.getStartIndex(); - - Coordinate routeStartCoordinate = startIndex.getCoordinate(routeGeometry); - Envelope envelope = new Envelope(routeStartCoordinate); - double distanceThreshold = DISTANCE_THRESHOLD; - envelope.expandBy(distanceThreshold); - - BinHeap states = new BinHeap<>(); - List nearbyEdges = index.query(envelope); - while (nearbyEdges.isEmpty()) { - envelope.expandBy(distanceThreshold); - distanceThreshold *= 2; - nearbyEdges = index.query(envelope); - } - - // compute initial states - for (Edge initialEdge : nearbyEdges) { - Geometry edgeGeometry = initialEdge.getGeometry(); - - LocationIndexedLine indexedEdge = new LocationIndexedLine(edgeGeometry); - LinearLocation initialLocation = indexedEdge.project(routeStartCoordinate); - - double error = MatchState.distance( - initialLocation.getCoordinate(edgeGeometry), - routeStartCoordinate - ); - MidblockMatchState state = new MidblockMatchState( - null, - routeGeometry, - initialEdge, - startIndex, - initialLocation, - error, - 0.01 - ); - states.insert(state, 0); //make sure all initial states are visited by inserting them at 0 - } - - // search for best-matching path - int seen_count = 0, total = 0; - HashSet seen = new HashSet<>(); - while (!states.empty()) { - double k = states.peek_min_key(); - MatchState state = states.extract_min(); - if (++total % 50000 == 0) { - log.debug("seen / total: {} / {}", seen_count, total); - } - if (seen.contains(state)) { - ++seen_count; - continue; - } else { - if (k != 0) { - //but do not mark states as closed if we start at them - seen.add(state); - } - } - if (state instanceof EndMatchState) { - return toEdgeList(state); - } - for (MatchState next : state.getNextStates()) { - if (seen.contains(next)) { - continue; - } - states.insert(next, next.getTotalError() - next.getDistanceAlongRoute()); - } - } - return null; - } - - STRtree createIndex() { - STRtree edgeIndex = new STRtree(); - for (Vertex v : graph.getVertices()) { - for (Edge e : v.getOutgoing()) { - if (e instanceof StreetEdge) { - Envelope envelope; - Geometry geometry = e.getGeometry(); - envelope = geometry.getEnvelopeInternal(); - edgeIndex.insert(envelope, e); - } - } - } - log.debug("Created index"); - return edgeIndex; - } - - private Geometry removeDuplicatePoints(Geometry routeGeometry) { - List coords = new ArrayList<>(); - Coordinate last = null; - for (Coordinate c : routeGeometry.getCoordinates()) { - if (!c.equals(last)) { - last = c; - coords.add(c); - } - } - if (coords.size() < 2) { - return null; - } - Coordinate[] coordArray = new Coordinate[coords.size()]; - return routeGeometry.getFactory().createLineString(coords.toArray(coordArray)); - } - - private List toEdgeList(MatchState next) { - ArrayList edges = new ArrayList<>(); - Edge lastEdge = null; - while (next != null) { - Edge edge = next.getEdge(); - if (edge != lastEdge) { - edges.add(edge); - lastEdge = edge; - } - next = next.parent; - } - Collections.reverse(edges); - return edges; - } -} diff --git a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index 4a56a0722e4..d62aa1bf41f 100644 --- a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -114,8 +114,6 @@ public class BuildConfig implements OtpDataStoreConfig { public final boolean platformEntriesLinking; - public final boolean matchBusRoutesToStreets; - /** See {@link S3BucketConfig}. */ public final S3BucketConfig elevationBucket; @@ -271,14 +269,6 @@ When set to true (it is false by default), the elevation module will include the islandPruning = IslandPruningConfig.fromConfig(root); - matchBusRoutesToStreets = - root - .of("matchBusRoutesToStreets") - .since(V1_5) - .summary( - "Based on GTFS shape data, guess which OSM streets each bus runs on to improve stop linking." - ) - .asBoolean(false); maxDataImportIssuesPerFile = root .of("maxDataImportIssuesPerFile") diff --git a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java index e89ea1940f6..7057d9fd56e 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java +++ b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java @@ -159,11 +159,6 @@ public StopPattern getStopPattern() { return stopPattern; } - // TODO OTP2 this method modifies the state, it will be refactored in a subsequent step - public void setHopGeometry(int i, LineString hopGeometry) { - this.hopGeometries[i] = CompactLineStringUtils.compactLineString(hopGeometry, false); - } - public LineString getGeometry() { if (hopGeometries == null || hopGeometries.length == 0) { return null; @@ -176,10 +171,6 @@ public LineString getGeometry() { return GeometryUtils.concatenateLineStrings(lineStrings); } - public int numHopGeometries() { - return hopGeometries.length; - } - public int numberOfStops() { return stopPattern.getSize(); } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/map/StreetMatcherTest.java b/src/test/java/org/opentripplanner/graph_builder/module/map/StreetMatcherTest.java deleted file mode 100644 index 92001fbf8a7..00000000000 --- a/src/test/java/org/opentripplanner/graph_builder/module/map/StreetMatcherTest.java +++ /dev/null @@ -1,157 +0,0 @@ -package org.opentripplanner.graph_builder.module.map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.street.model._data.StreetModelForTest; -import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.street.model.vertex.SimpleVertex; -import org.opentripplanner.street.model.vertex.StreetVertex; -import org.opentripplanner.street.model.vertex.VertexLabel; - -public class StreetMatcherTest { - - static GeometryFactory gf = new GeometryFactory(); - - private Graph graph; - - @BeforeEach - public void before() { - graph = new Graph(); - - vertex("56th_24th", 47.669457, -122.387577); - vertex("56th_22nd", 47.669462, -122.384739); - vertex("56th_20th", 47.669457, -122.382106); - - vertex("market_24th", 47.668690, -122.387577); - vertex("market_ballard", 47.668683, -122.386096); - vertex("market_22nd", 47.668686, -122.384749); - vertex("market_leary", 47.668669, -122.384392); - vertex("market_russell", 47.668655, -122.382997); - vertex("market_20th", 47.668684, -122.382117); - - vertex("shilshole_24th", 47.668419, -122.387534); - vertex("shilshole_22nd", 47.666519, -122.384744); - vertex("shilshole_vernon", 47.665938, -122.384048); - vertex("shilshole_20th", 47.664356, -122.382192); - - vertex("ballard_turn", 47.668509, -122.386069); - vertex("ballard_22nd", 47.667624, -122.384744); - vertex("ballard_vernon", 47.666422, -122.383158); - vertex("ballard_20th", 47.665476, -122.382128); - - vertex("leary_vernon", 47.666863, -122.382353); - vertex("leary_20th", 47.666682, -122.382160); - - vertex("russell_20th", 47.667846, -122.382128); - - edges("56th_24th", "56th_22nd", "56th_20th"); - - edges("56th_24th", "market_24th"); - edges("56th_22nd", "market_22nd"); - edges("56th_20th", "market_20th"); - - edges( - "market_24th", - "market_ballard", - "market_22nd", - "market_leary", - "market_russell", - "market_20th" - ); - edges("market_24th", "shilshole_24th", "shilshole_22nd", "shilshole_vernon", "shilshole_20th"); - edges("market_ballard", "ballard_turn", "ballard_22nd", "ballard_vernon", "ballard_20th"); - edges("market_leary", "leary_vernon", "leary_20th"); - edges("market_russell", "russell_20th"); - - edges("market_22nd", "ballard_22nd", "shilshole_22nd"); - edges("leary_vernon", "ballard_vernon", "shilshole_vernon"); - edges("market_20th", "russell_20th", "leary_20th", "ballard_20th", "shilshole_20th"); - } - - @Test - public void testStreetMatcher() { - LineString geometry = geometry(-122.385689, 47.669484, -122.387384, 47.669470); - - StreetMatcher matcher = new StreetMatcher(graph); - - List match = matcher.match(geometry); - assertNotNull(match); - assertEquals(1, match.size()); - assertEquals("56th_24th", match.get(0).getToVertex().getLabelString()); - - geometry = geometry(-122.385689, 47.669484, -122.387384, 47.669470, -122.387588, 47.669325); - - match = matcher.match(geometry); - assertNotNull(match); - assertEquals(2, match.size()); - - geometry = - geometry( - -122.385689, - 47.669484, - -122.387384, - 47.669470, - -122.387588, - 47.669325, - -122.387255, - 47.668675 - ); - - match = matcher.match(geometry); - assertNotNull(match); - assertEquals(3, match.size()); - - geometry = - geometry( - -122.384756, - 47.669260, - -122.384777, - 47.667454, - -122.383554, - 47.666789, - -122.3825, - 47.666 - ); - match = matcher.match(geometry); - assertNotNull(match); - assertEquals(4, match.size()); - assertEquals("ballard_20th", match.get(3).getToVertex().getLabelString()); - } - - private LineString geometry(double... ordinates) { - Coordinate[] coords = new Coordinate[ordinates.length / 2]; - - for (int i = 0; i < ordinates.length; i += 2) { - coords[i / 2] = new Coordinate(ordinates[i], ordinates[i + 1]); - } - return gf.createLineString(coords); - } - - /**** - * Private Methods - ****/ - - private SimpleVertex vertex(String label, double lat, double lon) { - var v = new SimpleVertex(label, lat, lon); - graph.addVertex(v); - return v; - } - - private void edges(String... vLabels) { - for (int i = 0; i < vLabels.length - 1; i++) { - StreetVertex vA = (StreetVertex) graph.getVertex(VertexLabel.string(vLabels[i])); - StreetVertex vB = (StreetVertex) graph.getVertex(VertexLabel.string(vLabels[i + 1])); - - StreetModelForTest.streetEdge(vA, vB); - StreetModelForTest.streetEdge(vB, vA); - } - } -} From 515bd953eaf1c26c2fc625de5338fb21500b34d9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 22 Nov 2023 13:06:23 +0100 Subject: [PATCH 06/39] Remove property from example files --- docs/examples/entur/build-config.json | 1 - test/performance/norway/build-config.json | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/examples/entur/build-config.json b/docs/examples/entur/build-config.json index 3829e975f34..e9351882774 100644 --- a/docs/examples/entur/build-config.json +++ b/docs/examples/entur/build-config.json @@ -5,7 +5,6 @@ "embedRouterConfig": true, "areaVisibility": true, "platformEntriesLinking": true, - "matchBusRoutesToStreets": false, "staticParkAndRide": true, "staticBikeParkAndRide": true, "maxDataImportIssuesPerFile": 1000, diff --git a/test/performance/norway/build-config.json b/test/performance/norway/build-config.json index 5fa7f03ed58..83de34a027b 100644 --- a/test/performance/norway/build-config.json +++ b/test/performance/norway/build-config.json @@ -6,7 +6,6 @@ "embedRouterConfig": true, "areaVisibility": true, "platformEntriesLinking": true, - "matchBusRoutesToStreets": false, "staticParkAndRide": true, "staticBikeParkAndRide": true, "maxDataImportIssuesPerFile": 1000, From d05ac15a19da14909740cf4dd89e99ce82583fb3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 Nov 2023 10:15:19 +0100 Subject: [PATCH 07/39] Remove container images that have not been pulled for over a year --- .github/workflows/prune-container-images.yml | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/prune-container-images.yml diff --git a/.github/workflows/prune-container-images.yml b/.github/workflows/prune-container-images.yml new file mode 100644 index 00000000000..de5954b9ba9 --- /dev/null +++ b/.github/workflows/prune-container-images.yml @@ -0,0 +1,22 @@ +name: 'Delete unused snapshot container images' + +on: + schedule: + - cron: '0 12 1 * *' + workflow_dispatch: + +jobs: + container-image: + if: github.repository_owner == 'opentripplanner' + runs-on: ubuntu-latest + steps: + - name: Delete unused container images + env: + CONTAINER_REPO: opentripplanner/opentripplanner + CONTAINER_REGISTRY_USER: otpbot + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + run: | + # remove all snapshot container images that have not been pulled for over a year + # --keep-semver makes sure that any image with a x.y.z version scheme is unaffected by this + pip install prune-container-repo==0.0.3 + prune-container-repo -u ${CONTAINER_REGISTRY_USER} -r ${CONTAINER_REPO} --days=365 --keep-semver --activate From 8ce8061df23431709fac6968f1d740c930c51a7c Mon Sep 17 00:00:00 2001 From: Assad Riaz Date: Fri, 24 Nov 2023 20:25:54 +0100 Subject: [PATCH 08/39] Rename realtime to real-time in docs --- docs/Changelog.md | 36 +++++++++---------- docs/Configuration.md | 2 +- docs/Data-Sources.md | 2 +- docs/Deployments.md | 4 +-- docs/Netex-Norway.md | 10 +++--- docs/RouteRequest.md | 4 +-- docs/RouterConfiguration.md | 2 +- docs/SandboxExtension.md | 4 +-- docs/System-Requirements.md | 2 +- docs/examples/skanetrafiken/Readme.md | 10 +++--- .../examples/skanetrafiken/router-config.json | 2 +- docs/sandbox/MapboxVectorTilesApi.md | 8 ++--- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/Changelog.md b/docs/Changelog.md index bccd0c504d5..65b2650f38b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -40,7 +40,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Interpolate increasing stop times for GTFS-RT cancelled trips [#5348](https://github.com/opentripplanner/OpenTripPlanner/pull/5348) - Remove itineraries outside the search window in arriveBy search [#5433](https://github.com/opentripplanner/OpenTripPlanner/pull/5433) - Add back walk-reluctance in Transmodel API [#5471](https://github.com/opentripplanner/OpenTripPlanner/pull/5471) -- Make `feedId` required for realtime updaters [#5502](https://github.com/opentripplanner/OpenTripPlanner/pull/5502) +- Make `feedId` required for real-time updaters [#5502](https://github.com/opentripplanner/OpenTripPlanner/pull/5502) - Fix serialization of `AtomicInteger` [#5508](https://github.com/opentripplanner/OpenTripPlanner/pull/5508) - Improve linking of fixed stops used by flex trips [#5503](https://github.com/opentripplanner/OpenTripPlanner/pull/5503) - Keep min transfer filter is not local to group-by-filters [#5436](https://github.com/opentripplanner/OpenTripPlanner/pull/5436) @@ -88,7 +88,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Graceful timeout error handling [#5130](https://github.com/opentripplanner/OpenTripPlanner/pull/5130) - Log http request headers - like correlationId [#5131](https://github.com/opentripplanner/OpenTripPlanner/pull/5131) - Fix vertex removal race condition [#5141](https://github.com/opentripplanner/OpenTripPlanner/pull/5141) -- Comment out replacing DSJ-ID from planned data with ID from realtime-data [#5140](https://github.com/opentripplanner/OpenTripPlanner/pull/5140) +- Comment out replacing DSJ-ID from planned data with ID from real-time-data [#5140](https://github.com/opentripplanner/OpenTripPlanner/pull/5140) - Remove San Francisco and vehicle rental fare calculators [#5145](https://github.com/opentripplanner/OpenTripPlanner/pull/5145) - Remove batch query from Transmodel API [#5147](https://github.com/opentripplanner/OpenTripPlanner/pull/5147) - Fix nullable absolute direction in GTFS GraphQL API [#5159](https://github.com/opentripplanner/OpenTripPlanner/pull/5159) @@ -186,7 +186,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Remove all edges from stop vertex in island pruning [#4846](https://github.com/opentripplanner/OpenTripPlanner/pull/4846) - Filter functionality for GroupOfLines/GroupOfRoutes in TransmodelAPI [#4812](https://github.com/opentripplanner/OpenTripPlanner/pull/4812) - Mapping for maxAccessEgressDurationPerMode in Transmodel API [#4829](https://github.com/opentripplanner/OpenTripPlanner/pull/4829) -- Use headsign from the original pattern in a realtime added pattern if the stop sequence is unchanged [#4845](https://github.com/opentripplanner/OpenTripPlanner/pull/4845) +- Use headsign from the original pattern in a real-time added pattern if the stop sequence is unchanged [#4845](https://github.com/opentripplanner/OpenTripPlanner/pull/4845) - Remove RouteMatcher [#4821](https://github.com/opentripplanner/OpenTripPlanner/pull/4821) - Improve boarding location linking on platforms [#4852](https://github.com/opentripplanner/OpenTripPlanner/pull/4852) - Always check allowed modes in VehicleRentalEdge [#4810](https://github.com/opentripplanner/OpenTripPlanner/pull/4810) @@ -234,7 +234,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Experimental support for GTFS Fares V2 [#4338](https://github.com/opentripplanner/OpenTripPlanner/pull/4338) - Document JVM configuration options [#4492](https://github.com/opentripplanner/OpenTripPlanner/pull/4492) - Support for HTTPS datasource for Graph Building [#4482](https://github.com/opentripplanner/OpenTripPlanner/pull/4482) -- Metrics for realtime trip updaters [#4471](https://github.com/opentripplanner/OpenTripPlanner/pull/4471) +- Metrics for real-time trip updaters [#4471](https://github.com/opentripplanner/OpenTripPlanner/pull/4471) - Configuration Documentation generated programmatically [#4478](https://github.com/opentripplanner/OpenTripPlanner/pull/4478) @@ -265,7 +265,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Enable overriding maxDirectStreetDuration per mode [#4104](https://github.com/opentripplanner/OpenTripPlanner/pull/4104) - Preserve language in SIRI/GTFS-RT alert messages [#4117](https://github.com/opentripplanner/OpenTripPlanner/pull/4117) - Use board/alight cost only for transits [#4079](https://github.com/opentripplanner/OpenTripPlanner/pull/4079) -- Improve SIRI realtime performance by reducing stopPattern duplicates [#4038](https://github.com/opentripplanner/OpenTripPlanner/pull/4038) +- Improve SIRI real-time performance by reducing stopPattern duplicates [#4038](https://github.com/opentripplanner/OpenTripPlanner/pull/4038) - Siri updaters for Azure ServiceBus [#4106](https://github.com/opentripplanner/OpenTripPlanner/pull/4106) - Fallback to recorded/expected arrival/departure time if other one is missing in SIRI-ET [#4055](https://github.com/opentripplanner/OpenTripPlanner/pull/4055) - Allow overriding GBFS system_id with configuration [#4147](https://github.com/opentripplanner/OpenTripPlanner/pull/4147) @@ -274,7 +274,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Don't indicate stop has been updated when NO_DATA is defined [#3962](https://github.com/opentripplanner/OpenTripPlanner/pull/3962) - Implement nearby searches for car and bicycle parking [#4165](https://github.com/opentripplanner/OpenTripPlanner/pull/4165) - Do not link cars to stop vertices in routing [#4166](https://github.com/opentripplanner/OpenTripPlanner/pull/4166) -- Add Siri realtime occupancy info [#4180](https://github.com/opentripplanner/OpenTripPlanner/pull/4180) +- Add Siri real-time occupancy info [#4180](https://github.com/opentripplanner/OpenTripPlanner/pull/4180) - Add gtfs stop description translations [#4158](https://github.com/opentripplanner/OpenTripPlanner/pull/4158) - Add option to discard min transfer times [#4195](https://github.com/opentripplanner/OpenTripPlanner/pull/4195) - Use negative delay from first stop in a GTFS RT update in previous stop times when required [#4035](https://github.com/opentripplanner/OpenTripPlanner/pull/4035) @@ -303,7 +303,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Account for boarding restrictions when calculating direct transfers [#4421](https://github.com/opentripplanner/OpenTripPlanner/pull/4421) - Configure the import of OSM extracts individually [#4419](https://github.com/opentripplanner/OpenTripPlanner/pull/4419) - Configure the import of elevation data individually [#4423](https://github.com/opentripplanner/OpenTripPlanner/pull/4423) -- Return typed errors from realtime updates, prepare for realtime statistics [#4424](https://github.com/opentripplanner/OpenTripPlanner/pull/4424) +- Return typed errors from real-time updates, prepare for real-time statistics [#4424](https://github.com/opentripplanner/OpenTripPlanner/pull/4424) - Add feature switch for matching ET messages on stops [#4347](https://github.com/opentripplanner/OpenTripPlanner/pull/4347) - Make safety defaults customizable for walking and cycling [#4438](https://github.com/opentripplanner/OpenTripPlanner/pull/4438) - Fix block-based interlining when importing several GTFS feeds [#4468](https://github.com/opentripplanner/OpenTripPlanner/pull/4468) @@ -341,7 +341,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle ### Detailed changes by Pull Request -- Fix NullPointerException when a RealTime update do not match an existing TripPattern [#3284](https://github.com/opentripplanner/OpenTripPlanner/issues/3284) +- Fix NullPointerException when a Real-Time update do not match an existing TripPattern [#3284](https://github.com/opentripplanner/OpenTripPlanner/issues/3284) - Support for versioning the configuration files [#3282](https://github.com/opentripplanner/OpenTripPlanner/issues/3282) - Prioritize "direct" routes over transfers in group-filters [#3309](https://github.com/opentripplanner/OpenTripPlanner/issues/3309) - Remove poor transit results for short trips, when walking is better [#3331](https://github.com/opentripplanner/OpenTripPlanner/issues/3331) @@ -428,7 +428,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Remove build parameter 'useTransfersTxt' [#3791](https://github.com/opentripplanner/OpenTripPlanner/pull/3791) - Add cursor-based paging [#3759](https://github.com/opentripplanner/OpenTripPlanner/pull/3759) - Data overlay sandbox feature [#3760](https://github.com/opentripplanner/OpenTripPlanner/pull/3760) -- Add support for sandboxed realtime vehicle parking updaters [#3796](https://github.com/opentripplanner/OpenTripPlanner/pull/3796) +- Add support for sandboxed real-time vehicle parking updaters [#3796](https://github.com/opentripplanner/OpenTripPlanner/pull/3796) - Add reading and exposing of Netex submodes [#3793](https://github.com/opentripplanner/OpenTripPlanner/pull/3793) - Fix: Account for wait-time in no-wait Raptor strategy [#3798](https://github.com/opentripplanner/OpenTripPlanner/pull/3798) - Read in flex window from Netex feeds [#3800](https://github.com/opentripplanner/OpenTripPlanner/pull/3800) @@ -438,7 +438,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Store stop indices in leg and use them to simplify logic in TripTimeShortHelper [#3820](https://github.com/opentripplanner/OpenTripPlanner/pull/3820) - Include all trips in `stopTimesForStop` [#3817](https://github.com/opentripplanner/OpenTripPlanner/pull/3817) - Store all alerts and add support for route_type and direction_id selectors [#3780](https://github.com/opentripplanner/OpenTripPlanner/pull/3780) -- Remove outdated realtime-update from TimetableSnapshot [#3770](https://github.com/opentripplanner/OpenTripPlanner/pull/3770) +- Remove outdated real-time-update from TimetableSnapshot [#3770](https://github.com/opentripplanner/OpenTripPlanner/pull/3770) - Contributing Guide [#3769](https://github.com/opentripplanner/OpenTripPlanner/pull/3769) - OTP support for NeTEx branding [#3829](https://github.com/opentripplanner/OpenTripPlanner/pull/3829) - Not allowed transfers and support for GTFS transfer points [#3792](https://github.com/opentripplanner/OpenTripPlanner/pull/3792) @@ -465,7 +465,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Optimize RAPTOR trip search by pre-calculating arrival/departure time arrays [#3919](https://github.com/opentripplanner/OpenTripPlanner/pull/3919) - Make turn restrictions faster and thread-safe by moving them into StreetEdge [#3899](https://github.com/opentripplanner/OpenTripPlanner/pull/3899) - Add routing using frequency trips [#3916](https://github.com/opentripplanner/OpenTripPlanner/pull/3916) -- Remove ET realtime override code [#3912](https://github.com/opentripplanner/OpenTripPlanner/pull/3912) +- Remove ET real-time override code [#3912](https://github.com/opentripplanner/OpenTripPlanner/pull/3912) - Allow traversal of pathways without traversal time, distance or steps [#3910](https://github.com/opentripplanner/OpenTripPlanner/pull/3910) @@ -495,8 +495,8 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Support for next/previous paging trip search results [#2941](https://github.com/opentripplanner/OpenTripPlanner/issues/2941) - Fix mismatch in duration for walk legs, resulting in negative wait times [#2955](https://github.com/opentripplanner/OpenTripPlanner/issues/2955) - NeTEx import now supports ServiceLinks [#2951](https://github.com/opentripplanner/OpenTripPlanner/issues/2951) -- Also check TripPatterns added by realtime when showing stoptimes for stop [#2954](https://github.com/opentripplanner/OpenTripPlanner/issues/2954) -- Copy geometries from previous TripPattern when realtime updates result in a TripPattern being replaced [#2987](https://github.com/opentripplanner/OpenTripPlanner/issues/2987) +- Also check TripPatterns added by real-time when showing stoptimes for stop [#2954](https://github.com/opentripplanner/OpenTripPlanner/issues/2954) +- Copy geometries from previous TripPattern when real-time updates result in a TripPattern being replaced [#2987](https://github.com/opentripplanner/OpenTripPlanner/issues/2987) - Support for the Norwegian language. - Update pathways support to official GTFS specification [#2923](https://github.com/opentripplanner/OpenTripPlanner/issues/2923) - Support for XML (de-)serialization is REMOVED from the REST API [#3031](https://github.com/opentripplanner/OpenTripPlanner/issues/3031) @@ -532,7 +532,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Remove the coupling to OneBusAway GTFS within OTP's internal model by creating new classes replacing the external classes [#2494](https://github.com/opentripplanner/OpenTripPlanner/issues/2494) - Allow itineraries in response to be sorted by duration [#2593](https://github.com/opentripplanner/OpenTripPlanner/issues/2593) - Fix reverse optimization bug #2653, #2411 -- increase GTFS-realtime feeds size limit from 64MB to 2G [#2738](https://github.com/opentripplanner/OpenTripPlanner/issues/2738) +- increase GTFS-real-time feeds size limit from 64MB to 2G [#2738](https://github.com/opentripplanner/OpenTripPlanner/issues/2738) - Fix XML response serialization [#2685](https://github.com/opentripplanner/OpenTripPlanner/issues/2685) - Refactor InterleavedBidirectionalHeuristic [#2671](https://github.com/opentripplanner/OpenTripPlanner/issues/2671) - Add "Accept" headers to GTFS-RT HTTP requests [#2796](https://github.com/opentripplanner/OpenTripPlanner/issues/2796) @@ -544,7 +544,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Support OSM highway=razed tag [#2660](https://github.com/opentripplanner/OpenTripPlanner/issues/2660) - Add bicimad bike rental updater [#2503](https://github.com/opentripplanner/OpenTripPlanner/issues/2503) - Add Smoove citybikes updater [#2515](https://github.com/opentripplanner/OpenTripPlanner/issues/2515) -- Allow big GTFS-realtime feeds by increasing protobuf size limit to 2G [#2739](https://github.com/opentripplanner/OpenTripPlanner/issues/2739) +- Allow big GTFS-real-time feeds by increasing protobuf size limit to 2G [#2739](https://github.com/opentripplanner/OpenTripPlanner/issues/2739) - Cannot transfer between stops at exactly the same location [#2371](https://github.com/opentripplanner/OpenTripPlanner/issues/2371) - Improve documentation for `mode` routing parameter [#2809](https://github.com/opentripplanner/OpenTripPlanner/issues/2809) - Switched to single license file, removing all OTP and OBA file license headers @@ -781,7 +781,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Disable some time consuming graph building steps by default - Finnish and Swedish translations - Subway-specific JSON configuration options (street to platform time) -- Realtime fetch / streaming configurable via JSON +- Real-time fetch / streaming configurable via JSON - Stairs reluctance is much higher when carrying a bike - Graph visualizer routing progress animates when a search is triggered via the web API - Assume WGS84 (spherical distance calculations) everywhere @@ -849,7 +849,7 @@ represents a much earlier stage in the development of OTP. ## 0.7.0 (2012-04-29) - Bike rental support (thanks Laurent Grégoire) -- Realtime bike rental availability feed support +- Real-time bike rental availability feed support - Updated to new version of One Bus Away GTFS/CSV, fixing timezone and string interning issues (thanks Brian Ferris) - Bugfixes in area routing, OSM loading, nonexistant NED tiles, route short names - Dutch and French language updates @@ -876,7 +876,7 @@ represents a much earlier stage in the development of OTP. - git commit IDs included in MavenVersion, allowing clearer OTP/Graph version mismatch warnings - fix problems with immediate reboarding and unexpected edges in itinerary builder - favicon (thanks Joel Haasnoot) -- Legs in API response have TripId (for realtime information) +- Legs in API response have TripId (for real-time information) - Polish locale (thanks Łukasz Witkowski) - transfers.txt can define station paths, entry costs for stations - allow loading a base graph into graphbuilder instead of starting from scratch diff --git a/docs/Configuration.md b/docs/Configuration.md index bfd168d5955..6da188c7a1d 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -241,7 +241,7 @@ Here is a list of all features which can be toggled on/off and their default val | `FaresV2` | Enable import of GTFS-Fares v2 data. | | ✓️ | | `FlexRouting` | Enable FLEX routing. | | ✓️ | | `GoogleCloudStorage` | Enable Google Cloud Storage integration. | | ✓️ | -| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with realtime data | | ✓️ | +| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data | | ✓️ | | `ReportApi` | Enable the report API. | | ✓️ | | `RestAPIPassInDefaultConfigAsJson` | Enable a default RouteRequest to be passed in as JSON on the REST API - FOR DEBUGGING ONLY! | | | | `SandboxAPIGeocoder` | Enable the Geocoder API. | | ✓️ | diff --git a/docs/Data-Sources.md b/docs/Data-Sources.md index 317e0a1f2f7..bf426186f57 100644 --- a/docs/Data-Sources.md +++ b/docs/Data-Sources.md @@ -5,7 +5,7 @@ At the core of OpenTripPlanner is a library of Java code that finds efficient paths through multi-modal transportation networks built from [OpenStreetMap](http://wiki.openstreetmap.org/wiki/Main_Page) -and [GTFS](https://developers.google.com/transit/gtfs/) data. It can also receive GTFS-RT (realtime) +and [GTFS](https://developers.google.com/transit/gtfs/) data. It can also receive GTFS-RT (real-time) data. In addition to GTFS, OTP can also load data in the Nordic Profile of Netex, the EU-standard transit diff --git a/docs/Deployments.md b/docs/Deployments.md index f2946a9f0ba..d1df3984b05 100644 --- a/docs/Deployments.md +++ b/docs/Deployments.md @@ -6,7 +6,7 @@ The following are known deployments of OTP in a government- or agency-sponsored * **Norway (nationwide)** Since November 2017, the national integrated ticketing agency Entur has prodvided a [national journey planner](https://en-tur.no/) which consumes schedule data in the EU - standard NeTEx format with SIRI realtime updates. Entur has contributed greatly to the OTP2 effort + standard NeTEx format with SIRI real-time updates. Entur has contributed greatly to the OTP2 effort and primarily uses OTP2 in production, handling peak loads in excess of 20 requests per second. Most regional agencies in Norway, like **Ruter, Oslo area** uses OTP as a service provided by Entur. * **Finland (nationwide)** The [Helsinki Regional Transport Authority](https://www.reittiopas.fi/), @@ -22,7 +22,7 @@ The following are known deployments of OTP in a government- or agency-sponsored service [Matkahuolto](https://en.wikipedia.org/wiki/Matkahuolto) has [developed a trip planner in partnership with Kyyti](https://www.kyyti.com/matkahuoltos-new-app-brings-real-travel-chains-within-the-reach-of-citizens-in-addition-to-coach-travel-hsl-tickets-are-also-available/). * **Skåne, Sweden**, the JourneyPlanner and mobile app for the regional transit agency [Skånetrafiken](https://www.skanetrafiken.se/) - uses OTP2 with the nordic profile of NeTEx and SIRI for realtime updates. + uses OTP2 with the nordic profile of NeTEx and SIRI for real-time updates. * [**Northern Colorado**](https://discover.rideno.co/) * [**Philadelphia and surrounding areas**](https://plan.septa.org) * **Portland, Oregon** TriMet is the agency that originally started the OpenTripPlanner project. diff --git a/docs/Netex-Norway.md b/docs/Netex-Norway.md index 6157849bedc..0a447237592 100644 --- a/docs/Netex-Norway.md +++ b/docs/Netex-Norway.md @@ -85,11 +85,11 @@ using OTP's built in testing web client. Try some long trips like Oslo to Bergen get long distance trains and flights as alternatives. You might need to increase the walking limit above its very low default value. -## Adding SIRI Realtime Data +## Adding SIRI Real-time Data Another important feature in OTP2 is the ability to -use [SIRI realtime data](https://en.wikipedia.org/wiki/Service_Interface_for_Real_Time_Information). -Within the EU data standards, SIRI is analogous to GTFS-RT: a way to apply realtime updates on top +use [SIRI real-time data](https://en.wikipedia.org/wiki/Service_Interface_for_Real_Time_Information). +Within the EU data standards, SIRI is analogous to GTFS-RT: a way to apply real-time updates on top of schedule data. While technically a distinct specification from Netex, both Netex and SIRI use the Transmodel vocabulary, allowing SIRI messages to reference entities in Netex schedule data. Like GTFS-RT, SIRI is consumed by OTP2 using "graph updaters" which are configured in @@ -143,6 +143,6 @@ Note that between these SIRI updaters and the GTFS-RT Websocket updater, we now and streaming examples of GTFS-RT "incrementality" semantics, so should be able to finalize that part of the specification. -The final updater regularly performs a copy of the realtime data into a format suitable for use by -OTP2's new Raptor router. Without this updater the realtime data will be received and cataloged, but +The final updater regularly performs a copy of the real-time data into a format suitable for use by +OTP2's new Raptor router. Without this updater the real-time data will be received and cataloged, but not visible to the router. diff --git a/docs/RouteRequest.md b/docs/RouteRequest.md index 28c49764810..6825ee67183 100644 --- a/docs/RouteRequest.md +++ b/docs/RouteRequest.md @@ -47,7 +47,7 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe | elevatorHopTime | `integer` | How long does it take to advance one floor on an elevator? | *Optional* | `20` | 2.0 | | escalatorReluctance | `double` | A multiplier for how bad being in an escalator is compared to being in transit for equal lengths of time | *Optional* | `1.5` | 2.4 | | geoidElevation | `boolean` | If true, the Graph's ellipsoidToGeoidDifference is applied to all elevations returned by this query. | *Optional* | `false` | 2.0 | -| ignoreRealtimeUpdates | `boolean` | When true, realtime updates are ignored during this search. | *Optional* | `false` | 2.0 | +| ignoreRealtimeUpdates | `boolean` | When true, real-time updates are ignored during this search. | *Optional* | `false` | 2.0 | | [intersectionTraversalModel](#rd_intersectionTraversalModel) | `enum` | The model that computes the costs of turns. | *Optional* | `"simple"` | 2.2 | | locale | `locale` | TODO | *Optional* | `"en_US"` | 2.0 | | [maxDirectStreetDuration](#rd_maxDirectStreetDuration) | `duration` | This is the maximum duration for a direct street search for each mode. | *Optional* | `"PT4H"` | 2.1 | @@ -297,7 +297,7 @@ Raptor iteration. The value is dynamically assigned a suitable value, if not se medium size operation you may use a fixed value, like 60 minutes. If you have a mixture of high frequency cities routes and infrequent long distant journeys, the best option is normally to use the dynamic auto assignment. If not provided the value is resolved depending on the other input -parameters, available transit options and realtime changes. +parameters, available transit options and real-time changes. There is no need to set this when going to the next/previous page. The OTP Server will increase/decrease the search-window when paging to match the requested number of itineraries. diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index d0a58384819..34a3db8f6c6 100644 --- a/docs/RouterConfiguration.md +++ b/docs/RouterConfiguration.md @@ -46,7 +46,7 @@ A full list of them can be found in the [RouteRequest](RouteRequest.md). |          [logKey](#server_traceParameters_0_logKey) | `string` | The log event key used. | *Optional* | | 2.4 | | timetableUpdates | `object` | Global configuration for timetable updaters. | *Optional* | | 2.2 | |    [maxSnapshotFrequency](#timetableUpdates_maxSnapshotFrequency) | `duration` | How long a snapshot should be cached. | *Optional* | `"PT1S"` | 2.2 | -|    purgeExpiredData | `boolean` | Should expired realtime data be purged from the graph. Apply to GTFS-RT and Siri updates. | *Optional* | `true` | 2.2 | +|    purgeExpiredData | `boolean` | Should expired real-time data be purged from the graph. Apply to GTFS-RT and Siri updates. | *Optional* | `true` | 2.2 | | [transit](#transit) | `object` | Configuration for transit searches with RAPTOR. | *Optional* | | na | |    [iterationDepartureStepInSeconds](#transit_iterationDepartureStepInSeconds) | `integer` | Step for departure times between each RangeRaptor iterations. | *Optional* | `60` | na | |    [maxNumberOfTransfers](#transit_maxNumberOfTransfers) | `integer` | This parameter is used to allocate enough memory space for Raptor. | *Optional* | `12` | na | diff --git a/docs/SandboxExtension.md b/docs/SandboxExtension.md index 55ee214979a..4a3f500ae80 100644 --- a/docs/SandboxExtension.md +++ b/docs/SandboxExtension.md @@ -15,8 +15,8 @@ provided "as is". - [Transfer analyser](sandbox/transferanalyzer.md) - Module used for analyzing the transfers between nearby stops generated by routing via OSM data. - [Transmodel API](sandbox/TransmodelApi.md) - Enturs GraphQL Transmodel API. -- [SIRI Updater](sandbox/SiriUpdater.md) - Update OTP with realtime information from a Transmodel SIRI data source. -- [SIRI Azure Updater](sandbox/SiriAzureUpdater.md) - fetch SIRI realtime data through *Azure Service Bus* +- [SIRI Updater](sandbox/SiriUpdater.md) - Update OTP with real-time information from a Transmodel SIRI data source. +- [SIRI Azure Updater](sandbox/SiriAzureUpdater.md) - fetch SIRI real-time data through *Azure Service Bus* - [VehicleRentalServiceDirectory](sandbox/VehicleRentalServiceDirectory.md) - GBFS service directory endpoint. - [Smoove Bike Rental Updator Support](sandbox/SmooveBikeRental.md) - Smoove Bike Rental Updator(HSL) - [Mapbox Vector Tiles API](sandbox/MapboxVectorTilesApi.md) - Mapbox Vector Tiles API diff --git a/docs/System-Requirements.md b/docs/System-Requirements.md index 133370b8ede..387bba0cd03 100644 --- a/docs/System-Requirements.md +++ b/docs/System-Requirements.md @@ -10,7 +10,7 @@ OTP is relatively memory-hungry as it includes all the required data in memory. Single thread performance is an important factor for OTP's performance. Additionally, OTP benefits from larger CPU cache as reading from memory can be a bottleneck. -OTP's performance scales with the number of available CPU cores. OTP processes each request in a separate thread and usually one request doesn't utilize more than one thread, but with some requests and configurations, it's possible that multiple threads are used in parallel for a small part of a request or to process multiple queries within one request. How much parallel processing we utilize in requests might change in the future. Realtime updates also run in a separate thread. Therefore, to have a good performance, it makes sense to have multiple cores available. How OTP uses parallel processing also depends on the available cores (<= 2 cores vs >2 cores) in some cases. Therefore, load testing should be done against a machine that doesn't differ too much from production machines. +OTP's performance scales with the number of available CPU cores. OTP processes each request in a separate thread and usually one request doesn't utilize more than one thread, but with some requests and configurations, it's possible that multiple threads are used in parallel for a small part of a request or to process multiple queries within one request. How much parallel processing we utilize in requests might change in the future. Real-time updates also run in a separate thread. Therefore, to have a good performance, it makes sense to have multiple cores available. How OTP uses parallel processing also depends on the available cores (<= 2 cores vs >2 cores) in some cases. Therefore, load testing should be done against a machine that doesn't differ too much from production machines. Entur and the Digitransit project have found that the 3rd generation AMD processors have a slightly better performance for OTP2 than the Intel 3rd generation CPUs (and especially better than the 2nd generation CPUs). diff --git a/docs/examples/skanetrafiken/Readme.md b/docs/examples/skanetrafiken/Readme.md index ab7ef5f33a6..fc342f4192b 100644 --- a/docs/examples/skanetrafiken/Readme.md +++ b/docs/examples/skanetrafiken/Readme.md @@ -35,13 +35,13 @@ To reduced graph size, only data for southern part of Sweden is used. OSM data is downloaded from `http://download.geofabrik.de/europe/denmark-latest.osm.pbf`. To reduce graph size, only data for northern part of Denmark is used. -## Realtime +## Real-time -The **Azure Service Bus** is used to propagate SIRI SX and ET realtime messages to OTP. +The **Azure Service Bus** is used to propagate SIRI SX and ET real-time messages to OTP. This is solved through Siri Azure updaters that Skånetrafiken had implemented in OTP. There are separate updaters for SIRI SX and ET. Those updaters are used to provide data for Swedish traffic (NeTEx). Right now, there is no -connection to any realtime source for danish traffic (GTFS data). +connection to any real-time source for danish traffic (GTFS data). Except for receiving messages from **Service Bus** there are two endpoints through which historical ET and SX messages can be downloaded at OTP startup. @@ -54,8 +54,8 @@ the subscription. Once the updaters are done with processing of history messages they will change their status to primed, and the system will start channeling request to this OTP instance. -This ensures that no realtime message is omitted and all OTP instance that ran in the -cluster does have exact same realtime data. +This ensures that no real-time message is omitted and all OTP instance that ran in the +cluster does have exact same real-time data. Thi means that no matter which instance the client is hitting it will always get the same search results. diff --git a/docs/examples/skanetrafiken/router-config.json b/docs/examples/skanetrafiken/router-config.json index 9cc8b659734..ddec543e516 100644 --- a/docs/examples/skanetrafiken/router-config.json +++ b/docs/examples/skanetrafiken/router-config.json @@ -33,7 +33,7 @@ "fuzzyTripMatching": false, "history": { "url": "", - // Get all realtime history for current operating day date + // Get all real-time history for current operating day date "fromDateTime": "-P0D", "timeout": 300000 } diff --git a/docs/sandbox/MapboxVectorTilesApi.md b/docs/sandbox/MapboxVectorTilesApi.md index a503e6a839d..8ef8ee179e7 100644 --- a/docs/sandbox/MapboxVectorTilesApi.md +++ b/docs/sandbox/MapboxVectorTilesApi.md @@ -80,7 +80,7 @@ The feature must be configured in `router-config.json` as follows "minZoom": 14, "cacheMaxSeconds": 600 }, - // Contains just stations and realtime information for them + // Contains just stations and real-time information for them { "name": "realtimeRentalStations", "type": "VehicleRentalStation", @@ -90,7 +90,7 @@ The feature must be configured in `router-config.json` as follows "cacheMaxSeconds": 60 }, // This exists for backwards compatibility. At some point, we might want - // to add a new realtime parking mapper with better translation support + // to add a new real-time parking mapper with better translation support // and less unnecessary fields. { "name": "stadtnaviVehicleParking", @@ -101,7 +101,7 @@ The feature must be configured in `router-config.json` as follows "cacheMaxSeconds": 60, "expansionFactor": 0.25 }, - // no realtime, translatable fields are translated based on accept-language header + // no real-time, translatable fields are translated based on accept-language header // and contains less fields than the Stadtnavi mapper { "name": "vehicleParking", @@ -190,4 +190,4 @@ key, and a function to create the mapper, with a `Graph` object as a parameter, * Translatable fields are now translated based on accept-language header * Added DigitransitRealtime for vehicle rental stations * Changed old vehicle parking mapper to be Stadtnavi - * Added a new Digitransit vehicle parking mapper with no realtime information and less fields + * Added a new Digitransit vehicle parking mapper with no real-time information and less fields From 053f87572b2c733d9624bc96e19b331ede0a5ff5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 26 Nov 2023 19:01:21 +0100 Subject: [PATCH 09/39] Correct cron expression --- .github/workflows/prune-container-images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prune-container-images.yml b/.github/workflows/prune-container-images.yml index de5954b9ba9..518e8afb67b 100644 --- a/.github/workflows/prune-container-images.yml +++ b/.github/workflows/prune-container-images.yml @@ -2,7 +2,7 @@ name: 'Delete unused snapshot container images' on: schedule: - - cron: '0 12 1 * *' + - cron: '0 12 * * 1' workflow_dispatch: jobs: From 8588bf6e37fb16ceda7126cdca8ad4a3b4e6039c Mon Sep 17 00:00:00 2001 From: sharhio Date: Tue, 28 Nov 2023 16:54:24 +0200 Subject: [PATCH 10/39] Emissions filter returned after erroneous deletion --- .../algorithm/mapping/RouteRequestToFilterChainMapper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java index 5c105f4804a..e86249f6fc8 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java @@ -4,9 +4,11 @@ import java.time.Instant; import java.util.List; import java.util.function.Consumer; +import org.opentripplanner.ext.emissions.EmissionsFilter; import org.opentripplanner.ext.fares.FaresFilter; import org.opentripplanner.ext.ridehailing.RideHailingFilter; import org.opentripplanner.ext.stopconsolidation.ConsolidatedStopNameFilter; +import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.routing.algorithm.filterchain.GroupBySimilarity; import org.opentripplanner.routing.algorithm.filterchain.ItineraryListFilterChain; import org.opentripplanner.routing.algorithm.filterchain.ItineraryListFilterChainBuilder; @@ -107,6 +109,10 @@ public static ItineraryListFilterChain createFilterChain( ); } + if (OTPFeature.Co2Emissions.isOn() && context.emissionsService() != null) { + builder.withEmissions(new EmissionsFilter(context.emissionsService())); + } + if (context.stopConsolidationService() != null) { builder.withStopConsolidationFilter( new ConsolidatedStopNameFilter(context.stopConsolidationService()) From c3cb814d9c231c9fc8806264f4f662953ac95c9b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 02:03:12 +0000 Subject: [PATCH 11/39] fix(deps): update dependency ch.qos.logback:logback-classic to v1.4.13 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a47094978fc..b69f9a93a2b 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 5.10.1 1.11.5 5.5.3 - 1.4.12 + 1.4.13 9.8.0 2.0.9 2.0.15 From 91fcf0359fed47861ee600644c4c98c1e5a1ee35 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 30 Nov 2023 12:07:19 +0000 Subject: [PATCH 12/39] Add changelog entry for #5523 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index bccd0c504d5..cb3fef8df5a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -51,6 +51,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Transfer cost limit [#5516](https://github.com/opentripplanner/OpenTripPlanner/pull/5516) - Fix missed trip when arrive-by search-window is off by one minute [#5520](https://github.com/opentripplanner/OpenTripPlanner/pull/5520) - Transit group priority - Part 1 [#4999](https://github.com/opentripplanner/OpenTripPlanner/pull/4999) +- Remove `matchBusRoutesToStreets` [#5523](https://github.com/opentripplanner/OpenTripPlanner/pull/5523) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From 7d052cbb5cbff1dea9122e8b026b9d37411435f4 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 30 Nov 2023 14:30:15 +0100 Subject: [PATCH 13/39] Added JavaDoc to TripTimes --- .../transit/model/timetable/TripTimes.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java index a7a5b34e7c2..f45d549fe3d 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java @@ -10,6 +10,15 @@ import org.opentripplanner.model.BookingInfo; import org.opentripplanner.transit.model.basic.Accessibility; +/** + * A TripTimes represents the arrival and departure times for a single trip in a timetable. It is + * one of the core class used for transit routing. This interface allow different kind of trip + * to implement their own trip times. Scheduled/planned trips should be immutable, real-time + * trip times should allow updates and more info, frequency-based trips can use a more compact + * implementation, and Flex may expose part of the trip as a "scheduled/regular" stop-to-stop + * trip using this interface. All times are expressed as seconds since midnight (as in + * GTFS). + */ public interface TripTimes extends Serializable, Comparable { /** * Copy scheduled times, but not the actual times. From 15c87b434762e58431ebef3757d48e9cad5452fa Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 30 Nov 2023 14:44:22 +0100 Subject: [PATCH 14/39] review: Apply suggestions from review --- docs/Configuration.md | 46 +++++++++---------- .../ext/siri/ModifiedTripBuilder.java | 3 +- .../api/mapping/TripPlanMapper.java | 2 +- .../model/plan/pagecursor/readme.md | 2 +- .../netex/mapping/TripPatternMapper.java | 2 +- .../config/framework/json/ConfigType.java | 2 +- .../model/timetable/ScheduledTripTimes.java | 8 +++- .../transit/model/timetable/TripTimes.java | 4 +- 8 files changed, 38 insertions(+), 31 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index bfd168d5955..76eeeeeb578 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -65,29 +65,29 @@ documentation below we will refer to the following types: -| Type | Description | Examples | -|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| -| `boolean` | This is the Boolean JSON type | `true`, `false` | -| `string` | This is the String JSON type. | `"This is a string!"` | -| `double` | A decimal floating point _number_. 64 bit. | `3.15` | -| `integer` | A decimal integer _number_. 32 bit. | `1`, `-7`, `2100` | -| `long` | A decimal integer _number_. 64 bit. | `-1234567890` | -| `enum` | A fixed set of string literals. | `"RAIL"`, `"BUS"` | -| `enum-map` | List of key/value pairs, where the key is a enum and the value can be any given type. | `{ "RAIL: 1.2, "BUS": 2.3 }` | -| `enum-set` | List of enum string values | `[ "RAIL", "TRAM" ]` | -| `locale` | _`Language[\_country[\_variant]]`_. A Locale object represents a specific geographical, political, or cultural region. For more information see the [Java Locale](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Locale.html). | `"en_US"`, `"nn_NO"` | -| `date` | Local date. The format is _YYYY-MM-DD_ (ISO-8601). | `"2020-09-21"` | -| `date-or-period` | A _local date_, or a _period_ relative to today. The local date has the format `YYYY-MM-DD` and the period has the format `PnYnMnD` or `-PnYnMnD` where `n` is a integer number. | `"P1Y"`, `"-P3M2D"`, `"P1D"` | -| `duration` | A _duration_ is a amount of time. The format is `PnDTnHnMnS` or `nDnHnMnS` where `n` is a integer number. The `D`(days), `H`(hours), `M`(minutes) and `S`(seconds) are not case sensitive. | `"3h"`, `"2m"`, `"1d5h2m3s"`, `"-P2dT-1s"` | -| `regexp` | A regular expression pattern used to match a sting. | `"$^"`, `"gtfs"`, `"\w{3})-.*\.xml"` | -| `uri` | An URI path to a resource like a file or a URL. Relative URIs are resolved relative to the OTP base path. | `"http://foo.bar/"`, `"file:///Users/jon/local/file"`, `"graph.obj"` | -| `time-zone` | Time-Zone ID | `"UTC"`, `"Europe/Paris"`, `"-05:00"` | -| `feed-scoped-id` | FeedScopedId | `"NO:1001"`, `"1:101"` | -| `cost-linear-function` | A cost-linear-function used to calculate a cost from another cost or time/duration. Given a function of time: ``` f(t) = a + b * t ``` then `a` is the constant time part, `b` is the time-coefficient, and `t` is the variable. If `a=0s` and `b=0.0`, then the cost is always `0`(zero). Examples: `0s + 2.5t`, `10m + 0t` and `1h5m59s + 9.9t` The `constant` must be 0 or a positive number or duration. The unit is seconds unless specified using the duration format. A duration is automatically converted to a cost. The `coefficient` must be in range: [0.0, 100.0] | | -| `time-penalty` | A time-penalty is used to add a penalty to the duration/arrival-time/depature-time for a path. It will be invisible to the end user, but used during the routing when comparing stop-arrival/paths. Given a function of time: ``` f(t) = a + b * t ``` then `a` is the constant time part, `b` is the time-coefficient, and `t` is the variable. If `a=0s` and `b=0.0`, then the cost is always `0`(zero). Examples: `0s + 2.5t`, `10m + 0 x` and `1h5m59s + 9.9t` The `constant` must be 0 or a positive number(seconds) or a duration. The `coefficient` must be in range: [0.0, 100.0] | | -| `map` | List of key/value pairs, where the key is a string and the value can be any given type. | `{ "one": 1.2, "two": 2.3 }` | -| `object` | Config object containing nested elements | `"walk": { "speed": 1.3, "reluctance": 5 }` | -| `array` | Config object containing an array/list of elements | `"array": [ 1, 2, 3 ]` | +| Type | Description | Examples | +|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| +| `boolean` | This is the Boolean JSON type | `true`, `false` | +| `string` | This is the String JSON type. | `"This is a string!"` | +| `double` | A decimal floating point _number_. 64 bit. | `3.15` | +| `integer` | A decimal integer _number_. 32 bit. | `1`, `-7`, `2100` | +| `long` | A decimal integer _number_. 64 bit. | `-1234567890` | +| `enum` | A fixed set of string literals. | `"RAIL"`, `"BUS"` | +| `enum-map` | List of key/value pairs, where the key is a enum and the value can be any given type. | `{ "RAIL: 1.2, "BUS": 2.3 }` | +| `enum-set` | List of enum string values | `[ "RAIL", "TRAM" ]` | +| `locale` | _`Language[\_country[\_variant]]`_. A Locale object represents a specific geographical, political, or cultural region. For more information see the [Java Locale](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Locale.html). | `"en_US"`, `"nn_NO"` | +| `date` | Local date. The format is _YYYY-MM-DD_ (ISO-8601). | `"2020-09-21"` | +| `date-or-period` | A _local date_, or a _period_ relative to today. The local date has the format `YYYY-MM-DD` and the period has the format `PnYnMnD` or `-PnYnMnD` where `n` is a integer number. | `"P1Y"`, `"-P3M2D"`, `"P1D"` | +| `duration` | A _duration_ is a amount of time. The format is `PnDTnHnMnS` or `nDnHnMnS` where `n` is a integer number. The `D`(days), `H`(hours), `M`(minutes) and `S`(seconds) are not case sensitive. | `"3h"`, `"2m"`, `"1d5h2m3s"`, `"-P2dT-1s"` | +| `regexp` | A regular expression pattern used to match a sting. | `"$^"`, `"gtfs"`, `"\w{3})-.*\.xml"` | +| `uri` | An URI path to a resource like a file or a URL. Relative URIs are resolved relative to the OTP base path. | `"http://foo.bar/"`, `"file:///Users/jon/local/file"`, `"graph.obj"` | +| `time-zone` | Time-Zone ID | `"UTC"`, `"Europe/Paris"`, `"-05:00"` | +| `feed-scoped-id` | FeedScopedId | `"NO:1001"`, `"1:101"` | +| `cost-linear-function` | A cost-linear-function used to calculate a cost from another cost or time/duration. Given a function of time: ``` f(t) = a + b * t ``` then `a` is the constant time part, `b` is the time-coefficient, and `t` is the variable. If `a=0s` and `b=0.0`, then the cost is always `0`(zero). Examples: `0s + 2.5t`, `10m + 0t` and `1h5m59s + 9.9t` The `constant` must be 0 or a positive number or duration. The unit is seconds unless specified using the duration format. A duration is automatically converted to a cost. The `coefficient` must be in range: [0.0, 100.0] | | +| `time-penalty` | A time-penalty is used to add a penalty to the duration/arrival-time/departure-time for a path. It will be invisible to the end user, but used during the routing when comparing stop-arrival/paths. Given a function of time: ``` f(t) = a + b * t ``` then `a` is the constant time part, `b` is the time-coefficient, and `t` is the variable. If `a=0s` and `b=0.0`, then the cost is always `0`(zero). Examples: `0s + 2.5t`, `10m + 0 x` and `1h5m59s + 9.9t` The `constant` must be 0 or a positive number(seconds) or a duration. The `coefficient` must be in range: [0.0, 100.0] | | +| `map` | List of key/value pairs, where the key is a string and the value can be any given type. | `{ "one": 1.2, "two": 2.3 }` | +| `object` | Config object containing nested elements | `"walk": { "speed": 1.3, "reluctance": 5 }` | +| `array` | Config object containing an array/list of elements | `"array": [ 1, 2, 3 ]` | diff --git a/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java index 56cc28d82f4..df4509eb2d6 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/ModifiedTripBuilder.java @@ -34,7 +34,8 @@ */ public class ModifiedTripBuilder { - private static final Logger LOG = LoggerFactory.getLogger(TimetableHelper.class); + private static final Logger LOG = LoggerFactory.getLogger(ModifiedTripBuilder.class); + private final TripTimes existingTripTimes; private final TripPattern pattern; private final LocalDate serviceDate; diff --git a/src/main/java/org/opentripplanner/api/mapping/TripPlanMapper.java b/src/main/java/org/opentripplanner/api/mapping/TripPlanMapper.java index 3247b675b65..8c5459af8ed 100644 --- a/src/main/java/org/opentripplanner/api/mapping/TripPlanMapper.java +++ b/src/main/java/org/opentripplanner/api/mapping/TripPlanMapper.java @@ -21,7 +21,7 @@ public ApiTripPlan mapTripPlan(TripPlan domain) { } ApiTripPlan api = new ApiTripPlan(); api.date = Date.from(domain.date); - // The origin/destination do not have arrival/depature times; Hence {@code null} is used. + // The origin/destination do not have arrival/departure times; Hence {@code null} is used. api.from = placeMapper.mapPlace(domain.from, null, null, null, null); api.to = placeMapper.mapPlace(domain.to, null, null, null, null); api.itineraries = itineraryMapper.mapItineraries(domain.itineraries); diff --git a/src/main/java/org/opentripplanner/model/plan/pagecursor/readme.md b/src/main/java/org/opentripplanner/model/plan/pagecursor/readme.md index d0707a93a0b..e98071cfa62 100644 --- a/src/main/java/org/opentripplanner/model/plan/pagecursor/readme.md +++ b/src/main/java/org/opentripplanner/model/plan/pagecursor/readme.md @@ -18,7 +18,7 @@ moving on to the next. request to the next page. **sw'** is the search window for the new next/previous page. The search window may change between requests, so we need to account for it when computing the next/previous page cursors. -- **earliest-departure-time (edt)** The search-window start with the earliest-depature-time, which +- **earliest-departure-time (edt)** The search-window start with the earliest-departure-time, which is the first possible time any itinerary may start. **edt'** is the calculated value for the new cursor. - **latest-arrival-time (lat)** The latest time an itinerary can arrive to get accepted. The diff --git a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java index 4babac12e03..6fa000c1049 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java @@ -363,7 +363,7 @@ private org.opentripplanner.transit.model.network.Route lookupRoute( private void createTripTimes(List trips, TripPattern tripPattern) { for (Trip trip : trips) { List stopTimes = result.tripStopTimes.get(trip); - if (stopTimes.size() == 0) { + if (stopTimes.isEmpty()) { issueStore.add( "TripWithoutTripTimes", "Trip %s does not contain any trip times.", diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java index 18de8ab6e6c..f62ec56dbfc 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java @@ -94,7 +94,7 @@ public enum ConfigType { TIME_PENALTY( JsonType.string, """ - A time-penalty is used to add a penalty to the duration/arrival-time/depature-time for + A time-penalty is used to add a penalty to the duration/arrival-time/departure-time for a path. It will be invisible to the end user, but used during the routing when comparing stop-arrival/paths. diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index 0d1efeea7c7..b950cdebc5e 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -20,6 +20,12 @@ import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.DeduplicatorService; +/** + * Regular/planed/scheduled read-only version of {@link TripTimes}. The set of static + * trip-times are build during graph-build and can not be changed using real-time updates. + * + * @see RealTimeTripTimes for real-time version + */ public final class ScheduledTripTimes implements TripTimes { /** @@ -318,7 +324,7 @@ private void validate() { /** * When creating scheduled trip times we could potentially imply negative running or dwell times. * We really don't want those being used in routing. This method checks that all times are - * increasing. The first stop arrival time and the last stops depature time is NOT checked - + * increasing. The first stop arrival time and the last stops departure time is NOT checked - * these should be ignored by raptor. */ private void validateNonIncreasingTimes() { diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java index f45d549fe3d..7bf1a826c1c 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java @@ -23,7 +23,7 @@ public interface TripTimes extends Serializable, Comparable { /** * Copy scheduled times, but not the actual times. */ - public RealTimeTripTimes copyScheduledTimes(); + RealTimeTripTimes copyScheduledTimes(); /** The code for the service on which this trip runs. For departure search optimizations. */ int getServiceCode(); @@ -59,7 +59,7 @@ public interface TripTimes extends Serializable, Comparable { int getDepartureDelay(int stop); /** - * Whether or not stopIndex is considered a GTFS timepoint. + * Whether stopIndex is considered a GTFS timepoint. */ boolean isTimepoint(int stopIndex); From 379a591ba878ddcb59abf3c22fdbf546a9623834 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 30 Nov 2023 14:47:03 +0100 Subject: [PATCH 15/39] spelling: Replace witch with which --- ARCHITECTURE.md | 2 +- CODE_CONVENTIONS.md | 2 +- .../framework/concurrent/OtpRequestThreadFactory.java | 2 +- .../org/opentripplanner/framework/text/MarkdownFormatter.java | 2 +- src/main/java/org/opentripplanner/framework/text/Table.java | 2 +- .../java/org/opentripplanner/framework/text/TableBuilder.java | 2 +- .../org/opentripplanner/framework/time/DurationUtils.java | 2 +- .../java/org/opentripplanner/framework/time/TimeUtils.java | 4 ++-- .../graph_builder/module/configure/GraphBuilderModules.java | 2 +- src/main/java/org/opentripplanner/model/plan/Itinerary.java | 2 +- .../org/opentripplanner/model/transfer/TransferPoint.java | 4 ++-- .../org/opentripplanner/model/transfer/TransferPointMap.java | 2 +- .../org/opentripplanner/openstreetmap/model/OSMWithTags.java | 2 +- .../raptor/rangeraptor/SystemErrDebugLogger.java | 2 +- .../multicriteria/configure/McRangeRaptorConfig.java | 2 +- .../rangeraptor/standard/heuristics/HeuristicsAdapter.java | 2 +- .../rangeraptor/standard/stoparrivals/view/StopsCursor.java | 4 ++-- .../raptor/rangeraptor/transit/EgressPaths.java | 4 ++-- src/main/java/org/opentripplanner/raptor/spi/Flyweight.java | 2 +- .../opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java | 2 +- .../routing/algorithm/raptoradapter/router/TransitRouter.java | 2 +- .../transit/constrainedtransfer/TransferIndexGenerator.java | 2 +- .../raptoradapter/transit/request/TripSearchTimetable.java | 2 +- .../api/request/preference/TimeSlopeSafetyTriangle.java | 2 +- .../standalone/api/OtpServerRequestContext.java | 4 ++-- .../standalone/config/framework/json/EnumMapper.java | 2 +- .../transit/model/framework/DataValidationException.java | 2 +- .../org/opentripplanner/transit/model/framework/Result.java | 4 ++-- .../transit/model/framework/TransitBuilder.java | 2 +- .../transit/model/network/RoutingTripPattern.java | 2 +- .../java/org/opentripplanner/_support/arch/ArchComponent.java | 2 +- .../moduletests/F05_OnBoardAccessEgressAndTransfersTest.java | 2 +- .../opentripplanner/transit/speed_test/SpeedTestRequest.java | 2 +- .../transit/speed_test/model/testcase/ExpectedResults.java | 2 +- 34 files changed, 40 insertions(+), 40 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index dd0f986e518..acd90b787e6 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -24,7 +24,7 @@ examples. The Transit model is more complex than the VehiclePosition model. a use-case or set of features. It may have an api with request/response classes. These are usually stateless; Hence the `Use Case Service` does normally not have a model. The implementing class has the same name as the interface with prefix `Default`. - - `Domain Model` A model witch encapsulate a business area. In the drawing two examples are shown, + - `Domain Model` A model which encapsulate a business area. In the drawing two examples are shown, the `transit` and `vhicleposition` domain model. The transit model is more complex so the implementation have a separate `Service` and `Repository`. Almost all http endpoints are , read-only so the `Service` can focus on serving the http API endpoints, while the repository diff --git a/CODE_CONVENTIONS.md b/CODE_CONVENTIONS.md index a9fd73a0497..ce816c481cc 100644 --- a/CODE_CONVENTIONS.md +++ b/CODE_CONVENTIONS.md @@ -38,7 +38,7 @@ be `org.opentripplanner...`. | Package | Description | | ------------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `o.o.` | At the top level we should divide OTP into "domain"s like `apis`, `framework`, `transit`, `street`, `astar`, `raptor`, `feeds`, `updaters`, and `application`. | -| `component` and `sub-component` | A group of packages/classes witch naturally belong together, think aggregate as in Domain Driven Design. | +| `component` and `sub-component` | A group of packages/classes which naturally belong together, think aggregate as in Domain Driven Design. | | `component.api` | Used for components to define the programing interface for the component. If present, (see Raptor) all outside dependencies to the component should be through the `api`. | | `component.model` | Used to create a model of a Entites, ValueObjects, ++. If exposed outside the component you should include an entry point like `xyz.model.XyzModel` and/or a Service (in api or component root package). | | `component.service` | Implementation of the service like `DefaultTransitService`, may also contain use-case specific code. Note, the Service interface goes into the component root or `api`, not in the service package. | diff --git a/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java b/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java index 1ad44728c80..e88bbb420cd 100644 --- a/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java +++ b/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java @@ -7,7 +7,7 @@ /** * This thread pool factory should be used to create all threads handling "user" requests in OTP. - * It is used to instrument new threads witch enable log information propagation and error handling, + * It is used to instrument new threads which enable log information propagation and error handling, * like thread interruption. By "user" we mean users of the query APIs like GTFS GraphQL API, * Transmodel GraphQL API and other http endpoints. *

diff --git a/src/main/java/org/opentripplanner/framework/text/MarkdownFormatter.java b/src/main/java/org/opentripplanner/framework/text/MarkdownFormatter.java index d74a14a3315..6fdb495a0d5 100644 --- a/src/main/java/org/opentripplanner/framework/text/MarkdownFormatter.java +++ b/src/main/java/org/opentripplanner/framework/text/MarkdownFormatter.java @@ -73,7 +73,7 @@ public static String escapeInTable(String text) { return text.replace("|", "¦"); } - /** Return whitespace witch can be used to indent inside a table cell. */ + /** Return whitespace which can be used to indent inside a table cell. */ public static String indentInTable(int level) { return level <= 0 ? "" : NBSP.repeat(3 * level); } diff --git a/src/main/java/org/opentripplanner/framework/text/Table.java b/src/main/java/org/opentripplanner/framework/text/Table.java index 9db832c9410..940868b82e2 100644 --- a/src/main/java/org/opentripplanner/framework/text/Table.java +++ b/src/main/java/org/opentripplanner/framework/text/Table.java @@ -48,7 +48,7 @@ public static TableBuilder of() { } /** - * Static method witch format a given table as valid Markdown table like: + * Static method which format a given table as valid Markdown table like: *

    * | A | B |
    * |---|---|
diff --git a/src/main/java/org/opentripplanner/framework/text/TableBuilder.java b/src/main/java/org/opentripplanner/framework/text/TableBuilder.java
index cf6623c2d99..12e80fd62a8 100644
--- a/src/main/java/org/opentripplanner/framework/text/TableBuilder.java
+++ b/src/main/java/org/opentripplanner/framework/text/TableBuilder.java
@@ -50,7 +50,7 @@ public TableBuilder withAlights(Collection aligns) {
   }
 
   /**
-   * Return the width needed for each column. The witch is calculated by taking
+   * Return the width needed for each column. The which is calculated by taking
    * the maximum of the {@code minWidth}, header width and the maximum width for all
    * cells in the column.
    */
diff --git a/src/main/java/org/opentripplanner/framework/time/DurationUtils.java b/src/main/java/org/opentripplanner/framework/time/DurationUtils.java
index fb30392cf9b..bf59964fbb2 100644
--- a/src/main/java/org/opentripplanner/framework/time/DurationUtils.java
+++ b/src/main/java/org/opentripplanner/framework/time/DurationUtils.java
@@ -124,7 +124,7 @@ public static Duration duration(String duration) {
   }
 
   /**
-   * This is used to parse a string witch may be a number {@code NNNN}(number of seconds) or a
+   * This is used to parse a string which may be a number {@code NNNN}(number of seconds) or a
    * duration with format {@code NhNmNs}, where {@code N} is a decimal number and
    * {@code h} is hours, {@code m} minutes and {@code s} seconds.
    * 

diff --git a/src/main/java/org/opentripplanner/framework/time/TimeUtils.java b/src/main/java/org/opentripplanner/framework/time/TimeUtils.java index afeeb77ff5d..61549eeced3 100644 --- a/src/main/java/org/opentripplanner/framework/time/TimeUtils.java +++ b/src/main/java/org/opentripplanner/framework/time/TimeUtils.java @@ -205,7 +205,7 @@ public static String msToString(long milliseconds) { * busy-wait again. *

* This method does a "busy" wait - it is not affected by a thread interrupt like - * {@link Thread#sleep(long)}; Hence do not interfere with timeout logic witch uses the interrupt + * {@link Thread#sleep(long)}; Hence do not interfere with timeout logic which uses the interrupt * flag. *

* THIS CODE IS NOT MEANT FOR PRODUCTION! @@ -226,7 +226,7 @@ public static long busyWaitOnce(int waitMs) { * number. *

* This method does a "busy" wait - it is not affected by a thread interrupt like - * {@link Thread#sleep(long)}; Hence do not interfere with timeout logic witch uses the interrupt + * {@link Thread#sleep(long)}; Hence do not interfere with timeout logic which uses the interrupt * flag. *

* THIS CODE IS NOT MEANT FOR PRODUCTION! diff --git a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index 82f183a0bc4..444adb5b727 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -46,7 +46,7 @@ import org.opentripplanner.transit.service.TransitModel; /** - * Configure all modules witch is not simple enough to be injected. + * Configure all modules which is not simple enough to be injected. */ @Module public class GraphBuilderModules { diff --git a/src/main/java/org/opentripplanner/model/plan/Itinerary.java b/src/main/java/org/opentripplanner/model/plan/Itinerary.java index 0c111999912..58320bf1652 100644 --- a/src/main/java/org/opentripplanner/model/plan/Itinerary.java +++ b/src/main/java/org/opentripplanner/model/plan/Itinerary.java @@ -428,7 +428,7 @@ public void setLegs(List legs) { * accessible the itinerary is as a whole. This is not a very scientific method but just a rough * guidance that expresses certainty or uncertainty about the accessibility. *

- * An alternative to this is to use the `generalized-cost` and use that to indicate witch itineraries is the + * An alternative to this is to use the `generalized-cost` and use that to indicate which itineraries is the * best/most friendly with respect to making the journey in a wheelchair. The `generalized-cost` include, not * only a penalty for unknown and inaccessible boardings, but also a penalty for undesired uphill and downhill * street traversal. diff --git a/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java b/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java index b10e96e6f1c..dcfc7d381ad 100644 --- a/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java +++ b/src/main/java/org/opentripplanner/model/transfer/TransferPoint.java @@ -52,7 +52,7 @@ */ public interface TransferPoint { /** - * Utility method witch can be used in APIs to get the trip, if it exists, from a transfer point. + * Utility method which can be used in APIs to get the trip, if it exists, from a transfer point. */ @Nullable static Trip getTrip(TransferPoint point) { @@ -60,7 +60,7 @@ static Trip getTrip(TransferPoint point) { } /** - * Utility method witch can be used in APIs to get the route, if it exists, from a transfer + * Utility method which can be used in APIs to get the route, if it exists, from a transfer * point. */ @Nullable diff --git a/src/main/java/org/opentripplanner/model/transfer/TransferPointMap.java b/src/main/java/org/opentripplanner/model/transfer/TransferPointMap.java index 3592153f080..9f231d06aab 100644 --- a/src/main/java/org/opentripplanner/model/transfer/TransferPointMap.java +++ b/src/main/java/org/opentripplanner/model/transfer/TransferPointMap.java @@ -73,7 +73,7 @@ E computeIfAbsent(TransferPoint point, Supplier creator) { } /** - * List all elements witch matches any of the transfer points added to the map. + * List all elements which matches any of the transfer points added to the map. */ List get(Trip trip, StopLocation stop, int stopPointInPattern) { return Stream diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index 6e596e52076..37757823362 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -201,7 +201,7 @@ public Optional getTagOpt(String network) { /** * Get tag and convert it to an integer. If the tag exist, but can not be parsed into a number, - * then the error handler is called with the value witch failed to parse. + * then the error handler is called with the value which failed to parse. */ public OptionalInt getTagAsInt(String tag, Consumer errorHandler) { String value = getTag(tag); diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java index b10783c830f..8c35f103106 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java @@ -28,7 +28,7 @@ import org.opentripplanner.raptor.rangeraptor.transit.TripTimesSearch; /** - * A debug logger witch can be plugged into Raptor to do debug logging to standard error. This is + * A debug logger which can be plugged into Raptor to do debug logging to standard error. This is * used by the REST API, SpeedTest and in module tests. *

* See the Raptor design doc for a general description of the logging functionality. diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java index b05b0aeb5b0..649737bc42f 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java @@ -57,7 +57,7 @@ public McRangeRaptorConfig( /** * The PassThroughPointsService is injected into the transit-calculator, so it needs to be - * created before the context(witch create the calculator).So, to be able to do this, this + * created before the context(which create the calculator).So, to be able to do this, this * factory is static, and the service is passed back in when this config is instantiated. */ public static PassThroughPointsService passThroughPointsService( diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java index 78aab07f21a..ff38fff6d40 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java @@ -185,7 +185,7 @@ private static AggregatedResults create( ); for (RaptorAccessEgress it : list) { - // Prevent transfer(walking) and the egress witch start with walking + // Prevent transfer(walking) and the egress which start with walking if (!(it.stopReachedOnBoard() || stopReachedByTransit)) { continue; } diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java index d20f4ba5db8..f72047597a7 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java @@ -59,7 +59,7 @@ public Access fictiveAccess(int round, RaptorAccessEgress accessPath, int arr /** * Return a fictive Transfer stop arrival view. The arrival does not exist in the state, but is - * linked with the previous arrival witch is a "real" arrival present in the state. This enables + * linked with the previous arrival which is a "real" arrival present in the state. This enables * path generation. */ public Transfer fictiveTransfer( @@ -76,7 +76,7 @@ public Transfer fictiveTransfer( /** * Return a fictive Transit stop arrival view. The arrival does not exist in the state, but is - * linked with the previous arrival witch is a "real" arrival present in the state. This enables + * linked with the previous arrival which is a "real" arrival present in the state. This enables * path generation. */ public Transit fictiveTransit( diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java index fa4fc57dc84..2038ab543df 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java @@ -54,7 +54,7 @@ public Collection listAll() { } /** - * List all stops with an egress path witch start by walking. These egress paths can only be used + * List all stops with an egress path which start by walking. These egress paths can only be used * if arriving at the stop by transit. */ public int[] egressesWitchStartByWalking() { @@ -62,7 +62,7 @@ public int[] egressesWitchStartByWalking() { } /** - * List all stops with an egress path witch start on-board a "transit" ride. These + * List all stops with an egress path which start on-board a "transit" ride. These * egress paths can be used when arriving at the stop with both transfer or transit. */ public int[] egressesWitchStartByARide() { diff --git a/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java b/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java index c2b0536c8ab..a3d9ee0db1c 100644 --- a/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java +++ b/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java @@ -6,7 +6,7 @@ import java.lang.annotation.Target; /** - * This interface is used to tag methods witch return flyweight objects. The implementation may + * This interface is used to tag methods which return flyweight objects. The implementation may * choose not to implement the return type as a flyweight object, but the Raptor implementation * is guaranteed to treat them as such - enabling the optimization. *

diff --git a/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java b/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java index da01e397c06..5913f950969 100644 --- a/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java +++ b/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java @@ -77,7 +77,7 @@ default int boardStopIndex() { /** * This is a helper method for the Raptor implementation to be able to board or execute * a alternativeBoardingFallback method depending on the event. This logic should ideally - * be put inside raptor, but due to performance(creating lambda instances, witch for some + * be put inside raptor, but due to performance(creating lambda instances, which for some * reason is not inlined) this need to be here. *

* @param boardCallback perform boarding if the event in none empty (or some other special diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java index b9f9685341c..f18aa056504 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java @@ -177,7 +177,7 @@ private AccessEgresses fetchAccessEgresses() { if (OTPFeature.ParallelRouting.isOn()) { try { - // TODO: This is not using {@link OtpRequestThreadFactory} witch mean we do not get + // TODO: This is not using {@link OtpRequestThreadFactory} which mean we do not get // log-trace-parameters-propagation and graceful timeout handling here. CompletableFuture .allOf( diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/TransferIndexGenerator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/TransferIndexGenerator.java index 9fc2dc69194..7acddd9cea5 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/TransferIndexGenerator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/TransferIndexGenerator.java @@ -57,7 +57,7 @@ public ConstrainedTransfersForPatterns generateTransfers() { for (ConstrainedTransfer tx : constrainedTransfers) { var c = tx.getTransferConstraint(); - // Only add transfers witch have an effect on the Raptor routing here. + // Only add transfers which have an effect on the Raptor routing here. // Some transfers only have the priority set, and that is used in optimized- // transfers, but not in Raptor. if (!c.includeInRaptorRouting()) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripSearchTimetable.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripSearchTimetable.java index 88e9d52e878..147eddfc605 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripSearchTimetable.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripSearchTimetable.java @@ -9,7 +9,7 @@ * This interface add two methods the the {@link RaptorTimeTable} to optimize the terip search * inside the transit model. They were previously in Raptor, but the trip Search is moded outside * of Raptor; We have keep these methods in an interface to be able to reuse the complex TripSearch - * in tests, witch do not use the transit model {@link TripSchedule}; Hence also the generic type + * in tests, which do not use the transit model {@link TripSchedule}; Hence also the generic type * on this interface. */ public interface TripSearchTimetable extends RaptorTimeTable { diff --git a/src/main/java/org/opentripplanner/routing/api/request/preference/TimeSlopeSafetyTriangle.java b/src/main/java/org/opentripplanner/routing/api/request/preference/TimeSlopeSafetyTriangle.java index e93dd5f5a6c..b901d738213 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/preference/TimeSlopeSafetyTriangle.java +++ b/src/main/java/org/opentripplanner/routing/api/request/preference/TimeSlopeSafetyTriangle.java @@ -52,7 +52,7 @@ public TimeSlopeSafetyTriangle(double time, double slope, double safety) { } /** - * Creates a special builder, witch used together with the + * Creates a special builder, which used together with the * {@link Builder#buildOrDefault(TimeSlopeSafetyTriangle)} can return a new instance or the * default value if no values are set. This is useful in the APIs where we want to fall back to * the default {@link TimeSlopeSafetyTriangle}, if no values are set. If only one or two values diff --git a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java index 8fa8069ba70..fa6ead99c5e 100644 --- a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java +++ b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java @@ -47,7 +47,7 @@ * *

* This class is not THREAD-SAFE, each HTTP request gets its own copy, but if there are multiple - * threads witch accesses this context within the HTTP Request, then the caller is responsible + * threads which accesses this context within the HTTP Request, then the caller is responsible * for the synchronization. Only request scoped components need to be synchronized - they are * potentially lazy initialized. */ @@ -107,7 +107,7 @@ public interface OtpServerRequestContext { TileRendererManager tileRendererManager(); /** - * Callback witch is injected into the {@code DirectStreetRouter}, used to visualize the + * Callback which is injected into the {@code DirectStreetRouter}, used to visualize the * search. */ @HttpRequestScoped diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/EnumMapper.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/EnumMapper.java index 1bad7033104..ce880058005 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/EnumMapper.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/EnumMapper.java @@ -31,7 +31,7 @@ public static String kebabCase(String input) { } /** - * Used to create a list of all values with description of each value witch can be included + * Used to create a list of all values with description of each value which can be included * in documentation. The list will look like this: * ``` * - `on` Turn on. diff --git a/src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java b/src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java index d1c985e6db0..20848ebecc1 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/DataValidationException.java @@ -3,7 +3,7 @@ import org.opentripplanner.framework.error.OtpError; /** - * This class is used to throw a data validation exception. It holds an error witch can be + * This class is used to throw a data validation exception. It holds an error which can be * inserted into build issue store, into the updater logs or returned to the APIs. The framework * to catch and handle this is NOT IN PLACE, see * Error code design #5070. diff --git a/src/main/java/org/opentripplanner/transit/model/framework/Result.java b/src/main/java/org/opentripplanner/transit/model/framework/Result.java index 621ce7569f2..9df1c41f190 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/Result.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/Result.java @@ -10,7 +10,7 @@ *

* It's very similar to the Either or Validation type found in functional programming languages. * - * @deprecated This not possible to use inside a constructor - can only return one thing. Witch, + * @deprecated This not possible to use inside a constructor - can only return one thing. Which, * then makes encapsulation harder and leaves the door open to forget. Also, having to create * error codes, mapping and handling code for hundreds of different possible validation error * types make changes harder, and cleanup impossible. This is a nice way to design APIs, but @@ -18,7 +18,7 @@ * amount of code branches this breaks. *

* I will use the {@link DataValidationException} for now, but we need to make an error - * handling strategy witch take all use-cases and goals into account, also pragmatic goals + * handling strategy which take all use-cases and goals into account, also pragmatic goals * like maintainability. The {@link DataValidationException} is not a solution, but it * at least it allows me to omit returning all possible error on every method ... *

diff --git a/src/main/java/org/opentripplanner/transit/model/framework/TransitBuilder.java b/src/main/java/org/opentripplanner/transit/model/framework/TransitBuilder.java index bd18690d9a3..52fd68165bc 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/TransitBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/TransitBuilder.java @@ -5,7 +5,7 @@ public interface TransitBuilder, B extends Transit * Build a new object based on the values set in the builder. This method is NOT context aware - * any context is not updated. Use the {@link TransitEntityBuilder#save()} method instead to * build an object and store it in the context. This method is useful if you need to build an - * object witch should be request scoped or used in a test. + * object which should be request scoped or used in a test. *

* For value objects are stored as "part of" an entity, but OTP tries to reuse objects using the * {@code Deduplicator}. This method may or may not be context aware, using a deduplicator to diff --git a/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java b/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java index 0d319af0529..00033b5f798 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java +++ b/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java @@ -12,7 +12,7 @@ * - The RTP is accessed frequently during the Raptor search, and we want it to be as small as * possible to load/access it in the cache and CPU for performance reasons. * - Also, we deduplicate these so a RTP can be reused by more than one TP. - * - This also provide explicit documentation on witch fields are used during a search and which + * - This also provide explicit documentation on which fields are used during a search and which * are not. */ public class RoutingTripPattern implements DefaultTripPattern, Serializable { diff --git a/src/test/java/org/opentripplanner/_support/arch/ArchComponent.java b/src/test/java/org/opentripplanner/_support/arch/ArchComponent.java index a1261d09648..7273b3123e3 100644 --- a/src/test/java/org/opentripplanner/_support/arch/ArchComponent.java +++ b/src/test/java/org/opentripplanner/_support/arch/ArchComponent.java @@ -9,7 +9,7 @@ public interface ArchComponent { /** * ArchUnit cached set of classes in OTP. It takes a bit of time to build the set of * classes, so it is nice to avoid this for every test. ArchUnit also support JUnit5 - * with a @ArchTest annotation witch cache and inject the classes, but the test become + * with a @ArchTest annotation which cache and inject the classes, but the test become * slightly more complex by using it and chasing it here works fine. */ JavaClasses OTP_CLASSES = new ClassFileImporter() diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java index 59d7dcd7d4e..72da3f71145 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java @@ -25,7 +25,7 @@ * FEATURE UNDER TEST *

* Raptor should be able to route Access arriving on-board and egress departing on-board connecting - * to transit by transfers. Access and egress witch arrive/depart at/from the same stops by + * to transit by transfers. Access and egress which arrive/depart at/from the same stops by * walking should not be possible. */ public class F05_OnBoardAccessEgressAndTransfersTest implements RaptorTestConstants { diff --git a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTestRequest.java b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTestRequest.java index 1bfe202f92f..2dfaad077c6 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTestRequest.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTestRequest.java @@ -63,7 +63,7 @@ RouteRequest toRouteRequest() { request.setTo(input.toPlace()); // Filter the results inside the SpeedTest, not in the itineraries filter, - // when ignoring street results. This will use the default witch is 50. + // when ignoring street results. This will use the default which is 50. if (!config.ignoreStreetResults) { request.setNumItineraries(opts.numOfItineraries()); } diff --git a/src/test/java/org/opentripplanner/transit/speed_test/model/testcase/ExpectedResults.java b/src/test/java/org/opentripplanner/transit/speed_test/model/testcase/ExpectedResults.java index 1b14bc8fc12..eb66b3671e4 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/model/testcase/ExpectedResults.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/model/testcase/ExpectedResults.java @@ -10,7 +10,7 @@ /** * This class contains the results for a given test case. A set of results * for each {@link SpeedTestProfile} is kept. A default set is also available, - * witch can be used if there is not set for a given profile. + * which can be used if there is not set for a given profile. */ public class ExpectedResults { From 2546c231060f0f34b0b646e85259d6da2ecc4419 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:36:56 +0000 Subject: [PATCH 16/39] fix(deps): update jersey monorepo to v3.1.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b69f9a93a2b..9241cafc838 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 30.1 2.48.1 2.16.0 - 3.1.3 + 3.1.4 5.10.1 1.11.5 5.5.3 From d60af303bdf5b0a22ed8a3e1c0e56edf86ee8899 Mon Sep 17 00:00:00 2001 From: eibakke Date: Fri, 1 Dec 2023 23:48:46 +0100 Subject: [PATCH 17/39] Adds sameSubmode to AlternativeLegsFilter. --- .../org/opentripplanner/ext/transmodelapi/model/EnumTypes.java | 1 + .../routing/alternativelegs/AlternativeLegsFilter.java | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java index 2b6942d7b8a..bc4146fa2f5 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java @@ -49,6 +49,7 @@ public class EnumTypes { .value("noFilter", AlternativeLegsFilter.NO_FILTER) .value("sameAuthority", AlternativeLegsFilter.SAME_AGENCY) .value("sameMode", AlternativeLegsFilter.SAME_MODE) + .value("sameSubmode", AlternativeLegsFilter.SAME_SUBMODE) .value("sameLine", AlternativeLegsFilter.SAME_ROUTE) .build(); diff --git a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java index 5154f8cd40a..79ea57324ca 100644 --- a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java +++ b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java @@ -14,6 +14,9 @@ public enum AlternativeLegsFilter { ), SAME_MODE((Leg leg) -> (TripPattern tripPattern) -> leg.getTrip().getMode().equals(tripPattern.getMode()) + ), + SAME_SUBMODE((Leg leg) -> + (TripPattern tripPattern) -> leg.getTrip().getNetexSubMode().equals(tripPattern.getNetexSubmode()) ); public final Function> predicateGenerator; From 3393edf4191e522b68ce4c89ff9e39c812102299 Mon Sep 17 00:00:00 2001 From: eibakke Date: Fri, 1 Dec 2023 23:48:46 +0100 Subject: [PATCH 18/39] Adds sameSubmode to AlternativeLegsFilter. --- src/ext/graphql/transmodelapi/schema.graphql | 1 + .../opentripplanner/ext/transmodelapi/model/EnumTypes.java | 1 + .../routing/alternativelegs/AlternativeLegsFilter.java | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/src/ext/graphql/transmodelapi/schema.graphql b/src/ext/graphql/transmodelapi/schema.graphql index 069fefdf54d..9a31b1cd5b9 100644 --- a/src/ext/graphql/transmodelapi/schema.graphql +++ b/src/ext/graphql/transmodelapi/schema.graphql @@ -1405,6 +1405,7 @@ enum AlternativeLegsFilter { sameAuthority sameLine sameMode + sameSubmode } enum ArrivalDeparture { diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java index 2b6942d7b8a..bc4146fa2f5 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java @@ -49,6 +49,7 @@ public class EnumTypes { .value("noFilter", AlternativeLegsFilter.NO_FILTER) .value("sameAuthority", AlternativeLegsFilter.SAME_AGENCY) .value("sameMode", AlternativeLegsFilter.SAME_MODE) + .value("sameSubmode", AlternativeLegsFilter.SAME_SUBMODE) .value("sameLine", AlternativeLegsFilter.SAME_ROUTE) .build(); diff --git a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java index 5154f8cd40a..9f883603e77 100644 --- a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java +++ b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegsFilter.java @@ -14,6 +14,10 @@ public enum AlternativeLegsFilter { ), SAME_MODE((Leg leg) -> (TripPattern tripPattern) -> leg.getTrip().getMode().equals(tripPattern.getMode()) + ), + SAME_SUBMODE((Leg leg) -> + (TripPattern tripPattern) -> + leg.getTrip().getNetexSubMode().equals(tripPattern.getNetexSubmode()) ); public final Function> predicateGenerator; From 7163a768b7e0e8e732eef7600f41e6cf4385256e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 22:34:11 +0000 Subject: [PATCH 19/39] fix(deps): update dependency ch.qos.logback:logback-classic to v1.4.14 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b69f9a93a2b..e0de45f1f7e 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 5.10.1 1.11.5 5.5.3 - 1.4.13 + 1.4.14 9.8.0 2.0.9 2.0.15 From f06c5d2188ed2ef6b6d393f989c7a3750aa3c700 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 01:01:13 +0000 Subject: [PATCH 20/39] fix(deps): update google.dagger.version to v2.49 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e0de45f1f7e..baff7d78b69 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 131 30.1 - 2.48.1 + 2.49 2.16.0 3.1.3 5.10.1 From 7cb67055059d4b0de58aef87ce4f7164eab1045e Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Mon, 4 Dec 2023 11:11:58 +0100 Subject: [PATCH 21/39] refactor: Fix real-time spelling in tests and doc Co-authored-by: Assad Riaz --- doc-templates/StopConsolidation.md | 2 +- docs/RouteRequest.md | 2 +- docs/sandbox/StopConsolidation.md | 2 +- src/ext/graphql/transmodelapi/schema.graphql | 24 +++++++++---------- .../ext/fares/impl/OrcaFareService.java | 2 +- .../realtimeresolver/RealtimeResolver.java | 8 +++---- .../ext/siri/AddedTripBuilder.java | 2 +- .../ext/siri/SiriFuzzyTripMatcher.java | 6 ++--- .../ext/siri/SiriTimetableSnapshotSource.java | 18 +++++++------- .../ext/siri/TimetableHelper.java | 12 +++++----- .../updater/SiriETGooglePubsubUpdater.java | 8 +++---- .../updater/SiriETHttpTripUpdateSource.java | 2 +- .../ext/siri/updater/SiriETUpdater.java | 2 +- .../ext/siri/updater/SiriFileLoader.java | 2 +- .../StopConsolidationModule.java | 2 +- .../model/DefaultRouteRequestType.java | 2 +- .../ext/transmodelapi/model/plan/LegType.java | 9 +++---- .../model/plan/TripPatternType.java | 10 ++++---- .../transmodelapi/model/plan/TripQuery.java | 2 +- .../model/siri/et/EstimatedCallType.java | 4 ++-- .../transmodelapi/model/stop/QuayType.java | 2 +- .../model/stop/StopPlaceType.java | 4 +--- .../timetable/DatedServiceJourneyType.java | 2 +- .../model/timetable/ServiceJourneyType.java | 4 ++-- .../hslpark/HslParkUpdater.java | 2 +- .../api/mapping/PlaceMapper.java | 2 +- .../api/mapping/TripTimeMapper.java | 8 +++---- .../api/model/ApiTripSearchMetadata.java | 4 ++-- .../api/model/ApiTripTimeShort.java | 8 +++---- .../model/ApiVehicleParkingWithEntrance.java | 18 +++++++------- .../apis/gtfs/GraphQLRequestContext.java | 2 +- .../apis/gtfs/datafetchers/PatternImpl.java | 2 +- .../apis/gtfs/datafetchers/StopImpl.java | 2 +- .../apis/gtfs/datafetchers/StoptimeImpl.java | 2 +- .../apis/gtfs/datafetchers/TripImpl.java | 2 +- .../framework/application/OTPFeature.java | 2 +- .../opentripplanner/model/TripTimeOnDate.java | 2 +- .../preference/TransitPreferences.java | 2 +- .../config/routerconfig/UpdatersConfig.java | 2 +- .../routerequest/RouteRequestConfig.java | 2 +- .../updater/trip/TimetableSnapshotSource.java | 2 +- .../opentripplanner/apis/gtfs/schema.graphqls | 2 +- .../__snapshots__/CarSnapshotTest.snap | 4 ++-- 43 files changed, 101 insertions(+), 102 deletions(-) diff --git a/doc-templates/StopConsolidation.md b/doc-templates/StopConsolidation.md index 690fe0b98e6..c92cab6afe1 100644 --- a/doc-templates/StopConsolidation.md +++ b/doc-templates/StopConsolidation.md @@ -30,7 +30,7 @@ This has the following consequences However, this feature has also severe downsides: - - It makes realtime trip updates referencing a stop id much more complicated and in many cases + - It makes real-time trip updates referencing a stop id much more complicated and in many cases impossible to resolve. You can only reference a stop by its sequence, which only works in GTFS-RT, not Siri. - Fare calculation and transfers are unlikely to work as expected. diff --git a/docs/RouteRequest.md b/docs/RouteRequest.md index 6825ee67183..af6dcf00142 100644 --- a/docs/RouteRequest.md +++ b/docs/RouteRequest.md @@ -297,7 +297,7 @@ Raptor iteration. The value is dynamically assigned a suitable value, if not se medium size operation you may use a fixed value, like 60 minutes. If you have a mixture of high frequency cities routes and infrequent long distant journeys, the best option is normally to use the dynamic auto assignment. If not provided the value is resolved depending on the other input -parameters, available transit options and real-time changes. +parameters, available transit options and realtime changes. There is no need to set this when going to the next/previous page. The OTP Server will increase/decrease the search-window when paging to match the requested number of itineraries. diff --git a/docs/sandbox/StopConsolidation.md b/docs/sandbox/StopConsolidation.md index fa3ff822e88..750a873b547 100644 --- a/docs/sandbox/StopConsolidation.md +++ b/docs/sandbox/StopConsolidation.md @@ -30,7 +30,7 @@ This has the following consequences However, this feature has also severe downsides: - - It makes realtime trip updates referencing a stop id much more complicated and in many cases + - It makes real-time trip updates referencing a stop id much more complicated and in many cases impossible to resolve. You can only reference a stop by its sequence, which only works in GTFS-RT, not Siri. - Fare calculation and transfers are unlikely to work as expected. diff --git a/src/ext/graphql/transmodelapi/schema.graphql b/src/ext/graphql/transmodelapi/schema.graphql index 069fefdf54d..f90c186ce02 100644 --- a/src/ext/graphql/transmodelapi/schema.graphql +++ b/src/ext/graphql/transmodelapi/schema.graphql @@ -158,7 +158,7 @@ type Contact { "A planned journey on a specific day" type DatedServiceJourney { - "Returns scheduled passingTimes for this dated service journey, updated with realtime-updates (if available). " + "Returns scheduled passingTimes for this dated service journey, updated with real-time-updates (if available). " estimatedCalls: [EstimatedCall] @timingData id: ID! "JourneyPattern for the dated service journey." @@ -211,7 +211,7 @@ type EstimatedCall { aimedDepartureTime: DateTime! "Booking arrangements for this EstimatedCall." bookingArrangements: BookingArrangement - "Whether stop is cancelled. This means that either the ServiceJourney has a planned cancellation, the ServiceJourney has been cancelled by realtime data, or this particular StopPoint has been cancelled. This also means that both boarding and alighting has been cancelled." + "Whether stop is cancelled. This means that either the ServiceJourney has a planned cancellation, the ServiceJourney has been cancelled by real-time data, or this particular StopPoint has been cancelled. This also means that both boarding and alighting has been cancelled." cancellation: Boolean! "The date the estimated call is valid for." date: Date! @@ -315,9 +315,9 @@ type Leg { includes both the start and end coordinate. """ elevationProfile: [ElevationProfileStep]! - "The expected, realtime adjusted date and time this leg ends." + "The expected, real-time adjusted date and time this leg ends." expectedEndTime: DateTime! - "The expected, realtime adjusted date and time this leg starts." + "The expected, real-time adjusted date and time this leg starts." expectedStartTime: DateTime! "EstimatedCall for the quay where the leg originates." fromEstimatedCall: EstimatedCall @timingData @@ -560,7 +560,7 @@ type Quay implements PlaceInterface { estimatedCalls( "Filters results by either departures, arrivals or both. For departures forBoarding has to be true and the departure time has to be within the specified time range. For arrivals, forAlight has to be true and the arrival time has to be within the specified time range. If both are asked for, either the conditions for arrivals or the conditions for departures will have to be true for an EstimatedCall to show." arrivalDeparture: ArrivalDeparture = departures, - "Indicates that realtime-cancelled trips should also be included." + "Indicates that real-time-cancelled trips should also be included." includeCancelledTrips: Boolean = false, "Limit the total number of departures returned." numberOfDepartures: Int = 5, @@ -797,7 +797,7 @@ type QueryType { filters: [TripFilterInput!], "The start location" from: Location!, - "When true, realtime updates are ignored during this search." + "When true, real-time updates are ignored during this search." ignoreRealtimeUpdates: Boolean = false, "When true, service journeys cancelled in scheduled route data will be included during this search." includePlannedCancellations: Boolean = false, @@ -1012,7 +1012,7 @@ type RoutingParameters { elevatorHopTime: Int "Whether to apply the ellipsoid->geoid offset to all elevations in the response." geoIdElevation: Boolean - "When true, realtime updates are ignored during this search." + "When true, real-time updates are ignored during this search." ignoreRealTimeUpdates: Boolean "When true, service journeys cancelled in scheduled route data will be included during this search." includedPlannedCancellations: Boolean @@ -1081,7 +1081,7 @@ type ServiceJourney { "Booking arrangements for flexible services." bookingArrangements: BookingArrangement @deprecated(reason : "BookingArrangements are defined per stop, and can be found under `passingTimes` or `estimatedCalls`") directionType: DirectionType - "Returns scheduled passingTimes for this ServiceJourney for a given date, updated with realtime-updates (if available). NB! This takes a date as argument (default=today) and returns estimatedCalls for that date and should only be used if the date is known when creating the request. For fetching estimatedCalls for a given trip.leg, use leg.serviceJourneyEstimatedCalls instead." + "Returns scheduled passingTimes for this ServiceJourney for a given date, updated with real-time-updates (if available). NB! This takes a date as argument (default=today) and returns estimatedCalls for that date and should only be used if the date is known when creating the request. For fetching estimatedCalls for a given trip.leg, use leg.serviceJourneyEstimatedCalls instead." estimatedCalls( "Date to get estimated calls for. Defaults to today." date: Date @@ -1092,7 +1092,7 @@ type ServiceJourney { line: Line! notices: [Notice!]! operator: Operator - "Returns scheduled passing times only - without realtime-updates, for realtime-data use 'estimatedCalls'" + "Returns scheduled passing times only - without real-time-updates, for realtime-data use 'estimatedCalls'" passingTimes: [TimetabledPassingTime]! @timingData "Detailed path travelled by service journey. Not available for flexible trips." pointsOnLink: PointsOnLink @@ -1122,7 +1122,7 @@ type StopPlace implements PlaceInterface { "List of visits to this stop place as part of vehicle journeys." estimatedCalls( arrivalDeparture: ArrivalDeparture = departures, - "Indicates that realtime-cancelled trips should also be included." + "Indicates that real-time-cancelled trips should also be included." includeCancelledTrips: Boolean = false, "Limit the total number of departures returned." numberOfDepartures: Int = 5, @@ -1280,9 +1280,9 @@ type TripPattern { duration: Long "Time that the trip arrives." endTime: DateTime @deprecated(reason : "Replaced with expectedEndTime") - "The expected, realtime adjusted date and time the trip ends." + "The expected, real-time adjusted date and time the trip ends." expectedEndTime: DateTime! - "The expected, realtime adjusted date and time the trip starts." + "The expected, real-time adjusted date and time the trip starts." expectedStartTime: DateTime! "Generalized cost or weight of the itinerary. Used for debugging." generalizedCost: Int diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java index 486362697b3..4f1aca5bf63 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java @@ -395,7 +395,7 @@ protected Optional getRidePrice( * If free transfers are applicable, the most expensive discount fare across all legs is added to * the final cumulative price. *

- * The computed fare for Orca card users takes into account realtime trip updates where available, + * The computed fare for Orca card users takes into account real-time trip updates where available, * so that, for instance, when a leg on a long itinerary is delayed to begin after the initial two * hour window has expired, the calculated fare for that trip will be two one-way fares instead of * one. diff --git a/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java b/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java index a59d9fc589a..d03ba1a2fc5 100644 --- a/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java +++ b/src/ext/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolver.java @@ -11,7 +11,7 @@ public class RealtimeResolver { /** - * Loop through all itineraries and populate legs with realtime data using legReference from the original leg + * Loop through all itineraries and populate legs with real-time data using legReference from the original leg */ public static void populateLegsWithRealtime( List itineraries, @@ -35,10 +35,10 @@ public static void populateLegsWithRealtime( return leg; } - var realtimeLeg = ref.getLeg(transitService); - if (realtimeLeg != null) { + var realTimeLeg = ref.getLeg(transitService); + if (realTimeLeg != null) { return combineReferenceWithOriginal( - realtimeLeg.asScheduledTransitLeg(), + realTimeLeg.asScheduledTransitLeg(), leg.asScheduledTransitLeg() ); } diff --git a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java index 2d7789422ea..7ef843575e5 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java @@ -246,7 +246,7 @@ Result build() { } /** - * Method to create a Route. Commonly used to create a route if a realtime message + * Method to create a Route. Commonly used to create a route if a real-time message * refers to a route that is not in the transit model. * * We will find the first Route with same Operator, and use the same Authority diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java b/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java index 006329a404b..2f2f113fc79 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java @@ -297,9 +297,9 @@ private TripAndPattern getTripAndPatternForJourney( continue; } - var realtimeAddedTripPattern = getRealtimeAddedTripPattern.apply(trip.getId(), serviceDate); - TripPattern tripPattern = realtimeAddedTripPattern != null - ? realtimeAddedTripPattern + var realTimeAddedTripPattern = getRealtimeAddedTripPattern.apply(trip.getId(), serviceDate); + TripPattern tripPattern = realTimeAddedTripPattern != null + ? realTimeAddedTripPattern : transitService.getPatternForTrip(trip); var firstStop = tripPattern.firstStop(); diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index 0cedc491f79..7eb69546aad 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -35,8 +35,8 @@ import uk.org.siri.siri20.EstimatedVehicleJourney; /** - * This class should be used to create snapshots of lookup tables of realtime data. This is - * necessary to provide planning threads a consistent constant view of a graph with realtime data at + * This class should be used to create snapshots of lookup tables of real-time data. This is + * necessary to provide planning threads a consistent constant view of a graph with real-time data at * a specific point in time. */ public class SiriTimetableSnapshotSource implements TimetableSnapshotProvider { @@ -59,7 +59,7 @@ public class SiriTimetableSnapshotSource implements TimetableSnapshotProvider { */ private final SiriTripPatternIdGenerator tripPatternIdGenerator = new SiriTripPatternIdGenerator(); /** - * A synchronized cache of trip patterns that are added to the graph due to GTFS-realtime + * A synchronized cache of trip patterns that are added to the graph due to GTFS-real-time * messages. */ private final SiriTripPatternCache tripPatternCache; @@ -81,7 +81,7 @@ public class SiriTimetableSnapshotSource implements TimetableSnapshotProvider { */ private volatile TimetableSnapshot snapshot = null; - /** Should expired realtime data be purged from the graph. */ + /** Should expired real-time data be purged from the graph. */ private final boolean purgeExpiredData; protected LocalDate lastPurgeDate = null; @@ -371,7 +371,7 @@ private Result addTripToGraphAndBuffer(TripUpdate tr // Add new trip times to the buffer and return success var result = buffer.update(pattern, tripUpdate.tripTimes(), serviceDate); - LOG.debug("Applied realtime data for trip {} on {}", trip, serviceDate); + LOG.debug("Applied real-time data for trip {} on {}", trip, serviceDate); return result; } @@ -416,10 +416,8 @@ private boolean removePreviousRealtimeUpdate(final Trip trip, final LocalDate se final TripPattern pattern = buffer.getRealtimeAddedTripPattern(trip.getId(), serviceDate); if (pattern != null) { - /* - Remove the previous realtime-added TripPattern from buffer. - Only one version of the realtime-update should exist - */ + // Remove the previous real-time-added TripPattern from buffer. + // Only one version of the real-time-update should exist buffer.removeLastAddedTripPattern(trip.getId(), serviceDate); buffer.removeRealtimeUpdatedTripTimes(pattern, trip.getId(), serviceDate); success = true; @@ -436,7 +434,7 @@ private boolean purgeExpiredData() { return false; } - LOG.debug("purging expired realtime data"); + LOG.debug("purging expired real-time data"); lastPurgeDate = previously; diff --git a/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java b/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java index ade320db298..5f44b3486a9 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java +++ b/src/ext/java/org/opentripplanner/ext/siri/TimetableHelper.java @@ -75,29 +75,29 @@ public static void applyUpdates( } int scheduledArrivalTime = tripTimes.getArrivalTime(index); - int realtimeArrivalTime = getAvailableTime( + int realTimeArrivalTime = getAvailableTime( departureDate, call::getActualArrivalTime, call::getExpectedArrivalTime ); int scheduledDepartureTime = tripTimes.getDepartureTime(index); - int realtimeDepartureTime = getAvailableTime( + int realTimeDepartureTime = getAvailableTime( departureDate, call::getActualDepartureTime, call::getExpectedDepartureTime ); int[] possibleArrivalTimes = index == 0 - ? new int[] { realtimeArrivalTime, realtimeDepartureTime, scheduledArrivalTime } - : new int[] { realtimeArrivalTime, scheduledArrivalTime }; + ? new int[] { realTimeArrivalTime, realTimeDepartureTime, scheduledArrivalTime } + : new int[] { realTimeArrivalTime, scheduledArrivalTime }; var arrivalTime = handleMissingRealtime(possibleArrivalTimes); int arrivalDelay = arrivalTime - scheduledArrivalTime; tripTimes.updateArrivalDelay(index, arrivalDelay); int[] possibleDepartureTimes = isLastStop - ? new int[] { realtimeDepartureTime, realtimeArrivalTime, scheduledDepartureTime } - : new int[] { realtimeDepartureTime, scheduledDepartureTime }; + ? new int[] { realTimeDepartureTime, realTimeArrivalTime, scheduledDepartureTime } + : new int[] { realTimeDepartureTime, scheduledDepartureTime }; var departureTime = handleMissingRealtime(possibleDepartureTimes); int departureDelay = departureTime - scheduledDepartureTime; tripTimes.updateDepartureDelay(index, departureDelay); diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java index cb664632443..29c0fa0c7a2 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java @@ -126,10 +126,10 @@ public SiriETGooglePubsubUpdater( SiriTimetableSnapshotSource timetableSnapshot ) { this.configRef = config.configRef(); - /* - URL that responds to HTTP GET which returns all initial data in protobuf-format. - Will be called once to initialize realtime-data. All updates will be received from Google Cloud Pubsub - */ + + // URL that responds to HTTP GET which returns all initial data in protobuf-format. Will be + // called once to initialize real-time-data. All updates will be received from Google Cloud + // Pubsub this.dataInitializationUrl = URI.create(config.dataInitializationUrl()); this.feedId = config.feedId(); this.reconnectPeriod = config.reconnectPeriod(); diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETHttpTripUpdateSource.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETHttpTripUpdateSource.java index 3f1de760701..9dab72446ea 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETHttpTripUpdateSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETHttpTripUpdateSource.java @@ -88,7 +88,7 @@ public String toString() { } private static SiriLoader createLoader(String url, Parameters parameters) { - // Load realtime updates from a file. + // Load real-time updates from a file. if (SiriFileLoader.matchesUrl(url)) { return new SiriFileLoader(url); } diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java index b4cf5f143e3..c8ccd2c533b 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java @@ -39,7 +39,7 @@ public class SiriETUpdater extends PollingGraphUpdater { protected WriteToGraphCallback saveResultOnGraph; /** - * The place where we'll record the incoming realtime timetables to make them available to the + * The place where we'll record the incoming real-time timetables to make them available to the * router in a thread safe way. */ private final SiriTimetableSnapshotSource snapshotSource; diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriFileLoader.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriFileLoader.java index 306deeca484..84da542ef47 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriFileLoader.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriFileLoader.java @@ -72,7 +72,7 @@ private Optional fetchFeed() { if (!matchFilename(file)) { continue; } - LOG.info("Process realtime input file: " + file.getAbsolutePath()); + LOG.info("Process real-time input file: " + file.getAbsolutePath()); var inProgressFile = newFile(file, SUFFIX_IN_PROGRESS); try { file.renameTo(inProgressFile); diff --git a/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java b/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java index c70e91fca52..91cbe5e1856 100644 --- a/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java +++ b/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java @@ -20,7 +20,7 @@ * that represent the same stop place) and swaps the "secondary" stops in patterns with their * "primary" equivalent. *

- * NOTE: This will make realtime trip updates for a modified pattern a lot harder. For Arcadis' + * NOTE: This will make real-time trip updates for a modified pattern a lot harder. For Arcadis' * initial implementation this is acceptable and will serve as encouragement for the data producers to * produce a consolidated transit feed rather than relying on this feature. */ diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/DefaultRouteRequestType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/DefaultRouteRequestType.java index 0d52fb280d9..fd9af09822e 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/DefaultRouteRequestType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/DefaultRouteRequestType.java @@ -394,7 +394,7 @@ private GraphQLObjectType createGraphQLType() { GraphQLFieldDefinition .newFieldDefinition() .name("ignoreRealTimeUpdates") - .description("When true, realtime updates are ignored during this search.") + .description("When true, real-time updates are ignored during this search.") .type(Scalars.GraphQLBoolean) .dataFetcher(env -> preferences.transit().ignoreRealtimeUpdates()) .build() diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/LegType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/LegType.java index d233aaec3c2..5125efca902 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/LegType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/LegType.java @@ -75,7 +75,8 @@ public static GraphQLObjectType create( .name("aimedStartTime") .description("The aimed date and time this leg starts.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) - .dataFetcher(env -> // startTime is already adjusted for realtime - need to subtract delay to get aimed time + .dataFetcher(env -> + // startTime is already adjusted for real-time - need to subtract delay to get aimed time leg(env) .getStartTime() .minusSeconds(leg(env).getDepartureDelay()) @@ -88,7 +89,7 @@ public static GraphQLObjectType create( GraphQLFieldDefinition .newFieldDefinition() .name("expectedStartTime") - .description("The expected, realtime adjusted date and time this leg starts.") + .description("The expected, real-time adjusted date and time this leg starts.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) .dataFetcher(env -> leg(env).getStartTime().toInstant().toEpochMilli()) .build() @@ -99,7 +100,7 @@ public static GraphQLObjectType create( .name("aimedEndTime") .description("The aimed date and time this leg ends.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) - .dataFetcher(env -> // endTime is already adjusted for realtime - need to subtract delay to get aimed time + .dataFetcher(env -> // endTime is already adjusted for real-time - need to subtract delay to get aimed time leg(env) .getEndTime() .minusSeconds(leg(env).getArrivalDelay()) @@ -112,7 +113,7 @@ public static GraphQLObjectType create( GraphQLFieldDefinition .newFieldDefinition() .name("expectedEndTime") - .description("The expected, realtime adjusted date and time this leg ends.") + .description("The expected, real-time adjusted date and time this leg ends.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) .dataFetcher(env -> leg(env).getEndTime().toInstant().toEpochMilli()) .build() diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripPatternType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripPatternType.java index 4381557d472..de90b89b38f 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripPatternType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripPatternType.java @@ -50,7 +50,8 @@ public static GraphQLObjectType create( .name("aimedStartTime") .description("The aimed date and time the trip starts.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) - .dataFetcher(env -> // startTime is already adjusted for realtime - need to subtract delay to get aimed time + .dataFetcher(env -> + // startTime is already adjusted for real-time - need to subtract delay to get aimed time itinerary(env) .startTime() .minusSeconds(itinerary(env).departureDelay()) @@ -63,7 +64,7 @@ public static GraphQLObjectType create( GraphQLFieldDefinition .newFieldDefinition() .name("expectedStartTime") - .description("The expected, realtime adjusted date and time the trip starts.") + .description("The expected, real-time adjusted date and time the trip starts.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) .dataFetcher(env -> itinerary(env).startTime().toInstant().toEpochMilli()) .build() @@ -74,7 +75,8 @@ public static GraphQLObjectType create( .name("aimedEndTime") .description("The aimed date and time the trip ends.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) - .dataFetcher(env -> // endTime is already adjusted for realtime - need to subtract delay to get aimed time + .dataFetcher(env -> + // endTime is already adjusted for real-time - need to subtract delay to get aimed time itinerary(env) .endTime() .minusSeconds(itinerary(env).arrivalDelay()) @@ -87,7 +89,7 @@ public static GraphQLObjectType create( GraphQLFieldDefinition .newFieldDefinition() .name("expectedEndTime") - .description("The expected, realtime adjusted date and time the trip ends.") + .description("The expected, real-time adjusted date and time the trip ends.") .type(new GraphQLNonNull(gqlUtil.dateTimeScalar)) .dataFetcher(env -> itinerary(env).endTime().toInstant().toEpochMilli()) .build() diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java index cf56e2ef34d..4ac182f514f 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/plan/TripQuery.java @@ -188,7 +188,7 @@ public static GraphQLFieldDefinition create( GraphQLArgument .newArgument() .name("ignoreRealtimeUpdates") - .description("When true, realtime updates are ignored during this search.") + .description("When true, real-time updates are ignored during this search.") .type(Scalars.GraphQLBoolean) .defaultValue(preferences.transit().ignoreRealtimeUpdates()) .build() diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/siri/et/EstimatedCallType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/siri/et/EstimatedCallType.java index 7a39aee6cca..3111f17b698 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/siri/et/EstimatedCallType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/siri/et/EstimatedCallType.java @@ -194,7 +194,7 @@ public static GraphQLObjectType create( .newFieldDefinition() .name("realtimeState") .type(new GraphQLNonNull(EnumTypes.REALTIME_STATE)) - .dataFetcher(environment -> ((TripTimeOnDate) environment.getSource()).getRealtimeState()) + .dataFetcher(environment -> ((TripTimeOnDate) environment.getSource()).getRealTimeState()) .build() ) .field( @@ -258,7 +258,7 @@ public static GraphQLObjectType create( .description( "Whether stop is cancelled. This means that either the " + "ServiceJourney has a planned cancellation, the ServiceJourney has been " + - "cancelled by realtime data, or this particular StopPoint has been " + + "cancelled by real-time data, or this particular StopPoint has been " + "cancelled. This also means that both boarding and alighting has been " + "cancelled." ) diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/QuayType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/QuayType.java index 203efb10452..8b9296b9137 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/QuayType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/QuayType.java @@ -299,7 +299,7 @@ public static GraphQLObjectType create( GraphQLArgument .newArgument() .name("includeCancelledTrips") - .description("Indicates that realtime-cancelled trips should also be included.") + .description("Indicates that real-time-cancelled trips should also be included.") .type(Scalars.GraphQLBoolean) .defaultValue(false) .build() diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/StopPlaceType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/StopPlaceType.java index 5a803138eb1..a60ee5df20f 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/StopPlaceType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/stop/StopPlaceType.java @@ -20,7 +20,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -34,7 +33,6 @@ import org.opentripplanner.ext.transmodelapi.model.plan.JourneyWhiteListed; import org.opentripplanner.ext.transmodelapi.support.GqlUtil; import org.opentripplanner.framework.graphql.GraphQLUtils; -import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.StopTimesInPattern; import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.routing.stoptimes.ArrivalDeparture; @@ -348,7 +346,7 @@ public static GraphQLObjectType create( GraphQLArgument .newArgument() .name("includeCancelledTrips") - .description("Indicates that realtime-cancelled trips should also be included.") + .description("Indicates that real-time-cancelled trips should also be included.") .type(Scalars.GraphQLBoolean) .defaultValue(false) .build() diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/DatedServiceJourneyType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/DatedServiceJourneyType.java index 6634ee4e4d3..e9cb689036b 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/DatedServiceJourneyType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/DatedServiceJourneyType.java @@ -146,7 +146,7 @@ public static GraphQLObjectType create( .withDirective(gqlUtil.timingData) .description( "Returns scheduled passingTimes for this dated service journey, " + - "updated with realtime-updates (if available). " + "updated with real-time-updates (if available). " ) .dataFetcher(environment -> { TripOnServiceDate tripOnServiceDate = tripOnServiceDate(environment); diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/ServiceJourneyType.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/ServiceJourneyType.java index 3603db05120..f03dad44fc1 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/ServiceJourneyType.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/timetable/ServiceJourneyType.java @@ -240,7 +240,7 @@ public static GraphQLObjectType create( .type(new GraphQLNonNull(new GraphQLList(timetabledPassingTimeType))) .withDirective(gqlUtil.timingData) .description( - "Returns scheduled passing times only - without realtime-updates, for realtime-data use 'estimatedCalls'" + "Returns scheduled passing times only - without real-time-updates, for realtime-data use 'estimatedCalls'" ) .dataFetcher(env -> { Trip trip = trip(env); @@ -259,7 +259,7 @@ public static GraphQLObjectType create( .type(new GraphQLList(estimatedCallType)) .withDirective(gqlUtil.timingData) .description( - "Returns scheduled passingTimes for this ServiceJourney for a given date, updated with realtime-updates (if available). " + + "Returns scheduled passingTimes for this ServiceJourney for a given date, updated with real-time-updates (if available). " + "NB! This takes a date as argument (default=today) and returns estimatedCalls for that date and should only be used if the date is " + "known when creating the request. For fetching estimatedCalls for a given trip.leg, use leg.serviceJourneyEstimatedCalls instead." ) diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java index a921836d907..e5086630941 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java @@ -67,7 +67,7 @@ public HslParkUpdater( /** * Update the data from the sources. It first fetches parks from the facilities URL and park - * groups from hubs URL and then realtime updates from utilizations URL. If facilitiesFrequencySec + * groups from hubs URL and then real-time updates from utilizations URL. If facilitiesFrequencySec * is configured to be over 0, it also occasionally retches the parks as new parks might have been * added or the state of the old parks might have changed. * diff --git a/src/main/java/org/opentripplanner/api/mapping/PlaceMapper.java b/src/main/java/org/opentripplanner/api/mapping/PlaceMapper.java index f6b0defd709..e806c5615d2 100644 --- a/src/main/java/org/opentripplanner/api/mapping/PlaceMapper.java +++ b/src/main/java/org/opentripplanner/api/mapping/PlaceMapper.java @@ -130,7 +130,7 @@ private ApiVehicleParkingWithEntrance mapVehicleParking( .hasWheelchairAccessibleCarPlaces(vp.hasWheelchairAccessibleCarPlaces()) .availability(mapVehicleParkingSpaces(vp.getAvailability())) .capacity(mapVehicleParkingSpaces(vp.getCapacity())) - .realtime(vehicleParkingWithEntrance.isRealtime()) + .realTime(vehicleParkingWithEntrance.isRealtime()) .build(); } } diff --git a/src/main/java/org/opentripplanner/api/mapping/TripTimeMapper.java b/src/main/java/org/opentripplanner/api/mapping/TripTimeMapper.java index c5303c744cf..2f5bfe91026 100644 --- a/src/main/java/org/opentripplanner/api/mapping/TripTimeMapper.java +++ b/src/main/java/org/opentripplanner/api/mapping/TripTimeMapper.java @@ -28,13 +28,13 @@ public static ApiTripTimeShort mapToApi(TripTimeOnDate domain) { api.stopCount = domain.getStopCount(); api.scheduledArrival = domain.getScheduledArrival(); api.scheduledDeparture = domain.getScheduledDeparture(); - api.realtimeArrival = domain.getRealtimeArrival(); - api.realtimeDeparture = domain.getRealtimeDeparture(); + api.realTimeArrival = domain.getRealtimeArrival(); + api.realTimeDeparture = domain.getRealtimeDeparture(); api.arrivalDelay = domain.getArrivalDelay(); api.departureDelay = domain.getDepartureDelay(); api.timepoint = domain.isTimepoint(); - api.realtime = domain.isRealtime(); - api.realtimeState = ApiRealTimeState.RealTimeState(domain.getRealtimeState()); + api.realTime = domain.isRealtime(); + api.realTimeState = ApiRealTimeState.RealTimeState(domain.getRealTimeState()); api.blockId = domain.getBlockId(); api.headsign = I18NStringMapper.mapToApi(domain.getHeadsign(), null); api.tripId = FeedScopedIdMapper.mapToApi(domain.getTrip().getId()); diff --git a/src/main/java/org/opentripplanner/api/model/ApiTripSearchMetadata.java b/src/main/java/org/opentripplanner/api/model/ApiTripSearchMetadata.java index 49c98d6e5b6..486118883f9 100644 --- a/src/main/java/org/opentripplanner/api/model/ApiTripSearchMetadata.java +++ b/src/main/java/org/opentripplanner/api/model/ApiTripSearchMetadata.java @@ -22,7 +22,7 @@ public class ApiTripSearchMetadata { * This is the suggested search time for the "next page" or time-window. Insert it together with * the {@link #searchWindowUsed} in the request to get a new set of trips following in the * time-window AFTER the current search. No duplicate trips should be returned, unless a trip is - * delayed and new realtime-data is available. + * delayed and new real-time-data is available. *

* Be careful to use paging/scrolling with the {@code numOfItineraries} parameter set. It is safe * to scroll forward when the {@code arriveBy=false}, but not if {@code arriveBy=true}. If you @@ -42,7 +42,7 @@ public class ApiTripSearchMetadata { * This is the suggested search time for the "previous page" or time window. Insert it together * with the {@link #searchWindowUsed} in the request to get a new set of trips preceding in the * time-window BEFORE the current search. No duplicate trips should be returned, unless a trip is - * delayed and new realtime-data is available. + * delayed and new real-time-data is available. *

* Be careful to use paging/scrolling with the {@code numOfItineraries} parameter set. It is safe * to scroll backward when the {@code arriveBy=true}, but not if {@code arriveBy=false}. If you diff --git a/src/main/java/org/opentripplanner/api/model/ApiTripTimeShort.java b/src/main/java/org/opentripplanner/api/model/ApiTripTimeShort.java index 92a78f4aade..0a340d4a812 100644 --- a/src/main/java/org/opentripplanner/api/model/ApiTripTimeShort.java +++ b/src/main/java/org/opentripplanner/api/model/ApiTripTimeShort.java @@ -11,13 +11,13 @@ public class ApiTripTimeShort implements Serializable { public int stopCount; public int scheduledArrival = UNDEFINED; public int scheduledDeparture = UNDEFINED; - public int realtimeArrival = UNDEFINED; - public int realtimeDeparture = UNDEFINED; + public int realTimeArrival = UNDEFINED; + public int realTimeDeparture = UNDEFINED; public int arrivalDelay = UNDEFINED; public int departureDelay = UNDEFINED; public boolean timepoint = false; - public boolean realtime = false; - public ApiRealTimeState realtimeState = ApiRealTimeState.SCHEDULED; + public boolean realTime = false; + public ApiRealTimeState realTimeState = ApiRealTimeState.SCHEDULED; public long serviceDay; public String tripId; public String blockId; diff --git a/src/main/java/org/opentripplanner/api/model/ApiVehicleParkingWithEntrance.java b/src/main/java/org/opentripplanner/api/model/ApiVehicleParkingWithEntrance.java index 56e3e0c3710..8cac78f6c9e 100644 --- a/src/main/java/org/opentripplanner/api/model/ApiVehicleParkingWithEntrance.java +++ b/src/main/java/org/opentripplanner/api/model/ApiVehicleParkingWithEntrance.java @@ -73,15 +73,15 @@ public class ApiVehicleParkingWithEntrance { public final ApiVehicleParkingSpaces capacity; /** - * The number of available spaces. Only present if there is a realtime updater present. Maybe + * The number of available spaces. Only present if there is a real-time updater present. Maybe * {@code null} if unknown. */ public final ApiVehicleParkingSpaces availability; /** - * True if realtime information is used for checking availability. + * True if real-time information is used for checking availability. */ - public final boolean realtime; + public final boolean realTime; ApiVehicleParkingWithEntrance( String id, @@ -98,7 +98,7 @@ public class ApiVehicleParkingWithEntrance { boolean hasWheelchairAccessibleCarPlaces, ApiVehicleParkingSpaces capacity, ApiVehicleParkingSpaces availability, - boolean realtime + boolean realTime ) { this.id = id; this.name = name; @@ -114,7 +114,7 @@ public class ApiVehicleParkingWithEntrance { this.hasWheelchairAccessibleCarPlaces = hasWheelchairAccessibleCarPlaces; this.capacity = capacity; this.availability = availability; - this.realtime = realtime; + this.realTime = realTime; } public static ApiVehicleParkingWithEntranceBuilder builder() { @@ -137,7 +137,7 @@ public static class ApiVehicleParkingWithEntranceBuilder { private boolean hasWheelchairAccessibleCarPlaces; private ApiVehicleParkingSpaces capacity; private ApiVehicleParkingSpaces availability; - private boolean realtime; + private boolean realTime; ApiVehicleParkingWithEntranceBuilder() {} @@ -213,8 +213,8 @@ public ApiVehicleParkingWithEntranceBuilder availability(ApiVehicleParkingSpaces return this; } - public ApiVehicleParkingWithEntranceBuilder realtime(boolean realtime) { - this.realtime = realtime; + public ApiVehicleParkingWithEntranceBuilder realTime(boolean realTime) { + this.realTime = realTime; return this; } @@ -234,7 +234,7 @@ public ApiVehicleParkingWithEntrance build() { hasWheelchairAccessibleCarPlaces, capacity, availability, - realtime + realTime ); } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java index d7229b84b1f..dfc3e2d75f8 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java @@ -17,7 +17,7 @@ public record GraphQLRequestContext( FareService fareService, VehicleParkingService vehicleParkingService, VehicleRentalService vehicleRentalService, - RealtimeVehicleService realtimeVehicleService, + RealtimeVehicleService realTimeVehicleService, GraphFinder graphFinder, RouteRequest defaultRouteRequest ) { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PatternImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PatternImpl.java index 18ab5c5d5e9..241434c664f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PatternImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PatternImpl.java @@ -249,7 +249,7 @@ private List getTrips(DataFetchingEnvironment environment) { } private RealtimeVehicleService getRealtimeVehiclesService(DataFetchingEnvironment environment) { - return environment.getContext().realtimeVehicleService(); + return environment.getContext().realTimeVehicleService(); } private TransitService getTransitService(DataFetchingEnvironment environment) { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopImpl.java index 9353027439b..0730d2fbc91 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopImpl.java @@ -538,7 +538,7 @@ private List getTripTimeOnDatesForPatternAtStopIncludingTripsWit } /** - * Get a stream of {@link TripPattern} that were created realtime based of the provided pattern. + * Get a stream of {@link TripPattern} that were created real-time based of the provided pattern. * Only patterns that don't have removed (stops can still be skipped) or added stops are included. */ private Stream getRealtimeAddedPatternsAsStream( diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java index a74d4f9461d..faf59ef9d6e 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java @@ -71,7 +71,7 @@ public DataFetcher realtimeState() { return environment -> getSource(environment).isCanceledEffectively() ? RealTimeState.CANCELED.name() - : getSource(environment).getRealtimeState().name(); + : getSource(environment).getRealTimeState().name(); } @Override diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java index 78255ce7787..2502d2b9539 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java @@ -405,7 +405,7 @@ private TransitService getTransitService(DataFetchingEnvironment environment) { } private RealtimeVehicleService getRealtimeVehiclesService(DataFetchingEnvironment environment) { - return environment.getContext().realtimeVehicleService(); + return environment.getContext().realTimeVehicleService(); } private Trip getSource(DataFetchingEnvironment environment) { diff --git a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 9c730b94216..5f39872eed5 100644 --- a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -84,7 +84,7 @@ public enum OTPFeature { RealtimeResolver( false, true, - "When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with realtime data" + "When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data" ), ReportApi(false, true, "Enable the report API."), RestAPIPassInDefaultConfigAsJson( diff --git a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java index 5cbdc682a1e..9ba85d6b532 100644 --- a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java +++ b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java @@ -189,7 +189,7 @@ public boolean isNoDataStop() { return tripTimes.isNoDataStop(stopIndex); } - public RealTimeState getRealtimeState() { + public RealTimeState getRealTimeState() { return tripTimes.isNoDataStop(stopIndex) ? RealTimeState.SCHEDULED : tripTimes.getRealTimeState(); diff --git a/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java b/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java index ff72bf3efb5..da5c4aad60d 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java +++ b/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java @@ -138,7 +138,7 @@ public CostLinearFunction relaxTransitPriorityGroup() { } /** - * When true, realtime updates are ignored during this search. + * When true, real-time updates are ignored during this search. */ public boolean ignoreRealtimeUpdates() { return ignoreRealtimeUpdates; diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 7d8b0f7b33f..0f61fa4f7a2 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -125,7 +125,7 @@ private TimetableSnapshotSourceParameters timetableUpdates(NodeAdapter c) { .of("purgeExpiredData") .since(V2_2) .summary( - "Should expired realtime data be purged from the graph. Apply to GTFS-RT and Siri updates." + "Should expired real-time data be purged from the graph. Apply to GTFS-RT and Siri updates." ) .asBoolean(dflt.purgeExpiredData()) ); diff --git a/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java index 66f92da04eb..118dca43318 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -302,7 +302,7 @@ The board time is added to the time when going from the stop (offboard) to onboa c .of("ignoreRealtimeUpdates") .since(V2_0) - .summary("When true, realtime updates are ignored during this search.") + .summary("When true, real-time updates are ignored during this search.") .asBoolean(dft.ignoreRealtimeUpdates()) ) .setOtherThanPreferredRoutesPenalty( diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 6cda95eb983..e53939354b7 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -119,7 +119,7 @@ public class TimetableSnapshotSource implements TimetableSnapshotProvider { */ private volatile TimetableSnapshot snapshot = null; - /** Should expired realtime data be purged from the graph. */ + /** Should expired real-time data be purged from the graph. */ private final boolean purgeExpiredData; protected LocalDate lastPurgeDate = null; diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 9dba2c90a5e..d6cc164e3c1 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3098,7 +3098,7 @@ type QueryType { omitCanceled: Boolean = true """ - When true, realtime updates are ignored during this search. Default value: false + When true, real-time updates are ignored during this search. Default value: false """ ignoreRealtimeUpdates: Boolean diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap index 8f527b15bf7..31d59483807 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap @@ -106,7 +106,7 @@ org.opentripplanner.routing.algorithm.mapping.CarSnapshotTest.directCarPark=[ "hasWheelchairAccessibleCarPlaces" : false, "id" : "OSM:OSMWay/-102488", "name" : "P+R", - "realtime" : false, + "realTime" : false, "tags" : [ "osm:amenity=parking" ] @@ -137,7 +137,7 @@ org.opentripplanner.routing.algorithm.mapping.CarSnapshotTest.directCarPark=[ "hasWheelchairAccessibleCarPlaces" : false, "id" : "OSM:OSMWay/-102488", "name" : "P+R", - "realtime" : false, + "realTime" : false, "tags" : [ "osm:amenity=parking" ] From bdae8134146bb06e9e6514d960ae5e4a56ed4f3c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 4 Dec 2023 17:35:39 +0100 Subject: [PATCH 22/39] Fix syntax [ci skip] --- .github/workflows/prune-container-images.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prune-container-images.yml b/.github/workflows/prune-container-images.yml index 518e8afb67b..17da231ff61 100644 --- a/.github/workflows/prune-container-images.yml +++ b/.github/workflows/prune-container-images.yml @@ -1,4 +1,4 @@ -name: 'Delete unused snapshot container images' +name: 'Prune container images' on: schedule: @@ -19,4 +19,4 @@ jobs: # remove all snapshot container images that have not been pulled for over a year # --keep-semver makes sure that any image with a x.y.z version scheme is unaffected by this pip install prune-container-repo==0.0.3 - prune-container-repo -u ${CONTAINER_REGISTRY_USER} -r ${CONTAINER_REPO} --days=365 --keep-semver --activate + prune-container-repo -u ${CONTAINER_REGISTRY_USER} -r ${CONTAINER_REPO} --days=365 --keep-semver=true --activate From 449c297fab00afd20d78551c959207ba418cb460 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 4 Dec 2023 17:54:48 +0100 Subject: [PATCH 23/39] Upgrade prune version [ci skip] --- .github/workflows/prune-container-images.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prune-container-images.yml b/.github/workflows/prune-container-images.yml index 17da231ff61..c1653701c3b 100644 --- a/.github/workflows/prune-container-images.yml +++ b/.github/workflows/prune-container-images.yml @@ -18,5 +18,5 @@ jobs: run: | # remove all snapshot container images that have not been pulled for over a year # --keep-semver makes sure that any image with a x.y.z version scheme is unaffected by this - pip install prune-container-repo==0.0.3 - prune-container-repo -u ${CONTAINER_REGISTRY_USER} -r ${CONTAINER_REPO} --days=365 --keep-semver=true --activate + pip install prune-container-repo==0.0.4 + prune-container-repo -u ${CONTAINER_REGISTRY_USER} -r ${CONTAINER_REPO} --days=365 --keep-semver --activate From 15d0a81c958efdb7dfc1876223a041b15c8f8086 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 5 Dec 2023 10:51:56 +0000 Subject: [PATCH 24/39] Add changelog entry for #5535 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 36cdc98f5a2..0fa8e2363cf 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -52,6 +52,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix missed trip when arrive-by search-window is off by one minute [#5520](https://github.com/opentripplanner/OpenTripPlanner/pull/5520) - Transit group priority - Part 1 [#4999](https://github.com/opentripplanner/OpenTripPlanner/pull/4999) - Remove `matchBusRoutesToStreets` [#5523](https://github.com/opentripplanner/OpenTripPlanner/pull/5523) +- Rename realtime to real-time in docs [#5535](https://github.com/opentripplanner/OpenTripPlanner/pull/5535) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From 5759a42fbf4953d5bf18f35c0faddc4af99ba54d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:56:29 +0000 Subject: [PATCH 25/39] fix(deps): update dependency org.apache.httpcomponents.client5:httpclient5 to v5.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e0de45f1f7e..c43b576b446 100644 --- a/pom.xml +++ b/pom.xml @@ -898,7 +898,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.2.1 + 5.3 commons-cli From a66b53411d0287e9ba3a9f3f5b81c1944c8aa22f Mon Sep 17 00:00:00 2001 From: Henrik Abrahamsson Date: Wed, 6 Dec 2023 14:38:55 +0100 Subject: [PATCH 26/39] Fix issue where stop points are sometimes added twice to index This only happens when there are duplicate JourneyPattern in the netex files. --- .../netex/mapping/GroupNetexMapper.java | 4 +- .../netex/mapping/NetexMapper.java | 3 +- .../netex/mapping/TransferMapper.java | 39 ++++++++++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/mapping/GroupNetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/GroupNetexMapper.java index 3428f0f8abd..7abb05854f8 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/GroupNetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/GroupNetexMapper.java @@ -3,7 +3,9 @@ import com.google.common.collect.ArrayListMultimap; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.model.impl.OtpTransitServiceBuilder; import org.opentripplanner.model.transfer.ConstrainedTransfer; @@ -25,7 +27,7 @@ class GroupNetexMapper { /** * A map from trip/serviceJourney id to an ordered list of scheduled stop point ids. */ - final ArrayListMultimap scheduledStopPointsIndex = ArrayListMultimap.create(); + final Map> scheduledStopPointsIndex = new HashMap<>(); GroupNetexMapper( FeedScopedIdFactory idFactory, diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index 4c20eee2ce6..26016947c09 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -1,6 +1,7 @@ package org.opentripplanner.netex.mapping; import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import jakarta.xml.bind.JAXBElement; import java.time.LocalDateTime; import java.time.ZoneId; @@ -469,7 +470,7 @@ private void mapTripPatterns(Map serviceIds) { transitBuilder.getTripPatterns().put(it.getKey(), it.getValue()); } currentMapperIndexes.addStopTimesByNetexId(result.stopTimeByNetexId); - groupMapper.scheduledStopPointsIndex.putAll(result.scheduledStopPointsIndex); + groupMapper.scheduledStopPointsIndex.putAll(Multimaps.asMap(result.scheduledStopPointsIndex)); transitBuilder.getTripOnServiceDates().addAll(result.tripOnServiceDates); } } diff --git a/src/main/java/org/opentripplanner/netex/mapping/TransferMapper.java b/src/main/java/org/opentripplanner/netex/mapping/TransferMapper.java index b5b8f42ba36..cb908922160 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/TransferMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/TransferMapper.java @@ -1,6 +1,7 @@ package org.opentripplanner.netex.mapping; -import com.google.common.collect.ArrayListMultimap; +import java.util.List; +import java.util.Map; import javax.annotation.Nullable; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.model.transfer.ConstrainedTransfer; @@ -23,13 +24,13 @@ public class TransferMapper { private final FeedScopedIdFactory idFactory; private final DataImportIssueStore issueStore; - private final ArrayListMultimap scheduledStopPointsIndex; + private final Map> scheduledStopPointsIndex; private final EntityById trips; public TransferMapper( FeedScopedIdFactory idFactory, DataImportIssueStore issueStore, - ArrayListMultimap scheduledStopPointsIndex, + Map> scheduledStopPointsIndex, EntityById trips ) { this.idFactory = idFactory; @@ -139,32 +140,34 @@ private int findStopPosition( ScheduledStopPointRefStructure scheduledStopPointRef ) { String sspId = scheduledStopPointRef.getRef(); + var scheduledStopPoints = scheduledStopPointsIndex.get(sjId); + String errorMessage; + + if (scheduledStopPoints != null) { + var index = + switch (label) { + case Label.TO -> scheduledStopPoints.indexOf(sspId); + case Label.FROM -> scheduledStopPoints.lastIndexOf(sspId); + }; + if (index >= 0) { + return index; + } - int index = -1; - if (label == Label.TO) { - index = scheduledStopPointsIndex.get(sjId).indexOf(sspId); - } else if (label == Label.FROM) { - index = scheduledStopPointsIndex.get(sjId).lastIndexOf(sspId); - } - - if (index >= 0) { - return index; + errorMessage = "Scheduled-stop-point-ref not found"; + } else { + errorMessage = "Service-journey not found"; } - String detailedMsg = scheduledStopPointsIndex.containsKey(sjId) - ? "Scheduled-stop-point-ref not found" - : "Service-journey not found"; - issueStore.add( new InterchangePointMappingFailed( - detailedMsg, + errorMessage, interchangeId, label.label(fieldName), sjId, sspId ) ); - return index; + return -1; } private FeedScopedId createId(String id) { From 13cd9fca521b489010ef2ac1e664e380996993e4 Mon Sep 17 00:00:00 2001 From: Eivind Morris Bakke Date: Wed, 6 Dec 2023 21:45:58 +0100 Subject: [PATCH 27/39] Adds doc to src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java Co-authored-by: Thomas Gran --- .../org/opentripplanner/ext/transmodelapi/model/EnumTypes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java index bc4146fa2f5..e585ad55c57 100644 --- a/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java +++ b/src/ext/java/org/opentripplanner/ext/transmodelapi/model/EnumTypes.java @@ -49,7 +49,7 @@ public class EnumTypes { .value("noFilter", AlternativeLegsFilter.NO_FILTER) .value("sameAuthority", AlternativeLegsFilter.SAME_AGENCY) .value("sameMode", AlternativeLegsFilter.SAME_MODE) - .value("sameSubmode", AlternativeLegsFilter.SAME_SUBMODE) + .value("sameSubmode", AlternativeLegsFilter.SAME_SUBMODE, "Must match both subMode and mode") .value("sameLine", AlternativeLegsFilter.SAME_ROUTE) .build(); From 7f075c1b5e788659bc069e76d04904a1c6fcdfe7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:32:04 +0000 Subject: [PATCH 28/39] fix(deps): update dependency org.entur.gbfs:gbfs-java-model to v3.0.16 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c43b576b446..c133a8e9310 100644 --- a/pom.xml +++ b/pom.xml @@ -703,7 +703,7 @@ org.entur.gbfs gbfs-java-model - 3.0.13 + 3.0.16 From 9e9e0786ce6fa6016d0bffdd8747d03652322fe3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:07:04 +0000 Subject: [PATCH 29/39] fix(deps): update dependency org.onebusaway:onebusaway-gtfs to v1.4.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index baf3cc48fc0..195d2580fda 100644 --- a/pom.xml +++ b/pom.xml @@ -853,7 +853,7 @@ org.onebusaway onebusaway-gtfs - 1.4.5 + 1.4.9 org.slf4j From 28c892d7587cc1f357fbcbbc3e0913c070e51110 Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Thu, 7 Dec 2023 14:56:11 +0000 Subject: [PATCH 30/39] Bump serialization version id for #5525 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index baf3cc48fc0..d48aa7056a6 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ - 131 + 132 30.1 2.49 From c894b80a7290c3ecf5aa1c54a9fdce61152aa045 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 8 Dec 2023 09:41:52 +0100 Subject: [PATCH 31/39] refactor: Add "-Dps" as alias for -P prettierSkip --- pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pom.xml b/pom.xml index d48aa7056a6..f9a7037b8bc 100644 --- a/pom.xml +++ b/pom.xml @@ -966,6 +966,17 @@ prettierSkip + + + + ps + + + From 3e011bdb46b5a3d730713087e322605f5ce75e2f Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 8 Dec 2023 10:29:56 +0100 Subject: [PATCH 32/39] Update pom.xml Co-authored-by: Leonard Ehrenfried --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f9a7037b8bc..f670d56cfc3 100644 --- a/pom.xml +++ b/pom.xml @@ -969,7 +969,7 @@ From 978603516eae95fbc72af49943f6152a8c0d7a47 Mon Sep 17 00:00:00 2001 From: eibakke Date: Fri, 8 Dec 2023 22:28:23 +0100 Subject: [PATCH 33/39] Updates schema. --- src/ext/graphql/transmodelapi/schema.graphql | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ext/graphql/transmodelapi/schema.graphql b/src/ext/graphql/transmodelapi/schema.graphql index 9a31b1cd5b9..f6eed80d2b3 100644 --- a/src/ext/graphql/transmodelapi/schema.graphql +++ b/src/ext/graphql/transmodelapi/schema.graphql @@ -1405,6 +1405,7 @@ enum AlternativeLegsFilter { sameAuthority sameLine sameMode + "Must match both subMode and mode" sameSubmode } From 421e58b39afed7c15b1c1c838b8c63fe1e1df856 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 00:28:55 +0000 Subject: [PATCH 34/39] chore(deps): update dependency com.tngtech.archunit:archunit to v1.2.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ddd90349001..523aeb20d03 100644 --- a/pom.xml +++ b/pom.xml @@ -722,7 +722,7 @@ com.tngtech.archunit archunit - 1.2.0 + 1.2.1 test From 48fb2533f66b1af42a5574c66f00cc43e512f7b7 Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Thu, 7 Dec 2023 15:56:11 +0100 Subject: [PATCH 35/39] fix: Relax TripTimes validation to [-12h, 20d] Some trips last for more than 10 days, so we relax this sanity check to 20 days. --- .../model/timetable/ScheduledTripTimes.java | 2 +- .../timetable/ScheduledTripTimesTest.java | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index b950cdebc5e..90ed7ac31cc 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -35,7 +35,7 @@ public final class ScheduledTripTimes implements TripTimes { /** * We allow a trip to last for maximum 10 days. In Norway the longest trip is 6 days. */ - private static final int MAX_TIME = DurationUtils.durationInSeconds("10d"); + private static final int MAX_TIME = DurationUtils.durationInSeconds("20d"); /** * Implementation notes: This allows re-using the same scheduled arrival and departure time diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java index d548be2309b..ed6c5c70f92 100644 --- a/src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java +++ b/src/test/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.transit.model._data.TransitModelForTest.id; @@ -36,7 +37,7 @@ class ScheduledTripTimesTest { } private final ScheduledTripTimes subject = ScheduledTripTimes - .of(null) + .of() .withArrivalTimes("10:00 11:00 12:00") .withDepartureTimes("10:01 11:02 12:03") .withServiceCode(SERVICE_CODE) @@ -98,6 +99,21 @@ void isTimepoint() { assertFalse(subject.isTimepoint(STOP_POS_2)); } + @Test + void validateLastArrivalTimeIsNotMoreThan20DaysAfterFirstDepartureTime() { + var ex = assertThrows( + IllegalArgumentException.class, + () -> + ScheduledTripTimes + .of() + .withDepartureTimes("10:00 12:00 10:00:01+20d") + .withServiceCode(SERVICE_CODE) + .withTrip(TRIP) + .build() + ); + assertEquals("The value is not in range[-43200, 1728000]: 1728001", ex.getMessage()); + } + @Test void getTrip() { assertEquals(TRIP, subject.getTrip()); From 168c0951e2b54b0ef927b401a25eb346ee208a76 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Mon, 11 Dec 2023 14:56:18 +0100 Subject: [PATCH 36/39] Update src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java Co-authored-by: Leonard Ehrenfried --- .../transit/model/timetable/ScheduledTripTimes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index 90ed7ac31cc..f502a220073 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -33,7 +33,7 @@ public final class ScheduledTripTimes implements TripTimes { */ private static final int MIN_TIME = DurationUtils.durationInSeconds("-12h"); /** - * We allow a trip to last for maximum 10 days. In Norway the longest trip is 6 days. + * We allow a trip to last for maximum 20 days. In Norway the longest trip is 6 days. */ private static final int MAX_TIME = DurationUtils.durationInSeconds("20d"); From 6265fc8d1866f3d5d2bc9e606a913fa013651031 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Dec 2023 01:13:32 +0000 Subject: [PATCH 37/39] chore(deps): update dependency org.mockito:mockito-core to v5.8.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 523aeb20d03..6110c4034f2 100644 --- a/pom.xml +++ b/pom.xml @@ -728,7 +728,7 @@ org.mockito mockito-core - 5.7.0 + 5.8.0 test From ff814e36f8d1ada1a6d77250a631a3eb9fcf9ae6 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 12 Dec 2023 09:48:58 +0000 Subject: [PATCH 38/39] Add changelog entry for #5548 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 0fa8e2363cf..9f15a929505 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -53,6 +53,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Transit group priority - Part 1 [#4999](https://github.com/opentripplanner/OpenTripPlanner/pull/4999) - Remove `matchBusRoutesToStreets` [#5523](https://github.com/opentripplanner/OpenTripPlanner/pull/5523) - Rename realtime to real-time in docs [#5535](https://github.com/opentripplanner/OpenTripPlanner/pull/5535) +- Add same submode in alternative legs filter [#5548](https://github.com/opentripplanner/OpenTripPlanner/pull/5548) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From 0af8a79ab2a308ae04d1e5bb0cb7f142c5685fc4 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 12 Dec 2023 09:52:36 +0000 Subject: [PATCH 39/39] Add changelog entry for #5552 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 9f15a929505..06beb1d120c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -54,6 +54,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Remove `matchBusRoutesToStreets` [#5523](https://github.com/opentripplanner/OpenTripPlanner/pull/5523) - Rename realtime to real-time in docs [#5535](https://github.com/opentripplanner/OpenTripPlanner/pull/5535) - Add same submode in alternative legs filter [#5548](https://github.com/opentripplanner/OpenTripPlanner/pull/5548) +- Fix issue where stop points are sometimes added twice to index [#5552](https://github.com/opentripplanner/OpenTripPlanner/pull/5552) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13)