Skip to content

Commit e1bb7d6

Browse files
Make flex linking work together with boarding locations
1 parent 0fe4bdd commit e1bb7d6

File tree

3 files changed

+100
-2
lines changed

3 files changed

+100
-2
lines changed

application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor
103103
continue;
104104
}
105105
// check if stop is already linked, to allow multiple idempotent linking cycles
106-
if (tStop.isConnectedToGraph()) {
106+
if (isAlreadyLinked(tStop, stopLocationsUsedForFlexTrips)) {
107107
continue;
108108
}
109109

@@ -127,6 +127,24 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor
127127
LOG.info(progress.completeMessage());
128128
}
129129

130+
/**
131+
* Determines if a given transit stop vertex is already linked to the street network, taking into
132+
* account that flex stop need special linking to both a walkable and drivable edge.
133+
*
134+
* @param stopVertex The transit stop vertex to be checked.
135+
* @param stopLocationsUsedForFlexTrips A set of stop locations that are used for flexible trips.
136+
*/
137+
private static boolean isAlreadyLinked(
138+
TransitStopVertex stopVertex,
139+
Set<StopLocation> stopLocationsUsedForFlexTrips
140+
) {
141+
if (stopLocationsUsedForFlexTrips.contains(stopVertex.getStop())) {
142+
return stopVertex.isLinkedToDrivableEdge() && stopVertex.isLinkedToWalkableEdge();
143+
} else {
144+
return stopVertex.isConnectedToGraph();
145+
}
146+
}
147+
130148
/**
131149
* Link a stop to the nearest "relevant" edges.
132150
* <p>

application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package org.opentripplanner.street.model.vertex;
22

3+
import static org.opentripplanner.street.search.TraverseMode.CAR;
4+
import static org.opentripplanner.street.search.TraverseMode.WALK;
5+
36
import java.util.HashSet;
47
import java.util.Set;
58
import org.opentripplanner.street.model.edge.Edge;
69
import org.opentripplanner.street.model.edge.PathwayEdge;
10+
import org.opentripplanner.street.model.edge.StreetTransitEntityLink;
11+
import org.opentripplanner.street.search.TraverseMode;
712
import org.opentripplanner.transit.model.basic.Accessibility;
813
import org.opentripplanner.transit.model.basic.TransitMode;
914
import org.opentripplanner.transit.model.site.RegularStop;
@@ -94,4 +99,25 @@ public StationElement getStationElement() {
9499
public boolean isConnectedToGraph() {
95100
return getDegreeOut() + getDegreeIn() > 0;
96101
}
102+
103+
public boolean isLinkedToDrivableEdge() {
104+
return isLinkedToEdgeWhichAllows(CAR);
105+
}
106+
107+
public boolean isLinkedToWalkableEdge() {
108+
return isLinkedToEdgeWhichAllows(WALK);
109+
}
110+
111+
private boolean isLinkedToEdgeWhichAllows(TraverseMode traverseMode) {
112+
return getOutgoing()
113+
.stream()
114+
.anyMatch(edge ->
115+
edge instanceof StreetTransitEntityLink<?> link &&
116+
link
117+
.getToVertex()
118+
.getOutgoingStreetEdges()
119+
.stream()
120+
.anyMatch(se -> se.canTraverse(traverseMode))
121+
);
122+
}
97123
}

application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import java.util.Arrays;
1313
import java.util.List;
14+
import java.util.Set;
1415
import org.junit.jupiter.api.Test;
1516
import org.opentripplanner.ext.flex.trip.UnscheduledTrip;
1617
import org.opentripplanner.framework.application.OTPFeature;
@@ -20,8 +21,10 @@
2021
import org.opentripplanner.routing.graph.Graph;
2122
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
2223
import org.opentripplanner.street.model._data.StreetModelForTest;
24+
import org.opentripplanner.street.model.edge.BoardingLocationToStopLink;
2325
import org.opentripplanner.street.model.edge.Edge;
2426
import org.opentripplanner.street.model.edge.StreetTransitStopLink;
27+
import org.opentripplanner.street.model.vertex.OsmBoardingLocationVertex;
2528
import org.opentripplanner.street.model.vertex.SplitterVertex;
2629
import org.opentripplanner.street.model.vertex.TransitStopVertex;
2730
import org.opentripplanner.transit.model._data.TimetableRepositoryForTest;
@@ -103,6 +106,38 @@ void linkFlexStop() {
103106
});
104107
}
105108

109+
@Test
110+
void linkFlexStopWithBoardingLocation() {
111+
OTPFeature.FlexRouting.testOn(() -> {
112+
var model = new TestModel().withStopLinkedToBoardingLocation();
113+
var flexTrip = TimetableRepositoryForTest.of().unscheduledTrip("flex", model.stop());
114+
model.withFlexTrip(flexTrip);
115+
116+
var module = model.streetLinkerModule();
117+
118+
module.buildGraph();
119+
120+
assertTrue(model.stopVertex().isConnectedToGraph());
121+
122+
// stop is used by a flex trip, needs to be linked to both the walk and car edge,
123+
// also linked to the boarding location
124+
assertThat(model.stopVertex().getOutgoing()).hasSize(3);
125+
var links = model.outgoingLinks();
126+
assertInstanceOf(BoardingLocationToStopLink.class, links.getFirst());
127+
var linkToWalk = links.get(1);
128+
SplitterVertex walkSplit = (SplitterVertex) linkToWalk.getToVertex();
129+
130+
assertTrue(walkSplit.isConnectedToWalkingEdge());
131+
assertFalse(walkSplit.isConnectedToDriveableEdge());
132+
133+
var linkToCar = links.getLast();
134+
SplitterVertex carSplit = (SplitterVertex) linkToCar.getToVertex();
135+
136+
assertFalse(carSplit.isConnectedToWalkingEdge());
137+
assertTrue(carSplit.isConnectedToDriveableEdge());
138+
});
139+
}
140+
106141
@Test
107142
void linkCarsAllowedStop() {
108143
var model = new TestModel();
@@ -140,6 +175,7 @@ private static class TestModel {
140175
private final StreetLinkerModule module;
141176
private final RegularStop stop;
142177
private final TimetableRepository timetableRepository;
178+
private final Graph graph;
143179

144180
public TestModel() {
145181
var from = StreetModelForTest.intersectionVertex(
@@ -151,7 +187,7 @@ public TestModel() {
151187
KONGSBERG_PLATFORM_1.x + DELTA
152188
);
153189

154-
Graph graph = new Graph();
190+
this.graph = new Graph();
155191
graph.addVertex(from);
156192
graph.addVertex(to);
157193

@@ -232,5 +268,23 @@ public void withCarsAllowedTrip(Trip trip, StopLocation... stops) {
232268

233269
timetableRepository.addTripPattern(tripPattern.getId(), tripPattern);
234270
}
271+
272+
/**
273+
* Links the stop to a boarding location as can happen during regular graph build.
274+
*/
275+
public TestModel withStopLinkedToBoardingLocation() {
276+
var boardingLocation = new OsmBoardingLocationVertex(
277+
"boarding-location",
278+
KONGSBERG_PLATFORM_1.x - 0.0001,
279+
KONGSBERG_PLATFORM_1.y - 0.0001,
280+
null,
281+
Set.of(stop.getId().getId())
282+
);
283+
graph.addVertex(boardingLocation);
284+
285+
BoardingLocationToStopLink.createBoardingLocationToStopLink(boardingLocation, stopVertex);
286+
BoardingLocationToStopLink.createBoardingLocationToStopLink(stopVertex, boardingLocation);
287+
return this;
288+
}
235289
}
236290
}

0 commit comments

Comments
 (0)