-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #708 from conveyal/transfer-time
Trip and pattern filtering
- Loading branch information
Showing
17 changed files
with
594 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
279 changes: 137 additions & 142 deletions
279
src/main/java/com/conveyal/r5/profile/FastRaptorWorker.java
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
src/main/java/com/conveyal/r5/transit/FilteredPattern.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package com.conveyal.r5.transit; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.ArrayList; | ||
import java.util.BitSet; | ||
import java.util.List; | ||
|
||
/** | ||
* FilteredPatterns correspond to a single specific TripPattern, indicating all the trips running on a particular day. | ||
* TripPatterns contain all the trips on a route that follow the same stop sequence. This often includes trips on | ||
* different days of the week or special schedules where vehicles travel faster or slower. By filtering down to only | ||
* those trips running on a particular day (a particular set of service codes), we usually get a smaller set of trips | ||
* with no overtaking, which enables certain optimizations and is more efficient for routing. | ||
*/ | ||
public class FilteredPattern { | ||
|
||
private static Logger LOG = LoggerFactory.getLogger(FilteredPattern.class); | ||
|
||
/** | ||
* Schedule-based (i.e. not frequency-based) trips running in a particular set of GTFS services, sorted in | ||
* ascending order by time of departure from first stop | ||
*/ | ||
public List<TripSchedule> runningScheduledTrips = new ArrayList<>(); | ||
|
||
/** Frequency-based trips active in a particular set of GTFS services */ | ||
public List<TripSchedule> runningFrequencyTrips = new ArrayList<>(); | ||
|
||
/** If no active schedule-based trip of this filtered pattern overtakes another. */ | ||
public boolean noScheduledOvertaking; | ||
|
||
/** | ||
* Filter the trips in a source TripPattern, excluding trips not active in the supplied set of services, and | ||
* dividing them into separate scheduled and frequency trip lists. Check the runningScheduledTrips for overtaking. | ||
*/ | ||
public FilteredPattern (TripPattern source, BitSet servicesActive) { | ||
for (TripSchedule schedule : source.tripSchedules) { | ||
if (servicesActive.get(schedule.serviceCode)) { | ||
if (schedule.headwaySeconds == null) { | ||
runningScheduledTrips.add(schedule); | ||
} else { | ||
runningFrequencyTrips.add(schedule); | ||
} | ||
} | ||
} | ||
// Check whether any running trip on this pattern overtakes another | ||
noScheduledOvertaking = true; | ||
for (int i = 0; i < runningScheduledTrips.size() - 1; i++) { | ||
if (overtakes(runningScheduledTrips.get(i), runningScheduledTrips.get(i + 1))) { | ||
noScheduledOvertaking = false; | ||
LOG.warn("Overtaking: route {} pattern {}", source.routeId, source.originalId); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
private static boolean overtakes (TripSchedule a, TripSchedule b) { | ||
for (int s = 0; s < a.departures.length; s++) { | ||
if (a.departures[s] > b.departures[s]) return true; | ||
} | ||
return false; | ||
} | ||
|
||
} |
50 changes: 50 additions & 0 deletions
50
src/main/java/com/conveyal/r5/transit/FilteredPatternCache.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.conveyal.r5.transit; | ||
|
||
import com.conveyal.r5.api.util.TransitModes; | ||
import com.conveyal.r5.util.Tuple2; | ||
import com.github.benmanes.caffeine.cache.Caffeine; | ||
import com.github.benmanes.caffeine.cache.LoadingCache; | ||
|
||
import java.util.BitSet; | ||
import java.util.EnumSet; | ||
|
||
/** | ||
* Stores the patterns and trips relevant for routing based on the transit modes and date in an analysis request. | ||
* We can't just cache the single most recently used filtered patterns, because a worker might need to simultaneously | ||
* handle two requests for the same scenario on different dates or with different modes. | ||
* | ||
* There are good reasons why this cache is specific to a single TransitLayer (representing one specific scenario). | ||
* To create FilteredPatterns we need the source TransitLayer object. LoadingCaches must compute values based only on | ||
* their keys. So a system-wide FilteredPatternCache would either need to recursively look up TransportNetworks in | ||
* the TransportNetworkCache, or would need to have TransportNetwork or TransitLayer references in its keys. Neither | ||
* of these seems desirable - the latter would impede garbage collection of evicted TransportNetworks. | ||
*/ | ||
public class FilteredPatternCache { | ||
|
||
/** | ||
* All FilteredPatterns stored in this cache will be derived from this single TransitLayer representing a single | ||
* scenario, but for different unique combinations of (transitModes, services). | ||
*/ | ||
private final TransitLayer transitLayer; | ||
|
||
private final LoadingCache<Key, FilteredPatterns> cache; | ||
|
||
public FilteredPatternCache (TransitLayer transitLayer) { | ||
this.transitLayer = transitLayer; | ||
this.cache = Caffeine.newBuilder().maximumSize(2).build(key -> { | ||
return new FilteredPatterns(transitLayer, key.a, key.b); | ||
}); | ||
} | ||
|
||
// TODO replace all keys and tuples with Java 16/17 Records | ||
private static class Key extends Tuple2<EnumSet<TransitModes>, BitSet> { | ||
public Key (EnumSet<TransitModes> transitModes, BitSet servicesActive) { | ||
super(transitModes, servicesActive); | ||
} | ||
} | ||
|
||
public FilteredPatterns get (EnumSet<TransitModes> transitModes, BitSet servicesActive) { | ||
return cache.get(new Key(transitModes, servicesActive)); | ||
} | ||
|
||
} |
63 changes: 63 additions & 0 deletions
63
src/main/java/com/conveyal/r5/transit/FilteredPatterns.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package com.conveyal.r5.transit; | ||
|
||
import com.conveyal.r5.api.util.TransitModes; | ||
import com.conveyal.r5.util.Tuple2; | ||
|
||
import java.util.ArrayList; | ||
import java.util.BitSet; | ||
import java.util.EnumSet; | ||
import java.util.List; | ||
|
||
import static com.conveyal.r5.transit.TransitLayer.getTransitModes; | ||
|
||
/** | ||
* Holds all the FilteredPatterns instances for a particular TransitLayer (scenario) given a particular set of | ||
* filtering criteria (transit modes and active services). There is one FilteredPattern instance for each TripPattern | ||
* that is present in the filtered TransitLayer. Many TripPatterns contain a mixture of trips from different days, | ||
* and those trips appear to overtake one another if we do not filter them down. Filtering allows us to flag more | ||
* effectively which patterns have no overtaking, which is useful because departure time searches can be then optimized | ||
* for patterns with no overtaking. All trips in a TripPattern are defined to be on same route, and GTFS allows only one | ||
* mode per route. | ||
*/ | ||
public class FilteredPatterns { | ||
|
||
/** | ||
* List with the same length and indexes as the unfiltered TripPatterns in the input TransitLayer. | ||
* Patterns that do not meet the mode/services filtering criteria are recorded as null. | ||
*/ | ||
public final List<FilteredPattern> patterns; | ||
|
||
/** The indexes of the trip patterns running on a given day with frequency-based trips of selected modes. */ | ||
public BitSet runningFrequencyPatterns = new BitSet(); | ||
|
||
/** The indexes of the trip patterns running on a given day with scheduled trips of selected modes. */ | ||
public BitSet runningScheduledPatterns = new BitSet(); | ||
|
||
/** | ||
* Construct FilteredPatterns from the given TransitLayer, filtering for the specified modes and active services. | ||
* It's tempting to use List.of() or Collectors.toUnmodifiableList() but these cause an additional array copy. | ||
*/ | ||
public FilteredPatterns (TransitLayer transitLayer, EnumSet<TransitModes> modes, BitSet services) { | ||
List<TripPattern> sourcePatterns = transitLayer.tripPatterns; | ||
patterns = new ArrayList<>(sourcePatterns.size()); | ||
for (int patternIndex = 0; patternIndex < sourcePatterns.size(); patternIndex++) { | ||
TripPattern pattern = sourcePatterns.get(patternIndex); | ||
RouteInfo routeInfo = transitLayer.routes.get(pattern.routeIndex); | ||
TransitModes mode = getTransitModes(routeInfo.route_type); | ||
if (pattern.servicesActive.intersects(services) && modes.contains(mode)) { | ||
patterns.add(new FilteredPattern(pattern, services)); | ||
// At least one trip on this pattern is relevant, based on the profile request's date and modes. | ||
if (pattern.hasFrequencies) { | ||
runningFrequencyPatterns.set(patternIndex); | ||
} | ||
// Schedule case is not an "else" clause because we support patterns with both frequency and schedule. | ||
if (pattern.hasSchedules) { | ||
runningScheduledPatterns.set(patternIndex); | ||
} | ||
} else { | ||
patterns.add(null); | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.conveyal.r5.util; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* Generic logic for a 2-tuple of different types. | ||
* Reduces high-maintenance boilerplate clutter when making map key types. | ||
* TODO replace with Records in Java 16 or 17 | ||
*/ | ||
public class Tuple2<A, B> { | ||
public final A a; | ||
public final B b; | ||
|
||
public Tuple2 (A a, B b) { | ||
this.a = a; | ||
this.b = b; | ||
} | ||
|
||
@Override | ||
public boolean equals (Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
Tuple2<?, ?> tuple2 = (Tuple2<?, ?>) o; | ||
return Objects.equals(a, tuple2.a) && Objects.equals(b, tuple2.b); | ||
} | ||
|
||
@Override | ||
public int hashCode () { | ||
return Objects.hash(a, b); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,4 +32,4 @@ public static void assertExpectedDistribution (Distribution expectedDistribution | |
} | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.