/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.routing.edgetype; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.StateEditor; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.vertextype.TransitStop; import org.opentripplanner.routing.vertextype.TransitStopDepart; /** * PreBoard edges connect a TransitStop to its agency_stop_depart vertices; PreAlight edges connect * an agency_stop_arrive vertex to its TransitStop. * * Applies the local stop rules (see TransitStop.java and LocalStopFinder.java) as well as transfer * limits, timed and preferred transfer rules, transfer penalties, and boarding costs. This avoids * applying these costs/rules repeatedly in (Pattern)Board edges. These are single station or * station-to-station specific costs, rather than trip-pattern specific costs. */ public class PreBoardEdge extends FreeEdge { private static final long serialVersionUID = -8046937388471651897L; public PreBoardEdge(TransitStop from, TransitStopDepart to) { super(from, to); if (!(from instanceof TransitStop)) throw new IllegalStateException("Preboard edges must lead out of a transit stop."); } @Override public State traverse(State s0) { RoutingRequest options = s0.getOptions(); // Ignore this edge if its stop is banned if (!options.getBannedStops().isEmpty() && fromv instanceof TransitStop) { if (options.getBannedStops().matches(((TransitStop) fromv).getStop())) { return null; } } if (options.isArriveBy()) { /* Traverse backward: not much to do */ StateEditor s1 = s0.edit(this); TransitStop fromVertex = (TransitStop) getFromVertex(); if (fromVertex.isLocal()) { s1.setAlightedLocal(true); } //apply board slack s1.incrementTimeInSeconds(options.getBoardSlack()); s1.alightTransit(); s1.setBackMode(getMode()); return s1.makeState(); } else { /* Traverse forward: apply stop(pair)-specific costs */ // Do not pre-board if transit modes are not selected. // Return null here rather than in StreetTransitLink so that walk-only // options can be used to find transit stops without boarding vehicles. if (!options.getModes().isTransit()) return null; TransitStop fromVertex = (TransitStop) getFromVertex(); // Do not board once one has alighted from a local stop if (fromVertex.isLocal() && s0.isEverBoarded()) { return null; } // If we've hit our transfer limit, don't go any further if (s0.getNumBoardings() > options.maxTransfers) return null; /* apply transfer rules */ /* * look in the global transfer table for the rules from the previous stop to this stop. */ long t0 = s0.getTimeSeconds(); long slack; if (s0.isEverBoarded()) { slack = options.getTransferSlack() - options.getAlightSlack(); } else { slack = options.getBoardSlack(); } long board_after = t0 + slack; long transfer_penalty = 0; // penalize transfers more heavily if requested by the user if (s0.isEverBoarded()) { // this is not the first boarding, therefore we must have "transferred" -- whether // via a formal transfer or by walking. transfer_penalty += options.transferPenalty; } StateEditor s1 = s0.edit(this); s1.setTimeSeconds(board_after); s1.setEverBoarded(true); long wait_cost = board_after - t0; s1.incrementWeight(wait_cost + transfer_penalty); s1.setBackMode(getMode()); return s1.makeState(); } } public TraverseMode getMode() { return TraverseMode.LEG_SWITCH; } public State optimisticTraverse(State s0) { // do not include minimum transfer time in heuristic weight // (it is path-dependent) StateEditor s1 = s0.edit(this); s1.setBackMode(getMode()); return s1.makeState(); } public String toString() { return "preboard edge at stop " + fromv; } }