/**
  * Fetch upcoming vehicle arrivals at a stop. This is a rather convoluted process because all of
  * the departure search functions currently assume the existence of a State and a routing context.
  * It would be nice to have another function that gives all departures within a time window at a
  * stop, being careful to get a mix of all patterns passing through that stop. In fact, such a
  * function could replace the current boarding logic if we want to allow boarding more than one
  * trip on the same route at once (return more than one state). The current implementation is a
  * sketch and does not adequately
  */
 public Collection<StopTimesInPattern> stopTimesForStop(Stop stop) {
   List<StopTimesInPattern> ret = Lists.newArrayList();
   RoutingRequest req = new RoutingRequest();
   req.setRoutingContext(graph, (Vertex) null, (Vertex) null);
   State state = new State(req);
   for (TripPattern pattern : patternsForStop.get(stop)) {
     StopTimesInPattern times = new StopTimesInPattern(pattern);
     // Should actually be getUpdatedTimetable
     Timetable table = pattern.scheduledTimetable;
     // A Stop may occur more than once in a pattern, so iterate over all Stops.
     int sidx = 0;
     for (Stop currStop : table.pattern.stopPattern.stops) {
       if (currStop != stop) continue;
       for (ServiceDay sd : req.rctx.serviceDays) {
         TripTimes tt = table.getNextTrip(state, sd, sidx, true);
         if (tt != null) {
           times.times.add(new TripTimeShort(tt, sidx, stop));
         }
       }
       sidx++;
     }
     if (!times.times.isEmpty()) ret.add(times);
   }
   return ret;
 }
Example #2
0
  /**
   * Fetch upcoming vehicle departures from a stop. It goes though all patterns passing the stop for
   * the previous, current and next service date. It uses a priority queue to keep track of the next
   * departures. The queue is shared between all dates, as services from the previous service date
   * can visit the stop later than the current service date's services. This happens eg. with
   * sleeper trains.
   *
   * <p>TODO: Add frequency based trips
   *
   * @param stop Stop object to perform the search for
   * @param startTime Start time for the search. Seconds from UNIX epoch
   * @param timeRange Searches forward for timeRange seconds from startTime
   * @param numberOfDepartures Number of departures to fetch per pattern
   * @return
   */
  public List<StopTimesInPattern> stopTimesForStop(
      Stop stop, long startTime, int timeRange, int numberOfDepartures) {

    if (startTime == 0) {
      startTime = System.currentTimeMillis() / 1000;
    }
    List<StopTimesInPattern> ret = new ArrayList<>();
    TimetableSnapshot snapshot = null;
    if (graph.timetableSnapshotSource != null) {
      snapshot = graph.timetableSnapshotSource.getTimetableSnapshot();
    }
    ServiceDate[] serviceDates = {
      new ServiceDate().previous(), new ServiceDate(), new ServiceDate().next()
    };

    for (TripPattern pattern : patternsForStop.get(stop)) {

      // Use the Lucene PriorityQueue, which has a fixed size
      PriorityQueue<TripTimeShort> pq =
          new PriorityQueue<TripTimeShort>(numberOfDepartures) {
            @Override
            protected boolean lessThan(TripTimeShort tripTimeShort, TripTimeShort t1) {
              // Calculate exact timestamp
              return (tripTimeShort.serviceDay + tripTimeShort.realtimeDeparture)
                  > (t1.serviceDay + t1.realtimeDeparture);
            }
          };

      // Loop through all possible days
      for (ServiceDate serviceDate : serviceDates) {
        ServiceDay sd =
            new ServiceDay(graph, serviceDate, calendarService, pattern.route.getAgency().getId());
        Timetable tt;
        if (snapshot != null) {
          tt = snapshot.resolve(pattern, serviceDate);
        } else {
          tt = pattern.scheduledTimetable;
        }

        if (!tt.temporallyViable(sd, startTime, timeRange, true)) continue;

        int secondsSinceMidnight = sd.secondsSinceMidnight(startTime);
        int sidx = 0;
        for (Stop currStop : pattern.stopPattern.stops) {
          if (currStop == stop) {
            for (TripTimes t : tt.tripTimes) {
              if (!sd.serviceRunning(t.serviceCode)) continue;
              if (t.getDepartureTime(sidx) != -1
                  && t.getDepartureTime(sidx) >= secondsSinceMidnight) {
                pq.insertWithOverflow(new TripTimeShort(t, sidx, stop, sd));
              }
            }

            // TODO: This needs to be adapted after #1647 is merged
            for (FrequencyEntry freq : tt.frequencyEntries) {
              if (!sd.serviceRunning(freq.tripTimes.serviceCode)) continue;
              int departureTime = freq.nextDepartureTime(sidx, secondsSinceMidnight);
              if (departureTime == -1) continue;
              int lastDeparture =
                  freq.endTime
                      + freq.tripTimes.getArrivalTime(sidx)
                      - freq.tripTimes.getDepartureTime(0);
              int i = 0;
              while (departureTime <= lastDeparture && i < numberOfDepartures) {
                pq.insertWithOverflow(
                    new TripTimeShort(freq.materialize(sidx, departureTime, true), sidx, stop, sd));
                departureTime += freq.headway;
                i++;
              }
            }
          }
          sidx++;
        }
      }

      if (pq.size() != 0) {
        StopTimesInPattern stopTimes = new StopTimesInPattern(pattern);
        while (pq.size() != 0) {
          stopTimes.times.add(0, pq.pop());
        }
        ret.add(stopTimes);
      }
    }
    return ret;
  }