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

Draft: P&R performance #138

Draft
wants to merge 24 commits into
base: dev-2.x
Choose a base branch
from
Draft
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
7c15dff
Use heuristic to limit park+ride accesses
leonardehrenfried Feb 15, 2022
684a285
Finetune maxScore
leonardehrenfried Feb 15, 2022
a821b1d
Add Javadoc
leonardehrenfried Feb 15, 2022
ecdf088
Extend the included modes
leonardehrenfried Feb 15, 2022
75ed569
Increase the value for railway stops
leonardehrenfried Feb 16, 2022
20683ca
Use correct comment
leonardehrenfried Feb 17, 2022
b686750
Improve strategies for P+R, B+R
leonardehrenfried Feb 28, 2022
c25fd3a
Satisfy compiler
leonardehrenfried Feb 28, 2022
1200689
Resolve compilation error by replacing stream
leonardehrenfried Feb 28, 2022
093d499
Help compiler
leonardehrenfried Feb 28, 2022
f28db68
Merge remote-tracking branch 'upstream/dev-2.x' into park-and-ride-he…
leonardehrenfried Feb 28, 2022
24b36b5
Fix compilation errors
leonardehrenfried Feb 28, 2022
f905838
Increase car reluctance and change tests
leonardehrenfried Feb 28, 2022
4e77ab1
Merge branch 'park-and-ride-heuristics' into mfdz-park-and-ride
leonardehrenfried Feb 28, 2022
18fa219
Change build path
leonardehrenfried Feb 28, 2022
a9e93fc
Build docker image on branch
leonardehrenfried Feb 28, 2022
cec135b
Use Bike mode for P&R
leonardehrenfried Mar 1, 2022
3378c0b
Check for null in TemporaryStreetEdge, return empty list
leonardehrenfried Mar 1, 2022
ee08ebe
Improve performance of direct CAR_PARK
leonardehrenfried Mar 1, 2022
a2ffbab
Don't check for reverseDirection for bike + transit
leonardehrenfried Mar 1, 2022
6979f98
Use array instead of List
leonardehrenfried Mar 1, 2022
cff741a
Add Javadoc
leonardehrenfried Mar 1, 2022
683a417
Move feature behind a feature flag
leonardehrenfried Mar 1, 2022
dc0e947
Merge branch 'park-and-ride-heuristics' into mfdz-park-and-ride
leonardehrenfried Mar 1, 2022
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
3 changes: 2 additions & 1 deletion .github/workflows/cibuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
- master
- dev-1.x
- dev-2.x
- mfdz-park-and-ride
pull_request:
branches:
- master
Expand Down Expand Up @@ -44,7 +45,7 @@ jobs:
files: target/site/jacoco/jacoco.xml

- name: Publish to Dockerhub
if: ${{ github.repository_owner == 'mfdz' && github.ref == 'refs/heads/dev-2.x' }}
if: ${{ github.repository_owner == 'mfdz' && github.ref == 'refs/heads/mfdz-park-and-ride' }}
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: mfdz/opentripplanner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,9 @@ public RequestModes getRequestModes() {
accessMode = StreetMode.CAR_TO_PARK;
transferMode = StreetMode.WALK;
egressMode = StreetMode.WALK;
if(transitModes.isEmpty()) {
directMode = StreetMode.CAR_TO_PARK;
} else {
directMode = StreetMode.BIKE;
}
// CAR_TO_PARK is quite expensive to compute, so we only use it if there are no
// transit modes
directMode = transitModes.isEmpty() ? StreetMode.CAR_TO_PARK : StreetMode.BIKE;
} else if (requestMode.qualifiers.contains(Qualifier.PICKUP)) {
accessMode = StreetMode.WALK;
transferMode = StreetMode.WALK;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import com.beust.jcommander.internal.Sets;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import org.locationtech.jts.geom.Coordinate;
import org.opentripplanner.common.MinMap;
import org.opentripplanner.ext.flex.trip.FlexTrip;
Expand All @@ -13,9 +17,14 @@
import org.opentripplanner.model.StopLocation;
import org.opentripplanner.model.TripPattern;
import org.opentripplanner.routing.algorithm.astar.AStar;
import org.opentripplanner.routing.algorithm.astar.strategies.BikeToStopSkipEdgeStrategy;
import org.opentripplanner.routing.algorithm.astar.strategies.ComposingSkipEdgeStrategy;
import org.opentripplanner.routing.algorithm.astar.strategies.DurationSkipEdgeStrategy;
import org.opentripplanner.routing.algorithm.astar.strategies.SkipEdgeStrategy;
import org.opentripplanner.routing.algorithm.astar.strategies.TrivialRemainingWeightHeuristic;
import org.opentripplanner.routing.algorithm.astar.strategies.VehicleToStopSkipEdgeStrategy;
import org.opentripplanner.routing.api.request.RoutingRequest;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.edgetype.StreetEdge;
Expand All @@ -30,13 +39,6 @@
import org.opentripplanner.routing.vertextype.StreetVertex;
import org.opentripplanner.routing.vertextype.TransitStopVertex;
import org.opentripplanner.util.OTPFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
* These library functions are used by the streetless and streetful stop linkers, and in profile transfer generation.
Expand All @@ -49,14 +51,13 @@
*/
public class NearbyStopFinder {

private static final Logger LOG = LoggerFactory.getLogger(NearbyStopFinder.class);

public final boolean useStreets;
private final Graph graph;
private final double durationLimitInSeconds;

private DirectGraphFinder directGraphFinder;


/**
* Construct a NearbyStopFinder for the given graph and search radius, choosing whether to search via the street
* network or straight line distance based on the presence of OSM street data in the graph.
Expand Down Expand Up @@ -188,7 +189,9 @@ public List<NearbyStop> findNearbyStopsViaStreets (
routingRequest.dominanceFunction = new DominanceFunction.MinimumWeight();

var astar = new AStar();
astar.setSkipEdgeStrategy(new DurationSkipEdgeStrategy(durationLimitInSeconds));
var skipEdgeStrategy = getSkipEdgeStrategy(reverseDirection, routingRequest);
astar.setSkipEdgeStrategy(skipEdgeStrategy);

ShortestPathTree spt = astar.getShortestPathTree(routingRequest);

// Only used if OTPFeature.FlexRouting.isOn()
Expand Down Expand Up @@ -241,18 +244,34 @@ public List<NearbyStop> findNearbyStopsViaStreets (
return stopsFound;
}

public List<NearbyStop> findNearbyStopsViaStreets (
Set<Vertex> originVertices,
boolean reverseDirection,
boolean removeTempEdges
private SkipEdgeStrategy getSkipEdgeStrategy(
boolean reverseDirection,
RoutingRequest routingRequest
) {
RoutingRequest routingRequest = new RoutingRequest(TraverseMode.WALK);
return findNearbyStopsViaStreets(
originVertices,
reverseDirection,
removeTempEdges,
routingRequest
);
var durationSkipEdgeStrategy = new DurationSkipEdgeStrategy(durationLimitInSeconds);

// if we compute the accesses for Park+Ride, Bike+Ride and Bike+Transit we don't want to
// search the full durationLimit as this returns way too many stops.
// this is both slow and returns suboptimal results as it favours long drives with short
// transit legs.
// therefore, we use a heuristic based on the number of routes and their mode to determine
// what are "good" stops for those accesses. if we have reached a threshold of "good" stops
// we stop the access search.
if (!reverseDirection
&& OTPFeature.VehicleToStopHeuristics.isOn()
&& VehicleToStopSkipEdgeStrategy.applicableModes.contains(
routingRequest.modes.accessMode)) {
var strategy = new VehicleToStopSkipEdgeStrategy(graph.index::getRoutesForStop);
return new ComposingSkipEdgeStrategy(strategy, durationSkipEdgeStrategy);
}
else if (OTPFeature.VehicleToStopHeuristics.isOn()
&& routingRequest.modes.accessMode == StreetMode.BIKE) {
var strategy = new BikeToStopSkipEdgeStrategy(graph.index::getTripsForStop);
return new ComposingSkipEdgeStrategy(strategy, durationSkipEdgeStrategy);
}
else {
return durationSkipEdgeStrategy;
}
}

private boolean canBoardFlex(State state, boolean reverse) {
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/opentripplanner/model/TransitMode.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package org.opentripplanner.model;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Equivalent to GTFS route_type or to NeTEx TransportMode.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ class RunState {
public int nVisited;
public List<State> targetAcceptedStates;
public RunStatus status;
private RoutingRequest options;
private SearchTerminationStrategy terminationStrategy;
private final RoutingRequest options;
private final SearchTerminationStrategy terminationStrategy;
public Vertex u_vertex;

public RunState(RoutingRequest options, SearchTerminationStrategy terminationStrategy) {
Expand Down Expand Up @@ -164,7 +164,8 @@ boolean iterate(){
runState.rctx.fromVertices,
runState.rctx.toVertices,
runState.u,
edge,runState.spt,
edge,
runState.spt,
runState.options
)
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.opentripplanner.routing.algorithm.astar.strategies;

import java.util.Collection;
import java.util.Set;
import java.util.function.Function;
import org.opentripplanner.model.BikeAccess;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.Trip;
import org.opentripplanner.routing.api.request.RoutingRequest;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.spt.ShortestPathTree;
import org.opentripplanner.routing.vertextype.TransitStopVertex;

/**
* When wanting to take a bike onto transit we want to improve the performance by limiting the
* number of accesses to those stops which actually have trips where you can take the bike on.
* Once we have reached enough of trips we skip all further edges.
*/
public class BikeToStopSkipEdgeStrategy
implements SkipEdgeStrategy {

private static final int LIMIT = 100;
private static final double MAX_FACTOR = 1.2;

private final Function<Stop, Collection<Trip>> getTripsForStop;

int numberOfBikeableTripsReached = 0;
double distanceLimit = Double.MAX_VALUE;

public BikeToStopSkipEdgeStrategy(Function<Stop, Collection<Trip>> getTripsForStop) {
this.getTripsForStop = getTripsForStop;
}

@Override
public boolean shouldSkipEdge(
Set<Vertex> origins,
Set<Vertex> targets,
State current,
Edge edge,
ShortestPathTree spt,
RoutingRequest traverseOptions
) {
if (current.getVertex() instanceof TransitStopVertex && distanceLimit == Double.MAX_VALUE) {
var stopVertex = (TransitStopVertex) current.getVertex();
var stop = stopVertex.getStop();
numberOfBikeableTripsReached += getTripsForStop.apply(stop).stream().filter(
BikeToStopSkipEdgeStrategy::bikeAccessForTrip).count();
if (numberOfBikeableTripsReached >= LIMIT) {
distanceLimit = current.getWalkDistance() * MAX_FACTOR;
}
}
return current.getWalkDistance() > distanceLimit;
}


public static boolean bikeAccessForTrip(Trip trip) {
if (trip.getBikesAllowed() != BikeAccess.UNKNOWN) {
return trip.getBikesAllowed() == BikeAccess.ALLOWED;
}

return trip.getRoute().getBikesAllowed() == BikeAccess.ALLOWED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.opentripplanner.routing.algorithm.astar.strategies;

import java.util.Set;
import org.opentripplanner.routing.api.request.RoutingRequest;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.spt.ShortestPathTree;

/**
* Use several strategies in composition with each other, for example by limiting by time and number
* of stops visited. Only one needs to be skipped in order for {@link
* ComposingSkipEdgeStrategy#shouldSkipEdge(Set, Set, State, Edge, ShortestPathTree,
* RoutingRequest)} to return null.
*/
public class ComposingSkipEdgeStrategy implements SkipEdgeStrategy {

final SkipEdgeStrategy[] strategies;

public ComposingSkipEdgeStrategy(SkipEdgeStrategy... strategies) {
this.strategies = strategies;
}

@Override
public boolean shouldSkipEdge(
Set<Vertex> origins,
Set<Vertex> targets,
State current,
Edge edge,
ShortestPathTree spt,
RoutingRequest traverseOptions
) {
for (var strategy : strategies) {
if (strategy.shouldSkipEdge(origins, targets, current, edge, spt, traverseOptions)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.routing.api.request.RoutingRequest;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.VehicleRentalState;
import org.opentripplanner.routing.edgetype.FreeEdge;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Vertex;
Expand All @@ -19,14 +20,17 @@ public class EuclideanRemainingWeightHeuristic implements RemainingWeightHeurist
private double lat;
private double lon;
private double maxStreetSpeed;
private double walkingSpeed;
private boolean arriveBy;

// TODO This currently only uses the first toVertex. If there are multiple toVertices, it will
// not work correctly.
@Override
public void initialize(RoutingRequest options, long abortTime) {
RoutingRequest req = options;
public void initialize(RoutingRequest req, long abortTime) {
Vertex target = req.rctx.toVertices.iterator().next();
maxStreetSpeed = req.getStreetSpeedUpperBound();
walkingSpeed = req.walkSpeed;
arriveBy = req.arriveBy;

if (target.getDegreeIn() == 1) {
Edge edge = Iterables.getOnlyElement(target.getIncoming());
Expand All @@ -46,8 +50,13 @@ public void initialize(RoutingRequest options, long abortTime) {
public double estimateRemainingWeight (State s) {
Vertex sv = s.getVertex();
double euclideanDistance = SphericalDistanceLibrary.fastDistance(sv.getLat(), sv.getLon(), lat, lon);
// all travel is on-street, no transit involved
return euclideanDistance / maxStreetSpeed;
// After parking or finishing the rental of a vehicle, you can't ever move faster than walking speed.
final boolean useWalkSpeed = !arriveBy && (
s.isVehicleParked() || s.getVehicleRentalState() == VehicleRentalState.HAVE_RENTED
);

final double streetSpeed = useWalkSpeed ? walkingSpeed : maxStreetSpeed;
return euclideanDistance / streetSpeed;
}

@Override
Expand Down
Loading