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

org.opentripplanner.transit.service.TransitModelIndex Maven / Gradle / Ivy

package org.opentripplanner.transit.service;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import java.time.LocalDate;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentripplanner.ext.flex.FlexIndex;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.model.TimetableSnapshot;
import org.opentripplanner.model.calendar.CalendarService;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.GroupOfRoutes;
import org.opentripplanner.transit.model.network.Route;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.organization.Agency;
import org.opentripplanner.transit.model.organization.Operator;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate;
import org.opentripplanner.transit.model.timetable.TripOnServiceDate;
import org.opentripplanner.util.OTPFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Indexed access to Transit entities.
 * For performance reasons these indexes are not part of the serialized state of the graph.
 * They are rebuilt at runtime after graph deserialization.
 */
public class TransitModelIndex {

  private static final Logger LOG = LoggerFactory.getLogger(TransitModelIndex.class);

  // TODO: consistently key on model object or id string
  private final Map agencyForId = new HashMap<>();
  private final Map operatorForId = new HashMap<>();

  private final Map tripForId = new HashMap<>();
  private final Map routeForId = new HashMap<>();

  private final Map patternForTrip = new HashMap<>();
  private final Multimap patternsForRoute = ArrayListMultimap.create();
  private final Multimap patternsForStopId = ArrayListMultimap.create();

  private final Map serviceCodesRunningForDate = new HashMap<>();
  private final Map tripOnServiceDateById = new HashMap<>();
  private final Map tripOnServiceDateForTripAndDay = new HashMap<>();

  private final Multimap routesForGroupOfRoutes = ArrayListMultimap.create();

  private final Map groupOfRoutesForId = new HashMap<>();
  private FlexIndex flexIndex = null;

  TransitModelIndex(TransitModel transitModel) {
    LOG.info("Transit model index init...");

    for (Agency agency : transitModel.getAgencies()) {
      this.agencyForId.put(agency.getId(), agency);
    }

    for (Operator operator : transitModel.getOperators()) {
      this.operatorForId.put(operator.getId(), operator);
    }

    for (TripPattern pattern : transitModel.getAllTripPatterns()) {
      patternsForRoute.put(pattern.getRoute(), pattern);
      pattern
        .scheduledTripsAsStream()
        .forEach(trip -> {
          patternForTrip.put(trip, pattern);
          tripForId.put(trip.getId(), trip);
        });
      for (StopLocation stop : pattern.getStops()) {
        patternsForStopId.put(stop, pattern);
      }
    }
    for (Route route : patternsForRoute.asMap().keySet()) {
      routeForId.put(route.getId(), route);
      for (GroupOfRoutes groupOfRoutes : route.getGroupsOfRoutes()) {
        routesForGroupOfRoutes.put(groupOfRoutes, route);
      }
    }
    for (GroupOfRoutes groupOfRoutes : routesForGroupOfRoutes.keySet()) {
      groupOfRoutesForId.put(groupOfRoutes.getId(), groupOfRoutes);
    }

    for (TripOnServiceDate tripOnServiceDate : transitModel.getAllTripOnServiceDates()) {
      tripOnServiceDateById.put(tripOnServiceDate.getId(), tripOnServiceDate);
      tripOnServiceDateForTripAndDay.put(
        new TripIdAndServiceDate(
          tripOnServiceDate.getTrip().getId(),
          tripOnServiceDate.getServiceDate()
        ),
        tripOnServiceDate
      );
    }

    initalizeServiceCodesForDate(transitModel);

    if (OTPFeature.FlexRouting.isOn()) {
      flexIndex = new FlexIndex(transitModel);
      for (Route route : flexIndex.getAllFlexRoutes()) {
        routeForId.put(route.getId(), route);
      }
      for (FlexTrip flexTrip : flexIndex.getAllFlexTrips()) {
        tripForId.put(flexTrip.getId(), flexTrip.getTrip());
      }
    }

    LOG.info("Transit Model index init complete.");
  }

  public Agency getAgencyForId(FeedScopedId id) {
    return agencyForId.get(id);
  }

  public Route getRouteForId(FeedScopedId id) {
    return routeForId.get(id);
  }

  /**
   * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix
   *           - this when doing the issue #3030.
   */
  public void addRoutes(Route route) {
    routeForId.put(route.getId(), route);
  }

  /** Dynamically generate the set of Routes passing though a Stop on demand. */
  public Set getRoutesForStop(StopLocation stop) {
    Set routes = new HashSet<>();
    for (TripPattern p : getPatternsForStop(stop)) {
      routes.add(p.getRoute());
    }
    return routes;
  }

  public Collection getPatternsForStop(StopLocation stop) {
    return patternsForStopId.get(stop);
  }

  public Collection getTripsForStop(StopLocation stop) {
    return getPatternsForStop(stop)
      .stream()
      .flatMap(TripPattern::scheduledTripsAsStream)
      .collect(Collectors.toList());
  }

  /**
   * Returns all the patterns for a specific stop. If timetableSnapshot is included, new patterns
   * added by realtime updates are added to the collection. A set is used here because trip patterns
   * that were updated by realtime data is both part of the TransitModelIndex and the TimetableSnapshot.
   */
  public Collection getPatternsForStop(
    StopLocation stop,
    TimetableSnapshot timetableSnapshot
  ) {
    Set tripPatterns = new HashSet<>(getPatternsForStop(stop));

    if (timetableSnapshot != null) {
      tripPatterns.addAll(timetableSnapshot.getPatternsForStop(stop));
    }

    return tripPatterns;
  }

  /**
   * Get a list of all operators spanning across all feeds.
   */
  public Collection getAllOperators() {
    return getOperatorForId().values();
  }

  public Map getOperatorForId() {
    return operatorForId;
  }

  public Map getTripForId() {
    return tripForId;
  }

  public Map getTripOnServiceDateById() {
    return tripOnServiceDateById;
  }

  public Map getTripOnServiceDateForTripAndDay() {
    return tripOnServiceDateForTripAndDay;
  }

  public Collection getAllRoutes() {
    return routeForId.values();
  }

  public Map getPatternForTrip() {
    return patternForTrip;
  }

  public Multimap getPatternsForRoute() {
    return patternsForRoute;
  }

  public Map getServiceCodesRunningForDate() {
    return serviceCodesRunningForDate;
  }

  public FlexIndex getFlexIndex() {
    return flexIndex;
  }

  private void initalizeServiceCodesForDate(TransitModel transitModel) {
    CalendarService calendarService = transitModel.getCalendarService();

    if (calendarService == null) {
      return;
    }

    // CalendarService has one main implementation (CalendarServiceImpl) which contains a
    // CalendarServiceData which can easily supply all of the dates. But it's impossible to
    // actually see those dates without modifying the interfaces and inheritance. So we have
    // to work around this abstraction and reconstruct the CalendarData.
    // Note the "multiCalendarServiceImpl" which has docs saying it expects one single
    // CalendarData. It seems to merge the calendar services from multiple GTFS feeds, but
    // its only documentation says it's a hack.
    // TODO OTP2 - This cleanup is added to the 'Final cleanup OTP2' issue #2757

    // Reconstruct set of all dates where service is defined, keeping track of which services
    // run on which days.
    Multimap serviceIdsForServiceDate = HashMultimap.create();

    for (FeedScopedId serviceId : calendarService.getServiceIds()) {
      Set serviceDatesForService = calendarService.getServiceDatesForServiceId(
        serviceId
      );
      for (LocalDate serviceDate : serviceDatesForService) {
        serviceIdsForServiceDate.put(serviceDate, serviceId);
      }
    }
    for (LocalDate serviceDate : serviceIdsForServiceDate.keySet()) {
      TIntSet serviceCodesRunning = new TIntHashSet();
      for (FeedScopedId serviceId : serviceIdsForServiceDate.get(serviceDate)) {
        serviceCodesRunning.add(transitModel.getServiceCodes().get(serviceId));
      }
      serviceCodesRunningForDate.put(serviceDate, serviceCodesRunning);
    }
  }

  public Multimap getRoutesForGroupOfRoutes() {
    return routesForGroupOfRoutes;
  }

  public Map getGroupOfRoutesForId() {
    return groupOfRoutesForId;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy