Skip to content

Commit e790986

Browse files
committed
Merge remote-tracking branch 'origin/partial-revert-pr6245' into dev-2.x
2 parents 015d153 + dfa66f8 commit e790986

File tree

6 files changed

+197
-36
lines changed

6 files changed

+197
-36
lines changed

application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java

Lines changed: 98 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import graphql.schema.DataFetcher;
55
import graphql.schema.DataFetchingEnvironment;
66
import java.text.ParseException;
7+
import java.time.Instant;
78
import java.time.LocalDate;
89
import java.time.ZoneId;
910
import java.util.ArrayList;
@@ -23,6 +24,7 @@
2324
import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper;
2425
import org.opentripplanner.apis.gtfs.model.TripOccupancy;
2526
import org.opentripplanner.apis.support.SemanticHash;
27+
import org.opentripplanner.model.Timetable;
2628
import org.opentripplanner.model.TripTimeOnDate;
2729
import org.opentripplanner.routing.alertpatch.EntitySelector;
2830
import org.opentripplanner.routing.alertpatch.TransitAlert;
@@ -35,6 +37,7 @@
3537
import org.opentripplanner.transit.model.site.StopLocation;
3638
import org.opentripplanner.transit.model.timetable.Direction;
3739
import org.opentripplanner.transit.model.timetable.Trip;
40+
import org.opentripplanner.transit.model.timetable.TripTimes;
3841
import org.opentripplanner.transit.service.TransitService;
3942
import org.opentripplanner.utils.time.ServiceDateUtils;
4043

@@ -126,13 +129,41 @@ public DataFetcher<Iterable<TransitAlert>> alerts() {
126129
@Override
127130
public DataFetcher<TripTimeOnDate> arrivalStoptime() {
128131
return environment -> {
129-
var serviceDate = getOptionalServiceDateArgument(environment);
130-
var trip = getSource(environment);
131-
var transitService = getTransitService(environment);
132-
var stopTimes = serviceDate
133-
.map(date -> transitService.getTripTimeOnDates(trip, date))
134-
.orElseGet(() -> transitService.getScheduledTripTimes(trip));
135-
return stopTimes.map(List::getLast).orElse(null);
132+
try {
133+
TransitService transitService = getTransitService(environment);
134+
TripPattern tripPattern = getTripPattern(environment);
135+
if (tripPattern == null) {
136+
return null;
137+
}
138+
Timetable timetable = tripPattern.getScheduledTimetable();
139+
140+
TripTimes tripTimes = timetable.getTripTimes(getSource(environment));
141+
if (tripTimes == null) {
142+
return null;
143+
}
144+
LocalDate serviceDate = null;
145+
Instant midnight = null;
146+
147+
var args = new GraphQLTypes.GraphQLTripArrivalStoptimeArgs(environment.getArguments());
148+
if (args.getGraphQLServiceDate() != null) {
149+
serviceDate = ServiceDateUtils.parseString(args.getGraphQLServiceDate());
150+
midnight = ServiceDateUtils.asStartOfService(
151+
serviceDate,
152+
transitService.getTimeZone()
153+
).toInstant();
154+
}
155+
156+
return new TripTimeOnDate(
157+
tripTimes,
158+
tripTimes.getNumStops() - 1,
159+
tripPattern,
160+
serviceDate,
161+
midnight
162+
);
163+
} catch (ParseException e) {
164+
// invalid date format
165+
return null;
166+
}
136167
};
137168
}
138169

@@ -149,13 +180,35 @@ public DataFetcher<String> blockId() {
149180
@Override
150181
public DataFetcher<TripTimeOnDate> departureStoptime() {
151182
return environment -> {
152-
var serviceDate = getOptionalServiceDateArgument(environment);
153-
var trip = getSource(environment);
154-
var transitService = getTransitService(environment);
155-
var stopTimes = serviceDate
156-
.map(date -> transitService.getTripTimeOnDates(trip, date))
157-
.orElseGet(() -> transitService.getScheduledTripTimes(trip));
158-
return stopTimes.map(List::getFirst).orElse(null);
183+
try {
184+
TransitService transitService = getTransitService(environment);
185+
TripPattern tripPattern = getTripPattern(environment);
186+
if (tripPattern == null) {
187+
return null;
188+
}
189+
Timetable timetable = tripPattern.getScheduledTimetable();
190+
191+
TripTimes tripTimes = timetable.getTripTimes(getSource(environment));
192+
if (tripTimes == null) {
193+
return null;
194+
}
195+
LocalDate serviceDate = null;
196+
Instant midnight = null;
197+
198+
var args = new GraphQLTypes.GraphQLTripDepartureStoptimeArgs(environment.getArguments());
199+
if (args.getGraphQLServiceDate() != null) {
200+
serviceDate = ServiceDateUtils.parseString(args.getGraphQLServiceDate());
201+
midnight = ServiceDateUtils.asStartOfService(
202+
serviceDate,
203+
transitService.getTimeZone()
204+
).toInstant();
205+
}
206+
207+
return new TripTimeOnDate(tripTimes, 0, tripPattern, serviceDate, midnight);
208+
} catch (ParseException e) {
209+
// invalid date format
210+
return null;
211+
}
159212
};
160213
}
161214

@@ -255,16 +308,37 @@ public DataFetcher<Iterable<TripTimeOnDate>> stoptimes() {
255308
@Override
256309
public DataFetcher<Iterable<TripTimeOnDate>> stoptimesForDate() {
257310
return environment -> {
258-
TransitService transitService = getTransitService(environment);
259-
Trip trip = getSource(environment);
260-
var args = new GraphQLTypes.GraphQLTripStoptimesForDateArgs(environment.getArguments());
261-
262-
ZoneId timeZone = transitService.getTimeZone();
263-
LocalDate serviceDate = args.getGraphQLServiceDate() != null
264-
? ServiceDateUtils.parseString(args.getGraphQLServiceDate())
265-
: LocalDate.now(timeZone);
266-
267-
return transitService.getTripTimeOnDates(trip, serviceDate).orElse(null);
311+
try {
312+
TransitService transitService = getTransitService(environment);
313+
Trip trip = getSource(environment);
314+
var args = new GraphQLTypes.GraphQLTripStoptimesForDateArgs(environment.getArguments());
315+
316+
ZoneId timeZone = transitService.getTimeZone();
317+
LocalDate serviceDate = args.getGraphQLServiceDate() != null
318+
? ServiceDateUtils.parseString(args.getGraphQLServiceDate())
319+
: LocalDate.now(timeZone);
320+
321+
TripPattern tripPattern = transitService.findPattern(trip, serviceDate);
322+
if (tripPattern == null) {
323+
return List.of();
324+
}
325+
326+
Instant midnight = ServiceDateUtils.asStartOfService(
327+
serviceDate,
328+
transitService.getTimeZone()
329+
).toInstant();
330+
Timetable timetable = transitService.findTimetable(tripPattern, serviceDate);
331+
return TripTimeOnDate.fromTripTimesWithScheduleFallback(
332+
timetable,
333+
trip,
334+
serviceDate,
335+
midnight,
336+
transitService
337+
);
338+
} catch (ParseException e) {
339+
// invalid date format
340+
return null;
341+
}
268342
};
269343
}
270344

application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,12 @@ public DataFetcher<Iterable<TripTimeOnDate>> stopCalls() {
6262
if (arguments.timetable() == null) {
6363
return List.of();
6464
}
65-
return TripTimeOnDate.fromTripTimes(
65+
return TripTimeOnDate.fromTripTimesWithScheduleFallback(
6666
arguments.timetable(),
6767
arguments.trip(),
6868
arguments.serviceDate(),
69-
arguments.midnight()
69+
arguments.midnight(),
70+
getTransitService(environment)
7071
);
7172
};
7273
}

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.opentripplanner.transit.model.timetable.Trip;
1717
import org.opentripplanner.transit.model.timetable.TripTimes;
1818
import org.opentripplanner.transit.model.timetable.booking.BookingInfo;
19+
import org.opentripplanner.transit.service.TransitService;
1920
import org.slf4j.Logger;
2021
import org.slf4j.LoggerFactory;
2122

@@ -80,6 +81,34 @@ public static List<TripTimeOnDate> fromTripTimes(Timetable table, Trip trip) {
8081
return out;
8182
}
8283

84+
/**
85+
* Must pass in both Timetable and Trip, because TripTimes do not have a reference to
86+
* StopPatterns.
87+
* <br>
88+
* If the timetable does not contain the trip, scheduledTimetable is used instead.
89+
*
90+
* @param table the timetable for the service day
91+
* @param serviceDate service day to set
92+
*/
93+
public static List<TripTimeOnDate> fromTripTimesWithScheduleFallback(
94+
Timetable table,
95+
Trip trip,
96+
LocalDate serviceDate,
97+
Instant midnight,
98+
TransitService transitService
99+
) {
100+
TripTimes times = table.getTripTimes(trip);
101+
if (times == null) {
102+
Timetable scheduledTimetable = transitService.findPattern(trip).getScheduledTimetable();
103+
return fromTripTimes(scheduledTimetable, trip);
104+
}
105+
List<TripTimeOnDate> out = new ArrayList<>();
106+
for (int i = 0; i < times.getNumStops(); ++i) {
107+
out.add(new TripTimeOnDate(times, i, table.getPattern(), serviceDate, midnight));
108+
}
109+
return out;
110+
}
111+
83112
/**
84113
* Must pass in both Timetable and Trip, because TripTimes do not have a reference to
85114
* StopPatterns.

application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,7 +2444,11 @@ type Trip implements Node {
24442444
"""
24452445
types: [TripAlertType]
24462446
): [Alert]
2447-
"Arrival time to the final stop"
2447+
"""
2448+
Arrival time to the final stop. If the trip does not run on the given date,
2449+
it will return scheduled times from another date. This field is slightly
2450+
confusing and will be deprecated when a better replacement is implemented.
2451+
"""
24482452
arrivalStoptime(
24492453
"""
24502454
Date for which the arrival time is returned. Format: YYYYMMDD. If this
@@ -2455,7 +2459,11 @@ type Trip implements Node {
24552459
"Whether bikes are allowed on board the vehicle running this trip"
24562460
bikesAllowed: BikesAllowed
24572461
blockId: String
2458-
"Departure time from the first stop"
2462+
"""
2463+
Departure time from the first stop. If the trip does not run on the given date,
2464+
it will return scheduled times from another date. This field is slightly
2465+
confusing and will be deprecated when a better replacement is implemented.
2466+
"""
24592467
departureStoptime(
24602468
"""
24612469
Date for which the departure time is returned. Format: YYYYMMDD. If this
@@ -2494,6 +2502,12 @@ type Trip implements Node {
24942502
stops: [Stop!]!
24952503
"List of times when this trip arrives to or departs from a stop"
24962504
stoptimes: [Stoptime]
2505+
"""
2506+
List of times when this trip arrives to or departs from each stop on a given date, or
2507+
today if the date is not given. If the trip does not run on the given date, it will
2508+
return scheduled times from another date. This field is slightly confusing and
2509+
will be deprecated when a better replacement is implemented.
2510+
"""
24972511
stoptimesForDate(
24982512
"Date for which stoptimes are returned. Format: YYYYMMDD"
24992513
serviceDate: String

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,23 @@
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
44
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNull;
56
import static org.junit.jupiter.api.Assertions.assertTrue;
67

8+
import java.time.Instant;
9+
import java.time.LocalDate;
710
import java.time.LocalTime;
811
import org.junit.jupiter.api.Test;
12+
import org.opentripplanner._support.time.ZoneIds;
913
import org.opentripplanner.model.plan.PlanTestConstants;
1014
import org.opentripplanner.transit.model._data.TimetableRepositoryForTest;
1115
import org.opentripplanner.transit.model.basic.TransitMode;
1216
import org.opentripplanner.transit.model.framework.Deduplicator;
17+
import org.opentripplanner.transit.model.timetable.ScheduledTripTimes;
1318
import org.opentripplanner.transit.model.timetable.TripTimesFactory;
19+
import org.opentripplanner.transit.service.DefaultTransitService;
20+
import org.opentripplanner.transit.service.TimetableRepository;
21+
import org.opentripplanner.utils.time.ServiceDateUtils;
1422

1523
class TripTimeOnDateTest implements PlanTestConstants {
1624

@@ -50,4 +58,43 @@ void isRecordedStop() {
5058

5159
assertTrue(subject.isRecordedStop());
5260
}
61+
62+
@Test
63+
void testFromTripTimesWithScheduleFallback() {
64+
var testModel = TimetableRepositoryForTest.of();
65+
var trip = TimetableRepositoryForTest.trip("123").build();
66+
var siteRepository = testModel.siteRepositoryBuilder().build();
67+
var timetableRepository = new TimetableRepository(siteRepository, new Deduplicator());
68+
var tripTimes = ScheduledTripTimes.of()
69+
.withTrip(trip)
70+
.withDepartureTimes(new int[] { 0, 1 })
71+
.build();
72+
var tripPattern = testModel
73+
.pattern(TransitMode.BUS)
74+
.withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes))
75+
.build();
76+
timetableRepository.addTripPattern(tripPattern.getId(), tripPattern);
77+
timetableRepository.index();
78+
var timetableSnapshot = new TimetableSnapshot();
79+
timetableSnapshot.commit();
80+
var transitService = new DefaultTransitService(timetableRepository, timetableSnapshot);
81+
var serviceDate = LocalDate.of(2025, 1, 1);
82+
// Construct a timetable which definitely does not contain this trip, because it is empty.
83+
Timetable timetable = Timetable.of()
84+
.withTripPattern(tripPattern)
85+
.withServiceDate(serviceDate)
86+
.build();
87+
Instant midnight = ServiceDateUtils.asStartOfService(serviceDate, ZoneIds.HELSINKI).toInstant();
88+
var tripTimeOnDates = TripTimeOnDate.fromTripTimesWithScheduleFallback(
89+
timetable,
90+
trip,
91+
serviceDate,
92+
midnight,
93+
transitService
94+
);
95+
for (var tripTimeOnDate : tripTimeOnDates) {
96+
assertNull(tripTimeOnDate.getServiceDay());
97+
assertEquals(tripTimeOnDate.getServiceDayMidnight(), TripTimeOnDate.UNDEFINED);
98+
}
99+
}
53100
}

application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/realtime-trip.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@
33
"addedTrip": {
44
"gtfsId": "F:ADDED_TRIP",
55
"timetabledOrigin": null,
6-
"datedOrigin": {
7-
"realtimeDeparture": 0
8-
},
6+
"datedOrigin": null,
97
"timetabledDestination": null,
10-
"datedDestination": {
11-
"realtimeArrival": 900
12-
},
8+
"datedDestination": null,
139
"stoptimes": null,
1410
"stoptimesForDate": [
1511
{
@@ -48,13 +44,13 @@
4844
"scheduledDeparture": 41400
4945
},
5046
"datedOrigin": {
51-
"realtimeDeparture": 0
47+
"realtimeDeparture": 41400
5248
},
5349
"timetabledDestination": {
5450
"scheduledArrival": 42000
5551
},
5652
"datedDestination": {
57-
"realtimeArrival": 900
53+
"realtimeArrival": 42000
5854
},
5955
"stoptimes": [
6056
{

0 commit comments

Comments
 (0)