use of com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate in project onebusaway-application-modules by camsys.
the class GtfsRealtimeTripLibraryTest method testTprInterpolation_0.
/**
* This method tests that we create timepoint prediction records for stops
* that have not been served yet if there are TPRs downstream. If TPRs exist
* downstream of a stop, the bus is assumed to be ahead of that stop. This
* assumption is not necessarily true for stop time updates. We require that
* the trip update delay indicates realtime schedule adherence for this
* behavior to make sense.
*
* Current time = 7:31. Trip update delay = 2 minutes
* Schedule time Real-time from feed Timepoint predicted departure time
* Stop A 7:30 ----- 7:32
* Stop B 7:40 7:43 7:43
*/
@Test
public void testTprInterpolation_0() {
_library.setCurrentTime(time(7, 31) * 1000);
TripEntryImpl tripA = trip("tripA");
stopTime(0, stop("stopA", 0, 0), tripA, time(7, 30), 0.0);
stopTime(1, stop("stopB", 0, 0), tripA, time(7, 40), 10.0);
BlockEntryImpl blockA = block("blockA");
BlockConfigurationEntry blockConfigA = blockConfiguration(blockA, serviceIds("s1"), tripA);
BlockInstance blockInstanceA = new BlockInstance(blockConfigA, 0L);
StopTimeUpdate.Builder stopTimeUpdate = stopTimeUpdateWithDepartureDelay("stopB", 180);
TripUpdate.Builder tripUpdate = tripUpdate("tripA", _library.getCurrentTime() / 1000, 120, stopTimeUpdate);
Mockito.when(_entitySource.getTrip("tripA")).thenReturn(tripA);
Mockito.when(_blockCalendarService.getActiveBlocks(Mockito.eq(blockA.getId()), Mockito.anyLong(), Mockito.anyLong())).thenReturn(Arrays.asList(blockInstanceA));
VehicleLocationRecord record = vehicleLocationRecord(tripUpdate);
long stopADept = getPredictedDepartureTimeByStopId(record, "stopA");
assertEquals(stopADept, time(7, 32) * 1000);
long stopBDept = getPredictedDepartureTimeByStopId(record, "stopB");
assertEquals(stopBDept, time(7, 43) * 1000);
}
use of com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate in project onebusaway-application-modules by camsys.
the class GtfsRealtimeTripLibraryTest method testTprInterpolation_1.
/**
* Same as above, but we should NOT create new timepoint prediction records
* because the stop has already been served. Only thing different is current
* time.
*
* Current time = 7:33. Trip update delay = 2 minutes
* Schedule time Real-time from feed Timepoint predicted departure time
* Stop A 7:30 ----- ----
* Stop B 7:40 7:43 7:43
*/
@Test
public void testTprInterpolation_1() {
_library.setCurrentTime(time(7, 33) * 1000);
TripEntryImpl tripA = trip("tripA");
stopTime(0, stop("stopA", 0, 0), tripA, time(7, 30), 0.0);
stopTime(1, stop("stopB", 0, 0), tripA, time(7, 40), 10.0);
BlockEntryImpl blockA = block("blockA");
BlockConfigurationEntry blockConfigA = blockConfiguration(blockA, serviceIds("s1"), tripA);
BlockInstance blockInstanceA = new BlockInstance(blockConfigA, 0L);
StopTimeUpdate.Builder stopTimeUpdate = stopTimeUpdateWithDepartureDelay("stopB", 180);
TripUpdate.Builder tripUpdate = tripUpdate("tripA", _library.getCurrentTime() / 1000, 120, stopTimeUpdate);
Mockito.when(_entitySource.getTrip("tripA")).thenReturn(tripA);
Mockito.when(_blockCalendarService.getActiveBlocks(Mockito.eq(blockA.getId()), Mockito.anyLong(), Mockito.anyLong())).thenReturn(Arrays.asList(blockInstanceA));
VehicleLocationRecord record = vehicleLocationRecord(tripUpdate);
long stopADept = getPredictedDepartureTimeByStopId(record, "stopA");
// no tpr for this stop
assertEquals(stopADept, -1);
long stopBDept = getPredictedDepartureTimeByStopId(record, "stopB");
assertEquals(stopBDept, time(7, 43) * 1000);
}
use of com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate in project onebusaway-application-modules by camsys.
the class GtfsRealtimeRetrieverImpl method writeTripUpdate.
private TripUpdate writeTripUpdate(TripUpdateModel model) {
TripUpdate.Builder tu = TripUpdate.newBuilder();
TripDescriptor.Builder t = TripDescriptor.newBuilder();
if (model.getDelay() != null) {
tu.setDelay(model.getDelay());
}
if (model.getTripId() != null) {
t.setTripId(parseId(model.getTripId()));
}
if (model.getRouteId() != null) {
t.setRouteId(parseId(model.getRouteId()));
}
if (model.getTripStart() != null) {
Date date = model.getTripStart();
String startDate = new SimpleDateFormat(GTFS_RT_DATE_FORMAT).format(date);
String startTime = new SimpleDateFormat(GTFS_RT_TIME_FORMAT).format(date);
t.setStartDate(startDate);
t.setStartTime(startTime);
}
TripDescriptor.ScheduleRelationship sr = TripDescriptor.ScheduleRelationship.valueOf(model.getScheduleRelationship());
if (sr != null) {
t.setScheduleRelationship(sr);
}
if (model.getVehicleId() != null) {
VehicleDescriptor.Builder v = VehicleDescriptor.newBuilder();
v.setId(model.getVehicleId());
if (!StringUtils.isEmpty(model.getVehicleLabel())) {
v.setLabel(model.getVehicleLabel());
}
if (!StringUtils.isEmpty(model.getVehicleLicensePlate())) {
v.setLicensePlate(model.getVehicleLicensePlate());
}
tu.setVehicle(v.build());
}
for (StopTimeUpdateModel stum : model.getStopTimeUpdates()) {
StopTimeUpdate stu = writeStopTimeUpdate(stum);
if (stum != null) {
tu.addStopTimeUpdate(stu);
}
}
tu.setTrip(t);
return tu.build();
}
use of com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate in project OpenTripPlanner by opentripplanner.
the class Timetable method createUpdatedTripTimes.
/**
* Apply the TripUpdate to the appropriate TripTimes from this Timetable. The existing TripTimes
* must not be modified directly because they may be shared with the underlying
* scheduledTimetable, or other updated Timetables. The {@link TimetableSnapshot} performs the
* protective copying of this Timetable. It is not done in this update method to avoid
* repeatedly cloning the same Timetable when several updates are applied to it at once. We
* assume here that all trips in a timetable are from the same feed, which should always be the
* case.
*
* @param tripUpdate GTFS-RT trip update
* @param timeZone time zone of trip update
* @param updateServiceDate service date of trip update
* @return new copy of updated TripTimes after TripUpdate has been applied on TripTimes of trip
* with the id specified in the trip descriptor of the TripUpdate; null if something
* went wrong
*/
public TripTimes createUpdatedTripTimes(TripUpdate tripUpdate, TimeZone timeZone, ServiceDate updateServiceDate) {
if (tripUpdate == null) {
LOG.error("A null TripUpdate pointer was passed to the Timetable class update method.");
return null;
}
// However, we want to apply trip updates on top of *scheduled* times
if (!tripUpdate.hasTrip()) {
LOG.error("TripUpdate object has no TripDescriptor field.");
return null;
}
TripDescriptor tripDescriptor = tripUpdate.getTrip();
if (!tripDescriptor.hasTripId()) {
LOG.error("TripDescriptor object has no TripId field");
return null;
}
String tripId = tripDescriptor.getTripId();
int tripIndex = getTripIndex(tripId);
if (tripIndex == -1) {
LOG.info("tripId {} not found in pattern.", tripId);
return null;
} else {
LOG.trace("tripId {} found at index {} in timetable.", tripId, tripIndex);
}
TripTimes newTimes = new TripTimes(getTripTimes(tripIndex));
if (tripDescriptor.hasScheduleRelationship() && tripDescriptor.getScheduleRelationship() == TripDescriptor.ScheduleRelationship.CANCELED) {
newTimes.cancel();
} else {
// The GTFS-RT reference specifies that StopTimeUpdates are sorted by stop_sequence.
Iterator<StopTimeUpdate> updates = tripUpdate.getStopTimeUpdateList().iterator();
if (!updates.hasNext()) {
LOG.warn("Won't apply zero-length trip update to trip {}.", tripId);
return null;
}
StopTimeUpdate update = updates.next();
int numStops = newTimes.getNumStops();
Integer delay = null;
for (int i = 0; i < numStops; i++) {
boolean match = false;
if (update != null) {
if (update.hasStopSequence()) {
match = update.getStopSequence() == newTimes.getStopSequence(i);
} else if (update.hasStopId()) {
match = pattern.getStop(i).getId().getId().equals(update.getStopId());
}
}
if (match) {
StopTimeUpdate.ScheduleRelationship scheduleRelationship = update.hasScheduleRelationship() ? update.getScheduleRelationship() : StopTimeUpdate.ScheduleRelationship.SCHEDULED;
if (scheduleRelationship == StopTimeUpdate.ScheduleRelationship.SKIPPED) {
LOG.warn("Partially canceled trips are unsupported by this method." + " Skipping TripUpdate.");
return null;
} else if (scheduleRelationship == StopTimeUpdate.ScheduleRelationship.NO_DATA) {
newTimes.updateArrivalDelay(i, 0);
newTimes.updateDepartureDelay(i, 0);
delay = 0;
} else {
long today = updateServiceDate.getAsDate(timeZone).getTime() / 1000;
if (update.hasArrival()) {
StopTimeEvent arrival = update.getArrival();
if (arrival.hasDelay()) {
delay = arrival.getDelay();
if (arrival.hasTime()) {
newTimes.updateArrivalTime(i, (int) (arrival.getTime() - today));
} else {
newTimes.updateArrivalDelay(i, delay);
}
} else if (arrival.hasTime()) {
newTimes.updateArrivalTime(i, (int) (arrival.getTime() - today));
delay = newTimes.getArrivalDelay(i);
} else {
LOG.error("Arrival time at index {} is erroneous.", i);
return null;
}
} else {
if (delay == null) {
newTimes.updateArrivalTime(i, TripTimes.UNAVAILABLE);
} else {
newTimes.updateArrivalDelay(i, delay);
}
}
if (update.hasDeparture()) {
StopTimeEvent departure = update.getDeparture();
if (departure.hasDelay()) {
delay = departure.getDelay();
if (departure.hasTime()) {
newTimes.updateDepartureTime(i, (int) (departure.getTime() - today));
} else {
newTimes.updateDepartureDelay(i, delay);
}
} else if (departure.hasTime()) {
newTimes.updateDepartureTime(i, (int) (departure.getTime() - today));
delay = newTimes.getDepartureDelay(i);
} else {
LOG.error("Departure time at index {} is erroneous.", i);
return null;
}
} else {
if (delay == null) {
newTimes.updateDepartureTime(i, TripTimes.UNAVAILABLE);
} else {
newTimes.updateDepartureDelay(i, delay);
}
}
}
if (updates.hasNext()) {
update = updates.next();
} else {
update = null;
}
} else {
if (delay == null) {
newTimes.updateArrivalTime(i, TripTimes.UNAVAILABLE);
newTimes.updateDepartureTime(i, TripTimes.UNAVAILABLE);
} else {
newTimes.updateArrivalDelay(i, delay);
newTimes.updateDepartureDelay(i, delay);
}
}
}
if (update != null) {
LOG.error("Part of a TripUpdate object could not be applied successfully to trip {}.", tripId);
return null;
}
}
if (!newTimes.timesIncreasing()) {
LOG.error("TripTimes are non-increasing after applying GTFS-RT delay propagation to trip {}.", tripId);
return null;
}
LOG.debug("A valid TripUpdate object was applied to trip {} using the Timetable class update method.", tripId);
return newTimes;
}
use of com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate in project OpenTripPlanner by opentripplanner.
the class TimetableSnapshotSource method checkNewStopTimeUpdatesAndFindStops.
/**
* Check stop time updates of trip update that results in a new trip (ADDED or MODIFIED) and
* find all stops of that trip.
*
* @param feedId feed id this trip update is intented for
* @param tripUpdate trip update
* @return stops when stop time updates are correct; null if there are errors
*/
private List<Stop> checkNewStopTimeUpdatesAndFindStops(final String feedId, final TripUpdate tripUpdate) {
Integer previousStopSequence = null;
Long previousTime = null;
final List<StopTimeUpdate> stopTimeUpdates = tripUpdate.getStopTimeUpdateList();
final List<Stop> stops = new ArrayList<>(stopTimeUpdates.size());
for (int index = 0; index < stopTimeUpdates.size(); ++index) {
final StopTimeUpdate stopTimeUpdate = stopTimeUpdates.get(index);
// Determine whether stop is skipped
final boolean skippedStop = isStopSkipped(stopTimeUpdate);
// Check stop sequence
if (stopTimeUpdate.hasStopSequence()) {
final Integer stopSequence = stopTimeUpdate.getStopSequence();
// Check non-negative
if (stopSequence < 0) {
LOG.warn("Trip update contains negative stop sequence, skipping.");
return null;
}
// Check whether sequence is increasing
if (previousStopSequence != null && previousStopSequence > stopSequence) {
LOG.warn("Trip update contains decreasing stop sequence, skipping.");
return null;
}
previousStopSequence = stopSequence;
} else {
// Allow missing stop sequences for ADDED and MODIFIED trips
}
// Find stops
if (stopTimeUpdate.hasStopId()) {
// Find stop
final Stop stop = getStopForStopId(feedId, stopTimeUpdate.getStopId());
if (stop != null) {
// Remember stop
stops.add(stop);
} else if (skippedStop) {
// Set a null value for a skipped stop
stops.add(null);
} else {
LOG.warn("Graph doesn't contain stop id \"{}\" of trip update, skipping.", stopTimeUpdate.getStopId());
return null;
}
} else {
LOG.warn("Trip update misses some stop ids, skipping.");
return null;
}
// Only check arrival and departure times for non-skipped stops
if (!skippedStop) {
// Check arrival time
if (stopTimeUpdate.hasArrival() && stopTimeUpdate.getArrival().hasTime()) {
// Check for increasing time
final Long time = stopTimeUpdate.getArrival().getTime();
if (previousTime != null && previousTime > time) {
LOG.warn("Trip update contains decreasing times, skipping.");
return null;
}
previousTime = time;
} else {
// TODO: should we support only requiring an arrival time on the last stop and interpolate?
for (int earlierIndex = 0; earlierIndex < index; earlierIndex++) {
final StopTimeUpdate earlierStopTimeUpdate = stopTimeUpdates.get(earlierIndex);
// Determine whether earlier stop is skipped
final boolean earlierSkippedStop = isStopSkipped(earlierStopTimeUpdate);
if (!earlierSkippedStop) {
LOG.warn("Trip update misses arrival time, skipping.");
return null;
}
}
}
// Check departure time
if (stopTimeUpdate.hasDeparture() && stopTimeUpdate.getDeparture().hasTime()) {
// Check for increasing time
final Long time = stopTimeUpdate.getDeparture().getTime();
if (previousTime != null && previousTime > time) {
LOG.warn("Trip update contains decreasing times, skipping.");
return null;
}
previousTime = time;
} else {
// TODO: should we support only requiring a departure time on the first stop and interpolate?
for (int laterIndex = stopTimeUpdates.size() - 1; laterIndex > index; laterIndex--) {
final StopTimeUpdate laterStopTimeUpdate = stopTimeUpdates.get(laterIndex);
// Determine whether later stop is skipped
final boolean laterSkippedStop = isStopSkipped(laterStopTimeUpdate);
if (!laterSkippedStop) {
LOG.warn("Trip update misses departure time, skipping.");
return null;
}
}
}
}
}
return stops;
}
Aggregations