diff --git a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java index 7ac3dd1caf6..1e2dd3f2d4e 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java @@ -230,10 +230,18 @@ private Duration searchWindowUsed() { : Duration.ofSeconds(raptorSearchParamsUsed.searchWindowInSeconds()); } - private Void routeDirectStreet( + private List routeDirectStreet( List itineraries, Collection routingErrors ) { + // 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 + // search. + if (request.isViaSearch()) { + return null; + } + debugTimingAggregator.startedDirectStreetRouter(); try { itineraries.addAll(DirectStreetRouter.route(serverContext, request)); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/FilterTransitWhenDirectModeIsEmpty.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/FilterTransitWhenDirectModeIsEmpty.java index 01a8c9ab112..09431f0be19 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/FilterTransitWhenDirectModeIsEmpty.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/FilterTransitWhenDirectModeIsEmpty.java @@ -55,7 +55,7 @@ public class FilterTransitWhenDirectModeIsEmpty { private final StreetMode originalDirectMode; public FilterTransitWhenDirectModeIsEmpty(RequestModes modes) { - this.originalDirectMode = modes.directMode; + this(modes.directMode); } public FilterTransitWhenDirectModeIsEmpty(StreetMode originalDirectMode) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java index 9220756f8bb..dc602b2071f 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java @@ -7,6 +7,7 @@ import java.time.ZonedDateTime; import java.util.Collection; import java.util.List; +import java.util.function.Predicate; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.raptor.api.model.GeneralizedCostRelaxFunction; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; @@ -18,6 +19,7 @@ import org.opentripplanner.raptor.api.request.PassThroughPoint; import org.opentripplanner.raptor.api.request.RaptorRequest; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; +import org.opentripplanner.raptor.api.request.RaptorViaLocation; import org.opentripplanner.raptor.rangeraptor.SystemErrDebugLogger; import org.opentripplanner.routing.algorithm.raptoradapter.router.performance.PerformanceTimersForRaptor; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; @@ -82,6 +84,13 @@ private RaptorRequest doMap() { var preferences = request.preferences(); + // TODO Fix the Raptor search so pass-through and via search can be used together. + if (hasViaLocationsAndPassThroughLocations()) { + throw new IllegalArgumentException( + "A mix of via-locations and pass-through is not allowed in this versionP." + ); + } + if (request.pageCursor() == null) { int time = relativeTime(request.dateTime()); @@ -122,7 +131,7 @@ private RaptorRequest doMap() { // Note! If a pass-through-point exists, then the transit-group-priority feature is disabled // TODO - We need handle via locations that are not pass-through-points here - if (request.getViaLocations().stream().allMatch(ViaLocation::isPassThroughLocation)) { + if (hasPassThroughOnly()) { mcBuilder.withPassThroughPoints(mapPassThroughPoints()); r.relaxGeneralizedCostAtDestination().ifPresent(mcBuilder::withRelaxCostAtDestination); } else if (!pt.relaxTransitGroupPriority().isNormal()) { @@ -151,6 +160,10 @@ private RaptorRequest doMap() { .addAccessPaths(accessPaths) .addEgressPaths(egressPaths); + if (hasViaLocationsOnly()) { + builder.searchParams().addViaLocations(mapViaLocations()); + } + var raptorDebugging = request.journey().transit().raptorDebugging(); if (raptorDebugging.isEnabled()) { @@ -182,10 +195,48 @@ private RaptorRequest doMap() { ) ); } - return builder.build(); } + private boolean hasPassThroughOnly() { + return request.getViaLocations().stream().allMatch(ViaLocation::isPassThroughLocation); + } + + private boolean hasViaLocationsOnly() { + return request.getViaLocations().stream().noneMatch(ViaLocation::isPassThroughLocation); + } + + private boolean hasViaLocationsAndPassThroughLocations() { + var c = request.getViaLocations(); + return ( + request.isViaSearch() && + c.stream().anyMatch(ViaLocation::isPassThroughLocation) && + c.stream().anyMatch(Predicate.not(ViaLocation::isPassThroughLocation)) + ); + } + + private List mapViaLocations() { + return request.getViaLocations().stream().map(this::mapViaLocation).toList(); + } + + private RaptorViaLocation mapViaLocation(ViaLocation input) { + if (input.isPassThroughLocation()) { + var builder = RaptorViaLocation.allowPassThrough(input.label()); + for (int stopIndex : lookUpStopIndex.lookupStopLocationIndexes(input.stopLocationIds())) { + builder.addViaStop(stopIndex); + } + return builder.build(); + } + // Visit Via location + else { + var builder = RaptorViaLocation.via(input.label(), input.minimumWaitTime()); + for (int stopIndex : lookUpStopIndex.lookupStopLocationIndexes(input.stopLocationIds())) { + builder.addViaStop(stopIndex); + } + return builder.build(); + } + } + private List mapPassThroughPoints() { return request.getViaLocations().stream().map(this::mapPassThroughPoints).toList(); } diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index aa5253a2e94..328ecb73e52 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -279,6 +279,13 @@ public void setTo(GenericLocation to) { this.to = to; } + /** + * Return {@code true} if at least one via location is set! + */ + public boolean isViaSearch() { + return !via.isEmpty(); + } + public List getViaLocations() { return via; } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java index f716db062d4..87e90e61b2e 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Duration; import java.time.ZonedDateTime; import java.util.List; import java.util.Map; @@ -18,6 +19,7 @@ import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.routing.api.request.via.PassThroughViaLocation; +import org.opentripplanner.routing.api.request.via.VisitViaLocation; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.StopLocation; @@ -55,6 +57,24 @@ void mapRelaxCost(CostLinearFunction input, int cost, int expected) { assertEquals(expected, calcCost.relax(cost)); } + @Test + void testViaLocation() { + var req = new RouteRequest(); + var minWaitTime = Duration.ofMinutes(13); + + req.setViaLocations( + List.of(new VisitViaLocation("Via A", minWaitTime, List.of(STOP_A.getId()), List.of())) + ); + + var result = map(req); + + assertTrue(result.searchParams().hasViaLocations()); + assertEquals( + "[Via{label: Via A, minWaitTime: 13m, connections: [0 13m]}]", + result.searchParams().viaLocations().toString() + ); + } + @Test void testPassThroughPoints() { var req = new RouteRequest();