Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make flex linking work together with boarding locations #6311

Open
wants to merge 5 commits into
base: dev-2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor
continue;
}
// check if stop is already linked, to allow multiple idempotent linking cycles
if (tStop.isConnectedToGraph()) {
if (isAlreadyLinked(tStop, stopLocationsUsedForFlexTrips)) {
continue;
}

Expand All @@ -127,6 +127,26 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor
LOG.info(progress.completeMessage());
}

/**
* Determines if a given transit stop vertex is already linked to the street network, taking into
* account that flex stop need special linking to both a walkable and drivable edge. For example,
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
* the {@link OsmBoardingLocationsModule}, which runs before this one, links stops often to
* walkable edges only.
*
* @param stopVertex The transit stop vertex to be checked.
* @param stopLocationsUsedForFlexTrips A set of stop locations that are used for flexible trips.
*/
private static boolean isAlreadyLinked(
TransitStopVertex stopVertex,
Set<StopLocation> stopLocationsUsedForFlexTrips
) {
if (stopLocationsUsedForFlexTrips.contains(stopVertex.getStop())) {
return stopVertex.isLinkedToDrivableEdge() && stopVertex.isLinkedToWalkableEdge();
} else {
return stopVertex.isConnectedToGraph();
}
}

/**
* Link a stop to the nearest "relevant" edges.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package org.opentripplanner.street.model.vertex;

import static org.opentripplanner.street.search.TraverseMode.CAR;
import static org.opentripplanner.street.search.TraverseMode.WALK;

import java.util.HashSet;
import java.util.Set;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.edge.PathwayEdge;
import org.opentripplanner.street.model.edge.StreetTransitEntityLink;
import org.opentripplanner.street.search.TraverseMode;
import org.opentripplanner.transit.model.basic.Accessibility;
import org.opentripplanner.transit.model.basic.TransitMode;
import org.opentripplanner.transit.model.site.RegularStop;
Expand Down Expand Up @@ -94,4 +99,37 @@ public StationElement getStationElement() {
public boolean isConnectedToGraph() {
return getDegreeOut() + getDegreeIn() > 0;
}

/**
* Determines if this vertex is linked (via a {@link StreetTransitEntityLink}) to a drivable edge
* in the street network.
* <p>
* This method is slow: only use this during graph build.
*/
public boolean isLinkedToDrivableEdge() {
return isLinkedToEdgeWhichAllows(CAR);
}

/**
* Determines if this vertex is linked (via a {@link StreetTransitEntityLink}) to a walkable edge
* in the street network.
* <p>
* This method is slow: only use this during graph build.
*/
public boolean isLinkedToWalkableEdge() {
return isLinkedToEdgeWhichAllows(WALK);
}

private boolean isLinkedToEdgeWhichAllows(TraverseMode traverseMode) {
return getOutgoing()
.stream()
.anyMatch(edge ->
edge instanceof StreetTransitEntityLink<?> link &&
link
.getToVertex()
.getOutgoingStreetEdges()
.stream()
.anyMatch(se -> se.canTraverse(traverseMode))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.opentripplanner.ext.flex.trip.UnscheduledTrip;
import org.opentripplanner.framework.application.OTPFeature;
Expand All @@ -20,8 +21,10 @@
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.street.model._data.StreetModelForTest;
import org.opentripplanner.street.model.edge.BoardingLocationToStopLink;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.edge.StreetTransitStopLink;
import org.opentripplanner.street.model.vertex.OsmBoardingLocationVertex;
import org.opentripplanner.street.model.vertex.SplitterVertex;
import org.opentripplanner.street.model.vertex.TransitStopVertex;
import org.opentripplanner.transit.model._data.TimetableRepositoryForTest;
Expand Down Expand Up @@ -103,6 +106,38 @@ void linkFlexStop() {
});
}

@Test
void linkFlexStopWithBoardingLocation() {
OTPFeature.FlexRouting.testOn(() -> {
var model = new TestModel().withStopLinkedToBoardingLocation();
var flexTrip = TimetableRepositoryForTest.of().unscheduledTrip("flex", model.stop());
model.withFlexTrip(flexTrip);

var module = model.streetLinkerModule();

module.buildGraph();

assertTrue(model.stopVertex().isConnectedToGraph());

// stop is used by a flex trip, needs to be linked to both the walk and car edge,
// also linked to the boarding location
assertThat(model.stopVertex().getOutgoing()).hasSize(3);
var links = model.outgoingLinks();
assertInstanceOf(BoardingLocationToStopLink.class, links.getFirst());
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved
var linkToWalk = links.get(1);
SplitterVertex walkSplit = (SplitterVertex) linkToWalk.getToVertex();

assertTrue(walkSplit.isConnectedToWalkingEdge());
assertFalse(walkSplit.isConnectedToDriveableEdge());

var linkToCar = links.getLast();
SplitterVertex carSplit = (SplitterVertex) linkToCar.getToVertex();

assertFalse(carSplit.isConnectedToWalkingEdge());
assertTrue(carSplit.isConnectedToDriveableEdge());
});
}

@Test
void linkCarsAllowedStop() {
var model = new TestModel();
Expand Down Expand Up @@ -140,6 +175,7 @@ private static class TestModel {
private final StreetLinkerModule module;
private final RegularStop stop;
private final TimetableRepository timetableRepository;
private final Graph graph;

public TestModel() {
var from = StreetModelForTest.intersectionVertex(
Expand All @@ -151,7 +187,7 @@ public TestModel() {
KONGSBERG_PLATFORM_1.x + DELTA
);

Graph graph = new Graph();
this.graph = new Graph();
graph.addVertex(from);
graph.addVertex(to);

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

timetableRepository.addTripPattern(tripPattern.getId(), tripPattern);
}

/**
* Links the stop to a boarding location as can happen during regular graph build.
*/
public TestModel withStopLinkedToBoardingLocation() {
var boardingLocation = new OsmBoardingLocationVertex(
"boarding-location",
KONGSBERG_PLATFORM_1.x - 0.0001,
KONGSBERG_PLATFORM_1.y - 0.0001,
null,
Set.of(stop.getId().getId())
);
graph.addVertex(boardingLocation);

BoardingLocationToStopLink.createBoardingLocationToStopLink(boardingLocation, stopVertex);
BoardingLocationToStopLink.createBoardingLocationToStopLink(stopVertex, boardingLocation);
return this;
}
}
}
Loading