Skip to content

Commit

Permalink
support frequency trips in thin workers.
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwigway committed Apr 29, 2015
1 parent 565742d commit 184f751
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 5 deletions.
69 changes: 67 additions & 2 deletions src/main/java/org/opentripplanner/profile/RaptorWorker.java
Expand Up @@ -168,12 +168,75 @@ public void runRaptor (TIntIntMap initialStops, int departureTime) {
public boolean doOneRound () {
//LOG.info("round {}", round);
stopsTouched.clear(); // clear any stops left over from previous round.
for (int p = patternsTouched.nextSetBit(0); p >= 0; p = patternsTouched.nextSetBit(p+1)) {
PATTERNS: for (int p = patternsTouched.nextSetBit(0); p >= 0; p = patternsTouched.nextSetBit(p+1)) {
//LOG.info("pattern {} {}", p, data.patternNames.get(p));
int onTrip = -1;
RaptorWorkerTimetable timetable = data.timetablesForPattern.get(p);
int[] stops = data.stopsForPattern.get(p);
int stopPositionInPattern = -1; // first increment will land this at zero

int bestFreqBoardTime = Integer.MAX_VALUE;
int bestFreqBoardStop = -1;
int bestFreq = -1;

// first look for a frequency entry
for (int stopIndex : stops) {
stopPositionInPattern += 1;

// the time at this stop if we remain on board a vehicle we had already boarded
int remainOnBoardTime;
if (bestFreq != -1) {
// we are already aboard a trip, stay on board
remainOnBoardTime = bestFreqBoardTime +
timetable.getFrequencyTravelTime(bestFreq, bestFreqBoardStop, stopPositionInPattern);
}
else {
// we cannot remain on board as we are not yet on board
remainOnBoardTime = Integer.MAX_VALUE;
}

// the time at this stop if we board a new vehicle
if (bestTimes[stopIndex] != UNREACHED) {
for (int trip = 0; trip < timetable.getFrequencyTripCount(); trip++) {
int boardTime = timetable.getFrequencyDeparture(trip, stopPositionInPattern, bestTimes[stopIndex], true);

if (boardTime != -1 && boardTime < remainOnBoardTime) {
// make sure we board the best frequency entry at a stop
if (bestFreqBoardStop == stopPositionInPattern && bestFreqBoardTime < boardTime)
continue;

// board this vehicle
// note: this boards the trip with the lowest headway at the given time.
// if there are overtaking trips all bets are off.
bestFreqBoardTime = boardTime;
bestFreqBoardStop = stopPositionInPattern;
bestFreq = trip;
// note that we do not break the loop in case there's another frequency entry that is better
}
}
}

// save the remain on board time. If we boarded a new trip then we know that the
// remain on board time must be larger than the arrival time at the stop so will
// not be saved; no need for an explicit check.
if (remainOnBoardTime != Integer.MAX_VALUE && remainOnBoardTime < max_time) {
if (bestNonTransferTimes[stopIndex] > remainOnBoardTime) {
bestNonTransferTimes[stopIndex] = remainOnBoardTime;

stopsTouched.set(stopIndex);

if (bestTimes[stopIndex] > remainOnBoardTime)
bestTimes[stopIndex] = remainOnBoardTime;
}
}
}

// don't mix frequencies and timetables
if (bestFreq != -1)
continue PATTERNS;

stopPositionInPattern = -1;

for (int stopIndex : stops) {
stopPositionInPattern += 1;
if (onTrip == -1) {
Expand All @@ -190,11 +253,13 @@ public boolean doOneRound () {
if (arrivalTime < max_time && arrivalTime < bestNonTransferTimes[stopIndex]) {
bestNonTransferTimes[stopIndex] = arrivalTime;

stopsTouched.set(stopIndex);

if (arrivalTime < bestTimes[stopIndex])
bestTimes[stopIndex] = arrivalTime;

stopsTouched.set(stopIndex);
}

// Check whether we can back up to an earlier trip. This could be due to an overtaking trip,
// or (more likely) because there was a faster way to get to a stop further down the line.
while (onTrip > 0) {
Expand Down
108 changes: 105 additions & 3 deletions src/main/java/org/opentripplanner/profile/RaptorWorkerTimetable.java
@@ -1,9 +1,13 @@
package org.opentripplanner.profile;

import com.google.common.collect.Lists;

import org.opentripplanner.routing.edgetype.TripPattern;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.trippattern.FrequencyEntry;
import org.opentripplanner.routing.trippattern.TripTimes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.BitSet;
Expand All @@ -12,12 +16,29 @@
import java.util.List;

public class RaptorWorkerTimetable implements Serializable {

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

// TODO put stop indexes in array here
// TODO serialize using deltas and variable-width from Protobuf libs

int nTrips, nStops;
private int[][] timesPerTrip;

/** Times (0-based) for frequency trips */
private int[][] frequencyTrips;

/** Headways (seconds) for frequency trips, parallel to above. Note that frequency trips are unsorted. */
private int[] headwaySecs;

/** Start times (seconds since noon - 12h) for frequency trips */
private int[] startTimes;

/** End times for frequency trips */
private int[] endTimes;

/** slack required when boarding a transit vehicle */
public static final int MIN_BOARD_TIME_SECONDS = 60;

private RaptorWorkerTimetable(int nTrips, int nStops) {
this.nTrips = nTrips;
Expand All @@ -26,11 +47,12 @@ private RaptorWorkerTimetable(int nTrips, int nStops) {
}

/**
* Return the trip index within the pattern of the soonest departure at the given stop number.
* Return the trip index within the pattern of the soonest departure at the given stop number, requiring at least
* MIN_BOARD_TIME_SECONDS seconds of slack.
*/
public int findDepartureAfter(int stop, int time) {
for (int trip = 0; trip < timesPerTrip.length; trip++) {
if (getDeparture(trip, stop) > time + 60) {
if (getDeparture(trip, stop) > time + MIN_BOARD_TIME_SECONDS) {
return trip;
}
}
Expand All @@ -44,6 +66,40 @@ public int getArrival (int trip, int stop) {
public int getDeparture (int trip, int stop) {
return timesPerTrip[trip][stop * 2 + 1];
}

/**
* Get the departure on frequency trip trip at stop stop after time time,
* assuming worst-case headway if worstCase is true.
*/
public int getFrequencyDeparture (int trip, int stop, int time, boolean worstCase) {
int timeToReachStop = frequencyTrips[trip][stop * 2 + 1];

// move time forward if the frequency has not yet started.
if (timeToReachStop + startTimes[trip] > time)
time = timeToReachStop + startTimes[trip];

if (time > timeToReachStop + endTimes[trip])
return -1;

if (worstCase)
time += headwaySecs[trip];

return time;
}

/**
* Get the travel time (departure to arrival) on frequency trip trip, from stop from to stop to.
*/
public int getFrequencyTravelTime (int trip, int from, int to) {
return frequencyTrips[trip][to * 2] - frequencyTrips[trip][from * 2 + 1];
}

/**
* Get the number of frequency trips on this pattern.
*/
public int getFrequencyTripCount () {
return headwaySecs.length;
}

/** This is a factory function rather than a constructor to avoid calling the super constructor for rejected patterns. */
public static RaptorWorkerTimetable forPattern (Graph graph, TripPattern pattern, TimeWindow window) {
Expand All @@ -59,9 +115,30 @@ public static RaptorWorkerTimetable forPattern (Graph graph, TripPattern pattern
tripTimes.add(tt);
}
}
if (tripTimes.isEmpty()) {

// find frequency trips
List<FrequencyEntry> freqs = Lists.newArrayList();
for (FrequencyEntry fe : pattern.scheduledTimetable.frequencyEntries) {
if (servicesRunning.get(fe.tripTimes.serviceCode) &&
fe.getMinDeparture() < window.to &&
fe.getMaxArrival() > window.from
) {
// this frequency entry has the potential to be used

if (fe.exactTimes) {
LOG.warn("Exact-times frequency trips not yet supported");
continue;
}


freqs.add(fe);
}
}

if (tripTimes.isEmpty() && freqs.isEmpty()) {
return null; // no trips active, don't bother storing a timetable
}


// Sort the trip times by their first arrival time
Collections.sort(tripTimes, new Comparator<TripTimes>() {
Expand All @@ -84,6 +161,31 @@ public int compare(TripTimes tt1, TripTimes tt2) {
}
rwtt.timesPerTrip[t++] = times;
}

// save frequency times
rwtt.frequencyTrips = new int[freqs.size()][pattern.getStops().size() * 2];
rwtt.endTimes = new int[freqs.size()];
rwtt.startTimes = new int[freqs.size()];
rwtt.headwaySecs = new int[freqs.size()];

{
int i = 0;
for (FrequencyEntry fe : freqs) {
rwtt.headwaySecs[i] = fe.headway;
rwtt.startTimes[i] = fe.startTime;
rwtt.endTimes[i] = fe.endTime;

int[] times = rwtt.frequencyTrips[i];

for (int s = 0; s < fe.tripTimes.getNumStops(); s++) {
times[s * 2] = fe.tripTimes.getScheduledArrivalTime(s);
times[s * 2 + 1] = fe.tripTimes.getScheduledDepartureTime(s);
}

i++;
}
}

return rwtt;
}

Expand Down

0 comments on commit 184f751

Please sign in to comment.