org.opentripplanner.routing.graphfinder.PlaceFinderTraverseVisitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.routing.graphfinder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.opentripplanner.routing.algorithm.astar.TraverseVisitor;
import org.opentripplanner.routing.algorithm.astar.strategies.SkipEdgeStrategy;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.vehicle_parking.VehicleParking;
import org.opentripplanner.routing.vehicle_rental.VehicleRentalPlace;
import org.opentripplanner.routing.vertextype.TransitStopVertex;
import org.opentripplanner.routing.vertextype.VehicleParkingEntranceVertex;
import org.opentripplanner.routing.vertextype.VehicleRentalPlaceVertex;
import org.opentripplanner.transit.model.basic.TransitMode;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.service.TransitService;
/**
* A TraverseVisitor used in finding various types of places while walking the street graph.
*/
public class PlaceFinderTraverseVisitor implements TraverseVisitor {
public final List placesFound = new ArrayList<>();
private final TransitService transitService;
private final Set filterByModes;
private final Set filterByStops;
private final Set filterByRoutes;
private final Set filterByVehicleRental;
private final Set seenPatternAtStops = new HashSet<>();
private final Set seenStops = new HashSet<>();
private final Set seenVehicleRentalPlaces = new HashSet<>();
private final Set seenParkingLots = new HashSet<>();
private final boolean includeStops;
private final boolean includePatternAtStops;
private final boolean includeVehicleRentals;
private final boolean includeCarParking;
private final boolean includeBikeParking;
private final int maxResults;
private final double radiusMeters;
/**
* @param transitService A TransitService used in finding information about the
* various places.
* @param filterByModes A list of TransitModes for which to find Stops and
* PatternAtStops. Use null to disable the filtering.
* @param filterByPlaceTypes A list of PlaceTypes to search for. Use null to disable the
* filtering, and search for all types.
* @param filterByStops A list of Stop ids for which to find Stops and
* PatternAtStops. Use null to disable the filtering.
* @param filterByRoutes A list of Route ids used for filtering Stops. Only the stops
* which are served by the route are returned. Use null to
* disable the filtering.
* @param filterByBikeRentalStations A list of VehicleRentalStation ids to use in filtering. Use
* null to disable the filtering.
* @param maxResults Maximum number of results to return.
*/
public PlaceFinderTraverseVisitor(
TransitService transitService,
List filterByModes,
List filterByPlaceTypes,
List filterByStops,
List filterByRoutes,
List filterByBikeRentalStations,
int maxResults,
double radiusMeters
) {
this.transitService = transitService;
this.filterByModes = toSet(filterByModes);
this.filterByStops = toSet(filterByStops);
this.filterByRoutes = toSet(filterByRoutes);
this.filterByVehicleRental = toSet(filterByBikeRentalStations);
includeStops = shouldInclude(filterByPlaceTypes, PlaceType.STOP);
includePatternAtStops = shouldInclude(filterByPlaceTypes, PlaceType.PATTERN_AT_STOP);
includeVehicleRentals = shouldInclude(filterByPlaceTypes, PlaceType.VEHICLE_RENT);
includeCarParking = shouldInclude(filterByPlaceTypes, PlaceType.CAR_PARK);
includeBikeParking = shouldInclude(filterByPlaceTypes, PlaceType.BIKE_PARK);
this.maxResults = maxResults;
this.radiusMeters = radiusMeters;
}
@Override
public void visitEdge(Edge edge) {}
@Override
public void visitVertex(State state) {
Vertex vertex = state.getVertex();
double distance = state.getWalkDistance();
if (vertex instanceof TransitStopVertex transitVertex) {
RegularStop stop = transitVertex.getStop();
handleStop(stop, distance);
handlePatternsAtStop(stop, distance);
} else if (vertex instanceof VehicleRentalPlaceVertex rentalVertex) {
handleVehicleRental(rentalVertex.getStation(), distance);
} else if (vertex instanceof VehicleParkingEntranceVertex parkingVertex) {
handleParking(parkingVertex.getVehicleParking(), distance);
}
}
@Override
public void visitEnqueue() {}
/**
* @return A SkipEdgeStrategy to be used with this TraverseVisitor. It skips edges when either the
* maximum number of places or the furthest distance has been reached. However, when the maximum
* number of places has been reached, it continues searching along other paths until the distance
* of the place that is furthest away. This is to account for the fact that the a star does not
* traverse edges ordered by distance.
*/
public SkipEdgeStrategy getSkipEdgeStrategy() {
return (current, edge) -> {
double furthestDistance = radiusMeters;
if (
PlaceFinderTraverseVisitor.this.placesFound.size() >=
PlaceFinderTraverseVisitor.this.maxResults
) {
furthestDistance = 0;
for (PlaceAtDistance pad : PlaceFinderTraverseVisitor.this.placesFound) {
if (pad.distance() > furthestDistance) {
furthestDistance = pad.distance();
}
}
}
return current.getWalkDistance() > furthestDistance;
};
}
private static Set toSet(List list) {
if (list == null) {
return null;
}
return Set.copyOf(list);
}
private void handleParking(VehicleParking parking, double distance) {
if (!seenParkingLots.contains(parking.getId())) {
if (includeBikeParking && parking.hasBicyclePlaces()) {
placesFound.add(new PlaceAtDistance(parking, distance));
seenParkingLots.add(parking.getId());
}
// make sure that we don't add the same place twice if it has bike and car parking spaces
if (
includeCarParking && parking.hasAnyCarPlaces() && !seenParkingLots.contains(parking.getId())
) {
placesFound.add(new PlaceAtDistance(parking, distance));
seenParkingLots.add(parking.getId());
}
}
}
private boolean shouldInclude(List filterByPlaceTypes, PlaceType type) {
return filterByPlaceTypes == null || filterByPlaceTypes.contains(type);
}
private boolean stopHasRoutesWithMode(RegularStop stop, Set modes) {
return transitService
.getPatternsForStop(stop)
.stream()
.map(TripPattern::getMode)
.anyMatch(modes::contains);
}
private void handleStop(RegularStop stop, double distance) {
if (filterByStops != null && !filterByStops.contains(stop.getId())) {
return;
}
if (
includeStops &&
!seenStops.contains(stop.getId()) &&
(filterByModes == null || stopHasRoutesWithMode(stop, filterByModes))
) {
placesFound.add(new PlaceAtDistance(stop, distance));
seenStops.add(stop.getId());
}
}
private void handlePatternsAtStop(RegularStop stop, double distance) {
if (includePatternAtStops) {
List patterns = transitService
.getPatternsForStop(stop)
.stream()
.filter(pattern -> filterByModes == null || filterByModes.contains(pattern.getMode()))
.filter(pattern ->
filterByRoutes == null || filterByRoutes.contains(pattern.getRoute().getId())
)
.filter(pattern -> pattern.canBoard(stop))
.toList();
for (TripPattern pattern : patterns) {
String seenKey = pattern.getRoute().getId().toString() + ":" + pattern.getId().toString();
if (!seenPatternAtStops.contains(seenKey)) {
PatternAtStop row = new PatternAtStop(stop, pattern);
PlaceAtDistance place = new PlaceAtDistance(row, distance);
placesFound.add(place);
seenPatternAtStops.add(seenKey);
}
}
}
}
private void handleVehicleRental(VehicleRentalPlace station, double distance) {
if (!includeVehicleRentals) {
return;
}
if (filterByVehicleRental != null && !filterByVehicleRental.contains(station.getStationId())) {
return;
}
if (seenVehicleRentalPlaces.contains(station.getId())) {
return;
}
seenVehicleRentalPlaces.add(station.getId());
placesFound.add(new PlaceAtDistance(station, distance));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy