diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java index e8b8ed43c1c..84f3030b09f 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java @@ -12,6 +12,7 @@ import java.util.function.Function; import javax.annotation.Nullable; import org.opentripplanner.ext.accessibilityscore.DecorateWithAccessibilityScore; +import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.collection.ListSection; import org.opentripplanner.framework.lang.Sandbox; import org.opentripplanner.model.plan.Itinerary; @@ -27,6 +28,8 @@ import org.opentripplanner.routing.algorithm.filterchain.filters.system.NumItinerariesFilter; import org.opentripplanner.routing.algorithm.filterchain.filters.system.OutsideSearchWindowFilter; import org.opentripplanner.routing.algorithm.filterchain.filters.system.PagingFilter; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax.McMaxLimitFilter; import org.opentripplanner.routing.algorithm.filterchain.filters.transit.DecorateTransitAlert; import org.opentripplanner.routing.algorithm.filterchain.filters.transit.KeepItinerariesWithFewestTransfers; import org.opentripplanner.routing.algorithm.filterchain.filters.transit.RemoveItinerariesWithShortStreetLeg; @@ -64,7 +67,6 @@ public class ItineraryListFilterChainBuilder { private static final int NOT_SET = -1; private final SortOrder sortOrder; private final List groupBySimilarity = new ArrayList<>(); - private ItineraryFilterDebugProfile debug = ItineraryFilterDebugProfile.OFF; private int maxNumberOfItineraries = NOT_SET; private ListSection maxNumberOfItinerariesCropSection = ListSection.TAIL; @@ -86,6 +88,7 @@ public class ItineraryListFilterChainBuilder { private double minBikeParkingDistance; private boolean removeTransitIfWalkingIsBetter = true; private ItinerarySortKey itineraryPageCut; + private boolean transitGroupPriorityUsed = false; /** * Sandbox filters which decorate the itineraries with extra information. @@ -292,6 +295,15 @@ public ItineraryListFilterChainBuilder withPagingDeduplicationFilter( return this; } + /** + * Adjust filters to include multi-criteria parameter c2 and treat it as the + * transit-group. + */ + public ItineraryListFilterChainBuilder withTransitGroupPriority() { + this.transitGroupPriorityUsed = true; + return this; + } + /** * If set, walk-all-the-way itineraries are removed. This happens AFTER e.g. the group-by and * remove-transit-with-higher-cost-than-best-on-street-only filter. This make sure that poor @@ -531,7 +543,7 @@ private ItineraryListFilter buildGroupBySameRoutesAndStopsFilter() { GroupBySameRoutesAndStops::new, List.of( new SortingFilter(SortOrderComparator.comparator(sortOrder)), - new RemoveFilter(new MaxLimit(GroupBySameRoutesAndStops.TAG, 1)) + new RemoveFilter(createMaxLimitFilter(GroupBySameRoutesAndStops.TAG, 1)) ) ); } @@ -574,7 +586,7 @@ private List buildGroupByTripIdAndDistanceFilters() { GroupByAllSameStations::new, List.of( new SortingFilter(generalizedCostComparator()), - new RemoveFilter(new MaxLimit(innerGroupName, 1)) + new RemoveFilter(createMaxLimitFilter(innerGroupName, 1)) ) ) ); @@ -587,7 +599,7 @@ private List buildGroupByTripIdAndDistanceFilters() { } addSort(nested, generalizedCostComparator()); - addRemoveFilter(nested, new MaxLimit(tag, group.maxNumOfItinerariesPerGroup)); + addRemoveFilter(nested, createMaxLimitFilter(tag, group.maxNumOfItinerariesPerGroup)); nested.add(new KeepItinerariesWithFewestTransfers(sysTags)); @@ -620,4 +632,20 @@ private static void addDecorateFilter( ) { filters.add(new DecorateFilter(decorator)); } + + private RemoveItineraryFlagger createMaxLimitFilter(String filterName, int maxLimit) { + if (OTPFeature.MultiCriteriaGroupMaxFilter.isOn()) { + List comparators = new ArrayList<>(); + comparators.add(SingeCriteriaComparator.compareGeneralizedCost()); + comparators.add(SingeCriteriaComparator.compareNumTransfers()); + if (transitGroupPriorityUsed) { + comparators.add(SingeCriteriaComparator.compareTransitGroupsPriority()); + } + return new McMaxLimitFilter(filterName, maxLimit, comparators); + } + // Default is to just use a "hard" max limit + else { + return new MaxLimit(filterName, maxLimit); + } + } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java index aa876d5bcdc..528542d40df 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java @@ -44,7 +44,7 @@ static SingeCriteriaComparator compareGeneralizedCost() { } @SuppressWarnings("OptionalGetWithoutIsPresent") - static SingeCriteriaComparator compareTransitPriorityGroups() { + static SingeCriteriaComparator compareTransitGroupsPriority() { return (left, right) -> TransitGroupPriority32n.dominate( left.getGeneralizedCost2().get(), diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java index 651d94b4eac..c1fab68f999 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java @@ -94,6 +94,10 @@ public static ItineraryListFilterChain createFilterChain( .withRemoveTransitIfWalkingIsBetter(true) .withDebugEnabled(params.debug()); + if (!request.preferences().transit().relaxTransitGroupPriority().isNormal()) { + builder.withTransitGroupPriority(); + } + var fareService = context.graph().getFareService(); if (fareService != null) { builder.withFareDecorator(new DecorateWithFare(fareService)); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java index 5435a8864c7..8f4b2b4ddeb 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java @@ -57,7 +57,7 @@ static void setUp() { void strictOrder() { assertTrue(SingeCriteriaComparator.compareNumTransfers().strictOrder()); assertTrue(SingeCriteriaComparator.compareGeneralizedCost().strictOrder()); - assertFalse(SingeCriteriaComparator.compareTransitPriorityGroups().strictOrder()); + assertFalse(SingeCriteriaComparator.compareTransitGroupsPriority().strictOrder()); } @Test @@ -99,7 +99,7 @@ void compareTransitPriorityGroups() { .withGeneralizedCost2(TransitGroupPriority32n.mergeInGroupId(1, 2)) .build(); - var subject = SingeCriteriaComparator.compareTransitPriorityGroups(); + var subject = SingeCriteriaComparator.compareTransitGroupsPriority(); assertTrue(subject.leftDominanceExist(group1, group2)); assertTrue(subject.leftDominanceExist(group2, group1)); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java index 14d0fe62337..3c35b0172f8 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java @@ -50,7 +50,7 @@ class McMaxLimitFilterTest { List.of( SingeCriteriaComparator.compareGeneralizedCost(), SingeCriteriaComparator.compareNumTransfers(), - SingeCriteriaComparator.compareTransitPriorityGroups() + SingeCriteriaComparator.compareTransitGroupsPriority() ) );