Skip to content

Commit 772db47

Browse files
Merge pull request opentripplanner#7299 from opentripplanner/siri-arrived-departed
Support for Siri ArrivalStatus.ARRIVED on EstimatedCalls
2 parents 115b93a + c68efba commit 772db47

14 files changed

Lines changed: 274 additions & 97 deletions

File tree

application/src/main/java/org/opentripplanner/model/TripTimeOnDate.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,14 +251,14 @@ public int getRealtimeDeparture() {
251251
* Returns the actual arrival time if available. Otherwise -1 is returned.
252252
*/
253253
public int getActualArrival() {
254-
return isRecordedStop() ? tripTimes.getArrivalTime(stopPosition) : UNDEFINED;
254+
return hasArrived() ? tripTimes.getArrivalTime(stopPosition) : UNDEFINED;
255255
}
256256

257257
/**
258258
* Returns the actual departure time if available. Otherwise -1 is returned.
259259
*/
260260
public int getActualDeparture() {
261-
return isRecordedStop() ? tripTimes.getDepartureTime(stopPosition) : UNDEFINED;
261+
return hasDeparted() ? tripTimes.getDepartureTime(stopPosition) : UNDEFINED;
262262
}
263263

264264
public int getArrivalDelay() {
@@ -305,12 +305,14 @@ public boolean isNoDataStop() {
305305
return tripTimes.isNoDataStop(stopPosition);
306306
}
307307

308-
/**
309-
* Is the real-time time a recorded time (i.e. has the vehicle already passed the stop).
310-
* This information is currently only available from SIRI feeds.
311-
*/
312-
public boolean isRecordedStop() {
313-
return tripTimes.isRecordedStop(stopPosition);
308+
/// True if there is realtime information indicating that the trip has arrived at the stop.
309+
public boolean hasArrived() {
310+
return tripTimes.hasArrived(stopPosition);
311+
}
312+
313+
/// True if there is realtime information indicating that the trip has departed from the stop.
314+
public boolean hasDeparted() {
315+
return tripTimes.hasDeparted(stopPosition);
314316
}
315317

316318
public RealTimeState getRealTimeState() {

application/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.time.Duration;
77
import java.util.Arrays;
8+
import java.util.BitSet;
89
import java.util.List;
910
import java.util.Objects;
1011
import java.util.OptionalInt;
@@ -30,7 +31,9 @@ public final class RealTimeTripTimes implements TripTimes<RealTimeTripTimes> {
3031
private final int[] departureTimes;
3132
private final RealTimeState realTimeState;
3233
private final StopRealTimeState[] stopRealTimeStates;
33-
private final boolean[] extraCalls;
34+
private final BitSet extraCalls;
35+
private final BitSet hasArrived;
36+
private final BitSet hasDeparted;
3437

3538
@Nullable
3639
private final I18NString tripHeadsign;
@@ -50,6 +53,8 @@ public final class RealTimeTripTimes implements TripTimes<RealTimeTripTimes> {
5053
stopHeadsigns = builder.stopHeadsigns();
5154
occupancyStatus = builder.occupancyStatus();
5255
wheelchairAccessibility = builder.wheelchairAccessibility();
56+
hasArrived = builder.hasArrived();
57+
hasDeparted = builder.hasDeparted();
5358
validateNonIncreasingTimes();
5459
}
5560

@@ -67,6 +72,8 @@ private RealTimeTripTimes(RealTimeTripTimes original, ScheduledTripTimes schedul
6772
this.stopHeadsigns = original.stopHeadsigns;
6873
this.occupancyStatus = original.occupancyStatus;
6974
this.wheelchairAccessibility = original.wheelchairAccessibility;
75+
this.hasArrived = original.hasArrived;
76+
this.hasDeparted = original.hasDeparted;
7077
}
7178

7279
/**
@@ -86,6 +93,8 @@ private RealTimeTripTimes(RealTimeTripTimes original, int timeShift) {
8693
this.stopHeadsigns = original.stopHeadsigns;
8794
this.occupancyStatus = original.occupancyStatus;
8895
this.wheelchairAccessibility = original.wheelchairAccessibility;
96+
this.hasArrived = original.hasArrived;
97+
this.hasDeparted = original.hasDeparted;
8998
}
9099

91100
ScheduledTripTimes scheduledTripTimes() {
@@ -180,8 +189,14 @@ public boolean isCancelledStop(int stopPos) {
180189
return isStopRealTimeStates(stopPos, StopRealTimeState.CANCELLED);
181190
}
182191

183-
public boolean isRecordedStop(int stopPos) {
184-
return isStopRealTimeStates(stopPos, StopRealTimeState.RECORDED);
192+
@Override
193+
public boolean hasArrived(int stopPos) {
194+
return hasArrived.get(stopPos);
195+
}
196+
197+
@Override
198+
public boolean hasDeparted(int stopPos) {
199+
return hasDeparted.get(stopPos);
185200
}
186201

187202
public boolean isNoDataStop(int stopPos) {
@@ -193,7 +208,7 @@ public boolean isPredictionInaccurate(int stopPos) {
193208
}
194209

195210
public boolean isExtraCall(int stopPos) {
196-
return extraCalls[stopPos];
211+
return extraCalls.get(stopPos);
197212
}
198213

199214
public boolean isRealTimeUpdated(int stopPos) {

application/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesBuilder.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.opentripplanner.transit.model.timetable.TimetableValidationError.ErrorCode.MISSING_DEPARTURE_TIME;
55

66
import java.util.Arrays;
7+
import java.util.BitSet;
78
import java.util.stream.IntStream;
89
import javax.annotation.Nullable;
910
import org.opentripplanner.core.model.accessibility.Accessibility;
@@ -21,7 +22,9 @@ public class RealTimeTripTimesBuilder {
2122

2223
private final StopRealTimeState[] stopRealTimeStates;
2324

24-
private final boolean[] extraCalls;
25+
private final BitSet extraCalls;
26+
private final BitSet hasArrived;
27+
private final BitSet hasDeparted;
2528

2629
@Nullable
2730
private I18NString tripHeadsign;
@@ -49,10 +52,12 @@ public class RealTimeTripTimesBuilder {
4952
departureTimes = new Integer[numStops];
5053
stopRealTimeStates = new StopRealTimeState[numStops];
5154
Arrays.fill(stopRealTimeStates, StopRealTimeState.DEFAULT);
52-
extraCalls = new boolean[numStops];
55+
extraCalls = new BitSet(numStops);
5356
stopHeadsigns = new I18NString[numStops];
5457
occupancyStatus = new OccupancyStatus[numStops];
5558
Arrays.fill(occupancyStatus, OccupancyStatus.NO_DATA_AVAILABLE);
59+
hasArrived = new BitSet(numStops);
60+
hasDeparted = new BitSet(numStops);
5661
}
5762

5863
/**
@@ -202,12 +207,16 @@ public StopRealTimeState[] stopRealTimeStates() {
202207
return stopRealTimeStates.clone();
203208
}
204209

205-
public boolean[] extraCalls() {
206-
return extraCalls.clone();
210+
public BitSet extraCalls() {
211+
return (BitSet) extraCalls.clone();
207212
}
208213

209-
public RealTimeTripTimesBuilder withRecorded(int stop) {
210-
return withStopRealTimeState(stop, StopRealTimeState.RECORDED);
214+
public BitSet hasArrived() {
215+
return (BitSet) hasArrived.clone();
216+
}
217+
218+
public BitSet hasDeparted() {
219+
return (BitSet) hasDeparted.clone();
211220
}
212221

213222
public RealTimeTripTimesBuilder withCanceled(int stop) {
@@ -228,7 +237,23 @@ public RealTimeTripTimesBuilder withStopRealTimeState(int stop, StopRealTimeStat
228237
}
229238

230239
public RealTimeTripTimesBuilder withExtraCall(int stop, boolean extraCall) {
231-
this.extraCalls[stop] = extraCall;
240+
this.extraCalls.set(stop, extraCall);
241+
return this;
242+
}
243+
244+
public RealTimeTripTimesBuilder withHasArrived(int stop, boolean arrived) {
245+
if (stop > numberOfStops()) {
246+
throw new IllegalArgumentException("Stop index out of range");
247+
}
248+
this.hasArrived.set(stop, arrived);
249+
return this;
250+
}
251+
252+
public RealTimeTripTimesBuilder withHasDeparted(int stop, boolean departed) {
253+
if (stop > numberOfStops()) {
254+
throw new IllegalArgumentException("Stop index out of range");
255+
}
256+
this.hasDeparted.set(stop, departed);
232257
return this;
233258
}
234259

application/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,12 @@ public boolean isCancelledStop(int stopPos) {
231231
}
232232

233233
@Override
234-
public boolean isRecordedStop(int stopPos) {
234+
public boolean hasArrived(int stopPosition) {
235+
return false;
236+
}
237+
238+
@Override
239+
public boolean hasDeparted(int stopPosition) {
235240
return false;
236241
}
237242

application/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,11 @@ default int compareTo(TripTimes other) {
133133

134134
boolean isCancelledStop(int stopPos);
135135

136-
boolean isRecordedStop(int stopPos);
136+
/// True if there is realtime information indicating that the trip has arrived at the stop.
137+
boolean hasArrived(int stopPosition);
138+
139+
/// True if there is realtime information indicating that the trip has departed from the stop.
140+
boolean hasDeparted(int stopPosition);
137141

138142
boolean isNoDataStop(int stopPos);
139143

application/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesStringBuilder.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ public class TripTimesStringBuilder {
1515
*
1616
* Where flags are:
1717
* C: Canceled
18-
* R: Recorded
18+
* R: Recorded (Arrived & Departed)
19+
* A: Arrived but not departed
1920
* PI: Prediction Inaccurate
2021
* ND: No Data
2122
* </pre>
@@ -39,8 +40,12 @@ public static String encodeTripTimes(TripTimes tripTimes, TripPattern pattern) {
3940
if (tripTimes.isCancelledStop(i)) {
4041
flags.add("C");
4142
}
42-
if (tripTimes.isRecordedStop(i)) {
43-
flags.add("R");
43+
if (tripTimes.hasArrived(i)) {
44+
if (tripTimes.hasDeparted(i)) {
45+
flags.add("R");
46+
} else {
47+
flags.add("A");
48+
}
4449
}
4550
if (tripTimes.isPredictionInaccurate(i)) {
4651
flags.add("PI");

application/src/main/java/org/opentripplanner/updater/trip/siri/CallWrapper.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ static List<CallWrapper> of(EstimatedVehicleJourney estimatedVehicleJourney) {
6060
CallStatusEnumeration getDepartureStatus();
6161
DepartureBoardingActivityEnumeration getDepartureBoardingActivity();
6262

63+
/// Whether the call is a RecordedCall or not
64+
boolean isRecorded();
65+
6366
final class EstimatedCallWrapper implements CallWrapper {
6467

6568
private final EstimatedCall call;
@@ -148,6 +151,11 @@ public DepartureBoardingActivityEnumeration getDepartureBoardingActivity() {
148151
return call.getDepartureBoardingActivity();
149152
}
150153

154+
@Override
155+
public boolean isRecorded() {
156+
return false;
157+
}
158+
151159
@Override
152160
public int hashCode() {
153161
return call.hashCode();
@@ -250,6 +258,11 @@ public DepartureBoardingActivityEnumeration getDepartureBoardingActivity() {
250258
return null;
251259
}
252260

261+
@Override
262+
public boolean isRecorded() {
263+
return true;
264+
}
265+
253266
@Override
254267
public int hashCode() {
255268
return call.hashCode();

application/src/main/java/org/opentripplanner/updater/trip/siri/TimetableHelper.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.opentripplanner.transit.model.timetable.RealTimeTripTimesBuilder;
99
import org.opentripplanner.updater.trip.siri.mapping.OccupancyMapper;
1010
import org.opentripplanner.utils.time.ServiceDateUtils;
11+
import uk.org.siri.siri21.CallStatusEnumeration;
1112
import uk.org.siri.siri21.NaturalLanguageStringStructure;
1213
import uk.org.siri.siri21.OccupancyEnumeration;
1314

@@ -59,9 +60,11 @@ public static void applyUpdates(
5960
CallWrapper call,
6061
OccupancyEnumeration journeyOccupancy
6162
) {
62-
if (call.getActualDepartureTime() != null || call.getActualArrivalTime() != null) {
63-
//Flag as recorded
64-
tripTimesBuilder.withRecorded(index);
63+
if (call.isRecorded() || call.getArrivalStatus() == CallStatusEnumeration.ARRIVED) {
64+
tripTimesBuilder.withHasArrived(index, true);
65+
}
66+
if (call.isRecorded()) {
67+
tripTimesBuilder.withHasDeparted(index, true);
6568
}
6669

6770
// Set flag for inaccurate prediction if either call OR journey has inaccurate-flag set.

application/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,39 @@ void gtfsSequence() {
5050
}
5151

5252
@Test
53-
void isRecordedStop() {
53+
void hasArrivedStop() {
5454
var pattern = TEST_MODEL.pattern(TransitMode.BUS).build();
5555
var trip = TimetableRepositoryForTest.trip("123").build();
5656
var stopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, "11:00");
5757

5858
var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator())
5959
.createRealTimeFromScheduledTimes()
60-
.withRecorded(1)
60+
.withHasArrived(1, true)
6161
.build();
6262

6363
var subject = new TripTimeOnDate(tripTimes, 0, pattern);
64-
65-
assertFalse(subject.isRecordedStop());
64+
assertFalse(subject.hasArrived());
6665

6766
subject = new TripTimeOnDate(tripTimes, 1, pattern);
67+
assertTrue(subject.hasArrived());
68+
}
69+
70+
@Test
71+
void hasDepartedStop() {
72+
var pattern = TEST_MODEL.pattern(TransitMode.BUS).build();
73+
var trip = TimetableRepositoryForTest.trip("123").build();
74+
var stopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, "11:00");
6875

69-
assertTrue(subject.isRecordedStop());
76+
var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator())
77+
.createRealTimeFromScheduledTimes()
78+
.withHasDeparted(1, true)
79+
.build();
80+
81+
var subject = new TripTimeOnDate(tripTimes, 0, pattern);
82+
assertFalse(subject.hasDeparted());
83+
84+
subject = new TripTimeOnDate(tripTimes, 1, pattern);
85+
assertTrue(subject.hasDeparted());
7086
}
7187

7288
@Test

application/src/test/java/org/opentripplanner/updater/trip/siri/AddedTripBuilderTest.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,8 @@ void testAddedTrip() {
194194
assertEquals(secondsInDay(10, 20), scheduledTimes.getDepartureTime(0));
195195
assertEquals(0, scheduledTimes.getDepartureDelay(0));
196196
assertEquals(HEADSIGN, scheduledTimes.getHeadsign(0).toString());
197-
assertFalse(
198-
scheduledTimes.isRecordedStop(0),
199-
"Scheduled timetable should not have actual departure time"
200-
);
197+
assertFalse(scheduledTimes.hasArrived(0), "Scheduled timetable should not have arrived");
198+
assertFalse(scheduledTimes.hasDeparted(0), "Scheduled timetable should not have departed");
201199
assertEquals(secondsInDay(10, 30), scheduledTimes.getArrivalTime(1));
202200
assertEquals(secondsInDay(10, 30), scheduledTimes.getDepartureTime(1));
203201
assertEquals(0, scheduledTimes.getArrivalDelay(1));
@@ -215,16 +213,19 @@ void testAddedTrip() {
215213
assertEquals(secondsInDay(10, 19), times.getDepartureTime(0));
216214
assertEquals(-60, times.getDepartureDelay(0));
217215
assertEquals(HEADSIGN, times.getHeadsign(0).toString());
218-
assertTrue(times.isRecordedStop(0), "First stop has actual departure time");
216+
assertTrue(times.hasArrived(0), "First stop should have arrived");
217+
assertTrue(times.hasDeparted(0), "First stop should have departed");
219218
assertEquals(secondsInDay(10, 29), times.getArrivalTime(1));
220219
assertEquals(secondsInDay(10, 31), times.getDepartureTime(1));
221220
assertEquals(-60, times.getArrivalDelay(1));
222221
assertEquals(60, times.getDepartureDelay(1));
223-
assertFalse(times.isRecordedStop(1), "First stop has actual departure time");
222+
assertFalse(times.hasArrived(1), "Second stop should not have arrived");
223+
assertFalse(times.hasDeparted(1), "Second stop should not have departed");
224224
assertEquals(secondsInDay(10, 41), times.getArrivalTime(2));
225225
assertEquals(secondsInDay(10, 41), times.getDepartureTime(2));
226226
assertEquals(60, times.getArrivalDelay(2));
227-
assertFalse(times.isRecordedStop(2), "First stop has actual departure time");
227+
assertFalse(times.hasArrived(1), "Third stop should not have arrived");
228+
assertFalse(times.hasDeparted(1), "Third stop should not have departed");
228229
}
229230

230231
@Test
@@ -599,6 +600,7 @@ private static List<CallWrapper> getCalls(int hour) {
599600
.withAimedDepartureTime(zonedDateTime(hour, 20))
600601
.withExpectedDepartureTime(zonedDateTime(hour, 20))
601602
.withActualDepartureTime(zonedDateTime(hour, 19))
603+
.withIsRecorded(true)
602604
.build(),
603605
TestCall.of()
604606
.withStopPointRef(STOP_B.getId().getId())

0 commit comments

Comments
 (0)