Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor NearbyStopsFinder to follow strategy pattern #5906

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -12,6 +12,10 @@
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.nearbystops.NearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.PatternConsideringNearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.StraightLineNearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinder;
import org.opentripplanner.model.PathTransfer;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.request.StreetRequest;
Expand Down Expand Up @@ -68,20 +72,7 @@ public void buildGraph() {
}

/* The linker will use streets if they are available, or straight-line distance otherwise. */
NearbyStopFinder nearbyStopFinder = new NearbyStopFinder(
new DefaultTransitService(transitModel),
radiusByDuration,
0,
null,
graph.hasStreets
);
if (nearbyStopFinder.useStreets) {
LOG.info("Creating direct transfer edges between stops using the street network from OSM...");
} else {
LOG.info(
"Creating direct transfer edges between stops using straight line distance (not streets)..."
);
}
NearbyStopFinder nearbyStopFinder = createNearbyStopFinder();

List<TransitStopVertex> stops = graph.getVerticesOfType(TransitStopVertex.class);

Expand Down Expand Up @@ -189,21 +180,39 @@ public void buildGraph() {
);
}

/**
* Factory method for creating a NearbyStopFinder. Will create different finders depending on
* whether the graph has a street network and if ConsiderPatternsForDirectTransfers feature is
* enabled.
*/
private NearbyStopFinder createNearbyStopFinder() {
var transitService = new DefaultTransitService(transitModel);
NearbyStopFinder finder;
if (!graph.hasStreets) {
LOG.info(
"Creating direct transfer edges between stops using straight line distance (not streets)..."
);
finder = new StraightLineNearbyStopFinder(transitService, radiusByDuration);
} else {
LOG.info("Creating direct transfer edges between stops using the street network from OSM...");
finder = new StreetNearbyStopFinder(radiusByDuration, 0, null);
}

if (OTPFeature.ConsiderPatternsForDirectTransfers.isOn()) {
return new PatternConsideringNearbyStopFinder(transitService, finder);
} else {
return finder;
}
}

private static Iterable<NearbyStop> findNearbyStops(
NearbyStopFinder nearbyStopFinder,
Vertex vertex,
RouteRequest request,
StreetRequest streetRequest,
boolean reverseDirection
) {
return OTPFeature.ConsiderPatternsForDirectTransfers.isOn()
? nearbyStopFinder.findNearbyStopsConsideringPatterns(
vertex,
request,
streetRequest,
reverseDirection
)
: nearbyStopFinder.findNearbyStops(vertex, request, streetRequest, reverseDirection);
return nearbyStopFinder.findNearbyStops(vertex, request, streetRequest, reverseDirection);
}

private record TransferKey(StopLocation source, StopLocation target, List<Edge> edges) {}
Expand Down
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.nearbystops;

import java.util.HashMap;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.opentripplanner.graph_builder.module.nearbystops;

import java.util.Collection;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.request.StreetRequest;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.street.model.vertex.Vertex;

/**
* Interface for finding nearby stops from a given vertex. It is used by access
* and egress searches, and in transfer generation.
*/
public interface NearbyStopFinder {
/**
* Return all stops within a certain distance from the given vertex.
*/
Collection<NearbyStop> findNearbyStops(
Vertex vertex,
RouteRequest routingRequest,
StreetRequest streetRequest,
boolean reverseDirection
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.opentripplanner.graph_builder.module.nearbystops;

import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.request.StreetRequest;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.street.model.vertex.Vertex;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.service.TransitService;

public class PatternConsideringNearbyStopFinder implements NearbyStopFinder {

private final NearbyStopFinder delegateNearbyStopFinder;
private final TransitService transitService;

public PatternConsideringNearbyStopFinder(
TransitService transitService,
NearbyStopFinder delegateNearbyStopFinder
) {
this.transitService = transitService;
this.delegateNearbyStopFinder = delegateNearbyStopFinder;
}

/**
* Find all unique nearby stops that are the closest stop on some trip pattern or flex trip. Note
* that the result will include the origin vertex if it is an instance of StopVertex. This is
* intentional: we don't want to return the next stop down the line for trip patterns that pass
* through the origin vertex. Taking the patterns into account reduces the number of transfers
* significantly compared to simple traverse-duration-constrained all-to-all stop linkage.
*/
@Override
public List<NearbyStop> findNearbyStops(
Vertex vertex,
RouteRequest routingRequest,
StreetRequest streetRequest,
boolean reverseDirection
) {
/* Track the closest stop on each pattern passing nearby. */
MinMap<TripPattern, NearbyStop> closestStopForPattern = new MinMap<>();

/* Track the closest stop on each flex trip nearby. */
MinMap<FlexTrip<?, ?>, NearbyStop> closestStopForFlexTrip = new MinMap<>();

/* Iterate over nearby stops via the street network or using straight-line distance. */
for (NearbyStop nearbyStop : delegateNearbyStopFinder.findNearbyStops(
vertex,
routingRequest,
streetRequest,
reverseDirection
)) {
StopLocation ts1 = nearbyStop.stop;

if (ts1 instanceof RegularStop) {
/* Consider this destination stop as a candidate for every trip pattern passing through it. */
for (TripPattern pattern : transitService.getPatternsForStop(ts1)) {
if (
reverseDirection
? pattern.canAlight(nearbyStop.stop)
: pattern.canBoard(nearbyStop.stop)
) {
closestStopForPattern.putMin(pattern, nearbyStop);
}
}
}

if (OTPFeature.FlexRouting.isOn()) {
for (FlexTrip<?, ?> trip : transitService.getFlexIndex().getFlexTripsByStop(ts1)) {
if (
reverseDirection
? trip.isAlightingPossible(nearbyStop.stop)
: trip.isBoardingPossible(nearbyStop.stop)
) {
closestStopForFlexTrip.putMin(trip, nearbyStop);
}
}
}
}

/* Make a transfer from the origin stop to each destination stop that was the closest stop on any pattern. */
Set<NearbyStop> uniqueStops = new HashSet<>();
uniqueStops.addAll(closestStopForFlexTrip.values());
uniqueStops.addAll(closestStopForPattern.values());
// TODO: don't convert to list
return uniqueStops.stream().toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.opentripplanner.graph_builder.module.nearbystops;

import java.time.Duration;
import java.util.List;
import org.locationtech.jts.geom.Coordinate;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.preference.WalkPreferences;
import org.opentripplanner.routing.api.request.request.StreetRequest;
import org.opentripplanner.routing.graphfinder.DirectGraphFinder;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.street.model.vertex.Vertex;
import org.opentripplanner.transit.service.TransitService;

public class StraightLineNearbyStopFinder implements NearbyStopFinder {

private final Duration durationLimit;
private final DirectGraphFinder directGraphFinder;

public StraightLineNearbyStopFinder(TransitService transitService, Duration durationLimit) {
this.durationLimit = durationLimit;

// We need to accommodate straight line distance (in meters) but when streets are present we
// use an earliest arrival search, which optimizes on time. Ideally we'd specify in meters,
// but we don't have much of a choice here. Use the default walking speed to convert.
this.directGraphFinder = new DirectGraphFinder(transitService::findRegularStops);
}

/**
* Find nearby stops using straight line distance.
*/
@Override
public List<NearbyStop> findNearbyStops(
Vertex vertex,
RouteRequest routingRequest,
StreetRequest streetRequest,
boolean reverseDirection
) {
return findNearbyStopsViaDirectTransfers(vertex);
}

private List<NearbyStop> findNearbyStopsViaDirectTransfers(Vertex vertex) {
// It make sense for the directGraphFinder to use meters as a limit, so we convert first
double limitMeters = durationLimit.toSeconds() * WalkPreferences.DEFAULT.speed();
Coordinate c0 = vertex.getCoordinate();
return directGraphFinder.findClosestStops(c0, limitMeters);
}
}
Loading
Loading