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

org.opentripplanner.routing.graph.GraphIndex Maven / Gradle / Ivy

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

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import org.locationtech.jts.geom.Envelope;
import org.opentripplanner.common.geometry.CompactElevationProfile;
import org.opentripplanner.common.geometry.HashGridSpatialIndex;
import org.opentripplanner.ext.flex.FlexIndex;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.model.Agency;
import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.model.MultiModalStation;
import org.opentripplanner.model.Operator;
import org.opentripplanner.model.Route;
import org.opentripplanner.model.Station;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.StopLocation;
import org.opentripplanner.model.TimetableSnapshot;
import org.opentripplanner.model.Trip;
import org.opentripplanner.model.TripPattern;
import org.opentripplanner.model.calendar.CalendarService;
import org.opentripplanner.model.calendar.ServiceDate;
import org.opentripplanner.routing.vertextype.TransitStopVertex;
import org.opentripplanner.util.OTPFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class GraphIndex {

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

    // TODO: consistently key on model object or id string
    private final Map agencyForId = Maps.newHashMap();
    private final Map operatorForId = Maps.newHashMap();
    private final Map stopForId = Maps.newHashMap();
    private final Map tripForId = Maps.newHashMap();
    private final Map routeForId = Maps.newHashMap();
    private final Map stopVertexForStop = Maps.newHashMap();
    private final Map patternForTrip = Maps.newHashMap();
    private final Multimap patternsForFeedId = ArrayListMultimap.create();
    private final Multimap patternsForRoute = ArrayListMultimap.create();
    private final Multimap patternsForStopId = ArrayListMultimap.create();
    private final Map multiModalStationForStations = Maps.newHashMap();
    private final HashGridSpatialIndex stopSpatialIndex = new HashGridSpatialIndex<>();
    private final Map serviceCodesRunningForDate = new HashMap<>();
    private FlexIndex flexIndex = null;

    public GraphIndex(Graph graph) {
        LOG.info("GraphIndex init...");
        CompactElevationProfile.setDistanceBetweenSamplesM(graph.getDistanceBetweenElevationSamples());

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

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

        /* We will keep a separate set of all vertices in case some have the same label.
         * Maybe we should just guarantee unique labels. */
        for (Vertex vertex : graph.getVertices()) {
            if (vertex instanceof TransitStopVertex) {
                TransitStopVertex stopVertex = (TransitStopVertex) vertex;
                Stop stop = stopVertex.getStop();
                stopForId.put(stop.getId(), stop);
                stopVertexForStop.put(stop, stopVertex);
            }
        }
        for (TransitStopVertex stopVertex : stopVertexForStop.values()) {
            Envelope envelope = new Envelope(stopVertex.getCoordinate());
            stopSpatialIndex.insert(envelope, stopVertex);
        }
        for (TripPattern pattern : graph.tripPatternForId.values()) {
            patternsForFeedId.put(pattern.getFeedId(), pattern);
            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 (MultiModalStation multiModalStation : graph.multiModalStationById.values()) {
            for (Station childStation : multiModalStation.getChildStations()) {
                multiModalStationForStations.put(childStation, multiModalStation);
            }
        }

        initalizeServiceCodesForDate(graph);

        if (OTPFeature.FlexRouting.isOn()) {
            flexIndex = new FlexIndex(graph);
            for (Route route : flexIndex.routeById.values()) {
                routeForId.put(route.getId(), route);
            }
            for (FlexTrip flexTrip : flexIndex.tripById.values()) {
                tripForId.put(flexTrip.getId(), flexTrip.getTrip());
                flexTrip.getStops().stream().forEach(stop -> stopForId.put(stop.getId(), stop));
            }

        }

        LOG.info("GraphIndex init complete.");
    }

    private void initalizeServiceCodesForDate(Graph graph) {

        CalendarService calendarService = graph.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 (ServiceDate serviceDate : serviceDatesForService) {
                serviceIdsForServiceDate.put(serviceDate, serviceId);
            }
        }
        for (ServiceDate serviceDate : serviceIdsForServiceDate.keySet()) {
            TIntSet serviceCodesRunning = new TIntHashSet();
            for (FeedScopedId serviceId : serviceIdsForServiceDate.get(serviceDate)) {
                serviceCodesRunning.add(graph.getServiceCodes().get(serviceId));
            }
            serviceCodesRunningForDate.put(serviceDate, serviceCodesRunning);
        }
    }

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

    public StopLocation getStopForId(FeedScopedId id) {
        return stopForId.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 = Sets.newHashSet();
        for (TripPattern p : getPatternsForStop(stop)) {
            routes.add(p.getRoute());
        }
        return routes;
    }

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

    /**
     * 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 GraphIndex 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 Collection getAllStops() {
        return stopForId.values();
    }

    public Map getTripForId() {
        return tripForId;
    }

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

    public Map getStopVertexForStop() {
        return stopVertexForStop;
    }

    public Map getPatternForTrip() {
        return patternForTrip;
    }

    public Multimap getPatternsForFeedId() {
        return patternsForFeedId;
    }

    public Multimap getPatternsForRoute() {
        return patternsForRoute;
    }

    public Map getMultiModalStationForStations() {
        return multiModalStationForStations;
    }

    public HashGridSpatialIndex getStopSpatialIndex() {
        return stopSpatialIndex;
    }

    public Map getServiceCodesRunningForDate() {
        return serviceCodesRunningForDate;
    }

    public FlexIndex getFlexIndex() {
        return flexIndex;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy