Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5129f9f
refactor: Enable injecting stops in GraphRoutingTest
t2gran Nov 5, 2025
c71079e
refactor: Split DirectTransferGeneratorTest, extract CAR cases
t2gran Nov 5, 2025
35fb6ff
feat: add support for real-time used stops in transfer generation
t2gran Nov 5, 2025
b27bac7
refactor: Improve assertions in DirectTransferGeneratorTest
t2gran Nov 9, 2025
36e8853
refactor: Rename ts1 to stop PatternConsideringNearbyStopFinder
t2gran Nov 9, 2025
765b529
refactor: cleanup DirectTransferGeneratorTest
t2gran Nov 10, 2025
72bbd0b
refactor: improve comments and test names in DirectTransferGenerator …
t2gran Nov 11, 2025
397ecc6
refactor: Split PatternConsideringNearbyStopFinder into classes with …
t2gran Nov 11, 2025
b3cb1d7
refactor: Move DirectTransferGenerator(++) into o.o.graph_builder.mo…
t2gran Nov 11, 2025
626b07c
fix: Nearby-stops with transfers-not-allowed are removed after filter…
t2gran Nov 11, 2025
37cf8b9
fix: Critical error in StopPattern#canAlight(StopLocation), allways r…
t2gran Nov 11, 2025
e7be5cb
refactor: Simplify DirectTransferGenerator tests and introduce PathTr…
t2gran Nov 12, 2025
9700789
refactor: Sort OTPFeature alphabetically
t2gran Nov 11, 2025
f80117b
refactor: Correct spelling of CompositeNearbyStopFilter
t2gran Nov 11, 2025
4a4c785
refactor: Cleanup MinMap and add unit test
t2gran Nov 12, 2025
694710f
feat: Filter unused from stops in Transfers generation
t2gran Nov 12, 2025
32d09bc
refactor: Rename canBoard/Alight(StopLocation) to boarding/alightingE…
t2gran Nov 12, 2025
3df0b60
review: Fix spelling and other documentation issues
t2gran Nov 14, 2025
954ba62
Merge remote-tracking branch 'otp/dev-2.x' into fix-missing-tx
t2gran Nov 14, 2025
8dadd0b
test: Add unit tests for StopMapper functionality
t2gran Nov 17, 2025
5822cdd
feat: Enhance StopMapper to include sometimes-used-realtime-stops in …
t2gran Nov 17, 2025
bbadd27
review: Fix spelling errors and add JavaDoc comments for NearbyStopFi…
t2gran Nov 17, 2025
73a22ad
refactor: Correct spelling of 'Realtime' in IncludeStopsUsedRealTimeI…
t2gran Nov 18, 2025
c6f8673
Merge remote-tracking branch 'otp/dev-2.x' into fix-missing-tx
t2gran Nov 18, 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 @@ -20,8 +20,8 @@
import org.opentripplanner.TestServerContext;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.graph_builder.module.DirectTransferGenerator;
import org.opentripplanner.graph_builder.module.TestStreetLinkerModule;
import org.opentripplanner.graph_builder.module.transfer.DirectTransferGenerator;
import org.opentripplanner.gtfs.graphbuilder.GtfsBundleTestFactory;
import org.opentripplanner.gtfs.graphbuilder.GtfsModule;
import org.opentripplanner.model.GenericLocation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,6 @@ public enum OTPFeature {
),
APIServerInfo(true, false, "Enable the server info endpoint."),
APIUpdaterStatus(true, false, "Enable endpoint for graph updaters status."),
IncludeEmptyRailStopsInTransfers(
false,
false,
"""
Turning this on guarantees that Rail stops without scheduled departures still get included
when generating transfers using `ConsiderPatternsForDirectTransfers`. It is common for stops
to be assign at real-time for Rail. Turning this on will help to avoid dropping transfers which
are needed, when the stop is in use later. Turning this on, if
ConsiderPatternsForDirectTransfers is off has no effect.
"""
),
ConsiderPatternsForDirectTransfers(
true,
false,
Expand All @@ -54,6 +43,20 @@ public enum OTPFeature {
),
FloatingBike(true, false, "Enable floating bike routing."),
GtfsGraphQlApi(true, false, "Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md)."),
IncludeStopsUsedRealTimeInTransfers(
false,
false,
"""
When generating transfers, stops without any patterns are excluded to improve performance if
`ConsiderPatternsForDirectTransfers` is enabled. However, some stops are only used by trips
changed or added by real-time updates. Since transfer generation happens before real-time
updates are applied, OTP cannot know which stops will be needed. Instead, OTP will attempt to
identify stops likely to be used by real-time updates at import time. Common cases include rail
stops (which often have late platform assignments) and stops reserved for replacement services.
This is detected examining the stop `subMode`(NeTEx) and `vehicleType`(GTFS). This feature has
no effect if `ConsiderPatternsForDirectTransfers` is disabled.
"""
),
/**
* If this feature flag is switched on, then the minimum transfer time is not the minimum transfer
* time, but the definitive transfer time. Use this to override what we think the transfer will
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.opentripplanner.graph_builder.GraphBuilderDataSources;
import org.opentripplanner.graph_builder.configure.GraphBuilderModule;
import org.opentripplanner.graph_builder.issue.report.DataImportIssueReporter;
import org.opentripplanner.graph_builder.module.DirectTransferGenerator;
import org.opentripplanner.graph_builder.module.GraphCoherencyCheckerModule;
import org.opentripplanner.graph_builder.module.OsmBoardingLocationsModule;
import org.opentripplanner.graph_builder.module.RouteToCentroidStationIdsValidator;
Expand All @@ -33,6 +32,7 @@
import org.opentripplanner.graph_builder.module.islandpruning.PruneIslands;
import org.opentripplanner.graph_builder.module.ned.ElevationModule;
import org.opentripplanner.graph_builder.module.osm.OsmModule;
import org.opentripplanner.graph_builder.module.transfer.DirectTransferGenerator;
import org.opentripplanner.gtfs.graphbuilder.GtfsModule;
import org.opentripplanner.netex.NetexModule;
import org.opentripplanner.routing.fares.FareServiceFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.opentripplanner.graph_builder.issue.report.DataImportIssueReporter;
import org.opentripplanner.graph_builder.issue.service.DefaultDataImportIssueStore;
import org.opentripplanner.graph_builder.model.ConfiguredDataSource;
import org.opentripplanner.graph_builder.module.DirectTransferGenerator;
import org.opentripplanner.graph_builder.module.RouteToCentroidStationIdsValidator;
import org.opentripplanner.graph_builder.module.StreetLinkerModule;
import org.opentripplanner.graph_builder.module.TurnRestrictionModule;
Expand All @@ -33,6 +32,7 @@
import org.opentripplanner.graph_builder.module.ned.parameter.DemExtractParameters;
import org.opentripplanner.graph_builder.module.osm.OsmModule;
import org.opentripplanner.graph_builder.module.osm.parameters.OsmExtractParameters;
import org.opentripplanner.graph_builder.module.transfer.DirectTransferGenerator;
import org.opentripplanner.graph_builder.services.ned.ElevationGridCoverageFactory;
import org.opentripplanner.gtfs.graphbuilder.GtfsBundle;
import org.opentripplanner.gtfs.graphbuilder.GtfsModule;
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.opentripplanner.graph_builder.module;
package org.opentripplanner.graph_builder.module.transfer;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
Expand All @@ -15,12 +15,13 @@
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.graph_builder.issues.StopNotLinkedForTransfers;
import org.opentripplanner.graph_builder.model.GraphBuilderModule;
import org.opentripplanner.graph_builder.module.TransferParameters;
import org.opentripplanner.graph_builder.module.nearbystops.NearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.PatternConsideringNearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.SiteRepositoryResolver;
import org.opentripplanner.graph_builder.module.nearbystops.StopResolver;
import org.opentripplanner.graph_builder.module.nearbystops.StraightLineNearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinder;
import org.opentripplanner.graph_builder.module.transfer.filter.PatternConsideringNearbyStopFinder;
import org.opentripplanner.model.PathTransfer;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.StreetMode;
Expand Down Expand Up @@ -388,9 +389,6 @@ private void calculateDefaultTransfers(
if (sd.stop == stop) {
continue;
}
if (sd.stop.transfersNotAllowed()) {
continue;
}
createPathTransfer(stop, sd.stop, sd, distinctTransfers, mode);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.opentripplanner.graph_builder.module.transfer.filter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.transit.model.framework.FeedScopedId;

/**
* Combines multiple {@link NearbyStopFilter}s into a single filter.
* <p>
* This filter applies all configured filters and returns the union of their results. A stop is
* included if ANY of the component filters include it (OR logic for from-stops, union for
* to-stops).
*/
class CompositeNearbyStopFilter implements NearbyStopFilter {

private final List<NearbyStopFilter> filters;

private CompositeNearbyStopFilter(List<NearbyStopFilter> filters) {
this.filters = filters;
}

static Builder of() {
return new Builder();
}

@Override
public boolean includeFromStop(FeedScopedId id, boolean reverseDirection) {
for (NearbyStopFilter filter : filters) {
if (filter.includeFromStop(id, reverseDirection)) {
return true;
}
}
return false;
}

@Override
public Collection<NearbyStop> filterToStops(
Collection<NearbyStop> nearbyStops,
boolean reverseDirection
) {
Set<NearbyStop> result = new HashSet<>();

for (NearbyStopFilter it : filters) {
result.addAll(it.filterToStops(nearbyStops, reverseDirection));
}
return result;
}

static class Builder {

List<NearbyStopFilter> filters = new ArrayList<>();

Builder add(NearbyStopFilter filter) {
filters.add(filter);
return this;
}

NearbyStopFilter build() {
if (filters.size() == 1) {
return filters.getFirst();
}
return new CompositeNearbyStopFilter(filters);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.opentripplanner.graph_builder.module.transfer.filter;

import java.util.Collection;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.service.TransitService;

/**
* Filters nearby stops based on flex trip availability.
* <p>
* This filter ensures that transfers are generated for stops used by flex trips. For each flex
* trip, it keeps only the closest stop where the flex trip can board or alight (depending on
* direction).
*/
class FlexTripNearbyStopFilter implements NearbyStopFilter {

private final TransitService transitService;

FlexTripNearbyStopFilter(TransitService transitService) {
this.transitService = transitService;
}

@Override
public boolean includeFromStop(FeedScopedId id, boolean reverseDirection) {
var stop = transitService.getStopLocation(id);
return !transitService.getFlexIndex().getFlexTripsByStop(stop).isEmpty();
}

@Override
public Collection<NearbyStop> filterToStops(
Collection<NearbyStop> nearbyStops,
boolean reverseDirection
) {
MinMap<FlexTrip<?, ?>, NearbyStop> closestStopForFlexTrip = new MinMap<>();
for (var it : nearbyStops) {
var stop = it.stop;
var flexTrips = transitService.getFlexIndex().getFlexTripsByStop(stop);

for (FlexTrip<?, ?> trip : flexTrips) {
if (reverseDirection ? trip.isAlightingPossible(stop) : trip.isBoardingPossible(stop)) {
closestStopForFlexTrip.putMin(trip, it);
}
}
}
return closestStopForFlexTrip.values();
}
}
Loading
Loading