Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ee8bc2d
Refactor how temporary vertices are created for a routing request
optionsome Jun 17, 2025
ccbff2f
Refactor more and fix missing use of stop vertices
optionsome Jun 19, 2025
60433ff
Move Exception handling where it belongs after refactoring
optionsome Jun 23, 2025
6ba2932
Refactor how coordinate visit via location vertices/edges are created
optionsome Jun 24, 2025
5e1834d
Set inputField as VIA for routing errors related to via points
optionsome Jun 24, 2025
445ba25
Move FromToViaVertexRequest and create a builder for TemporaryVertice…
optionsome Jun 27, 2025
b89d493
Adjust edges related to via points as well
optionsome Jun 27, 2025
0403a3a
Remove unused constructor
optionsome Jun 27, 2025
c83c04c
Only adjust edges between adjacent edges and adjust edges between via…
optionsome Jun 28, 2025
2ccfc1a
Filter which modes are used for temporary edges based on request
optionsome Jun 30, 2025
ce298b5
Move test to correct package
optionsome Jun 30, 2025
6bcbf19
Add tests
optionsome Jun 30, 2025
beb3499
Add javadoc
optionsome Jul 1, 2025
06f208e
Rename via getters to follow naming conventions
optionsome Jul 1, 2025
0c11058
Fix memory leak when builder throws exception
optionsome Jul 2, 2025
4867330
Use FromToViaVertexRequest#findVertices for from/to as well
optionsome Jul 4, 2025
b0474b1
Merge remote-tracking branch 'upstream/dev-2.x' into temporary-vertex…
optionsome Aug 15, 2025
07dbb76
feature: Add support for via in access/egress.
t2gran Feb 28, 2025
c6b8989
Fix formatting
optionsome Mar 10, 2025
9324c50
Refactor to only allow one coordinate outside of raptor
optionsome Mar 10, 2025
16b37ef
Formatting
optionsome Jul 4, 2025
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 @@ -18,15 +18,18 @@
import org.opentripplanner.ext.flex.FlexIntegrationTestData;
import org.opentripplanner.framework.geometry.EncodedPolyline;
import org.opentripplanner.graph_builder.module.ValidateAndInterpolateStopTimesForEachTrip;
import org.opentripplanner.graph_builder.module.linking.TestVertexLinker;
import org.opentripplanner.model.GenericLocation;
import org.opentripplanner.model.StopTime;
import org.opentripplanner.model.plan.Itinerary;
import org.opentripplanner.routing.algorithm.raptoradapter.router.AdditionalSearchDays;
import org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.framework.DebugTimingAggregator;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.standalone.api.OtpServerRequestContext;
import org.opentripplanner.street.search.TemporaryVerticesContainer;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService;
import org.opentripplanner.transit.service.TimetableRepository;
Expand Down Expand Up @@ -167,16 +170,28 @@ private static List<Itinerary> getItineraries(

var transitStartOfTime = ServiceDateUtils.asStartOfService(request.dateTime(), zoneId);
var additionalSearchDays = AdditionalSearchDays.defaults(dateTime);
var result = TransitRouter.route(
request,
serverContext,
TransitGroupPriorityService.empty(),
transitStartOfTime,
additionalSearchDays,
new DebugTimingAggregator()
);

return result.getItineraries();
try (
var temporaryVerticesContainer = TemporaryVerticesContainer.of(
serverContext.graph(),
TestVertexLinker.of(graph)
)
.withFrom(from, StreetMode.WALK)
.withTo(to, StreetMode.WALK)
.build()
) {
var result = TransitRouter.route(
request,
serverContext,
TransitGroupPriorityService.empty(),
transitStartOfTime,
additionalSearchDays,
new DebugTimingAggregator(),
temporaryVerticesContainer.createFromToViaVertexRequest()
);

return result.getItineraries();
}
}

private static FlexTrip<?, ?> getFlexTrip() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public static GraphQLInputField toGraphQL(InputField inputField) {
return switch (inputField) {
case DATE_TIME -> GraphQLInputField.DATE_TIME;
case FROM_PLACE -> GraphQLInputField.FROM;
case TO_PLACE, INTERMEDIATE_PLACE -> GraphQLInputField.TO;
case TO_PLACE -> GraphQLInputField.TO;
case INTERMEDIATE_PLACE -> GraphQLInputField.VIA;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,7 @@ public enum GraphQLInputField {
DATE_TIME,
FROM,
TO,
VIA,
}

public static class GraphQLInputFiltersInput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ private static ViaLocation mapViaLocation(Map<String, Object> via) {
visit.getGraphQLLabel(),
visit.getGraphQLMinimumWaitTime(),
mapStopLocationIds(visit.getGraphQLStopLocationIds()),
mapCoordinate(visit.getGraphQLCoordinate()).map(List::of).orElse(List.of())
mapCoordinate(visit.getGraphQLCoordinate()).orElse(null)
);
} else {
throw new IllegalArgumentException("ViaLocation must define either pass-through or visit.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ private static List<FeedScopedId> mapStopLocationIds(Map<String, Object> map) {
return c == null ? List.of() : c.stream().map(TransitIdMapper::mapIDToDomain).toList();
}

private static List<WgsCoordinate> mapCoordinate(Map<String, Object> map) {
return CoordinateInputType.mapToWgsCoordinate(ViaLocationInputType.FIELD_COORDINATE, map)
.map(List::of)
.orElseGet(List::of);
@Nullable
private static WgsCoordinate mapCoordinate(Map<String, Object> map) {
return CoordinateInputType.mapToWgsCoordinate(
ViaLocationInputType.FIELD_COORDINATE,
map
).orElse(null);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
Expand Down Expand Up @@ -32,6 +33,8 @@
import org.opentripplanner.routing.framework.DebugTimingAggregator;
import org.opentripplanner.service.paging.PagingService;
import org.opentripplanner.standalone.api.OtpServerRequestContext;
import org.opentripplanner.street.search.TemporaryVerticesContainer;
import org.opentripplanner.street.search.request.FromToViaVertexRequest;
import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService;
import org.opentripplanner.utils.time.ServiceDateUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -97,20 +100,30 @@ public RoutingResponse route() {

var result = RoutingResult.empty();

if (OTPFeature.ParallelRouting.isOn()) {
// TODO: This is not using {@link OtpRequestThreadFactory} which means we do not get
// log-trace-parameters-propagation and graceful timeout handling here.
try {
var r1 = CompletableFuture.supplyAsync(this::routeDirectStreet);
var r2 = CompletableFuture.supplyAsync(this::routeDirectFlex);
var r3 = CompletableFuture.supplyAsync(this::routeTransit);

result.merge(r1.join(), r2.join(), r3.join());
} catch (CompletionException e) {
RoutingValidationException.unwrapAndRethrowCompletionException(e);
try (var temporaryVerticesContainer = createTemporaryVerticesContainer()) {
var requestVertexService = temporaryVerticesContainer.createFromToViaVertexRequest();

if (OTPFeature.ParallelRouting.isOn()) {
// TODO: This is not using {@link OtpRequestThreadFactory} which means we do not get
// log-trace-parameters-propagation and graceful timeout handling here.
try {
var r1 = CompletableFuture.supplyAsync(() -> routeDirectStreet(requestVertexService));
var r2 = CompletableFuture.supplyAsync(() -> routeDirectFlex(requestVertexService));
var r3 = CompletableFuture.supplyAsync(() -> routeTransit(requestVertexService));

result.merge(r1.join(), r2.join(), r3.join());
} catch (CompletionException e) {
RoutingValidationException.unwrapAndRethrowCompletionException(e);
}
} else {
result.merge(
routeDirectStreet(requestVertexService),
routeDirectFlex(requestVertexService),
routeTransit(requestVertexService)
);
}
} else {
result.merge(routeDirectStreet(), routeDirectFlex(), routeTransit());
} catch (RoutingValidationException e) {
result.merge(RoutingResult.failed(e.getRoutingErrors()));
}

// Set C2 value for Street and FLEX if transit-group-priority is used
Expand Down Expand Up @@ -208,7 +221,7 @@ private Duration searchWindowUsed() {
: Duration.ofSeconds(raptorSearchParamsUsed.searchWindowInSeconds());
}

private RoutingResult routeDirectStreet() {
private RoutingResult routeDirectStreet(FromToViaVertexRequest fromToViaVertexRequest) {
// TODO: Add support for via search to the direct-street search and remove this.
// The direct search is used to prune away silly transit results and it
// would be nice to also support via as a feature in the direct-street
Expand All @@ -232,31 +245,33 @@ private RoutingResult routeDirectStreet() {
debugTimingAggregator.startedDirectStreetRouter();
try {
return RoutingResult.ok(
DirectStreetRouter.route(serverContext, directBuilder.buildRequest()),
DirectStreetRouter.route(
serverContext,
directBuilder.buildRequest(),
fromToViaVertexRequest
),
emptyDirectModeHandler.removeWalkAllTheWayResults()
);
} catch (RoutingValidationException e) {
return RoutingResult.failed(e.getRoutingErrors());
} finally {
debugTimingAggregator.finishedDirectStreetRouter();
}
}

private RoutingResult routeDirectFlex() {
private RoutingResult routeDirectFlex(FromToViaVertexRequest fromToViaVertexRequest) {
if (!OTPFeature.FlexRouting.isOn()) {
return RoutingResult.ok(List.of());
}
debugTimingAggregator.startedDirectFlexRouter();
try {
return RoutingResult.ok(DirectFlexRouter.route(serverContext, request, additionalSearchDays));
} catch (RoutingValidationException e) {
return RoutingResult.failed(e.getRoutingErrors());
return RoutingResult.ok(
DirectFlexRouter.route(serverContext, request, additionalSearchDays, fromToViaVertexRequest)
);
} finally {
debugTimingAggregator.finishedDirectFlexRouter();
}
}

private RoutingResult routeTransit() {
private RoutingResult routeTransit(FromToViaVertexRequest fromToViaVertexRequest) {
debugTimingAggregator.startedTransitRouting();
try {
var transitResults = TransitRouter.route(
Expand All @@ -265,12 +280,11 @@ private RoutingResult routeTransit() {
transitGroupPriorityService,
transitSearchTimeZero,
additionalSearchDays,
debugTimingAggregator
debugTimingAggregator,
fromToViaVertexRequest
);
raptorSearchParamsUsed = transitResults.getSearchParams();
return RoutingResult.ok(transitResults.getItineraries());
} catch (RoutingValidationException e) {
return RoutingResult.failed(e.getRoutingErrors());
} finally {
debugTimingAggregator.finishedTransitRouter();
}
Expand All @@ -291,4 +305,39 @@ private PagingService createPagingService(List<Itinerary> itineraries) {
itineraries
);
}

private TemporaryVerticesContainer createTemporaryVerticesContainer() {
var fromModes = request.journey().transit().enabled()
? EnumSet.of(request.journey().access().mode())
: EnumSet.noneOf(StreetMode.class);
var toModes = request.journey().transit().enabled()
? EnumSet.of(request.journey().egress().mode())
: EnumSet.noneOf(StreetMode.class);
var viaModes = request.journey().transit().enabled()
? EnumSet.of(
request.journey().access().mode(),
request.journey().egress().mode(),
request.journey().transfer().mode()
)
: EnumSet.noneOf(StreetMode.class);
var directMode = resolveDirectMode();
if (directMode != StreetMode.NOT_SET) {
fromModes.add(directMode);
toModes.add(directMode);
viaModes.add(directMode);
}
return TemporaryVerticesContainer.of(serverContext.graph(), serverContext.vertexLinker())
.withFrom(request.from(), fromModes)
.withTo(request.to(), toModes)
.withVia(request.listVisitViaLocations(), viaModes)
.build();
}

private StreetMode resolveDirectMode() {
var emptyDirectModeHandler = new FilterTransitWhenDirectModeIsEmpty(
request.journey().direct().mode(),
request.pageCursor() != null
);
return emptyDirectModeHandler.resolveDirectMode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import org.opentripplanner.routing.framework.DebugTimingAggregator;
import org.opentripplanner.routing.via.ViaCoordinateTransferFactory;
import org.opentripplanner.standalone.api.OtpServerRequestContext;
import org.opentripplanner.street.search.TemporaryVerticesContainer;
import org.opentripplanner.street.search.request.FromToViaVertexRequest;
import org.opentripplanner.transit.model.framework.EntityNotFoundException;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService;
Expand All @@ -60,25 +60,26 @@ public class TransitRouter {
private final DebugTimingAggregator debugTimingAggregator;
private final ZonedDateTime transitSearchTimeZero;
private final AdditionalSearchDays additionalSearchDays;
private final TemporaryVerticesContainer temporaryVerticesContainer;
private final ViaCoordinateTransferFactory viaTransferResolver;
private final FromToViaVertexRequest fromToViaVertexRequest;

private TransitRouter(
RouteRequest request,
OtpServerRequestContext serverContext,
TransitGroupPriorityService transitGroupPriorityService,
ZonedDateTime transitSearchTimeZero,
AdditionalSearchDays additionalSearchDays,
DebugTimingAggregator debugTimingAggregator
DebugTimingAggregator debugTimingAggregator,
FromToViaVertexRequest fromToViaVertexRequest
) {
this.request = request;
this.serverContext = serverContext;
this.transitGroupPriorityService = transitGroupPriorityService;
this.transitSearchTimeZero = transitSearchTimeZero;
this.additionalSearchDays = additionalSearchDays;
this.debugTimingAggregator = debugTimingAggregator;
this.temporaryVerticesContainer = createTemporaryVerticesContainer(request, serverContext);
this.viaTransferResolver = serverContext.viaTransferResolver();
this.fromToViaVertexRequest = fromToViaVertexRequest;
}

public static TransitRouterResult route(
Expand All @@ -87,26 +88,20 @@ public static TransitRouterResult route(
TransitGroupPriorityService priorityGroupConfigurator,
ZonedDateTime transitSearchTimeZero,
AdditionalSearchDays additionalSearchDays,
DebugTimingAggregator debugTimingAggregator
DebugTimingAggregator debugTimingAggregator,
FromToViaVertexRequest fromToViaVertexRequest
) {
TransitRouter transitRouter = new TransitRouter(
request,
serverContext,
priorityGroupConfigurator,
transitSearchTimeZero,
additionalSearchDays,
debugTimingAggregator
debugTimingAggregator,
fromToViaVertexRequest
);

return transitRouter.routeAndCleanupAfter();
}

private TransitRouterResult routeAndCleanupAfter() {
// try(auto-close):
// Make sure we clean up graph by removing temp-edges from the graph before we exit.
try (temporaryVerticesContainer) {
return route();
}
return transitRouter.route();
}

private TransitRouterResult route() {
Expand Down Expand Up @@ -144,7 +139,8 @@ private TransitRouterResult route() {
accessEgresses.getEgresses(),
serverContext.meterRegistry(),
viaTransferResolver,
this::listStopIndexes
this::listStopIndexes,
fromToViaVertexRequest
);

// Route transit
Expand Down Expand Up @@ -272,12 +268,12 @@ private Collection<? extends RoutingAccessEgress> fetchAccessEgresses(AccessEgre

var nearbyStops = AccessEgressRouter.findAccessEgresses(
accessRequest,
temporaryVerticesContainer,
streetRequest,
serverContext.dataOverlayContext(accessRequest),
type,
durationLimit,
stopCountLimit
stopCountLimit,
fromToViaVertexRequest
);
var accessEgresses = AccessEgressMapper.mapNearbyStops(nearbyStops, type);
accessEgresses = timeshiftRideHailing(streetRequest, type, accessEgresses);
Expand All @@ -288,12 +284,12 @@ private Collection<? extends RoutingAccessEgress> fetchAccessEgresses(AccessEgre
if (OTPFeature.FlexRouting.isOn() && mode == StreetMode.FLEXIBLE) {
var flexAccessList = FlexAccessEgressRouter.routeAccessEgress(
accessRequest,
temporaryVerticesContainer,
serverContext,
additionalSearchDays,
serverContext.flexParameters(),
serverContext.dataOverlayContext(accessRequest),
type
type,
fromToViaVertexRequest
);

results.addAll(AccessEgressMapper.mapFlexAccessEgresses(flexAccessList, type));
Expand Down Expand Up @@ -375,20 +371,6 @@ private void checkIfTransitConnectionExists(RaptorResponse<TripSchedule> respons
}
}

private TemporaryVerticesContainer createTemporaryVerticesContainer(
RouteRequest request,
OtpServerRequestContext serverContext
) {
return new TemporaryVerticesContainer(
serverContext.graph(),
serverContext.vertexLinker(),
request.from(),
request.to(),
request.journey().access().mode(),
request.journey().egress().mode()
);
}

private IntStream listStopIndexes(FeedScopedId stopLocationId) {
Collection<StopLocation> stops = serverContext
.transitService()
Expand Down
Loading
Loading