Skip to content

Commit

Permalink
Merge pull request #6238 from realCity/feature/edge-traverser-multipl…
Browse files Browse the repository at this point in the history
…e-states

Allow multiple states during transfer edge traversals
  • Loading branch information
flaktack authored Nov 22, 2024
2 parents c75ee9d + 4d11307 commit a68bc04
Show file tree
Hide file tree
Showing 11 changed files with 3,767 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ private FlexAccessEgress createFlexAccessEgress(
return null;
}

final var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState[0], transferEdges);
final var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState, transferEdges);

return finalStateOpt
.map(finalState -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private Optional<DirectFlexPath> createDirectGraphPath(

final State[] afterFlexState = flexEdge.traverse(accessNearbyStop.state);

var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState[0], egress.edges);
var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState, egress.edges);

if (finalStateOpt.isEmpty()) {
return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.opentripplanner.astar.model.GraphPath;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.framework.geometry.GeometryUtils;
Expand Down Expand Up @@ -38,8 +39,8 @@
import org.opentripplanner.street.search.TraverseMode;
import org.opentripplanner.street.search.request.StreetSearchRequest;
import org.opentripplanner.street.search.request.StreetSearchRequestMapper;
import org.opentripplanner.street.search.state.EdgeTraverser;
import org.opentripplanner.street.search.state.State;
import org.opentripplanner.street.search.state.StateEditor;
import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate;
import org.opentripplanner.transit.model.timetable.TripOnServiceDate;
import org.opentripplanner.transit.service.TransitService;
Expand Down Expand Up @@ -360,24 +361,15 @@ private List<Leg> mapNonTransitLeg(
.build()
);
} else {
StateEditor se = new StateEditor(edges.get(0).getFromVertex(), transferStreetRequest);
se.setTimeSeconds(createZonedDateTime(pathLeg.fromTime()).toEpochSecond());

State s = se.makeState();
ArrayList<State> transferStates = new ArrayList<>();
transferStates.add(s);
for (Edge e : edges) {
var states = e.traverse(s);
if (State.isEmpty(states)) {
s = null;
} else {
transferStates.add(states[0]);
s = states[0];
}
}

State[] states = transferStates.toArray(new State[0]);
var graphPath = new GraphPath<>(states[states.length - 1]);
var legTransferSearchRequest = transferStreetRequest
.copyOf(createZonedDateTime(pathLeg.fromTime()).toInstant())
.build();
var initialStates = State.getInitialStates(
Set.of(edges.getFirst().getFromVertex()),
legTransferSearchRequest
);
var state = EdgeTraverser.traverseEdges(initialStates, edges);
var graphPath = new GraphPath<>(state.get());

Itinerary subItinerary = graphPathToItineraryMapper.generateItinerary(graphPath);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.locationtech.jts.geom.Coordinate;
import org.opentripplanner.raptor.api.model.RaptorCostConverter;
import org.opentripplanner.raptor.api.model.RaptorTransfer;
import org.opentripplanner.routing.api.request.preference.WalkPreferences;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.search.request.StreetSearchRequest;
import org.opentripplanner.street.search.state.EdgeTraverser;
import org.opentripplanner.street.search.state.StateEditor;
import org.opentripplanner.street.search.state.State;
import org.opentripplanner.utils.logging.Throttle;
import org.opentripplanner.utils.tostring.ToStringBuilder;
import org.slf4j.Logger;
Expand Down Expand Up @@ -84,10 +85,8 @@ public Optional<RaptorTransfer> asRaptorTransfer(StreetSearchRequest request) {
);
}

StateEditor se = new StateEditor(edges.get(0).getFromVertex(), request);
se.setTimeSeconds(0);

var state = EdgeTraverser.traverseEdges(se.makeState(), edges);
var initialStates = State.getInitialStates(Set.of(edges.getFirst().getFromVertex()), request);
var state = EdgeTraverser.traverseEdges(initialStates, edges);

return state.map(s ->
new DefaultRaptorTransfer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ public DataOverlayContext dataOverlayContext() {
return dataOverlayContext;
}

public StreetSearchRequestBuilder copyOf(Instant time) {
return copyOf(this).withStartTime(time);
}

public StreetSearchRequestBuilder copyOfReversed(Instant time) {
return copyOf(this).withStartTime(time).withArriveBy(!arriveBy);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import java.util.Collection;
import java.util.Optional;
import org.opentripplanner.astar.model.ShortestPathTree;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.vertex.Vertex;
import org.opentripplanner.street.search.strategy.DominanceFunctions;

/**
* This is a very reduced version of the A* algorithm: from an initial state a number of edges are
Expand All @@ -14,24 +17,49 @@
*/
public class EdgeTraverser {

public static Optional<State> traverseEdges(final State s, final Collection<Edge> edges) {
var state = s;
public static Optional<State> traverseEdges(
final Collection<State> initialStates,
final Collection<Edge> edges
) {
return traverseEdges(initialStates.toArray(new State[0]), edges);
}

public static Optional<State> traverseEdges(
final State[] initialStates,
final Collection<Edge> edges
) {
if (edges.isEmpty()) {
return Optional.of(initialStates[0]);
}

// The shortest path tree is used to prune dominated parallel states. For example,
// CAR_PICKUP can return both a CAR/WALK state after each traversal of which only
// the optimal states need to be continued.
var dominanceFunction = new DominanceFunctions.MinimumWeight();
var spt = new ShortestPathTree<>(dominanceFunction);
for (State initialState : initialStates) {
spt.add(initialState);
}

Vertex lastVertex = null;
var isArriveBy = initialStates[0].getRequest().arriveBy();
for (Edge e : edges) {
var afterTraversal = e.traverse(state);
if (afterTraversal.length > 1) {
throw new IllegalStateException(
"Expected only a single state returned from edge %s but received %s".formatted(
e,
afterTraversal.length
)
);
}
if (State.isEmpty(afterTraversal)) {
var vertex = isArriveBy ? e.getToVertex() : e.getFromVertex();
var fromStates = spt.getStates(vertex);
if (fromStates == null || fromStates.isEmpty()) {
return Optional.empty();
} else {
state = afterTraversal[0];
}

for (State fromState : fromStates) {
var newToStates = e.traverse(fromState);
for (State newToState : newToStates) {
spt.add(newToState);
}
}

lastVertex = isArriveBy ? e.getFromVertex() : e.getToVertex();
}
return Optional.ofNullable(state);

return Optional.ofNullable(lastVertex).map(spt::getState);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public class Coordinates {

public static final Coordinate BERLIN = of(52.5212, 13.4105);
public static final Coordinate BERLIN_BRANDENBURG_GATE = of(52.51627, 13.37770);
public static final Coordinate BERLIN_FERNSEHTURM = of(52.52084, 13.40934);
public static final Coordinate BERLIN_ADMIRALBRUCKE = of(52.49526, 13.415093);
public static final Coordinate HAMBURG = of(53.5566, 10.0003);
public static final Coordinate KONGSBERG_PLATFORM_1 = of(59.67216, 9.65107);
public static final Coordinate BOSTON = of(42.36541, -71.06129);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.opentripplanner.routing.algorithm.mapping;

import au.com.origin.snapshots.junit5.SnapshotExtension;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.opentripplanner.model.GenericLocation;
import org.opentripplanner.model.modes.ExcludeAllTransitFilter;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter;

@ExtendWith(SnapshotExtension.class)
public class CarPickupSnapshotTest extends SnapshotTestBase {

static GenericLocation p0 = new GenericLocation(
"SE Stark St. & SE 17th Ave. (P0)",
null,
45.519320,
-122.648567
);

static GenericLocation p1 = new GenericLocation(
"SE Morrison St. & SE 17th Ave. (P1)",
null,
45.51726,
-122.64847
);

static GenericLocation p2 = new GenericLocation(
"NW Northrup St. & NW 22nd Ave. (P2)",
null,
45.53122,
-122.69659
);

@BeforeAll
public static void beforeClass() {
loadGraphBeforeClass(false);
}

@Test
public void test_trip_planning_with_car_pickup_only() {
RouteRequest request = createTestRequest(2009, 11, 17, 10, 0, 0);

request.journey().direct().setMode(StreetMode.CAR_PICKUP);
request.journey().transit().setFilters(List.of(ExcludeAllTransitFilter.of()));

request.setFrom(p0);
request.setTo(p2);

expectRequestResponseToMatchSnapshot(request);
}

@Test
public void test_trip_planning_with_car_pickup_transfer() {
RouteRequest request = createTestRequest(2009, 11, 17, 10, 0, 0);

request.journey().access().setMode(StreetMode.WALK);
request.journey().egress().setMode(StreetMode.WALK);
request.journey().direct().setMode(StreetMode.WALK);
request.journey().transfer().setMode(StreetMode.CAR_PICKUP);
request.journey().transit().setFilters(List.of(AllowAllTransitFilter.of()));

request.setFrom(p0);
request.setTo(p2);

expectRequestResponseToMatchSnapshot(request);
}
}
Loading

0 comments on commit a68bc04

Please sign in to comment.