All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.routing.StopTimesHelper Maven / Gradle / Ivy

There is a newer version: 2.5.0
Show newest version
package org.opentripplanner.routing;

import com.google.common.collect.MinMaxPriorityQueue;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.StopPattern;
import org.opentripplanner.model.StopTimesInPattern;
import org.opentripplanner.model.Timetable;
import org.opentripplanner.model.TimetableSnapshot;
import org.opentripplanner.model.TripPattern;
import org.opentripplanner.model.TripTimeShort;
import org.opentripplanner.model.calendar.ServiceDate;
import org.opentripplanner.routing.core.ServiceDay;
import org.opentripplanner.routing.trippattern.FrequencyEntry;
import org.opentripplanner.routing.trippattern.TripTimes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Queue;

public class StopTimesHelper {
  /**
   * 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.
   *
   * 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
   * @param omitNonPickups If true, do not include vehicles that will not pick up passengers.
   */
  public static List stopTimesForStop(
      RoutingService routingService,
      TimetableSnapshot timetableSnapshot,
      Stop stop,
      long startTime,
      int timeRange,
      int numberOfDepartures,
      boolean omitNonPickups
  ) {

    if (startTime == 0) {
      startTime = System.currentTimeMillis() / 1000;
    }
    List ret = new ArrayList<>();
    Date date = new Date(startTime * 1000);
    ServiceDate[] serviceDates = {new ServiceDate(date).previous(), new ServiceDate(date), new ServiceDate(date).next()};

    Collection patternsForStop = routingService.getPatternsForStop(stop, true);

    for (TripPattern pattern : patternsForStop) {

      Queue pq = listTripTimeShortsForPatternAtStop(
          routingService,
          timetableSnapshot,
          stop,
          pattern,
          startTime,
          timeRange,
          numberOfDepartures,
          omitNonPickups,
          serviceDates
      );

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

  /**
   * Get a list of all trips that pass through a stop during a single ServiceDate. Useful when creating complete stop
   * timetables for a single day.
   *
   * @param stop Stop object to perform the search for
   * @param serviceDate Return all departures for the specified date
   */
  public static List stopTimesForStop(
      RoutingService routingService,
      Stop stop,
      ServiceDate serviceDate,
      boolean omitNonPickups
  ) {
    List ret = new ArrayList<>();

    Collection patternsForStop = routingService.getPatternsForStop(stop, true);
    for (TripPattern pattern : patternsForStop) {
      StopTimesInPattern stopTimes = new StopTimesInPattern(pattern);
      Timetable tt;
      TimetableSnapshot timetableSnapshot = routingService.getTimetableSnapshot();
      if (timetableSnapshot != null){
        tt = timetableSnapshot.resolve(pattern, serviceDate);
      } else {
        tt = pattern.scheduledTimetable;
      }
      ServiceDay sd = new ServiceDay(routingService.getServiceCodes(), serviceDate, routingService.getCalendarService(), pattern.route.getAgency().getId());
      int sidx = 0;
      for (Stop currStop : pattern.stopPattern.stops) {
        if (currStop == stop) {
          if(omitNonPickups && pattern.stopPattern.pickups[sidx] == StopPattern.PICKDROP_NONE) continue;
          for (TripTimes t : tt.tripTimes) {
            if (!sd.serviceRunning(t.serviceCode)) continue;
            stopTimes.times.add(new TripTimeShort(t, sidx, stop, sd));
          }
        }
        sidx++;
      }
      ret.add(stopTimes);
    }
    return ret;
  }

  /**
   * Fetch upcoming vehicle departures from a stop for a single pattern, 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.
   *
   * TODO: Add frequency based trips
   * @param stop Stop object to perform the search for
   * @param pattern Pattern 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
   * @param omitNonPickups If true, do not include vehicles that will not pick up passengers.
   */
  public static List stopTimesForPatternAtStop(
          RoutingService routingService,
          TimetableSnapshot timetableSnapshot,
          Stop stop,
          TripPattern pattern,
          long startTime,
          int timeRange,
          int numberOfDepartures,
          boolean omitNonPickups) {

    if (startTime == 0) {
      startTime = System.currentTimeMillis() / 1000;
    }
    Date date = new Date(startTime * 1000);
    ServiceDate[] serviceDates = {new ServiceDate(date).previous(), new ServiceDate(date), new ServiceDate(date).next()};
    Queue pq = listTripTimeShortsForPatternAtStop(
        routingService,
        timetableSnapshot,
        stop,
        pattern,
        startTime,
        timeRange,
        numberOfDepartures,
        omitNonPickups,
        serviceDates
    );

    return new ArrayList<>(pq);
  }

  private static Queue listTripTimeShortsForPatternAtStop(
      RoutingService routingService,
      TimetableSnapshot timetableSnapshot,
      Stop stop,
      TripPattern pattern,
      long startTime,
      int timeRange,
      int numberOfDepartures,
      boolean omitNonPickups,
      ServiceDate[] serviceDates
  ) {

    // The bounded priority Q is used to keep a sorted short list of trip times. We can not
    // relay on the trip times to be in order because of real-time updates. This code can
    // probably be optimized, and the trip search in the Raptor search does almost the same
    // thing. This is no part of a routing request, but is a used frequently in some
    // operation like Entur for "departure boards" (apps, widgets, screens on platforms, and
    // hotel lobbies). Setting the numberOfDepartures and timeRange to a big number for a
    // transit hub could result in a DOS attack, but there are probably other more effective
    // ways to do it.
    //
    // The {@link MinMaxPriorityQueue} is marked beta, but we do not have a god alternative.
    MinMaxPriorityQueue pq = MinMaxPriorityQueue
            .orderedBy(Comparator.comparing((TripTimeShort tts) -> tts.serviceDay + tts.realtimeDeparture))
            .maximumSize(numberOfDepartures)
            .create();

    // Loop through all possible days
    for (ServiceDate serviceDate : serviceDates) {
      ServiceDay sd = new ServiceDay(routingService.getServiceCodes(), serviceDate, routingService.getCalendarService(), pattern.route.getAgency().getId());
      Timetable tt;
      if (timetableSnapshot != null) {
        tt = timetableSnapshot.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) {
          if (omitNonPickups && pattern.stopPattern.pickups[sidx] == StopPattern.PICKDROP_NONE) continue;
          for (TripTimes t : tt.tripTimes) {
            if (!sd.serviceRunning(t.serviceCode)) continue;
            if (t.getDepartureTime(sidx) != -1 &&
                    t.getDepartureTime(sidx) >= secondsSinceMidnight) {
              pq.add(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.add(
                      new TripTimeShort(
                              freq.materialize(sidx, departureTime, true),
                              sidx,
                              stop,
                              sd
                      )
              );
              departureTime += freq.headway;
              i++;
            }
          }
        }
        sidx++;
      }
    }
    return pq;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy