All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.graph_builder.module.AddTransitModelEntitiesToGraph Maven / Gradle / Ivy

There is a newer version: 2.5.0
Show newest version
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);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy