Skip to content

Commit 2dadef6

Browse files
committed
feat: Filter unused from stops in Transfers generation
1 parent a21292b commit 2dadef6

File tree

11 files changed

+120
-73
lines changed

11 files changed

+120
-73
lines changed

application/src/main/java/org/opentripplanner/graph_builder/module/transfer/DirectTransferGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void buildGraph() {
152152

153153
stops
154154
.stream()
155-
.parallel()
155+
//.parallel()
156156
.forEach(ts0 -> {
157157
/* Make transfers to each nearby stop that has lowest weight on some trip pattern.
158158
* Use map based on the list of edges, so that only distinct transfers are stored. */

application/src/main/java/org/opentripplanner/graph_builder/module/transfer/filter/CompositeNearbyStopFilter.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.List;
77
import java.util.Set;
88
import org.opentripplanner.routing.graphfinder.NearbyStop;
9+
import org.opentripplanner.transit.model.framework.FeedScopedId;
910

1011
class CompositeNearbyStopFilter implements NearbyStopFilter {
1112

@@ -19,6 +20,16 @@ static Builder of() {
1920
return new Builder();
2021
}
2122

23+
@Override
24+
public boolean includeFromStop(FeedScopedId id, boolean reverseDirection) {
25+
for (NearbyStopFilter filter : filters) {
26+
if (filter.includeFromStop(id, reverseDirection)) {
27+
return true;
28+
}
29+
}
30+
return false;
31+
}
32+
2233
@Override
2334
public Collection<NearbyStop> filterToStops(
2435
Collection<NearbyStop> nearbyStops,

application/src/main/java/org/opentripplanner/graph_builder/module/transfer/filter/FlexTripNearbyStopFilter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Collection;
44
import org.opentripplanner.ext.flex.trip.FlexTrip;
55
import org.opentripplanner.routing.graphfinder.NearbyStop;
6+
import org.opentripplanner.transit.model.framework.FeedScopedId;
67
import org.opentripplanner.transit.service.TransitService;
78

89
class FlexTripNearbyStopFilter implements NearbyStopFilter {
@@ -13,6 +14,12 @@ class FlexTripNearbyStopFilter implements NearbyStopFilter {
1314
this.transitService = transitService;
1415
}
1516

17+
@Override
18+
public boolean includeFromStop(FeedScopedId id, boolean reverseDirection) {
19+
var stop = transitService.getStopLocation(id);
20+
return !transitService.getFlexIndex().getFlexTripsByStop(stop).isEmpty();
21+
}
22+
1623
@Override
1724
public Collection<NearbyStop> filterToStops(
1825
Collection<NearbyStop> nearbyStops,

application/src/main/java/org/opentripplanner/graph_builder/module/transfer/filter/NearbyStopFilter.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,36 @@
22

33
import java.util.Collection;
44
import org.opentripplanner.routing.graphfinder.NearbyStop;
5+
import org.opentripplanner.transit.model.framework.FeedScopedId;
56

7+
/**
8+
* Filters nearby stops for transfer generation based on trip patterns and flex trips.
9+
* Returns only stops where boarding or alighting is possible, and for each pattern/trip,
10+
* returns only the closest stop to minimize transfer generation.
11+
*/
612
interface NearbyStopFilter {
13+
14+
/**
15+
* Checks if a stop should be included as a transfer origin/destination.
16+
*
17+
* @param id the stop-location to check
18+
* @param reverseDirection true for arrival searches (checks boarding), false for departure
19+
* searches (checks alighting)
20+
* @return true if the stop has relevant trip patterns or flex trips
21+
*/
22+
boolean includeFromStop(FeedScopedId id, boolean reverseDirection);
23+
724
/**
8-
* Find all unique nearby stops that are the closest stop on some trip pattern or flex trip. Note
9-
* that the result will include the origin vertex if it is an instance of StopVertex. This is
10-
* intentional: we don't want to return the next stop down the line for trip patterns that pass
11-
* through the origin vertex. Taking the patterns into account reduces the number of transfers
12-
* significantly compared to simple traverse-duration-constrained all-to-all stop linkage.
25+
* Filters nearby stops to find optimal transfer points. For each trip pattern or flex trip,
26+
* returns only the closest stop.
27+
* <p>
28+
* Note: The result will include the origin stop if it is a StopVertex. This is intentional - we
29+
* don't want to return the next stop down the line for patterns passing through the origin.
30+
*
31+
* @param nearbyStops the stops to filter, typically from a street search
32+
* @param reverseDirection true for arrival searches (filters by boarding), false for departure
33+
* searches (filters by alighting)
34+
* @return filtered stops, one per relevant trip pattern/flex trip
1335
*/
1436
Collection<NearbyStop> filterToStops(
1537
Collection<NearbyStop> nearbyStops,

application/src/main/java/org/opentripplanner/graph_builder/module/transfer/filter/PatternConsideringNearbyStopFinder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.opentripplanner.routing.api.request.RouteRequest;
88
import org.opentripplanner.routing.api.request.request.StreetRequest;
99
import org.opentripplanner.routing.graphfinder.NearbyStop;
10+
import org.opentripplanner.street.model.vertex.TransitStopVertex;
1011
import org.opentripplanner.street.model.vertex.Vertex;
1112
import org.opentripplanner.transit.service.TransitService;
1213

@@ -36,6 +37,17 @@ public List<NearbyStop> findNearbyStops(
3637
StreetRequest streetRequest,
3738
boolean reverseDirection
3839
) {
40+
if (!(vertex instanceof TransitStopVertex stopVertex)) {
41+
throw new IllegalArgumentException(
42+
"Transfers can only be created between stops. Vertex: " + vertex
43+
);
44+
}
45+
46+
// Check if the from stop can be used in a transfer
47+
if (!filter.includeFromStop(stopVertex.getId(), reverseDirection)) {
48+
return List.of();
49+
}
50+
3951
// fetch nearby stops via the street network or using straight-line distance.
4052
var nearbyStops = delegateNearbyStopFinder.findNearbyStops(
4153
vertex,

application/src/main/java/org/opentripplanner/graph_builder/module/transfer/filter/PatternNearbyStopFilter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ class PatternNearbyStopFilter implements NearbyStopFilter {
2020
this.transitService = transitService;
2121
}
2222

23+
@Override
24+
public boolean includeFromStop(FeedScopedId id, boolean reverseDirection) {
25+
var stop = transitService.getRegularStop(id);
26+
boolean hasPatterns = !findPatternsForStop(stop, !reverseDirection).isEmpty();
27+
return hasPatterns || includeStopUsedRealtime(stop);
28+
}
29+
2330
@Override
2431
public Collection<NearbyStop> filterToStops(
2532
Collection<NearbyStop> nearbyStops,

application/src/test/java/org/opentripplanner/graph_builder/module/transfer/DirectTransferGeneratorCarTest.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,7 @@ public void testMultipleRequestsWithPatternsAndWithCarsAllowedPatterns() {
9191
S0 - S11, 100m
9292
S0 - S21, 100m
9393
S0 - S22, 200m
94-
S11 - S21, 100m
95-
S11 - S22, 110m
96-
S12 - S22, 110m
97-
S13 - S22, 210m""";
94+
S12 - S22, 110m""";
9895
assertEquals(
9996
expected_walk_bike_results,
10097
pathToString(repository.findTransfers(StreetMode.WALK))
@@ -128,10 +125,7 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatterns() {
128125
S0 - S11, 100m
129126
S0 - S21, 100m
130127
S0 - S22, 200m
131-
S11 - S21, 100m
132-
S11 - S22, 110m
133-
S12 - S22, 110m
134-
S13 - S22, 210m""",
128+
S12 - S22, 110m""",
135129
pathToString(repository.getAllPathTransfers())
136130
);
137131
}
@@ -148,8 +142,6 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatternsWithoutCarInTra
148142
"""
149143
S0 - S11, 100m
150144
S0 - S21, 100m
151-
S11 - S21, 100m
152-
S11 - S22, 110m
153145
S12 - S22, 110m""",
154146
pathToString(repository.getAllPathTransfers())
155147
);
@@ -181,10 +173,7 @@ public void testDisableDefaultTransfersForMode() {
181173
S0 - S11, 100m
182174
S0 - S21, 100m
183175
S0 - S22, 200m
184-
S11 - S21, 100m
185-
S11 - S22, 110m
186-
S12 - S22, 110m
187-
S13 - S22, 210m""",
176+
S12 - S22, 110m""",
188177
pathToString(repository.findTransfers(StreetMode.WALK))
189178
);
190179

@@ -215,16 +204,12 @@ public void testMaxTransferDurationForMode() {
215204
"""
216205
S0 - S11, 100m
217206
S0 - S21, 100m
218-
S11 - S21, 100m
219-
S11 - S22, 110m
220207
S12 - S22, 110m""",
221208
pathToString(repository.findTransfers(StreetMode.WALK))
222209
);
223-
assertEquals(
224-
"""
225-
S0 - S11, 100m
226-
S0 - S21, 100m
227-
S11 - S21, 100m""",
210+
assertEquals("""
211+
S0 - S11, 100m
212+
S0 - S21, 100m""".indent(1).stripTrailing(),
228213
pathToString(repository.findTransfers(StreetMode.BIKE))
229214
);
230215
assertEquals("<Empty>", pathToString(repository.findTransfers(StreetMode.CAR)));
-52 Bytes
Loading

application/src/test/java/org/opentripplanner/graph_builder/module/transfer/DirectTransferGeneratorTest.java

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -101,23 +101,15 @@ public void testStraightLineTransfersWithPatternsPruning() {
101101

102102
// Exactly one transfer is expected from each stop to the closest place to board all
103103
// patterns, including the same pattern used by the stop (E.g. S12 - S11).
104+
// S11, S13, S21, S23 -> * No patterns alight here
105+
// * -> S0, S12, S13, S23 No patterns board here
104106
assertEquals(
105107
"""
106108
S0 - S11, 1668m
107109
S0 - S21, 1829m
108-
S11 - S0, 1668m
109-
S11 - S21, 751m
110-
S12 - S0, 3892m
111110
S12 - S11, 2224m
112111
S12 - S22, 751m
113-
S13 - S11, 4448m
114-
S13 - S22, 2347m
115-
S21 - S0, 1829m
116-
S21 - S11, 751m
117-
S22 - S0, 3964m
118-
S22 - S11, 2347m
119-
S23 - S11, 4511m
120-
S23 - S22, 2224m""",
112+
S22 - S11, 2347m""",
121113
pathToString(repository.getAllPathTransfers())
122114
);
123115
}
@@ -131,17 +123,12 @@ public void testStraightLineTransfersWithBoardingRestrictions() {
131123
.build();
132124

133125
assertEquals(
134-
// * - S11 is not allowed, because of boarding constraints
126+
// * -> S11 is not allowed, because of boarding constraints
127+
// S11, S13, S21 -> * No patterns alight here
128+
// * -> S0, S12, S23 No patterns board here
135129
"""
136130
S0 - S21, 1829m
137-
S11 - S0, 1668m
138-
S11 - S21, 751m
139-
S12 - S0, 3892m
140-
S12 - S22, 751m
141-
S13 - S22, 2347m
142-
S21 - S0, 1829m
143-
S22 - S0, 3964m
144-
S23 - S22, 2224m""",
131+
S12 - S22, 751m""",
145132
pathToString(repository.getAllPathTransfers())
146133
);
147134
}
@@ -165,6 +152,7 @@ public void testStreetTransfersWithoutPatternsPruning() {
165152
.withTransferRequests(REQUEST_WITH_WALK_TRANSFER)
166153
.build();
167154

155+
// All transfers reachable in the street graph is included.
168156
assertEquals(
169157
"""
170158
S0 - S11, 100m
@@ -195,13 +183,14 @@ public void testStreetTransfersWithPatterns() {
195183
.withTransferRequests(REQUEST_WITH_WALK_TRANSFER)
196184
.build();
197185

186+
// Best transfers between patterns; Hence S0 - S22 removed
187+
// S11, S13, S21 -> * No patterns alight here
188+
// * -> S0, S12, S23 No patterns board here
198189
assertEquals(
199190
"""
200191
S0 - S11, 100m
201192
S0 - S21, 100m
202-
S11 - S21, 100m
203-
S12 - S22, 110m
204-
S13 - S22, 210m""",
193+
S12 - S22, 110m""",
205194
pathToString(repository.getAllPathTransfers())
206195
);
207196
}
@@ -215,13 +204,15 @@ public void testStreetTransfersWithPatternsIncludeRealTimeUsedStops() {
215204
.withTransferRequests(REQUEST_WITH_WALK_TRANSFER)
216205
.build();
217206

207+
// Best transfers between patterns; Hence S0 - S22 removed
208+
// S11, S21 -> * No patterns alight here
209+
// * -> S0, S12 No patterns board here
210+
// S13, S23 Included, used real-time
218211
assertEquals(
219212
"""
220213
S0 - S11, 100m
221214
S0 - S21, 100m
222215
S0 - S23, 300m
223-
S11 - S21, 100m
224-
S11 - S23, 210m
225216
S12 - S22, 110m
226217
S12 - S23, 210m
227218
S13 - S22, 210m
@@ -244,34 +235,46 @@ public void testStreetTransfersWithMultipleRequestsWithPatterns() {
244235
var bikeTransfers = repository.findTransfers(StreetMode.BIKE);
245236
var carTransfers = repository.findTransfers(StreetMode.CAR);
246237

247-
String expected =
238+
// Best transfers between patterns; Hence S0 - S22 removed
239+
// S11, S13, S21 -> * No patterns alight here
240+
// * -> S0, S12, S23 No patterns board here
241+
String expectedWalkAndBike =
248242
"""
249243
S0 - S11, 100m
250244
S0 - S21, 100m
251-
S11 - S21, 100m
252-
S12 - S22, 110m
253-
S13 - S22, 210m""";
254-
assertEquals(expected, pathToString(walkTransfers));
255-
assertEquals(expected, pathToString(bikeTransfers));
245+
S12 - S22, 110m""";
246+
assertEquals(expectedWalkAndBike, pathToString(walkTransfers));
247+
assertEquals(expectedWalkAndBike, pathToString(bikeTransfers));
256248
assertEquals("<Empty>", pathToString(carTransfers));
257249
}
258250

259251
@Test
260252
public void testStreetTransfersWithStationWithTransfersNotAllowed() {
261-
var repository = DirectTransferGeneratorTestData.of()
262-
.withPatterns()
263-
.withStreetGraph()
264-
.withNoTransfersOnStationA()
265-
.withTransferRequests(REQUEST_WITH_WALK_TRANSFER)
266-
.build();
253+
OTPFeature.IncludeStopsUsedRealtimeInTransfers.testOn(() -> {
254+
var repository = DirectTransferGeneratorTestData.of()
255+
.withPatterns()
256+
.withStreetGraph()
257+
.withNoTransfersOnStationA()
258+
.withTransferRequests(REQUEST_WITH_WALK_TRANSFER)
259+
.build();
267260

268-
assertEquals(
269-
"""
270-
S0 - S22, 200m
271-
S12 - S22, 110m
272-
S13 - S22, 210m""",
273-
pathToString(repository.getAllPathTransfers())
274-
);
261+
// Best transfers between patterns; Hence S0 - S22 removed
262+
// S11, S13, S21 -> * No patterns alight here
263+
// * -> S0, S12, S23 No patterns board here
264+
// Not: S11, S21 Transfers NOT_ALLOWED for station
265+
// Allow: S13, S23 Included, used real-time
266+
assertEquals(
267+
"""
268+
S0 - S22, 200m
269+
S0 - S23, 300m
270+
S12 - S22, 110m
271+
S12 - S23, 210m
272+
S13 - S22, 210m
273+
S13 - S23, 310m
274+
S22 - S23, 100m""",
275+
pathToString(repository.getAllPathTransfers())
276+
);
277+
});
275278
}
276279

277280
@Test

application/src/test/java/org/opentripplanner/graph_builder/module/transfer/DirectTransferGeneratorTestData.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public void build() {
160160
tripPattern(
161161
TripPattern.of(TimetableRepositoryForTest.id("TP0"))
162162
.withRoute(route("R0", TransitMode.RAIL, agency))
163-
.withStopPattern(new StopPattern(List.of(st(S0), st(S_FAR_AWAY))))
163+
.withStopPattern(new StopPattern(List.of(st(S_FAR_AWAY), st(S0))))
164164
.build()
165165
);
166166
tripPattern(

0 commit comments

Comments
 (0)