diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 8d84c7f0b03..5e892c06368 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -28,6 +28,7 @@ import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.alternativelegs.AlternativeLegs; import org.opentripplanner.routing.alternativelegs.AlternativeLegsFilter; +import org.opentripplanner.routing.alternativelegs.NavigationDirection; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.timetable.Trip; @@ -275,8 +276,17 @@ private Leg getSource(DataFetchingEnvironment environment) { return environment.getSource(); } + @Override + public DataFetcher> previousLegs() { + return alternativeLegs(NavigationDirection.PREVIOUS); + } + @Override public DataFetcher> nextLegs() { + return alternativeLegs(NavigationDirection.NEXT); + } + + private DataFetcher> alternativeLegs(NavigationDirection direction) { return environment -> { if (environment.getSource() instanceof ScheduledTransitLeg originalLeg) { var args = new GraphQLTypes.GraphQLLegNextLegsArgs(environment.getArguments()); @@ -311,7 +321,7 @@ public DataFetcher> nextLegs() { environment.getSource(), numberOfLegs, environment.getContext().transitService(), - false, + direction, AlternativeLegsFilter.NO_FILTER, limitToExactOriginStop, limitToExactDestinationStop diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 7532faf28bd..235b1d2bb14 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -503,6 +503,8 @@ public interface GraphQLLeg { public DataFetcher pickupType(); + public DataFetcher> previousLegs(); + public DataFetcher realTime(); public DataFetcher realtimeState(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 48d60701a96..675230876dc 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -1318,6 +1318,69 @@ public void setGraphQLOriginModesWithParentStation( } } + public static class GraphQLLegPreviousLegsArgs { + + private List destinationModesWithParentStation; + private Integer numberOfLegs; + private List originModesWithParentStation; + + public GraphQLLegPreviousLegsArgs(Map args) { + if (args != null) { + if (args.get("destinationModesWithParentStation") != null) { + this.destinationModesWithParentStation = + ((List) args.get("destinationModesWithParentStation")).stream() + .map(item -> + item instanceof GraphQLTransitMode + ? item + : GraphQLTransitMode.valueOf((String) item) + ) + .map(GraphQLTransitMode.class::cast) + .collect(Collectors.toList()); + } + this.numberOfLegs = (Integer) args.get("numberOfLegs"); + if (args.get("originModesWithParentStation") != null) { + this.originModesWithParentStation = + ((List) args.get("originModesWithParentStation")).stream() + .map(item -> + item instanceof GraphQLTransitMode + ? item + : GraphQLTransitMode.valueOf((String) item) + ) + .map(GraphQLTransitMode.class::cast) + .collect(Collectors.toList()); + } + } + } + + public List getGraphQLDestinationModesWithParentStation() { + return this.destinationModesWithParentStation; + } + + public Integer getGraphQLNumberOfLegs() { + return this.numberOfLegs; + } + + public List getGraphQLOriginModesWithParentStation() { + return this.originModesWithParentStation; + } + + public void setGraphQLDestinationModesWithParentStation( + List destinationModesWithParentStation + ) { + this.destinationModesWithParentStation = destinationModesWithParentStation; + } + + public void setGraphQLNumberOfLegs(Integer numberOfLegs) { + this.numberOfLegs = numberOfLegs; + } + + public void setGraphQLOriginModesWithParentStation( + List originModesWithParentStation + ) { + this.originModesWithParentStation = originModesWithParentStation; + } + } + public static class GraphQLLocalDateRangeInput { private java.time.LocalDate end; diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java index 5bf56f75e4b..4b405f50f66 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java @@ -33,6 +33,7 @@ import org.opentripplanner.model.plan.TransitLeg; import org.opentripplanner.model.plan.legreference.LegReferenceSerializer; import org.opentripplanner.routing.alternativelegs.AlternativeLegs; +import org.opentripplanner.routing.alternativelegs.NavigationDirection; public class LegType { @@ -485,7 +486,7 @@ public static GraphQLObjectType create( leg, env.getArgument("previous"), GqlUtil.getTransitService(env), - true, + NavigationDirection.PREVIOUS, env.getArgument("filter") ); }) @@ -525,7 +526,7 @@ public static GraphQLObjectType create( leg, env.getArgument("next"), GqlUtil.getTransitService(env), - false, + NavigationDirection.NEXT, env.getArgument("filter") ); }) diff --git a/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java b/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java index 14dc7ade982..51af4a27f79 100644 --- a/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java +++ b/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java @@ -45,18 +45,10 @@ public static List getAlternativeLegs( Leg leg, Integer numberLegs, TransitService transitService, - boolean searchBackward, + NavigationDirection direction, AlternativeLegsFilter filter ) { - return getAlternativeLegs( - leg, - numberLegs, - transitService, - searchBackward, - filter, - false, - false - ); + return getAlternativeLegs(leg, numberLegs, transitService, direction, filter, false, false); } /** @@ -66,9 +58,8 @@ public static List getAlternativeLegs( * @param numberLegs The number of alternative legs requested. If fewer legs are found, * only the found legs are returned. * @param transitService The transit service used for the search - * @param includeDepartBefore Boolean indicating whether the alternative legs should depart - * earlier or later than the original leg True if earlier, false if - * later. + * @param direction Indicating whether the alternative legs should depart before or + * after than the original. * @param filter AlternativeLegsFilter indicating which properties of the original * leg should not change in the alternative legs * @param exactOriginStop Boolean indicating whether the exact departure stop of the original @@ -82,7 +73,7 @@ public static List getAlternativeLegs( Leg leg, Integer numberLegs, TransitService transitService, - boolean includeDepartBefore, + NavigationDirection direction, AlternativeLegsFilter filter, boolean exactOriginStop, boolean exactDestinationStop @@ -105,7 +96,7 @@ public static List getAlternativeLegs( ScheduledTransitLeg::getStartTime ); - if (includeDepartBefore) { + if (direction == NavigationDirection.PREVIOUS) { legComparator = legComparator.reversed(); } @@ -119,13 +110,7 @@ public static List getAlternativeLegs( .distinct() .flatMap(tripPattern -> withBoardingAlightingPositions(origins, destinations, tripPattern)) .flatMap(t -> - generateLegs( - transitService, - t, - leg.getStartTime(), - leg.getServiceDate(), - includeDepartBefore - ) + generateLegs(transitService, t, leg.getStartTime(), leg.getServiceDate(), direction) ) .filter(Predicate.not(leg::isPartiallySameTransitLeg)) .sorted(legComparator) @@ -142,7 +127,7 @@ private static Stream generateLegs( TripPatternBetweenStops tripPatternBetweenStops, ZonedDateTime departureTime, LocalDate originalDate, - boolean includeDepartBefore + NavigationDirection direction ) { TripPattern pattern = tripPatternBetweenStops.tripPattern; int boardingPosition = tripPatternBetweenStops.positions.boardingPosition; @@ -155,7 +140,7 @@ private static Stream generateLegs( tts.getServiceDayMidnight() + tts.getRealtimeDeparture() ); - if (includeDepartBefore) { + if (direction == NavigationDirection.PREVIOUS) { comparator = comparator.reversed(); } @@ -185,7 +170,7 @@ private static Stream generateLegs( continue; } - boolean departureTimeInRange = includeDepartBefore + boolean departureTimeInRange = direction == NavigationDirection.PREVIOUS ? tripTimes.getDepartureTime(boardingPosition) <= secondsSinceMidnight : tripTimes.getDepartureTime(boardingPosition) >= secondsSinceMidnight; diff --git a/application/src/main/java/org/opentripplanner/routing/alternativelegs/NavigationDirection.java b/application/src/main/java/org/opentripplanner/routing/alternativelegs/NavigationDirection.java new file mode 100644 index 00000000000..8254e3e28b7 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/routing/alternativelegs/NavigationDirection.java @@ -0,0 +1,16 @@ +package org.opentripplanner.routing.alternativelegs; + +/** + * This enum describes how the user navigates on a list of items. + */ +public enum NavigationDirection { + /** + * Get the next set of items. + */ + NEXT, + + /** + * Get the previous set of items. + */ + PREVIOUS, +} diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index de1230b9eca..db2ef711e4a 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -722,6 +722,24 @@ type Leg { pickupBookingInfo: BookingInfo "This is used to indicate if boarding this leg is possible only with special arrangements." pickupType: PickupDropoffType + "Previous legs with same origin and destination stops or stations" + previousLegs( + """ + Transportation modes for which all stops in the parent station are used as possible destination stops + for the previous legs. For modes not listed, only the exact destination stop of the leg is considered. + """ + destinationModesWithParentStation: [TransitMode!], + """ + The number of alternative legs searched. If fewer than the requested number are found, + then only the found legs are returned. + """ + numberOfLegs: Int!, + """ + Transportation modes for which all stops in the parent station are used as possible origin stops + for the previous legs. For modes not listed, only the exact origin stop of the leg is considered. + """ + originModesWithParentStation: [TransitMode!] + ): [Leg!] "Whether there is real-time data about this Leg" realTime: Boolean "State of real-time data" diff --git a/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java b/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java index 055fba784a9..65e99825a9e 100644 --- a/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java +++ b/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java @@ -12,6 +12,7 @@ import org.opentripplanner.model.plan.legreference.ScheduledTransitLegReference; import org.opentripplanner.routing.alternativelegs.AlternativeLegs; import org.opentripplanner.routing.alternativelegs.AlternativeLegsFilter; +import org.opentripplanner.routing.alternativelegs.NavigationDirection; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.DefaultTransitService; @@ -51,7 +52,7 @@ void testPreviousLegs() { originalLeg, 3, transitService, - true, + NavigationDirection.PREVIOUS, AlternativeLegsFilter.NO_FILTER ); @@ -85,7 +86,7 @@ void testNextLegs() { originalLeg, 3, transitService, - false, + NavigationDirection.NEXT, AlternativeLegsFilter.NO_FILTER ); @@ -119,7 +120,7 @@ void testCircularRoutes() { originalLeg, 2, transitService, - false, + NavigationDirection.NEXT, AlternativeLegsFilter.NO_FILTER ); @@ -147,7 +148,7 @@ void testComplexCircularRoutes() { originalLeg, 2, transitService, - false, + NavigationDirection.NEXT, AlternativeLegsFilter.NO_FILTER ); var legs = toString(alternativeLegs);