diff --git a/src/main/java/org/opentripplanner/index/model/TripTimeShort.java b/src/main/java/org/opentripplanner/index/model/TripTimeShort.java index c442edc974c..c60f1776123 100644 --- a/src/main/java/org/opentripplanner/index/model/TripTimeShort.java +++ b/src/main/java/org/opentripplanner/index/model/TripTimeShort.java @@ -1,18 +1,16 @@ package org.opentripplanner.index.model; -import java.util.List; -import java.util.Objects; - +import com.beust.jcommander.internal.Lists; import org.opentripplanner.model.FeedScopedId; import org.opentripplanner.model.Stop; import org.opentripplanner.model.Trip; -import org.onebusaway.gtfs.model.calendar.ServiceDate; import org.opentripplanner.routing.core.ServiceDay; import org.opentripplanner.routing.edgetype.Timetable; import org.opentripplanner.routing.trippattern.RealTimeState; import org.opentripplanner.routing.trippattern.TripTimes; -import com.beust.jcommander.internal.Lists; +import java.util.List; +import java.util.Objects; public class TripTimeShort { @@ -53,7 +51,7 @@ public TripTimeShort(TripTimes tt, int i, Stop stop) { realtimeDeparture = tt.getDepartureTime(i); departureDelay = tt.getDepartureDelay(i); timepoint = tt.isTimepoint(i); - realtime = !tt.isScheduled(); + realtime = !tt.isScheduled() && !tt.isNoDataStop(i); tripId = tt.trip.getId(); realtimeState = tt.isTimeCanceled(i) ? RealTimeState.CANCELED : tt.getRealTimeState(); blockId = tt.trip.getBlockId(); diff --git a/src/main/java/org/opentripplanner/routing/edgetype/Timetable.java b/src/main/java/org/opentripplanner/routing/edgetype/Timetable.java index 0c601506024..938ea38e0ba 100644 --- a/src/main/java/org/opentripplanner/routing/edgetype/Timetable.java +++ b/src/main/java/org/opentripplanner/routing/edgetype/Timetable.java @@ -1,20 +1,15 @@ package org.opentripplanner.routing.edgetype; -import java.io.Serializable; -import java.util.Arrays; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TimeZone; - import com.beust.jcommander.internal.Lists; - +import com.google.transit.realtime.GtfsRealtime.TripDescriptor; +import com.google.transit.realtime.GtfsRealtime.TripUpdate; +import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent; +import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; +import org.opentripplanner.common.MavenVersion; import org.opentripplanner.model.FeedScopedId; import org.opentripplanner.model.Stop; import org.opentripplanner.model.Trip; import org.opentripplanner.model.calendar.ServiceDate; -import org.opentripplanner.common.MavenVersion; import org.opentripplanner.routing.core.ServiceDay; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.StopTransfer; @@ -24,10 +19,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.transit.realtime.GtfsRealtime.TripDescriptor; -import com.google.transit.realtime.GtfsRealtime.TripUpdate; -import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent; -import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; +import java.io.Serializable; +import java.util.*; /** @@ -541,6 +534,10 @@ public TripTimes createUpdatedTripTimes(TripUpdate tripUpdate, TimeZone timeZone == TripDescriptor.ScheduleRelationship.CANCELED) { newTimes.cancel(); } else { + //Whether the trip update has any stop estimates (some trip updates can contain only stop cancellations) + boolean hasUpdates = tripUpdate.getStopTimeUpdateList().stream() + .anyMatch(stopTimeUpdate -> !stopTimeUpdate.hasScheduleRelationship() || stopTimeUpdate.getScheduleRelationship() == StopTimeUpdate.ScheduleRelationship.SCHEDULED); + // The GTFS-RT reference specifies that StopTimeUpdates are sorted by stop_sequence. Iterator updates = tripUpdate.getStopTimeUpdateList().iterator(); if (!updates.hasNext()) { @@ -572,6 +569,10 @@ public TripTimes createUpdatedTripTimes(TripUpdate tripUpdate, TimeZone timeZone StopTimeUpdate.ScheduleRelationship.NO_DATA) { newTimes.updateArrivalDelay(i, 0); newTimes.updateDepartureDelay(i, 0); + if (!hasUpdates) { + //If trip update does not contain stop estimates, mark stop in TripTimes having no data + newTimes.setStopWithNoData(i); + } delay = 0; if (firstDelay == null) firstDelay = delay; } else { diff --git a/src/main/java/org/opentripplanner/routing/trippattern/TripTimes.java b/src/main/java/org/opentripplanner/routing/trippattern/TripTimes.java index 8b4e9488d29..1197094c11a 100644 --- a/src/main/java/org/opentripplanner/routing/trippattern/TripTimes.java +++ b/src/main/java/org/opentripplanner/routing/trippattern/TripTimes.java @@ -1,14 +1,12 @@ package org.opentripplanner.routing.trippattern; -import java.io.Serializable; -import java.util.Arrays; -import java.util.BitSet; -import java.util.List; - -import org.opentripplanner.model.StopTime; -import org.opentripplanner.model.Trip; +import com.google.common.hash.HashCode; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hasher; import org.opentripplanner.common.MavenVersion; import org.opentripplanner.gtfs.BikeAccess; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.model.Trip; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.core.ServiceDay; import org.opentripplanner.routing.core.State; @@ -17,9 +15,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.hash.HashCode; -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hasher; +import java.io.Serializable; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; /** * A TripTimes represents the arrival and departure times for a single trip in an Timetable. It is carried @@ -89,6 +88,11 @@ public class TripTimes implements Serializable, Comparable, Cloneable BitSet canceledDepartureTimes; + /** + * Stops that had schedule relationship NO_DATA in the trip update + */ + BitSet stopsWithNoData; + /** * These are the GTFS stop sequence numbers, which show the order in which the vehicle visits * the stops. Despite the face that the StopPattern or TripPattern enclosing this TripTimes @@ -148,6 +152,7 @@ public TripTimes(final Trip trip, final List stopTimes, final Deduplic canceledArrivalTimes = new BitSet(nStops); canceledDepartureTimes = new BitSet(nStops); + stopsWithNoData = new BitSet(nStops); // Times are always shifted to zero. This is essential for frequencies and deduplication. timeShift = stopTimes.get(0).getArrivalTime(); @@ -227,6 +232,7 @@ public TripTimes(final TripTimes object) { this.timepoints = object.timepoints; canceledArrivalTimes = new BitSet(object.scheduledDepartureTimes.length); canceledDepartureTimes = new BitSet(object.scheduledDepartureTimes.length); + stopsWithNoData = new BitSet(object.scheduledDepartureTimes.length); this.continuousPickup = object.continuousPickup; this.continuousDropOff = object.continuousDropOff; this.serviceAreaRadius = object.serviceAreaRadius; @@ -591,6 +597,10 @@ public boolean isCanceledDeparture (final int stopIndex) { return canceledDepartureTimes.get(stopIndex); } + public boolean isNoDataStop(final int stopIndex) { + return stopsWithNoData.get(stopIndex); + } + /** * Hash the scheduled arrival/departure times. Used in creating stable IDs for trips across GTFS feed versions. * Use hops rather than stops because: @@ -621,6 +631,10 @@ public void unCancelDepartureTime(int i) { canceledDepartureTimes.clear(i); } + public void setStopWithNoData(int stopIndex) { + stopsWithNoData.set(stopIndex); + } + public boolean isTimeCanceled(int i) { return isCanceledArrival(i) || isCanceledDeparture(i) || isCanceled(); }