} 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);
/* 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);
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;
}