org.opentripplanner.graph_builder.module.AddTransitModelEntitiesToGraph 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.graph_builder.module;
import static org.opentripplanner.common.geometry.SphericalDistanceLibrary.distance;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.gtfs.GtfsContext;
import org.opentripplanner.model.Agency;
import org.opentripplanner.model.BoardingArea;
import org.opentripplanner.model.Entrance;
import org.opentripplanner.model.FeedInfo;
import org.opentripplanner.model.FlexLocationGroup;
import org.opentripplanner.model.FlexStopLocation;
import org.opentripplanner.model.GroupOfStations;
import org.opentripplanner.model.MultiModalStation;
import org.opentripplanner.model.OtpTransitService;
import org.opentripplanner.model.Pathway;
import org.opentripplanner.model.PathwayNode;
import org.opentripplanner.model.Station;
import org.opentripplanner.model.StationElement;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.StopLocation;
import org.opentripplanner.model.TransitMode;
import org.opentripplanner.model.TripPattern;
import org.opentripplanner.model.WheelChairBoarding;
import org.opentripplanner.routing.edgetype.ElevatorAlightEdge;
import org.opentripplanner.routing.edgetype.ElevatorBoardEdge;
import org.opentripplanner.routing.edgetype.ElevatorHopEdge;
import org.opentripplanner.routing.edgetype.PathwayEdge;
import org.opentripplanner.routing.edgetype.StreetTraversalPermission;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.vertextype.ElevatorOffboardVertex;
import org.opentripplanner.routing.vertextype.ElevatorOnboardVertex;
import org.opentripplanner.routing.vertextype.TransitBoardingAreaVertex;
import org.opentripplanner.routing.vertextype.TransitEntranceVertex;
import org.opentripplanner.routing.vertextype.TransitPathwayNodeVertex;
import org.opentripplanner.routing.vertextype.TransitStopVertex;
import org.opentripplanner.util.I18NString;
import org.opentripplanner.util.NonLocalizedString;
import org.opentripplanner.util.OTPFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AddTransitModelEntitiesToGraph {
private static final Logger LOG = LoggerFactory.getLogger(AddTransitModelEntitiesToGraph.class);
private final GtfsFeedId feedId;
private final OtpTransitService transitService;
// Map of all station elements and their vertices in the graph
private final Map stationElementNodes = new HashMap<>();
private final int subwayAccessTime;
public static void addToGraph(GtfsContext context, Graph graph) {
new AddTransitModelEntitiesToGraph(context).applyToGraph(graph);
}
public static void addToGraph(
GtfsFeedId feedId, OtpTransitService transitModel, int subwayAccessTime, Graph graph
) {
new AddTransitModelEntitiesToGraph(feedId, transitModel, subwayAccessTime).applyToGraph(graph);
}
private AddTransitModelEntitiesToGraph(GtfsContext context) {
this(context.getFeedId(), context.getTransitService(), 0);
}
/**
* @param subwayAccessTime a positive integer for the extra time to access a subway platform, if negative the
* default value of zero is used.
*/
private AddTransitModelEntitiesToGraph(GtfsFeedId feedId, OtpTransitService transitModel, int subwayAccessTime) {
this.feedId = feedId;
this.transitService = transitModel;
this.subwayAccessTime = Math.max(subwayAccessTime, 0);
}
private void applyToGraph(Graph graph) {
addStopsToGraphAndGenerateStopVertexes(graph);
addStationsToGraph(graph);
addMultiModalStationsToGraph(graph);
addGroupsOfStationsToGraph(graph);
addEntrancesToGraph(graph);
addPathwayNodesToGraph(graph);
addBoardingAreasToGraph(graph);
// Although pathways are loaded from GTFS they are street data, so we will put them in the street graph.
createPathwayEdgesAndAddThemToGraph(graph);
if (OTPFeature.FlexRouting.isOn()) {
addLocationsToGraph(graph);
addLocationGroupsToGraph(graph);
}
addFeedInfoToGraph(graph);
addAgenciesToGraph(graph);
/* Interpret the transfers explicitly defined in transfers.txt. */
addTransfersToGraph(graph);
if (OTPFeature.FlexRouting.isOn()) {
addFlexTripsToGraph(graph);
}
}
private void addStopsToGraphAndGenerateStopVertexes(Graph graph) {
// Compute the set of modes for each stop based on all the TripPatterns it is part of
Map> stopModeMap = new HashMap<>();
for (TripPattern pattern : transitService.getTripPatterns()) {
TransitMode mode = pattern.getMode();
graph.addTransitMode(mode);
for (var stop : pattern.getStops()) {
Set set = stopModeMap.computeIfAbsent(stop, s -> new HashSet<>());
set.add(mode);
}
}
// Add a vertex representing the stop.
// It is now possible for these vertices to not be connected to any edges.
for (Stop stop : transitService.getAllStops()) {
Set modes = stopModeMap.get(stop);
TransitStopVertex stopVertex = new TransitStopVertex(graph, stop, modes);
if (modes != null && modes.contains(TransitMode.SUBWAY)) {
stopVertex.setStreetToStopTime(subwayAccessTime);
}
// Add stops to internal index for Pathways to be created from this map
stationElementNodes.put(stop, stopVertex);
}
}
private void addStationsToGraph(Graph graph) {
for (Station station : transitService.getAllStations()) {
graph.stationById.put(station.getId(), station);
}
}
private void addMultiModalStationsToGraph(Graph graph) {
for (MultiModalStation multiModalStation : transitService.getAllMultiModalStations()) {
graph.multiModalStationById.put(multiModalStation.getId(), multiModalStation);
}
}
private void addGroupsOfStationsToGraph(Graph graph) {
for (GroupOfStations groupOfStations : transitService.getAllGroupsOfStations()) {
graph.groupOfStationsById.put(groupOfStations.getId(), groupOfStations);
}
}
private void addEntrancesToGraph(Graph graph) {
for (Entrance entrance : transitService.getAllEntrances()) {
TransitEntranceVertex entranceVertex = new TransitEntranceVertex(graph, entrance);
stationElementNodes.put(entrance, entranceVertex);
}
}
private void addPathwayNodesToGraph(Graph graph) {
for (PathwayNode node : transitService.getAllPathwayNodes()) {
TransitPathwayNodeVertex nodeVertex = new TransitPathwayNodeVertex(graph, node);
stationElementNodes.put(node, nodeVertex);
}
}
private void addBoardingAreasToGraph(Graph graph) {
for (BoardingArea boardingArea : transitService.getAllBoardingAreas()) {
TransitBoardingAreaVertex boardingAreaVertex =
new TransitBoardingAreaVertex(graph, boardingArea);
stationElementNodes.put(boardingArea, boardingAreaVertex);
if (boardingArea.getParentStop() != null) {
var platformVertex = stationElementNodes.get(boardingArea.getParentStop());
boolean wheelchair = boardingArea.getWheelchairBoarding() == WheelChairBoarding.POSSIBLE;
var name = new NonLocalizedString(boardingArea.getName());
PathwayEdge.lowCost(
boardingAreaVertex,
platformVertex,
boardingArea.getId(),
name,
wheelchair
);
PathwayEdge.lowCost(
platformVertex,
boardingAreaVertex,
boardingArea.getId(),
name,
wheelchair
);
}
}
}
private void createPathwayEdgesAndAddThemToGraph(Graph graph) {
for (Pathway pathway : transitService.getAllPathways()) {
Vertex fromVertex = stationElementNodes.get(pathway.getFromStop());
Vertex toVertex = stationElementNodes.get(pathway.getToStop());
if (fromVertex != null && toVertex != null) {
// Elevator
if (pathway.getPathwayMode() == 5) {
createElevatorEdgesAndAddThemToGraph(graph, pathway, fromVertex, toVertex);
}
else {
// the GTFS spec allows you to define a pathway which has neither traversal time, distance
// nor steps. This would lead to traversal costs of 0, so we compute the distance from the
// vertices as fallback.
double distance = Optional.of(pathway.getLength())
.filter(l -> l > 0)
.orElseGet(() -> distance(fromVertex.getCoordinate(), toVertex.getCoordinate()));
new PathwayEdge(
fromVertex,
toVertex,
pathway.getId(),
NonLocalizedString.ofNullable(pathway.getName()),
pathway.getTraversalTime(),
distance,
pathway.getStairCount(),
pathway.getSlope(),
pathway.isPathwayModeWheelchairAccessible()
);
if (pathway.isBidirectional()) {
new PathwayEdge(
toVertex,
fromVertex,
pathway.getId(),
NonLocalizedString.ofNullable(pathway.getReversedName()),
pathway.getTraversalTime(),
distance,
-1 * pathway.getStairCount(),
-1 * pathway.getSlope(),
pathway.isPathwayModeWheelchairAccessible()
);
}
}
}
else {
if (fromVertex == null) {
LOG.warn("The 'fromVertex' is missing for pathway from stop {}", pathway.getFromStop());
}
if (toVertex == null) {
LOG.warn("The 'toVertex' is missing for pathway to stop {}", pathway.getToStop());
}
}
}
}
/**
* Create elevator edges from pathways. As pathway based elevators are not vertices, but edges
* in the pathway model, we have to model each possible movement as an onboard-offboard pair,
* instead of having only one set of vertices per level and edges between them.
*/
private void createElevatorEdgesAndAddThemToGraph(
Graph graph,
Pathway pathway,
Vertex fromVertex,
Vertex toVertex
) {
StationElement fromStation = fromVertex.getStationElement();
I18NString fromVertexLevelName = fromStation == null || fromStation.getLevelName() == null
? fromVertex.getName()
: new NonLocalizedString(fromStation.getLevelName());
Double fromVertexLevelIndex = fromStation == null ? null : fromStation.getLevelIndex();
StationElement toStation = toVertex.getStationElement();
I18NString toVertexLevelName = toStation == null || toStation.getLevelName() == null
? toVertex.getName()
: new NonLocalizedString(toStation.getLevelName());
Double toVertexLevelIndex = toStation == null ? null : toStation.getLevelIndex();
double levels = 1;
if (fromVertexLevelIndex != null && toVertexLevelIndex != null
&& !fromVertexLevelIndex.equals(toVertexLevelIndex)) {
levels = Math.abs(fromVertexLevelIndex - toVertexLevelIndex);
}
ElevatorOffboardVertex fromOffboardVertex = new ElevatorOffboardVertex(
graph,
fromVertex.getLabel() + "_" + pathway.getId() + "_offboard",
fromVertex.getX(),
fromVertex.getY(),
fromVertexLevelName
);
ElevatorOffboardVertex toOffboardVertex = new ElevatorOffboardVertex(
graph,
toVertex.getLabel() + "_" + pathway.getId() + "_offboard",
toVertex.getX(),
toVertex.getY(),
toVertexLevelName
);
PathwayEdge.lowCost(fromVertex, fromOffboardVertex, fromVertex.getName());
PathwayEdge.lowCost(toOffboardVertex, toVertex, toVertex.getName());
ElevatorOnboardVertex fromOnboardVertex = new ElevatorOnboardVertex(
graph,
fromVertex.getLabel() + "_" + pathway.getId() + "_onboard",
fromVertex.getX(),
fromVertex.getY(),
fromVertexLevelName
);
ElevatorOnboardVertex toOnboardVertex = new ElevatorOnboardVertex(
graph,
toVertex.getLabel() + "_" + pathway.getId() + "_onboard",
toVertex.getX(),
toVertex.getY(),
toVertexLevelName
);
new ElevatorBoardEdge(fromOffboardVertex, fromOnboardVertex);
new ElevatorAlightEdge(toOnboardVertex, toOffboardVertex, toVertexLevelName);
StreetTraversalPermission permission = StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE;
new ElevatorHopEdge(
fromOnboardVertex,
toOnboardVertex,
permission,
levels,
pathway.getTraversalTime()
);
if (pathway.isBidirectional()) {
PathwayEdge.lowCost(fromOffboardVertex, fromVertex, fromVertex.getName());
PathwayEdge.lowCost(toVertex, toOffboardVertex, toVertex.getName());
new ElevatorBoardEdge(toOffboardVertex, toOnboardVertex);
new ElevatorAlightEdge(
fromOnboardVertex,
fromOffboardVertex,
fromVertexLevelName
);
new ElevatorHopEdge(
toOnboardVertex,
fromOnboardVertex,
permission,
levels,
pathway.getTraversalTime()
);
}
}
private void addLocationsToGraph(Graph graph) {
for (FlexStopLocation flexStopLocation : transitService.getAllLocations()) {
graph.locationsById.put(flexStopLocation.getId(), flexStopLocation);
}
}
private void addLocationGroupsToGraph(Graph graph) {
for (FlexLocationGroup flexLocationGroup : transitService.getAllLocationGroups()) {
graph.locationGroupsById.put(flexLocationGroup.getId(), flexLocationGroup);
}
}
private void addFeedInfoToGraph(Graph graph) {
for (FeedInfo info : transitService.getAllFeedInfos()) {
graph.addFeedInfo(info);
}
}
private void addAgenciesToGraph(Graph graph) {
for (Agency agency : transitService.getAllAgencies()) {
graph.addAgency(feedId.getId(), agency);
}
}
private void addTransfersToGraph(Graph graph) {
graph.getTransferService().addAll(
transitService.getAllTransfers()
);
}
private void addFlexTripsToGraph(Graph graph) {
for(FlexTrip flexTrip : transitService.getAllFlexTrips())
graph.flexTripsById.put(flexTrip.getId(), flexTrip);
}
}