Ejemplo n.º 1
0
  @Override
  public State traverse(State s0) {
    EdgeNarrative en = new FixedModeEdge(this, s0.getNonTransitMode(s0.getOptions()));
    RoutingRequest options = s0.getOptions();

    if (options.wheelchairAccessible && !wheelchairAccessible) {
      return null;
    }

    TraverseMode mode = s0.getNonTransitMode(options);

    if (mode == TraverseMode.WALK && !permission.allows(StreetTraversalPermission.PEDESTRIAN)) {
      return null;
    }

    if (mode == TraverseMode.BICYCLE && !permission.allows(StreetTraversalPermission.BICYCLE)) {
      return null;
    }
    // there are elevators which allow cars
    if (mode == TraverseMode.CAR && !permission.allows(StreetTraversalPermission.CAR)) {
      return null;
    }

    StateEditor s1 = s0.edit(this, en);
    s1.incrementWeight(options.elevatorHopCost);
    s1.incrementTimeInSeconds(options.elevatorHopTime);
    return s1.makeState();
  }
Ejemplo n.º 2
0
 @Override
 public State traverse(State s0) {
   EdgeNarrative en = new FixedModeEdge(this, s0.getNonTransitMode(s0.getOptions()));
   StateEditor s1 = s0.edit(this, en);
   s1.incrementWeight(1);
   return s1.makeState();
 }
Ejemplo n.º 3
0
 public State optimisticTraverse(State state0) {
   int runningTime = getPattern().getBestRunningTime(stopIndex);
   StateEditor s1 = state0.edit(this);
   s1.incrementTimeInSeconds(runningTime);
   s1.setBackMode(getMode());
   s1.incrementWeight(runningTime);
   return s1.makeState();
 }
Ejemplo n.º 4
0
 public State traverse(State s0) {
   TripTimes tripTimes = s0.getTripTimes();
   int runningTime = tripTimes.getRunningTime(stopIndex);
   StateEditor s1 = s0.edit(this);
   s1.incrementTimeInSeconds(runningTime);
   if (s0.getOptions().isArriveBy()) s1.setZone(getStartStop().getZoneId());
   else s1.setZone(getEndStop().getZoneId());
   // s1.setRoute(pattern.getExemplar().getRoute().getId());
   s1.incrementWeight(runningTime);
   s1.setBackMode(getMode());
   return s1.makeState();
 }
    @SuppressWarnings("unchecked")
    public Void call() throws Exception {
      // LOG.debug("ORIGIN " + origin);
      int oi = stopIndices.get(origin); // origin index
      // first check for walking transfers
      // LOG.debug("    Walk");

      BinHeap<State> heap;
      try {
        heap = (BinHeap<State>) heapPool.borrowObject();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      BasicShortestPathTree spt = new BasicShortestPathTree(500000);
      State s0 = new State(origin, options);
      spt.add(s0);
      heap.insert(s0, s0.getWeight());
      while (!heap.empty()) {
        double w = heap.peek_min_key();
        State u = heap.extract_min();
        if (!spt.visit(u)) continue;
        Vertex uVertex = u.getVertex();
        // LOG.debug("heap extract " + u + " weight " + w);
        if (w > MAX_WEIGHT) break;
        if (uVertex instanceof TransitStop) {
          int di = stopIndices.get(uVertex); // dest index
          table[oi][di] = (float) w;
          // LOG.debug("    Dest " + u + " w=" + w);
        }
        for (Edge e : uVertex.getOutgoing()) {
          if (!(e instanceof PreBoardEdge)) {
            State v = e.optimisticTraverse(u);
            if (v != null && spt.add(v)) heap.insert(v, v.getWeight());
          }
        }
      }

      // then check what is accessible in one transit trip
      heap.reset(); // recycle heap
      spt = new BasicShortestPathTree(50000);
      // first handle preboard edges
      Queue<Vertex> q = new ArrayDeque<Vertex>(100);
      q.add(origin);
      while (!q.isEmpty()) {
        Vertex u = q.poll();
        for (Edge e : u.getOutgoing()) {
          if (e instanceof PatternBoard) {
            Vertex v = ((PatternBoard) e).getToVertex();
            // give onboard vertices same index as their
            // corresponding station
            stopIndices.put(v, oi);
            StateEditor se = (new State(u, options)).edit(e);
            se.incrementWeight(OPTIMISTIC_BOARD_COST);
            s0 = se.makeState();
            spt.add(s0);
            heap.insert(s0, s0.getWeight());
            // _log.debug("    board " + tov);
          } else if (e instanceof FreeEdge) { // handle preboard
            Vertex v = ((FreeEdge) e).getToVertex();
            // give onboard vertices same index as their
            // corresponding station
            stopIndices.put(v, oi);
            q.add(v);
          }
        }
      }
      // all boarding edges for this stop have now been traversed
      // LOG.debug("    Transit");
      while (!heap.empty()) {
        // check for transit stops when pulling off of heap
        // and continue when one is found
        // this is enough to prevent reboarding
        // need to mark closed vertices because otherwise cycles may
        // appear (interlining...)
        double w = heap.peek_min_key();
        State u = heap.extract_min();
        if (!spt.visit(u)) continue;
        // LOG.debug("    Extract " + u + " w=" + w);
        Vertex uVertex = u.getVertex();
        if (uVertex instanceof TransitStop) {
          int di = stopIndices.get(uVertex); // dest index
          if (table[oi][di] > w) {
            table[oi][di] = (float) w;
            // LOG.debug("    Dest " + u + "w=" + w);
          }
          continue;
        }
        for (Edge e : uVertex.getOutgoing()) {
          // LOG.debug("        Edge " + e);
          State v = e.optimisticTraverse(u);
          if (v != null && spt.add(v)) heap.insert(v, v.getWeight());
          // else LOG.debug("        (skip)");
        }
      }
      heapPool.returnObject(heap);
      incrementCount();
      return null;
    }
Ejemplo n.º 6
0
  public State traverse(State state0) {
    RoutingContext rctx = state0.getContext();
    RoutingRequest options = state0.getOptions();
    Trip trip = pattern.getTrip();

    if (options.isArriveBy()) {
      /* reverse traversal, not so much to do */
      // do not alight immediately when arrive-depart dwell has been eliminated
      // this affects multi-itinerary searches
      if (state0.getBackEdge() instanceof TransitBoardAlight
          && !((TransitBoardAlight) state0.getBackEdge()).isBoarding()) {
        return null;
      }
      StateEditor s1 = state0.edit(this);
      int type = pattern.getBoardType(stopIndex);
      if (TransitUtils.handleBoardAlightType(s1, type)) {
        return null;
      }
      s1.setTripId(null);
      s1.setLastAlightedTime(state0.getTime());
      s1.setBackMode(TraverseMode.BOARDING);
      s1.setPreviousStop(fromv);
      return s1.makeState();
    } else {
      /* forward traversal: look for a transit trip on this pattern */
      if (!options.getModes().get(modeMask)) {
        return null;
      }
      /* find next boarding time */
      /*
       * check lists of transit serviceIds running yesterday, today, and tomorrow (relative to
       * initial state) if this pattern's serviceId is running look for the next boarding time
       * choose the soonest boarding time among trips starting yesterday, today, or tomorrow
       */
      long currentTime = state0.getTime();
      int bestWait = -1;
      TraverseMode mode = state0.getNonTransitMode();
      if (options.bannedTrips.containsKey(trip.getId())) {
        // see comment in FrequencyAlight for details
        return null;
      }
      for (ServiceDay sd : rctx.serviceDays) {
        int secondsSinceMidnight = sd.secondsSinceMidnight(currentTime);
        // only check for service on days that are not in the future
        // this avoids unnecessarily examining tomorrow's services
        if (secondsSinceMidnight < 0) continue;
        if (sd.serviceIdRunning(serviceId)) {
          int startTime =
              pattern.getNextDepartureTime(
                  stopIndex,
                  secondsSinceMidnight,
                  options.wheelchairAccessible,
                  mode == TraverseMode.BICYCLE,
                  true);
          if (startTime >= 0) {
            // a trip was found, wait will be non-negative

            int wait = (int) (sd.time(startTime) - currentTime);
            if (wait < 0) _log.error("negative wait time on board");
            if (bestWait < 0 || wait < bestWait) {
              // track the soonest departure over all relevant schedules
              bestWait = wait;
            }
          }
        }
      }
      if (bestWait < 0) {
        return null;
      }

      /* check if trip is banned for this plan */
      if (options.tripIsBanned(trip)) return null;

      /* check if route is preferred for this plan */
      long preferences_penalty = options.preferencesPenaltyForTrip(trip);

      StateEditor s1 = state0.edit(this);
      int type = pattern.getBoardType(stopIndex);
      if (TransitUtils.handleBoardAlightType(s1, type)) {
        return null;
      }
      s1.incrementTimeInSeconds(bestWait);
      s1.incrementNumBoardings();
      s1.setTripId(trip.getId());
      s1.setZone(pattern.getZone(stopIndex));
      s1.setRoute(trip.getRoute().getId());
      s1.setBackMode(TraverseMode.BOARDING);

      long wait_cost = bestWait;
      if (state0.getNumBoardings() == 0) {
        wait_cost *= options.waitAtBeginningFactor;
      } else {
        wait_cost *= options.waitReluctance;
      }
      s1.incrementWeight(preferences_penalty);
      s1.incrementWeight(wait_cost + options.getBoardCost(mode));
      return s1.makeState();
    }
  }
Ejemplo n.º 7
0
  @Override
  public State traverse(State s0) {
    RoutingRequest options = s0.getOptions();

    // Ignore this edge if its stop is banned
    if (!options.bannedStops.isEmpty()) {
      if (options.bannedStops.matches(((TransitStop) fromv).getStop())) {
        return null;
      }
    }
    if (!options.bannedStopsHard.isEmpty()) {
      if (options.bannedStopsHard.matches(((TransitStop) fromv).getStop())) {
        return null;
      }
    }

    if (options.arriveBy) {
      /* Traverse backward: not much to do */
      StateEditor s1 = s0.edit(this);
      TransitStop fromVertex = (TransitStop) getFromVertex();

      // apply board slack
      s1.incrementTimeInSeconds(options.boardSlack);
      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.modes.isTransit()) 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.transferSlack - options.alightSlack;
      } else {
        slack = options.boardSlack;
      }
      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);
      long wait_cost = board_after - t0;
      s1.incrementWeight(wait_cost + transfer_penalty);
      s1.setBackMode(getMode());
      return s1.makeState();
    }
  }
Ejemplo n.º 8
0
  /**
   * @param options
   * @param walkOptions
   * @param nBoardings
   * @param createdStates
   * @return whether search should continue
   */
  public boolean walkPhase(
      RoutingRequest options,
      RoutingRequest walkOptions,
      int nBoardings,
      List<RaptorState> createdStates) {

    double distanceToNearestTransitStop = 0;
    if (options.rctx.target != null) {
      distanceToNearestTransitStop = options.rctx.target.getDistanceToNearestTransitStop();
    }

    final int boardSlack =
        nBoardings == 1
            ? options.getBoardSlack()
            : (options.getTransferSlack() - options.getAlightSlack());
    ShortestPathTree spt;
    GenericDijkstra dijkstra = new GenericDijkstra(walkOptions);
    dijkstra.setShortestPathTreeFactory(bounder);
    List<State> transitStopStates = new ArrayList<State>();

    if (nBoardings == 0) {
      // TODO: retry min-time bounding with this and with maxtime

      if (options.rctx.target != null
          && bounder.getTargetDistance(options.rctx.origin) < options.getMaxWalkDistance())
        dijkstra.setHeuristic(bounder);

      MaxWalkState start = new MaxWalkState(options.rctx.origin, walkOptions);
      spt = dijkstra.getShortestPathTree(start);
      for (State state : spt.getAllStates()) {
        if (state.getVertex() instanceof TransitStop
            || state.getVertex() instanceof TransitStopArrive
            || state.getVertex() instanceof TransitStopDepart) transitStopStates.add(state);
      }
      // also, compute an initial spt from the target so that we can find out what transit
      // stops are nearby and what
      // the time is to them, so that we can start target bounding earlier
      if (maxTimeDayIndex > 0) {
        RoutingRequest reversedWalkOptions = walkOptions.clone();
        reversedWalkOptions.setArriveBy(!walkOptions.isArriveBy());
        GenericDijkstra destDijkstra = new GenericDijkstra(reversedWalkOptions);
        start = new MaxWalkState(options.rctx.target, reversedWalkOptions);
        ShortestPathTree targetSpt = destDijkstra.getShortestPathTree(start);
        for (State state : targetSpt.getAllStates()) {

          final Vertex vertex = state.getVertex();

          if (!(vertex instanceof TransitStop)) continue;
          RaptorStop stop = data.raptorStopsForStopId.get(((TransitStop) vertex).getStopId());
          if (stop == null) {
            // we have found a stop is totally unused, so skip it
            continue;
          }

          // Skip banned stops
          if (options.getBannedStops().matches(stop.stopVertex.getStop())) {
            continue;
          }
          if (options.getBannedStopsHard().matches(stop.stopVertex.getStop())) {
            continue;
          }

          addStopNearTarget(stop, state.getWalkDistance(), (int) state.getElapsedTimeSeconds());
        }
      }
    } else {

      final List<MaxWalkState> startPoints = new ArrayList<MaxWalkState>();

      for (RaptorState state : createdStates) {

        // bounding states
        // this reduces the number of initial vertices
        // and the state space size

        Vertex stopVertex =
            options.isArriveBy() ? state.stop.departVertex : state.stop.arriveVertex;
        if (stopVertex == null) {
          stopVertex = state.stop.stopVertex;
        }

        if (options.rctx.target != null) {
          double minWalk = distanceToNearestTransitStop;
          double targetDistance = bounder.getTargetDistance(stopVertex);

          if (targetDistance + state.walkDistance > options.getMaxWalkDistance()) {
            // can't walk to destination, so we can't alight at a local vertex
            if (state.stop.stopVertex.isLocal()) continue;
          }

          if (minWalk + state.walkDistance > options.getMaxWalkDistance()) {
            continue;
          }
        }

        StateEditor dijkstraState = new MaxWalkState.MaxWalkStateEditor(walkOptions, stopVertex);
        dijkstraState.setInitialWaitTimeSeconds(state.initialWaitTime);
        dijkstraState.setStartTimeSeconds(options.dateTime);
        dijkstraState.setNumBoardings(state.nBoardings);
        dijkstraState.setWalkDistance(state.walkDistance);
        dijkstraState.setTimeSeconds(state.arrivalTime);
        dijkstraState.setExtension("raptorParent", state);
        dijkstraState.setOptions(walkOptions);
        dijkstraState.incrementWeight(state.weight);
        MaxWalkState newState = (MaxWalkState) dijkstraState.makeState();
        startPoints.add(newState);
      }
      if (startPoints.size() == 0) {
        return false;
      }
      System.out.println("walk starts: " + startPoints.size() + " / " + visitedEver.size());
      dijkstra.setPriorityQueueFactory(
          new PrefilledPriorityQueueFactory(startPoints.subList(1, startPoints.size())));

      bounder.addSptStates(startPoints.subList(1, startPoints.size()));

      bounder.prepareForSearch();

      dijkstra.setSearchTerminationStrategy(bounder);
      if (options.rctx.target != null) {
        dijkstra.setSkipTraverseResultStrategy(bounder);
        dijkstra.setHeuristic(bounder);
      }

      // Do local search
      spt = dijkstra.getShortestPathTree(startPoints.get(0));
      transitStopStates = bounder.getTransitStopsVisited();
    }

    List<? extends State> targetStates = null;
    if (walkOptions.rctx.target != null) targetStates = spt.getStates(walkOptions.rctx.target);
    if (targetStates != null) {
      TARGET:
      for (State targetState : targetStates) {
        RaptorState parent = (RaptorState) targetState.getExtension("raptorParent");
        RaptorState state;
        if (parent != null) {
          state = new RaptorState(parent);
          state.nBoardings = parent.nBoardings;
          state.rentingBike = targetState.isBikeRenting();
        } else {
          state = new RaptorState(options);
        }
        state.weight = targetState.getWeight();
        state.walkDistance = targetState.getWalkDistance();
        state.arrivalTime = (int) targetState.getTimeSeconds();
        state.walkPath = targetState;
        for (Iterator<RaptorState> it = getTargetStates().iterator(); it.hasNext(); ) {
          RaptorState oldState = it.next();
          if (oldState.eDominates(state)) {
            continue TARGET;
          } else if (state.eDominates(oldState)) {
            it.remove();
          }
        }
        addTargetState(state);
        log.debug("Found target at: " + state + " on " + state.getTrips());
      }
    }
    for (State state : bounder.removedBoundingStates) {
      removeTargetState(state);
    }

    SPTSTATE:
    for (State state : transitStopStates) {
      final Vertex vertex = state.getVertex();

      RaptorStop stop = data.raptorStopsForStopId.get(((OffboardVertex) vertex).getStopId());
      if (stop == null) {
        // we have found a stop is totally unused, so skip it
        continue;
      }

      // Skip banned stops
      if (options.getBannedStops().matches(stop.stopVertex.getStop())) {
        continue;
      }
      if (options.getBannedStopsHard().matches(stop.stopVertex.getStop())) {
        continue;
      }

      if (options.rctx.target != null) {
        double minWalk = distanceToNearestTransitStop;

        double targetDistance = bounder.getTargetDistance(vertex);
        final double remainingWalk = options.maxWalkDistance - state.getWalkDistance();

        if (maxTimeDayIndex > 0 && remainingWalk < 3218) {
          double minTime =
              (targetDistance - minWalk) / Raptor.MAX_TRANSIT_SPEED
                  + minWalk / options.getStreetSpeedUpperBound();
          if (targetDistance > remainingWalk) minTime += boardSlack;

          int maxTimeForVertex = 0;
          int region = vertex.getGroupIndex();
          final int elapsedTime = (int) state.getElapsedTimeSeconds();
          for (StopNearTarget stopNearTarget : stopsNearTarget.values()) {
            int destinationRegion = stopNearTarget.stop.stopVertex.getGroupIndex();
            final int maxTimeFromThisRegion =
                data.maxTransitRegions.maxTransit[maxTimeDayIndex][destinationRegion][region];
            int maxTime = elapsedTime + maxTimeFromThisRegion + stopNearTarget.time;

            if (maxTime > maxTimeForVertex) {
              maxTimeForVertex = maxTime;
            }
          }
          if (maxTimeForVertex < maxTime) {
            maxTime = maxTimeForVertex;
          } else {
            if (elapsedTime + minTime > maxTime * 1.5) {
              continue;
            }
          }
        }
      }
      List<RaptorState> states = statesByStop[stop.index];
      if (states == null) {
        states = new ArrayList<RaptorState>();
        statesByStop[stop.index] = states;
      }

      RaptorState parent = (RaptorState) state.getExtension("raptorParent");
      RaptorState newState;
      if (parent != null) {
        newState = new RaptorState(parent);
      } else {
        // this only happens in round 0
        newState = new RaptorState(options);
      }
      newState.weight = state.getWeight();
      newState.nBoardings = nBoardings;
      newState.walkDistance = state.getWalkDistance();
      newState.arrivalTime = (int) state.getTimeSeconds();
      newState.walkPath = state;
      newState.stop = stop;
      newState.rentingBike = state.isBikeRenting();

      for (RaptorState oldState : states) {
        if (oldState.eDominates(newState)) {
          continue SPTSTATE;
        }
      }

      visitedLastRound.add(stop);
      visitedEver.add(stop);
      states.add(newState);
    }
    return true;
  }
  /**
   * return a StateEditor rather than a State so that we can make parking/mode switch modifications
   * for kiss-and-ride.
   */
  private StateEditor doTraverse(State s0, RoutingRequest options, TraverseMode traverseMode) {
    boolean walkingBike = options.walkingBike;
    boolean backWalkingBike = s0.isBackWalkingBike();
    TraverseMode backMode = s0.getBackMode();
    Edge backEdge = s0.getBackEdge();
    if (backEdge != null) {
      // No illegal U-turns.
      // NOTE(flamholz): we check both directions because both edges get a chance to decide
      // if they are the reverse of the other. Also, because it doesn't matter which direction
      // we are searching in - these traversals are always disallowed (they are U-turns in one
      // direction
      // or the other).
      // TODO profiling indicates that this is a hot spot.
      if (this.isReverseOf(backEdge) || backEdge.isReverseOf(this)) {
        return null;
      }
    }

    // Ensure we are actually walking, when walking a bike
    backWalkingBike &= TraverseMode.WALK.equals(backMode);
    walkingBike &= TraverseMode.WALK.equals(traverseMode);

    /* Check whether this street allows the current mode. If not and we are biking, attempt to walk the bike. */
    if (!canTraverse(options, traverseMode)) {
      if (traverseMode == TraverseMode.BICYCLE) {
        return doTraverse(s0, options.bikeWalkingOptions, TraverseMode.WALK);
      }
      return null;
    }

    // Automobiles have variable speeds depending on the edge type
    double speed = calculateSpeed(options, traverseMode, s0.getTimeInMillis());

    double time = getDistance() / speed;
    double weight;
    // TODO(flamholz): factor out this bike, wheelchair and walking specific logic to somewhere
    // central.
    if (options.wheelchairAccessible) {
      weight = getSlopeSpeedEffectiveLength() / speed;
    } else if (traverseMode.equals(TraverseMode.BICYCLE)) {
      time = getSlopeSpeedEffectiveLength() / speed;
      switch (options.optimize) {
        case SAFE:
          weight = bicycleSafetyFactor * getDistance() / speed;
          break;
        case GREENWAYS:
          weight = bicycleSafetyFactor * getDistance() / speed;
          if (bicycleSafetyFactor <= GREENWAY_SAFETY_FACTOR) {
            // greenways are treated as even safer than they really are
            weight *= 0.66;
          }
          break;
        case FLAT:
          /* see notes in StreetVertex on speed overhead */
          weight = getDistance() / speed + getSlopeWorkCostEffectiveLength();
          break;
        case QUICK:
          weight = getSlopeSpeedEffectiveLength() / speed;
          break;
        case TRIANGLE:
          double quick = getSlopeSpeedEffectiveLength();
          double safety = bicycleSafetyFactor * getDistance();
          // TODO This computation is not coherent with the one for FLAT
          double slope = getSlopeWorkCostEffectiveLength();
          weight =
              quick * options.triangleTimeFactor
                  + slope * options.triangleSlopeFactor
                  + safety * options.triangleSafetyFactor;
          weight /= speed;
          break;
        default:
          weight = getDistance() / speed;
      }
    } else {
      if (walkingBike) {
        // take slopes into account when walking bikes
        time = getSlopeSpeedEffectiveLength() / speed;
      }
      weight = time;
      if (traverseMode.equals(TraverseMode.WALK)) {
        // take slopes into account when walking
        // FIXME: this causes steep stairs to be avoided. see #1297.
        double costs = ElevationUtils.getWalkCostsForSlope(getDistance(), getMaxSlope());
        // as the cost walkspeed is assumed to be for 4.8km/h (= 1.333 m/sec) we need to adjust
        // for the walkspeed set by the user
        double elevationUtilsSpeed = 4.0 / 3.0;
        weight = costs * (elevationUtilsSpeed / speed);
        time =
            weight; // treat cost as time, as in the current model it actually is the same (this can
                    // be checked for maxSlope == 0)
        /*
        // debug code
        if(weight > 100){
            double timeflat = length / speed;
            System.out.format("line length: %.1f m, slope: %.3f ---> slope costs: %.1f , weight: %.1f , time (flat):  %.1f %n", length, elevationProfile.getMaxSlope(), costs, weight, timeflat);
        }
        */
      }
    }

    if (isStairs()) {
      weight *= options.stairsReluctance;
    } else {
      // TODO: this is being applied even when biking or driving.
      weight *= options.walkReluctance;
    }

    StateEditor s1 = s0.edit(this);
    s1.setBackMode(traverseMode);
    s1.setBackWalkingBike(walkingBike);

    /* Handle no through traffic areas. */
    if (this.isNoThruTraffic()) {
      // Record transition into no-through-traffic area.
      if (backEdge instanceof StreetEdge && !((StreetEdge) backEdge).isNoThruTraffic()) {
        s1.setEnteredNoThroughTrafficArea();
      }
      // If we transitioned into a no-through-traffic area at some point, check if we are exiting
      // it.
      if (s1.hasEnteredNoThroughTrafficArea()) {
        // Only Edges are marked as no-thru, but really we need to avoid creating dominant, pruned
        // states
        // on thru _Vertices_. This could certainly be improved somehow.
        for (StreetEdge se : Iterables.filter(s1.getVertex().getOutgoing(), StreetEdge.class)) {
          if (!se.isNoThruTraffic()) {
            // This vertex has at least one through-traffic edge. We can't dominate it with a
            // no-thru state.
            return null;
          }
        }
      }
    }

    /* Compute turn cost. */
    StreetEdge backPSE;
    if (backEdge != null && backEdge instanceof StreetEdge) {
      backPSE = (StreetEdge) backEdge;
      RoutingRequest backOptions =
          backWalkingBike ? s0.getOptions().bikeWalkingOptions : s0.getOptions();
      double backSpeed = backPSE.calculateSpeed(backOptions, backMode, s0.getTimeInMillis());
      final double realTurnCost; // Units are seconds.

      // Apply turn restrictions
      if (options.arriveBy && !canTurnOnto(backPSE, s0, backMode)) {
        return null;
      } else if (!options.arriveBy && !backPSE.canTurnOnto(this, s0, traverseMode)) {
        return null;
      }

      /*
       * This is a subtle piece of code. Turn costs are evaluated differently during
       * forward and reverse traversal. During forward traversal of an edge, the turn
       * *into* that edge is used, while during reverse traversal, the turn *out of*
       * the edge is used.
       *
       * However, over a set of edges, the turn costs must add up the same (for
       * general correctness and specifically for reverse optimization). This means
       * that during reverse traversal, we must also use the speed for the mode of
       * the backEdge, rather than of the current edge.
       */
      if (options.arriveBy && tov instanceof IntersectionVertex) { // arrive-by search
        IntersectionVertex traversedVertex = ((IntersectionVertex) tov);

        realTurnCost =
            backOptions
                .getIntersectionTraversalCostModel()
                .computeTraversalCost(
                    traversedVertex,
                    this,
                    backPSE,
                    backMode,
                    backOptions,
                    (float) speed,
                    (float) backSpeed);
      } else if (!options.arriveBy && fromv instanceof IntersectionVertex) { // depart-after search
        IntersectionVertex traversedVertex = ((IntersectionVertex) fromv);

        realTurnCost =
            options
                .getIntersectionTraversalCostModel()
                .computeTraversalCost(
                    traversedVertex,
                    backPSE,
                    this,
                    traverseMode,
                    options,
                    (float) backSpeed,
                    (float) speed);
      } else {
        // In case this is a temporary edge not connected to an IntersectionVertex
        LOG.debug("Not computing turn cost for edge {}", this);
        realTurnCost = 0;
      }

      if (!traverseMode.isDriving()) {
        s1.incrementWalkDistance(realTurnCost / 100); // just a tie-breaker
      }

      long turnTime = (long) Math.ceil(realTurnCost);
      time += turnTime;
      weight += options.turnReluctance * realTurnCost;
    }

    if (walkingBike || TraverseMode.BICYCLE.equals(traverseMode)) {
      if (!(backWalkingBike || TraverseMode.BICYCLE.equals(backMode))) {
        s1.incrementTimeInSeconds(options.bikeSwitchTime);
        s1.incrementWeight(options.bikeSwitchCost);
      }
    }

    if (!traverseMode.isDriving()) {
      s1.incrementWalkDistance(getDistance());
    }

    /* On the pre-kiss/pre-park leg, limit both walking and driving, either soft or hard. */
    int roundedTime = (int) Math.ceil(time);
    if (options.kissAndRide || options.parkAndRide) {
      if (options.arriveBy) {
        if (!s0.isCarParked()) s1.incrementPreTransitTime(roundedTime);
      } else {
        if (!s0.isEverBoarded()) s1.incrementPreTransitTime(roundedTime);
      }
      if (s1.isMaxPreTransitTimeExceeded(options)) {
        if (options.softPreTransitLimiting) {
          weight +=
              calculateOverageWeight(
                  s0.getPreTransitTime(),
                  s1.getPreTransitTime(),
                  options.maxPreTransitTime,
                  options.preTransitPenalty,
                  options.preTransitOverageRate);
        } else return null;
      }
    }

    /* Apply a strategy for avoiding walking too far, either soft (weight increases) or hard limiting (pruning). */
    if (s1.weHaveWalkedTooFar(options)) {

      // if we're using a soft walk-limit
      if (options.softWalkLimiting) {
        // just slap a penalty for the overage onto s1
        weight +=
            calculateOverageWeight(
                s0.getWalkDistance(),
                s1.getWalkDistance(),
                options.getMaxWalkDistance(),
                options.softWalkPenalty,
                options.softWalkOverageRate);
      } else {
        // else, it's a hard limit; bail
        LOG.debug("Too much walking. Bailing.");
        return null;
      }
    }

    s1.incrementTimeInSeconds(roundedTime);

    s1.incrementWeight(weight);

    return s1;
  }