Search in sources :

Example 11 with FrequencyEntry

use of org.opentripplanner.routing.trippattern.FrequencyEntry in project OpenTripPlanner by opentripplanner.

the class TimetableFilterTest method testDwellTimes.

/**
 * Test modification of dwell times
 */
@Test
public void testDwellTimes() {
    AdjustDwellTime adt = new AdjustDwellTime();
    adt.routeId = Arrays.asList(route.getId().getId());
    adt.agencyId = agency.getId();
    adt.stopId = Arrays.asList(stops[0].getId().getId(), stops[2].getId().getId());
    adt.dwellTime = 60;
    TripTimes tt2 = adt.apply(trip, pattern, times);
    assertNotNull(tt2);
    assertEquals(times.getArrivalTime(0), tt2.getArrivalTime(0));
    assertEquals(60, tt2.getDepartureTime(0) - tt2.getArrivalTime(0));
    assertEquals(30, tt2.getDepartureTime(1) - tt2.getArrivalTime(1));
    assertEquals(60, tt2.getDepartureTime(2) - tt2.getArrivalTime(2));
    assertEquals(30, tt2.getDepartureTime(3) - tt2.getArrivalTime(3));
    // make sure we didn't accidentally modify the orignal times
    assertEquals(30, times.getDepartureTime(2) - times.getArrivalTime(2));
    FrequencyEntry fe2 = adt.apply(trip, pattern, frequencyEntry);
    assertNotNull(fe2);
    tt2 = fe2.tripTimes;
    assertEquals(60, tt2.getDepartureTime(0) - tt2.getArrivalTime(0));
    assertEquals(30, tt2.getDepartureTime(1) - tt2.getArrivalTime(1));
    assertEquals(60, tt2.getDepartureTime(2) - tt2.getArrivalTime(2));
    assertEquals(30, tt2.getDepartureTime(3) - tt2.getArrivalTime(3));
    // make sure we didn't accidentally modify the original times
    assertEquals(30, frequencyEntry.tripTimes.getDepartureTime(2) - frequencyEntry.tripTimes.getArrivalTime(2));
    // wildcard
    adt.stopId = null;
    tt2 = adt.apply(trip, pattern, times);
    assertNotNull(tt2);
    assertEquals(times.getArrivalTime(0), tt2.getArrivalTime(0));
    assertEquals(60, tt2.getDepartureTime(0) - tt2.getArrivalTime(0));
    assertEquals(60, tt2.getDepartureTime(1) - tt2.getArrivalTime(1));
    assertEquals(60, tt2.getDepartureTime(2) - tt2.getArrivalTime(2));
    assertEquals(60, tt2.getDepartureTime(3) - tt2.getArrivalTime(3));
    // test repeated application
    adt.stopId = Arrays.asList(stops[2].getId().getId());
    adt.dwellTime = 17;
    tt2 = adt.apply(trip, pattern, tt2);
    assertNotNull(tt2);
    assertEquals(times.getArrivalTime(0), tt2.getArrivalTime(0));
    assertEquals(60, tt2.getDepartureTime(0) - tt2.getArrivalTime(0));
    assertEquals(60, tt2.getDepartureTime(1) - tt2.getArrivalTime(1));
    assertEquals(17, tt2.getDepartureTime(2) - tt2.getArrivalTime(2));
    assertEquals(60, tt2.getDepartureTime(3) - tt2.getArrivalTime(3));
}
Also used : TripTimes(org.opentripplanner.routing.trippattern.TripTimes) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) Test(org.junit.Test)

Example 12 with FrequencyEntry

use of org.opentripplanner.routing.trippattern.FrequencyEntry in project OpenTripPlanner by opentripplanner.

the class GTFSPatternHopFactory method run.

/**
 * Generate the edges. Assumes that there are already vertices in the graph for the stops.
 */
public void run(Graph graph) {
    if (fareServiceFactory == null) {
        fareServiceFactory = new DefaultFareServiceFactory();
    }
    fareServiceFactory.processGtfs(_dao);
    // TODO: Why are we loading stops? The Javadoc above says this method assumes stops are aleady loaded.
    loadStops(graph);
    loadPathways(graph);
    loadFeedInfo(graph);
    loadAgencies(graph);
    // TODO: Why is there cached "data", and why are we clearing it? Due to a general lack of comments, I have no idea.
    // Perhaps it is to allow name collisions with previously loaded feeds.
    clearCachedData();
    /* Assign 0-based numeric codes to all GTFS service IDs. */
    for (AgencyAndId serviceId : _dao.getAllServiceIds()) {
        // TODO: FIX Service code collision for multiple feeds.
        graph.serviceCodes.put(serviceId, graph.serviceCodes.size());
    }
    LOG.debug("building hops from trips");
    Collection<Trip> trips = _dao.getAllTrips();
    int tripCount = 0;
    /* First, record which trips are used by one or more frequency entries.
         * These trips will be ignored for the purposes of non-frequency routing, and
         * all the frequency entries referencing the same trip can be added at once to the same
         * Timetable/TripPattern.
         */
    ListMultimap<Trip, Frequency> frequenciesForTrip = ArrayListMultimap.create();
    for (Frequency freq : _dao.getAllFrequencies()) {
        frequenciesForTrip.put(freq.getTrip(), freq);
    }
    /* Then loop over all trips, handling each one as a frequency-based or scheduled trip. */
    int freqCount = 0;
    int nonFreqCount = 0;
    /* The hops don't actually exist when we build their geometries, but we have to build their geometries
         * below, before we throw away the modified stopTimes, saving only the tripTimes (which don't have enough
         * information to build a geometry). So we keep them here.
         *
         *  A trip pattern actually does not have a single geometry, but one per hop, so we store an array.
         *  FIXME _why_ doesn't it have a single geometry?
         */
    Map<TripPattern, LineString[]> geometriesByTripPattern = Maps.newHashMap();
    TRIP: for (Trip trip : trips) {
        if (++tripCount % 100000 == 0) {
            LOG.debug("loading trips {}/{}", tripCount, trips.size());
        }
        // TODO: move to a validator module
        if (!_calendarService.getServiceIds().contains(trip.getServiceId())) {
            LOG.warn(graph.addBuilderAnnotation(new TripUndefinedService(trip)));
            // Invalid trip, skip it, it will break later
            continue TRIP;
        }
        /* Fetch the stop times for this trip. Copy the list since it's immutable. */
        List<StopTime> stopTimes = new ArrayList<StopTime>(_dao.getStopTimesForTrip(trip));
        /* GTFS stop times frequently contain duplicate, missing, or incorrect entries. Repair them. */
        TIntList removedStopSequences = removeRepeatedStops(stopTimes);
        if (!removedStopSequences.isEmpty()) {
            LOG.warn(graph.addBuilderAnnotation(new RepeatedStops(trip, removedStopSequences)));
        }
        filterStopTimes(stopTimes, graph);
        interpolateStopTimes(stopTimes);
        /* If after filtering this trip does not contain at least 2 stoptimes, it does not serve any purpose. */
        if (stopTimes.size() < 2) {
            LOG.warn(graph.addBuilderAnnotation(new TripDegenerate(trip)));
            continue TRIP;
        }
        /* Try to get the direction id for the trip, set to -1 if not found */
        int directionId;
        try {
            directionId = Integer.parseInt(trip.getDirectionId());
        } catch (NumberFormatException e) {
            LOG.debug("Trip {} does not have direction id, defaults to -1");
            directionId = -1;
        }
        /* Get the existing TripPattern for this filtered StopPattern, or create one. */
        StopPattern stopPattern = new StopPattern(stopTimes);
        TripPattern tripPattern = findOrCreateTripPattern(stopPattern, trip.getRoute(), directionId);
        /* Create a TripTimes object for this list of stoptimes, which form one trip. */
        TripTimes tripTimes = new TripTimes(trip, stopTimes, graph.deduplicator);
        /* If this trip is referenced by one or more lines in frequencies.txt, wrap it in a FrequencyEntry. */
        List<Frequency> frequencies = frequenciesForTrip.get(trip);
        if (frequencies != null && !(frequencies.isEmpty())) {
            for (Frequency freq : frequencies) {
                tripPattern.add(new FrequencyEntry(freq, tripTimes));
                freqCount++;
            }
        // TODO replace: createGeometry(graph, trip, stopTimes, hops);
        } else /* This trip was not frequency-based. Add the TripTimes directly to the TripPattern's scheduled timetable. */
        {
            tripPattern.add(tripTimes);
            nonFreqCount++;
        }
        // there would be a trip pattern with no geometry yet because it failed some of these tests
        if (!geometriesByTripPattern.containsKey(tripPattern) && trip.getShapeId() != null && trip.getShapeId().getId() != null && !trip.getShapeId().getId().equals("")) {
            // save the geometry to later be applied to the hops
            geometriesByTripPattern.put(tripPattern, createGeometry(graph, trip, stopTimes));
        }
    }
    // end foreach TRIP
    LOG.info("Added {} frequency-based and {} single-trip timetable entries.", freqCount, nonFreqCount);
    graph.hasFrequencyService = graph.hasFrequencyService || freqCount > 0;
    graph.hasScheduledService = graph.hasScheduledService || nonFreqCount > 0;
    /* Generate unique human-readable names for all the TableTripPatterns. */
    TripPattern.generateUniqueNames(tripPatterns.values());
    /* Generate unique short IDs for all the TableTripPatterns. */
    TripPattern.generateUniqueIds(tripPatterns.values());
    /* Loop over all new TripPatterns, creating edges, setting the service codes and geometries, etc. */
    for (TripPattern tripPattern : tripPatterns.values()) {
        tripPattern.makePatternVerticesAndEdges(graph, context.stationStopNodes);
        // Add the geometries to the hop edges.
        LineString[] geom = geometriesByTripPattern.get(tripPattern);
        if (geom != null) {
            for (int i = 0; i < tripPattern.hopEdges.length; i++) {
                tripPattern.hopEdges[i].setGeometry(geom[i]);
            }
            // Make a geometry for the whole TripPattern from all its constituent hops.
            // This happens only if geometry is found in geometriesByTripPattern,
            // because that means that geometry was created from shapes instead "as crow flies"
            tripPattern.makeGeometry();
        }
        // TODO this could be more elegant
        tripPattern.setServiceCodes(graph.serviceCodes);
        /* Iterate over all stops in this pattern recording mode information. */
        TraverseMode mode = GtfsLibrary.getTraverseMode(tripPattern.route);
        for (TransitStop tstop : tripPattern.stopVertices) {
            tstop.addMode(mode);
            if (mode == TraverseMode.SUBWAY) {
                tstop.setStreetToStopTime(subwayAccessTime);
            }
            graph.addTransitMode(mode);
        }
    }
    /* Identify interlined trips and create the necessary edges. */
    interline(tripPatterns.values(), graph);
    /* Interpret the transfers explicitly defined in transfers.txt. */
    loadTransfers(graph);
    /* Store parent stops in graph, even if not linked. These are needed for clustering*/
    for (TransitStationStop stop : context.stationStopNodes.values()) {
        if (stop instanceof TransitStation) {
            TransitStation parentStopVertex = (TransitStation) stop;
            graph.parentStopById.put(parentStopVertex.getStopId(), parentStopVertex.getStop());
        }
    }
    // it is already done at deserialization, but standalone mode allows using graphs without serializing them.
    for (TripPattern tableTripPattern : tripPatterns.values()) {
        tableTripPattern.scheduledTimetable.finish();
    }
    // eh?
    clearCachedData();
    graph.putService(FareService.class, fareServiceFactory.makeFareService());
    graph.putService(OnBoardDepartService.class, new OnBoardDepartServiceImpl());
}
Also used : AgencyAndId(org.onebusaway.gtfs.model.AgencyAndId) TransitStop(org.opentripplanner.routing.vertextype.TransitStop) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) TIntArrayList(gnu.trove.list.array.TIntArrayList) TIntList(gnu.trove.list.TIntList) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) TraverseMode(org.opentripplanner.routing.core.TraverseMode) DefaultFareServiceFactory(org.opentripplanner.routing.impl.DefaultFareServiceFactory) StopTime(org.onebusaway.gtfs.model.StopTime) StopPattern(org.opentripplanner.model.StopPattern) Trip(org.onebusaway.gtfs.model.Trip) TripUndefinedService(org.opentripplanner.graph_builder.annotation.TripUndefinedService) ShapePoint(org.onebusaway.gtfs.model.ShapePoint) TripPattern(org.opentripplanner.routing.edgetype.TripPattern) TransitStation(org.opentripplanner.routing.vertextype.TransitStation) TripDegenerate(org.opentripplanner.graph_builder.annotation.TripDegenerate) LineString(com.vividsolutions.jts.geom.LineString) Frequency(org.onebusaway.gtfs.model.Frequency) RepeatedStops(org.opentripplanner.graph_builder.annotation.RepeatedStops) TIntList(gnu.trove.list.TIntList) TransitStationStop(org.opentripplanner.routing.vertextype.TransitStationStop) OnBoardDepartServiceImpl(org.opentripplanner.routing.impl.OnBoardDepartServiceImpl)

Example 13 with FrequencyEntry

use of org.opentripplanner.routing.trippattern.FrequencyEntry in project OpenTripPlanner by opentripplanner.

the class RoundBasedProfileRouter method route.

public void route() {
    LOG.info("access modes: {}", request.accessModes);
    LOG.info("egress modes: {}", request.egressModes);
    LOG.info("direct modes: {}", request.directModes);
    // TimeWindow could constructed in the caller, which does have access to the graph index.
    this.window = new TimeWindow(request.fromTime, request.toTime, graph.index.servicesRunning(request.date));
    // Establish search timeouts
    long searchBeginTime = System.currentTimeMillis();
    long abortTime = searchBeginTime + TIMEOUT * 1000;
    LOG.info("Finding access/egress paths.");
    // Look for stops that are within a given time threshold of the origin and destination
    // Find the closest stop on each pattern near the origin and destination
    // TODO consider that some stops may be closer by one mode than another
    // and that some stops may be accessible by one mode but not another
    ProfileStateStore store = RETAIN_PATTERNS ? new MultiProfileStateStore() : new SingleProfileStateStore();
    for (ProfileState ps : findInitialStops(false)) {
        store.put(ps);
    }
    LOG.info("Found {} initial stops", store.size());
    // we don't want to generate trips that are artificially forced to go past a transit stop.
    ROUNDS: for (int round = 0; round < MAX_ROUNDS; round++) {
        long roundStart = System.currentTimeMillis();
        LOG.info("Begin round {}; {} stops to explore", round, store.size());
        ProfileStateStore previousStore = store;
        store = RETAIN_PATTERNS ? new MultiProfileStateStore((MultiProfileStateStore) store) : new SingleProfileStateStore((SingleProfileStateStore) store);
        Set<TripPattern> patternsToExplore = Sets.newHashSet();
        // explore all of the patterns at the stops visited on the previous round
        for (TransitStop tstop : previousStore.keys()) {
            Collection<TripPattern> patterns = graph.index.patternsForStop.get(tstop.getStop());
            patternsToExplore.addAll(patterns);
        }
        LOG.info("Exploring {} patterns", patternsToExplore.size());
        // propagate all of the bounds down each pattern
        PATTERNS: for (final TripPattern pattern : patternsToExplore) {
            STOPS: for (int i = 0; i < pattern.stopVertices.length; i++) {
                if (!previousStore.containsKey(pattern.stopVertices[i]))
                    continue STOPS;
                Collection<ProfileState> statesToPropagate;
                // only propagate nondominated states
                statesToPropagate = previousStore.get(pattern.stopVertices[i]);
                // don't propagate states that use the same pattern
                statesToPropagate = Collections2.filter(statesToPropagate, new Predicate<ProfileState>() {

                    @Override
                    public boolean apply(ProfileState input) {
                        // don't reboard same pattern, and don't board patterns that are better boarded elsewhere
                        return !input.containsPattern(pattern) && (input.targetPatterns == null || input.targetPatterns.contains(pattern));
                    }
                });
                if (statesToPropagate.isEmpty())
                    continue STOPS;
                int minWaitTime = Integer.MAX_VALUE;
                int maxWaitTime = Integer.MIN_VALUE;
                // (i.e. the transfer time is different for the initial boarding than transfers)
                for (FrequencyEntry freq : pattern.scheduledTimetable.frequencyEntries) {
                    if (freq.exactTimes) {
                        throw new IllegalStateException("Exact times not yet supported in profile routing.");
                    }
                    int overlap = window.overlap(freq.startTime, freq.endTime, freq.tripTimes.serviceCode);
                    if (overlap > 0) {
                        if (freq.headway > maxWaitTime)
                            maxWaitTime = freq.headway;
                        // if any frequency-based trips are running a wait of 0 is always possible, because it could come
                        // just as you show up at the stop.
                        minWaitTime = 0;
                    }
                }
                DESTSTOPS: for (int j = i + 1; j < pattern.stopVertices.length; j++) {
                    // how long does it take to ride this trip from i to j?
                    int minRideTime = Integer.MAX_VALUE;
                    int maxRideTime = Integer.MIN_VALUE;
                    // how long does it take to get to stop j from stop i?
                    for (TripTimes tripTimes : pattern.scheduledTimetable.tripTimes) {
                        int depart = tripTimes.getDepartureTime(i);
                        int arrive = tripTimes.getArrivalTime(j);
                        if (window.includes(depart) && window.includes(arrive) && window.servicesRunning.get(tripTimes.serviceCode)) {
                            int t = arrive - depart;
                            if (t < minRideTime)
                                minRideTime = t;
                            if (t > maxRideTime)
                                maxRideTime = t;
                        }
                    }
                    /* Do the same thing for any frequency-based trips. */
                    for (FrequencyEntry freq : pattern.scheduledTimetable.frequencyEntries) {
                        TripTimes tt = freq.tripTimes;
                        int overlap = window.overlap(freq.startTime, freq.endTime, tt.serviceCode);
                        if (overlap == 0)
                            continue;
                        int depart = tt.getDepartureTime(i);
                        int arrive = tt.getArrivalTime(j);
                        int t = arrive - depart;
                        if (t < minRideTime)
                            minRideTime = t;
                        if (t > maxRideTime)
                            maxRideTime = t;
                    }
                    if (minWaitTime == Integer.MAX_VALUE || maxWaitTime == Integer.MIN_VALUE || minRideTime == Integer.MAX_VALUE || maxRideTime == Integer.MIN_VALUE)
                        // no trips in window that arrive at stop
                        continue DESTSTOPS;
                    if (minRideTime < 0 || maxRideTime < 0) {
                        LOG.error("Pattern {} travels backwards in time between stop {} and {}", pattern, pattern.stopVertices[i].getStop(), pattern.stopVertices[j].getStop());
                        continue DESTSTOPS;
                    }
                    // we've already checked to ensure we're not reboarding the same pattern
                    for (ProfileState ps : statesToPropagate) {
                        ProfileState ps2 = ps.propagate(minWaitTime + minRideTime, maxWaitTime + maxRideTime);
                        if (ps2.upperBound > CUTOFF_SECONDS)
                            continue;
                        ps2.stop = pattern.stopVertices[j];
                        ps2.accessType = Type.TRANSIT;
                        if (RETAIN_PATTERNS)
                            ps2.patterns = new TripPattern[] { pattern };
                        store.put(ps2);
                    }
                }
            }
        }
        // merge states that came from the same stop.
        if (RETAIN_PATTERNS) {
            LOG.info("Round completed, merging similar states");
            ((MultiProfileStateStore) store).mergeStates();
        }
        for (ProfileState ps : store.getAll()) {
            retainedStates.put(ps.stop, ps);
        }
        if (round == MAX_ROUNDS - 1) {
            LOG.info("Finished round {} in {} seconds", round, (System.currentTimeMillis() - roundStart) / 1000);
            break ROUNDS;
        }
        // propagate states to nearby stops (transfers)
        LOG.info("Finding transfers . . .");
        // avoid concurrent modification
        Set<TransitStop> touchedStopKeys = new HashSet<TransitStop>(store.keys());
        for (TransitStop tstop : touchedStopKeys) {
            List<Tuple2<TransitStop, Integer>> accessTimes = Lists.newArrayList();
            // find transfers for the stop
            for (Edge e : tstop.getOutgoing()) {
                if (e instanceof SimpleTransfer) {
                    SimpleTransfer t = (SimpleTransfer) e;
                    int time = (int) (t.getDistance() / request.walkSpeed);
                    accessTimes.add(new Tuple2((TransitStop) e.getToVertex(), time));
                }
            }
            // only transfer from nondominated states. only transfer to each pattern once
            Collection<ProfileState> statesAtStop = store.get(tstop);
            TObjectIntHashMap<TripPattern> minBoardTime = new TObjectIntHashMap<TripPattern>(1000, .75f, Integer.MAX_VALUE);
            Map<TripPattern, ProfileState> optimalBoardState = Maps.newHashMap();
            List<ProfileState> xferStates = Lists.newArrayList();
            // make a hashset of the patterns that stop here, because we don't want to transfer to them at another stop
            HashSet<TripPattern> patternsAtSource = new HashSet<TripPattern>(graph.index.patternsForStop.get(tstop.getStop()));
            for (ProfileState ps : statesAtStop) {
                for (Tuple2<TransitStop, Integer> atime : accessTimes) {
                    ProfileState ps2 = ps.propagate(atime.b);
                    ps2.accessType = Type.TRANSFER;
                    ps2.stop = atime.a;
                    for (TripPattern patt : graph.index.patternsForStop.get(atime.a.getStop())) {
                        // don't transfer to patterns that we can board at this stop.
                        if (patternsAtSource.contains(patt))
                            continue;
                        if (atime.b < minBoardTime.get(patt)) {
                            minBoardTime.put(patt, atime.b);
                            optimalBoardState.put(patt, ps2);
                        }
                    }
                    xferStates.add(ps2);
                }
            }
            for (Entry<TripPattern, ProfileState> e : optimalBoardState.entrySet()) {
                ProfileState ps = e.getValue();
                if (ps.targetPatterns == null)
                    ps.targetPatterns = Sets.newHashSet();
                ps.targetPatterns.add(e.getKey());
            }
            for (ProfileState ps : xferStates) {
                if (ps.targetPatterns != null && !ps.targetPatterns.isEmpty()) {
                    store.put(ps);
                }
            }
        }
        LOG.info("Finished round {} in {} seconds", round, (System.currentTimeMillis() - roundStart) / 1000);
    }
    LOG.info("Finished profile routing in {} seconds", (System.currentTimeMillis() - searchBeginTime) / 1000);
    makeSurfaces();
    LOG.info("Finished analyst request in {} seconds total", (System.currentTimeMillis() - searchBeginTime) / 1000);
}
Also used : HashSet(java.util.HashSet) QualifiedModeSet(org.opentripplanner.api.parameter.QualifiedModeSet) Set(java.util.Set) RangeSet(org.opentripplanner.analyst.TimeSurface.RangeSet) TransitStop(org.opentripplanner.routing.vertextype.TransitStop) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) Entry(java.util.Map.Entry) TObjectIntHashMap(gnu.trove.map.hash.TObjectIntHashMap) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) ArrayList(java.util.ArrayList) List(java.util.List) SimpleTransfer(org.opentripplanner.routing.edgetype.SimpleTransfer) HashSet(java.util.HashSet) TripPattern(org.opentripplanner.routing.edgetype.TripPattern) Tuple2(org.mapdb.Fun.Tuple2) Collection(java.util.Collection) Edge(org.opentripplanner.routing.graph.Edge) TObjectIntMap(gnu.trove.map.TObjectIntMap) Map(java.util.Map) TObjectIntHashMap(gnu.trove.map.hash.TObjectIntHashMap)

Example 14 with FrequencyEntry

use of org.opentripplanner.routing.trippattern.FrequencyEntry in project OpenTripPlanner by opentripplanner.

the class GraphIndex method stopTimesForStop.

/**
 * 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.
 * @return
 */
public List<StopTimesInPattern> stopTimesForStop(Stop stop, long startTime, int timeRange, int numberOfDepartures, boolean omitNonPickups) {
    if (startTime == 0) {
        startTime = System.currentTimeMillis() / 1000;
    }
    List<StopTimesInPattern> ret = new ArrayList<>();
    TimetableSnapshot snapshot = null;
    if (graph.timetableSnapshotSource != null) {
        snapshot = graph.timetableSnapshotSource.getTimetableSnapshot();
    }
    Date date = new Date(startTime * 1000);
    ServiceDate[] serviceDates = { new ServiceDate(date).previous(), new ServiceDate(date), new ServiceDate(date).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) {
                    if (omitNonPickups && pattern.stopPattern.pickups[sidx] == pattern.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.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;
}
Also used : Timetable(org.opentripplanner.routing.edgetype.Timetable) ServiceDay(org.opentripplanner.routing.core.ServiceDay) TransitStop(org.opentripplanner.routing.vertextype.TransitStop) Stop(org.onebusaway.gtfs.model.Stop) ArrayList(java.util.ArrayList) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) TimetableSnapshot(org.opentripplanner.routing.edgetype.TimetableSnapshot) PriorityQueue(org.apache.lucene.util.PriorityQueue) Date(java.util.Date) ServiceDate(org.onebusaway.gtfs.model.calendar.ServiceDate) LocalDate(org.joda.time.LocalDate) TripPattern(org.opentripplanner.routing.edgetype.TripPattern) TripTimeShort(org.opentripplanner.index.model.TripTimeShort) ServiceDate(org.onebusaway.gtfs.model.calendar.ServiceDate) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) StopTimesInPattern(org.opentripplanner.index.model.StopTimesInPattern)

Example 15 with FrequencyEntry

use of org.opentripplanner.routing.trippattern.FrequencyEntry in project OpenTripPlanner by opentripplanner.

the class ConvertToFrequency method apply.

public void apply(List<FrequencyEntry> frequencyEntries, List<TripTimes> scheduledTrips, Graph graph, BitSet servicesRunning, RaptorWorkerTimetable.BoardingAssumption assumption) {
    // preserve existing frequency entries
    this.frequencyEntries.addAll(frequencyEntries);
    Set<String> routeIds = new HashSet<>();
    if (routeId != null)
        Stream.of(routeId).forEach(routeIds::add);
    // loop over scheduled trips and figure out what to do with them
    for (TripTimes tt : scheduledTrips) {
        if (routeId == null || routeIds.contains(tt.trip.getRoute().getId().getId())) {
            // put this in the appropriate group for frequency conversion
            String key;
            switch(groupBy) {
                case ROUTE_DIRECTION:
                    key = tt.trip.getRoute().getId().getId() + "_" + tt.trip.getDirectionId();
                    break;
                case ROUTE:
                    key = tt.trip.getRoute().getId().getId();
                    break;
                case PATTERN:
                    key = graph.index.patternForTrip.get(tt.trip).getExemplar().getId().getId();
                    break;
                default:
                    throw new RuntimeException("Unrecognized group by value");
            }
            tripsToConvert.put(key, tt);
        } else {
            // don't touch this trip
            this.scheduledTrips.add(tt);
        }
    }
    // loop over all the groups and create frequency entries
    GROUPS: for (Map.Entry<String, Collection<TripTimes>> e : tripsToConvert.asMap().entrySet()) {
        // get just the running services
        List<TripTimes> group = e.getValue().stream().filter(tt -> servicesRunning.get(tt.serviceCode)).filter(tt -> windowStart < tt.getDepartureTime(0) && tt.getDepartureTime(0) < windowEnd).collect(Collectors.toList());
        if (group.isEmpty())
            continue GROUPS;
        if (group.size() == 1) {
            group.stream().forEach(scheduledTrips::add);
            continue GROUPS;
        }
        // find the dominant pattern
        TObjectIntMap<TripPattern> patternCount = new TObjectIntHashMap<>(5, 0.75f, 0);
        group.forEach(tt -> patternCount.adjustOrPutValue(graph.index.patternForTrip.get(tt.trip), 1, 1));
        int maxCount = 0;
        TripPattern tripPattern = null;
        for (TObjectIntIterator<TripPattern> it = patternCount.iterator(); it.hasNext(); ) {
            it.advance();
            if (it.value() > maxCount) {
                maxCount = it.value();
                tripPattern = it.key();
            }
        }
        // find a stop that is common to all trip patterns. Sort the list so that the same common stop is always returned
        NavigableSet<Stop> stops = new TreeSet<>((s1, s2) -> s1.getId().compareTo(s2.getId()));
        stops.addAll(tripPattern.getStops());
        patternCount.keySet().stream().forEach(p -> stops.retainAll(p.getStops()));
        if (stops.isEmpty()) {
            LOG.warn("Unable to find common stop for key {}, not converting to frequencies", e.getKey());
            scheduledTrips.addAll(e.getValue());
            continue GROUPS;
        }
        Stop stop = stops.stream().findFirst().get();
        // determine the median frequency at this stop
        // use a set to handle duplicated trips
        TIntSet arrivalTimes = new TIntHashSet();
        for (boolean filter : new boolean[] { true, false }) {
            for (TripTimes tt : group) {
                TripPattern tp = graph.index.patternForTrip.get(tt.trip);
                int arrivalTime = tt.getArrivalTime(tp.getStops().indexOf(stop));
                // however, if we apply the filter and end up with no trips at this stop, re-run with the filter disabled
                if (windowStart < arrivalTime && arrivalTime < windowEnd || !filter)
                    arrivalTimes.add(arrivalTime);
            }
            // if we didn't find stops, continue, which will turn off the filter
            if (arrivalTimes.size() > 1)
                break;
        }
        // now convert to elapsed times
        int[] arrivalTimeArray = arrivalTimes.toArray();
        Arrays.sort(arrivalTimeArray);
        int[] headway = new int[arrivalTimeArray.length - 1];
        for (int i = 1; i < arrivalTimeArray.length; i++) {
            headway[i - 1] = arrivalTimeArray[i] - arrivalTimeArray[i - 1];
        }
        Arrays.sort(headway);
        // the headway that we will use
        int aggregateHeadway;
        if (assumption == RaptorWorkerTimetable.BoardingAssumption.WORST_CASE)
            // simple: worst case analysis should use the worst case headway
            aggregateHeadway = Ints.max(headway);
        else {
            // we want the average headway, but we we want the average of the headways weighted
            // by themselves as if there is a two minute headway then a twenty-minute headway,
            // customers are ten times as likely to experience the twenty minute headway
            // (we want the average from the user's perspective, not the vehicle's perspective)
            // This is a weighted average where the weight is the same as the headway so it simplifies
            // to sum (headway^2) / sum(headway)
            aggregateHeadway = IntStream.of(headway).map(h -> h * h).sum() / IntStream.of(headway).sum();
        }
        LOG.info("Headway for route {} ({}) in direction {}: {}min", tripPattern.route.getShortName(), tripPattern.route.getId().getId(), tripPattern.directionId, aggregateHeadway / 60);
        // figure out running/dwell times based on the trips on this pattern
        final TripPattern chosenTp = tripPattern;
        List<TripTimes> candidates = group.stream().filter(tt -> graph.index.patternForTrip.get(tt.trip) == chosenTp).collect(Collectors.toList());
        // transposed from what you'd expect: stops on the rows
        int[][] hopTimes = new int[tripPattern.getStops().size() - 1][candidates.size()];
        int[][] dwellTimes = new int[tripPattern.getStops().size()][candidates.size()];
        int tripIndex = 0;
        for (TripTimes tt : candidates) {
            for (int stopIndex = 0; stopIndex < tripPattern.getStops().size(); stopIndex++) {
                dwellTimes[stopIndex][tripIndex] = tt.getDwellTime(stopIndex);
                if (stopIndex > 0)
                    hopTimes[stopIndex - 1][tripIndex] = tt.getArrivalTime(stopIndex) - tt.getDepartureTime(stopIndex - 1);
            }
            tripIndex++;
        }
        // collapse it down
        int[] meanHopTimes = new int[tripPattern.getStops().size() - 1];
        int hopIndex = 0;
        for (int[] hop : hopTimes) {
            meanHopTimes[hopIndex++] = IntStream.of(hop).sum() / hop.length;
        }
        int[] meanDwellTimes = new int[tripPattern.getStops().size()];
        int dwellIndex = 0;
        for (int[] dwell : dwellTimes) {
            meanDwellTimes[dwellIndex++] = IntStream.of(dwell).sum() / dwell.length;
        }
        // phew! now let's make a frequency entry
        TripTimes tt = new TripTimes(candidates.get(0));
        int cumulative = 0;
        for (int i = 0; i < tt.getNumStops(); i++) {
            tt.updateArrivalTime(i, cumulative);
            cumulative += meanDwellTimes[i];
            tt.updateDepartureTime(i, cumulative);
            if (i + 1 < tt.getNumStops())
                cumulative += meanHopTimes[i];
        }
        FrequencyEntry fe = new FrequencyEntry(windowStart - 60 * 60 * 3, windowEnd + 60 * 60 * 3, aggregateHeadway, false, tt);
        this.frequencyEntries.add(fe);
    }
}
Also used : IntStream(java.util.stream.IntStream) java.util(java.util) Logger(org.slf4j.Logger) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) TObjectIntHashMap(gnu.trove.map.hash.TObjectIntHashMap) LoggerFactory(org.slf4j.LoggerFactory) Multimap(com.google.common.collect.Multimap) Ints(com.google.common.primitives.Ints) TripPattern(org.opentripplanner.routing.edgetype.TripPattern) Collectors(java.util.stream.Collectors) TObjectIntMap(gnu.trove.map.TObjectIntMap) TIntSet(gnu.trove.set.TIntSet) TIntHashSet(gnu.trove.set.hash.TIntHashSet) RaptorWorkerTimetable(org.opentripplanner.profile.RaptorWorkerTimetable) HashMultimap(com.google.common.collect.HashMultimap) TObjectIntIterator(gnu.trove.iterator.TObjectIntIterator) Stop(org.onebusaway.gtfs.model.Stop) Stream(java.util.stream.Stream) Graph(org.opentripplanner.routing.graph.Graph) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) TObjectIntMap(gnu.trove.map.TObjectIntMap) Stop(org.onebusaway.gtfs.model.Stop) TIntSet(gnu.trove.set.TIntSet) TObjectIntIterator(gnu.trove.iterator.TObjectIntIterator) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) TripPattern(org.opentripplanner.routing.edgetype.TripPattern) TIntHashSet(gnu.trove.set.hash.TIntHashSet) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) TIntHashSet(gnu.trove.set.hash.TIntHashSet)

Aggregations

FrequencyEntry (org.opentripplanner.routing.trippattern.FrequencyEntry)15 TripTimes (org.opentripplanner.routing.trippattern.TripTimes)12 TripPattern (org.opentripplanner.routing.edgetype.TripPattern)7 Stop (org.onebusaway.gtfs.model.Stop)6 TransitStop (org.opentripplanner.routing.vertextype.TransitStop)4 ArrayList (java.util.ArrayList)3 StopTime (org.onebusaway.gtfs.model.StopTime)3 StopPattern (org.opentripplanner.model.StopPattern)3 TIntList (gnu.trove.list.TIntList)2 TIntArrayList (gnu.trove.list.array.TIntArrayList)2 TObjectIntMap (gnu.trove.map.TObjectIntMap)2 TObjectIntHashMap (gnu.trove.map.hash.TObjectIntHashMap)2 List (java.util.List)2 Test (org.junit.Test)2 AgencyAndId (org.onebusaway.gtfs.model.AgencyAndId)2 Trip (org.onebusaway.gtfs.model.Trip)2 HashMultimap (com.google.common.collect.HashMultimap)1 Multimap (com.google.common.collect.Multimap)1 Ints (com.google.common.primitives.Ints)1 LineString (com.vividsolutions.jts.geom.LineString)1