Search in sources :

Example 1 with TripTimes

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

the class SkipStop method apply.

@Override
public Collection<TripPattern> apply(TripPattern original) {
    if (!couldMatch(original))
        return Arrays.asList(original);
    // figure out which stops we skip
    TIntList skippedStops = new TIntArrayList();
    // retained stops
    // we use stop times to carry a little additional information, e.g. pickup/dropoff type.
    List<StopTime> stopTimes = Lists.newArrayList();
    {
        int i = 0;
        for (Stop stop : original.getStops()) {
            if (stopId.contains(stop.getId().getId()))
                skippedStops.add(i);
            else {
                // make a fake stop time
                StopTime stopTime = new StopTime();
                stopTime.setStop(stop);
                stopTime.setPickupType(original.stopPattern.pickups[i]);
                stopTime.setDropOffType(original.stopPattern.dropoffs[i]);
                stopTimes.add(stopTime);
            }
            i++;
        }
    }
    if (skippedStops.isEmpty()) {
        LOG.warn("No stops found to skip on matched trip pattern {}", original);
        return Arrays.asList(original);
    }
    if (original.getStops().size() - skippedStops.size() < 2) {
        // TODO best way to handle this case?
        LOG.warn("Trip with skipped stops would have less that two stops for TripPattern {}, not skipping stops", original);
        return Arrays.asList(original);
    }
    // make the new stop pattern
    StopPattern sp = new StopPattern(stopTimes);
    TripPattern modified = new TripPattern(original.route, sp);
    // Any trips that are not matched keep the original trip pattern, so put them here.
    TripPattern originalClone = new TripPattern(original.route, original.stopPattern);
    // keep track of what we have to return
    boolean anyTripsMatched = false;
    boolean allTripsMatched = true;
    for (TripTimes tt : original.scheduledTimetable.tripTimes) {
        if (!matches(tt.trip)) {
            // this trip should not be modified
            allTripsMatched = false;
            originalClone.scheduledTimetable.addTripTimes(tt);
        } else {
            // This trip should be modified
            anyTripsMatched = true;
            modified.scheduledTimetable.addTripTimes(omitStops(tt, skippedStops.toArray()));
        }
    }
    for (FrequencyEntry fe : original.scheduledTimetable.frequencyEntries) {
        if (!matches(fe.tripTimes.trip)) {
            allTripsMatched = false;
            originalClone.scheduledTimetable.addFrequencyEntry(fe);
        } else {
            anyTripsMatched = true;
            TripTimes newtt = omitStops(fe.tripTimes, skippedStops.toArray());
            FrequencyEntry newfe = new FrequencyEntry(fe.startTime, fe.endTime, fe.headway, fe.exactTimes, newtt);
            modified.scheduledTimetable.addFrequencyEntry(newfe);
        }
    }
    if (!anyTripsMatched)
        return Arrays.asList(original);
    List<TripPattern> ret = Lists.newArrayList();
    ret.add(modified);
    if (!allTripsMatched)
        ret.add(originalClone);
    return ret;
}
Also used : StopPattern(org.opentripplanner.model.StopPattern) Stop(org.onebusaway.gtfs.model.Stop) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry) TIntList(gnu.trove.list.TIntList) TIntArrayList(gnu.trove.list.array.TIntArrayList) TripPattern(org.opentripplanner.routing.edgetype.TripPattern) StopTime(org.onebusaway.gtfs.model.StopTime)

Example 2 with TripTimes

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

the class Timetable method setServiceCodes.

/**
 * Find and cache service codes. Duplicates information in trip.getServiceId for optimization.
 */
// TODO maybe put this is a more appropriate place
public void setServiceCodes(Map<AgencyAndId, Integer> serviceCodes) {
    for (TripTimes tt : this.tripTimes) {
        tt.serviceCode = serviceCodes.get(tt.trip.getServiceId());
    }
    // Repeated code... bad sign...
    for (FrequencyEntry freq : this.frequencyEntries) {
        TripTimes tt = freq.tripTimes;
        tt.serviceCode = serviceCodes.get(tt.trip.getServiceId());
    }
}
Also used : TripTimes(org.opentripplanner.routing.trippattern.TripTimes) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry)

Example 3 with TripTimes

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

the class Timetable method getNextTrip.

/**
 * Get the next (previous) trip that departs (arrives) from the specified stop at or after
 * (before) the specified time.
 * @return the TripTimes object representing the (possibly updated) best trip, or null if no
 * trip matches both the time and other criteria.
 */
public TripTimes getNextTrip(State s0, ServiceDay serviceDay, int stopIndex, boolean boarding) {
    /* Search at the state's time, but relative to midnight on the given service day. */
    int time = serviceDay.secondsSinceMidnight(s0.getTimeSeconds());
    // Adjust for possible boarding time TODO: This should be included in the trip and based on GTFS
    if (boarding) {
        time += s0.getOptions().getBoardTime(this.pattern.mode);
    } else {
        time -= s0.getOptions().getAlightTime(this.pattern.mode);
    }
    TripTimes bestTrip = null;
    Stop currentStop = pattern.getStop(stopIndex);
    // Linear search through the timetable looking for the best departure.
    // We no longer use a binary search on Timetables because:
    // 1. we allow combining trips from different service IDs on the same tripPattern.
    // 2. We mix frequency-based and one-off TripTimes together on tripPatterns.
    // 3. Stoptimes may change with realtime updates, and we cannot count on them being sorted.
    // The complexity of keeping sorted indexes up to date does not appear to be worth the
    // apparently minor speed improvement.
    int bestTime = boarding ? Integer.MAX_VALUE : Integer.MIN_VALUE;
    // We could invert this and skip some service days based on schedule overlap as in RRRR.
    for (TripTimes tt : tripTimes) {
        if (tt.isCanceled())
            continue;
        // TODO merge into call on next line
        if (!serviceDay.serviceRunning(tt.serviceCode))
            continue;
        if (!tt.tripAcceptable(s0, stopIndex))
            continue;
        int adjustedTime = adjustTimeForTransfer(s0, currentStop, tt.trip, boarding, serviceDay, time);
        if (adjustedTime == -1)
            continue;
        if (boarding) {
            int depTime = tt.getDepartureTime(stopIndex);
            // negative values were previously used for canceled trips/passed stops/skipped stops, but
            if (depTime < 0)
                continue;
            // for canceled trips
            if (depTime >= adjustedTime && depTime < bestTime) {
                bestTrip = tt;
                bestTime = depTime;
            }
        } else {
            int arvTime = tt.getArrivalTime(stopIndex);
            if (arvTime < 0)
                continue;
            if (arvTime <= adjustedTime && arvTime > bestTime) {
                bestTrip = tt;
                bestTime = arvTime;
            }
        }
    }
    // ACK all logic is identical to above.
    // A sign that FrequencyEntries and TripTimes need a common interface.
    FrequencyEntry bestFreq = null;
    for (FrequencyEntry freq : frequencyEntries) {
        TripTimes tt = freq.tripTimes;
        if (tt.isCanceled())
            continue;
        // TODO merge into call on next line
        if (!serviceDay.serviceRunning(tt.serviceCode))
            continue;
        if (!tt.tripAcceptable(s0, stopIndex))
            continue;
        int adjustedTime = adjustTimeForTransfer(s0, currentStop, tt.trip, boarding, serviceDay, time);
        if (adjustedTime == -1)
            continue;
        LOG.debug("  running freq {}", freq);
        if (boarding) {
            // min transfer time included in search
            int depTime = freq.nextDepartureTime(stopIndex, adjustedTime);
            if (depTime < 0)
                continue;
            if (depTime >= adjustedTime && depTime < bestTime) {
                bestFreq = freq;
                bestTime = depTime;
            }
        } else {
            // min transfer time included in search
            int arvTime = freq.prevArrivalTime(stopIndex, adjustedTime);
            if (arvTime < 0)
                continue;
            if (arvTime <= adjustedTime && arvTime > bestTime) {
                bestFreq = freq;
                bestTime = arvTime;
            }
        }
    }
    if (bestFreq != null) {
        // A FrequencyEntry beat all the TripTimes.
        // Materialize that FrequencyEntry entry at the given time.
        bestTrip = bestFreq.tripTimes.timeShift(stopIndex, bestTime, boarding);
    }
    return bestTrip;
}
Also used : Stop(org.onebusaway.gtfs.model.Stop) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry)

Example 4 with TripTimes

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

the class Timetable method createUpdatedTripTimes.

/**
 * Apply the TripUpdate to the appropriate TripTimes from this Timetable. The existing TripTimes
 * must not be modified directly because they may be shared with the underlying
 * scheduledTimetable, or other updated Timetables. The {@link TimetableSnapshot} performs the
 * protective copying of this Timetable. It is not done in this update method to avoid
 * repeatedly cloning the same Timetable when several updates are applied to it at once. We
 * assume here that all trips in a timetable are from the same feed, which should always be the
 * case.
 *
 * @param tripUpdate GTFS-RT trip update
 * @param timeZone time zone of trip update
 * @param updateServiceDate service date of trip update
 * @return new copy of updated TripTimes after TripUpdate has been applied on TripTimes of trip
 *         with the id specified in the trip descriptor of the TripUpdate; null if something
 *         went wrong
 */
public TripTimes createUpdatedTripTimes(TripUpdate tripUpdate, TimeZone timeZone, ServiceDate updateServiceDate) {
    if (tripUpdate == null) {
        LOG.error("A null TripUpdate pointer was passed to the Timetable class update method.");
        return null;
    }
    // However, we want to apply trip updates on top of *scheduled* times
    if (!tripUpdate.hasTrip()) {
        LOG.error("TripUpdate object has no TripDescriptor field.");
        return null;
    }
    TripDescriptor tripDescriptor = tripUpdate.getTrip();
    if (!tripDescriptor.hasTripId()) {
        LOG.error("TripDescriptor object has no TripId field");
        return null;
    }
    String tripId = tripDescriptor.getTripId();
    int tripIndex = getTripIndex(tripId);
    if (tripIndex == -1) {
        LOG.info("tripId {} not found in pattern.", tripId);
        return null;
    } else {
        LOG.trace("tripId {} found at index {} in timetable.", tripId, tripIndex);
    }
    TripTimes newTimes = new TripTimes(getTripTimes(tripIndex));
    if (tripDescriptor.hasScheduleRelationship() && tripDescriptor.getScheduleRelationship() == TripDescriptor.ScheduleRelationship.CANCELED) {
        newTimes.cancel();
    } else {
        // The GTFS-RT reference specifies that StopTimeUpdates are sorted by stop_sequence.
        Iterator<StopTimeUpdate> updates = tripUpdate.getStopTimeUpdateList().iterator();
        if (!updates.hasNext()) {
            LOG.warn("Won't apply zero-length trip update to trip {}.", tripId);
            return null;
        }
        StopTimeUpdate update = updates.next();
        int numStops = newTimes.getNumStops();
        Integer delay = null;
        for (int i = 0; i < numStops; i++) {
            boolean match = false;
            if (update != null) {
                if (update.hasStopSequence()) {
                    match = update.getStopSequence() == newTimes.getStopSequence(i);
                } else if (update.hasStopId()) {
                    match = pattern.getStop(i).getId().getId().equals(update.getStopId());
                }
            }
            if (match) {
                StopTimeUpdate.ScheduleRelationship scheduleRelationship = update.hasScheduleRelationship() ? update.getScheduleRelationship() : StopTimeUpdate.ScheduleRelationship.SCHEDULED;
                if (scheduleRelationship == StopTimeUpdate.ScheduleRelationship.SKIPPED) {
                    LOG.warn("Partially canceled trips are unsupported by this method." + " Skipping TripUpdate.");
                    return null;
                } else if (scheduleRelationship == StopTimeUpdate.ScheduleRelationship.NO_DATA) {
                    newTimes.updateArrivalDelay(i, 0);
                    newTimes.updateDepartureDelay(i, 0);
                    delay = 0;
                } else {
                    long today = updateServiceDate.getAsDate(timeZone).getTime() / 1000;
                    if (update.hasArrival()) {
                        StopTimeEvent arrival = update.getArrival();
                        if (arrival.hasDelay()) {
                            delay = arrival.getDelay();
                            if (arrival.hasTime()) {
                                newTimes.updateArrivalTime(i, (int) (arrival.getTime() - today));
                            } else {
                                newTimes.updateArrivalDelay(i, delay);
                            }
                        } else if (arrival.hasTime()) {
                            newTimes.updateArrivalTime(i, (int) (arrival.getTime() - today));
                            delay = newTimes.getArrivalDelay(i);
                        } else {
                            LOG.error("Arrival time at index {} is erroneous.", i);
                            return null;
                        }
                    } else {
                        if (delay == null) {
                            newTimes.updateArrivalTime(i, TripTimes.UNAVAILABLE);
                        } else {
                            newTimes.updateArrivalDelay(i, delay);
                        }
                    }
                    if (update.hasDeparture()) {
                        StopTimeEvent departure = update.getDeparture();
                        if (departure.hasDelay()) {
                            delay = departure.getDelay();
                            if (departure.hasTime()) {
                                newTimes.updateDepartureTime(i, (int) (departure.getTime() - today));
                            } else {
                                newTimes.updateDepartureDelay(i, delay);
                            }
                        } else if (departure.hasTime()) {
                            newTimes.updateDepartureTime(i, (int) (departure.getTime() - today));
                            delay = newTimes.getDepartureDelay(i);
                        } else {
                            LOG.error("Departure time at index {} is erroneous.", i);
                            return null;
                        }
                    } else {
                        if (delay == null) {
                            newTimes.updateDepartureTime(i, TripTimes.UNAVAILABLE);
                        } else {
                            newTimes.updateDepartureDelay(i, delay);
                        }
                    }
                }
                if (updates.hasNext()) {
                    update = updates.next();
                } else {
                    update = null;
                }
            } else {
                if (delay == null) {
                    newTimes.updateArrivalTime(i, TripTimes.UNAVAILABLE);
                    newTimes.updateDepartureTime(i, TripTimes.UNAVAILABLE);
                } else {
                    newTimes.updateArrivalDelay(i, delay);
                    newTimes.updateDepartureDelay(i, delay);
                }
            }
        }
        if (update != null) {
            LOG.error("Part of a TripUpdate object could not be applied successfully to trip {}.", tripId);
            return null;
        }
    }
    if (!newTimes.timesIncreasing()) {
        LOG.error("TripTimes are non-increasing after applying GTFS-RT delay propagation to trip {}.", tripId);
        return null;
    }
    LOG.debug("A valid TripUpdate object was applied to trip {} using the Timetable class update method.", tripId);
    return newTimes;
}
Also used : StopTimeEvent(com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent) TripDescriptor(com.google.transit.realtime.GtfsRealtime.TripDescriptor) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) StopTimeUpdate(com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate)

Example 5 with TripTimes

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

the class Timetable method finish.

/**
 * Finish off a Timetable once all TripTimes have been added to it. This involves caching
 * lower bounds on the running times and dwell times at each stop, and may perform other
 * actions to compact the data structure such as trimming and deduplicating arrays.
 */
public void finish() {
    int nStops = pattern.stopPattern.size;
    int nHops = nStops - 1;
    /* Find lower bounds on dwell and running times at each stop. */
    minDwellTimes = new int[nHops];
    minRunningTimes = new int[nHops];
    Arrays.fill(minDwellTimes, Integer.MAX_VALUE);
    Arrays.fill(minRunningTimes, Integer.MAX_VALUE);
    // Concatenate raw TripTimes and those referenced from FrequencyEntries
    List<TripTimes> allTripTimes = Lists.newArrayList(tripTimes);
    for (FrequencyEntry freq : frequencyEntries) allTripTimes.add(freq.tripTimes);
    for (TripTimes tt : allTripTimes) {
        for (int h = 0; h < nHops; ++h) {
            int dt = tt.getDwellTime(h);
            if (minDwellTimes[h] > dt) {
                minDwellTimes[h] = dt;
            }
            int rt = tt.getRunningTime(h);
            if (minRunningTimes[h] > rt) {
                minRunningTimes[h] = rt;
            }
        }
    }
    /* Find the time range over which this timetable is active. Allows departure search optimizations. */
    minTime = Integer.MAX_VALUE;
    maxTime = Integer.MIN_VALUE;
    for (TripTimes tt : tripTimes) {
        minTime = Math.min(minTime, tt.getDepartureTime(0));
        maxTime = Math.max(maxTime, tt.getArrivalTime(nStops - 1));
    }
    // Again it seems reasonable to have a shared interface between FrequencyEntries and normal TripTimes.
    for (FrequencyEntry freq : frequencyEntries) {
        minTime = Math.min(minTime, freq.getMinDeparture());
        maxTime = Math.max(maxTime, freq.getMaxArrival());
    }
}
Also used : TripTimes(org.opentripplanner.routing.trippattern.TripTimes) FrequencyEntry(org.opentripplanner.routing.trippattern.FrequencyEntry)

Aggregations

TripTimes (org.opentripplanner.routing.trippattern.TripTimes)40 TripPattern (org.opentripplanner.routing.edgetype.TripPattern)19 Stop (org.onebusaway.gtfs.model.Stop)13 FrequencyEntry (org.opentripplanner.routing.trippattern.FrequencyEntry)12 TransitStop (org.opentripplanner.routing.vertextype.TransitStop)10 ArrayList (java.util.ArrayList)9 Trip (org.onebusaway.gtfs.model.Trip)9 RoutingRequest (org.opentripplanner.routing.core.RoutingRequest)9 Timetable (org.opentripplanner.routing.edgetype.Timetable)9 AgencyAndId (org.onebusaway.gtfs.model.AgencyAndId)8 StopTime (org.onebusaway.gtfs.model.StopTime)8 ServiceDay (org.opentripplanner.routing.core.ServiceDay)8 Test (org.junit.Test)7 StopPattern (org.opentripplanner.model.StopPattern)7 StopTimeUpdate (com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate)6 PatternHop (org.opentripplanner.routing.edgetype.PatternHop)6 TimetableSnapshot (org.opentripplanner.routing.edgetype.TimetableSnapshot)6 TripDescriptor (com.google.transit.realtime.GtfsRealtime.TripDescriptor)5 Coordinate (com.vividsolutions.jts.geom.Coordinate)5 RoutingContext (org.opentripplanner.routing.core.RoutingContext)5