/* 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 (props, 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.reach;
import org.opentripplanner.common.geometry.DistanceLibrary;
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.routing.algorithm.strategies.RemainingWeightHeuristic;
import org.opentripplanner.routing.core.OptimizeType;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Vertex;
/**
* The default remaining weight heuristic, but with reach.
*
*/
public class ReachRemainingWeightHeuristic implements RemainingWeightHeuristic {
private static final long serialVersionUID = -5172878150967231550L;
private RoutingRequest options;
private boolean useTransit = false;
private boolean useReach = true;
private double maxSpeed;
private DistanceLibrary distanceLibrary = SphericalDistanceLibrary.getInstance();
@Override
public void initialize(State s, Vertex target) {
this.options = s.getOptions();
this.useTransit = options.getModes().isTransit();
this.maxSpeed = getMaxSpeed(options);
}
@Override
public double computeForwardWeight(State s, Vertex target) {
Vertex sv = s.getVertex();
double euclidianDistance = distanceLibrary.fastDistance(sv.getCoordinate(),
target.getCoordinate());
/* On a non-transit trip, the remaining weight is simply distance / speed
* On a transit trip, there are two cases:
* (1) we're not on a transit vehicle. In this case, there are two possible ways to
* compute the remaining distance, and we take whichever is smaller:
* (a) walking distance / walking speed
* (b) boarding cost + transit distance / transit speed (this is complicated a
* bit when we know that there is some walking portion of the trip).
* (2) we are on a transit vehicle, in which case the remaining weight is
* simply transit distance / transit speed (no need for boarding cost),
* again considering any mandatory walking.
*/
double remainingwalkDistance = options.getMaxWalkDistance()- s.getWalkDistance();
double speed = options.getStreetSpeedUpperBound();
Edge backEdge = s.getBackEdge();
EdgeWithReach edgeWithReach = null;
if (useReach && backEdge instanceof EdgeWithReach) {
edgeWithReach = (EdgeWithReach) backEdge;
}
double depth = s.getWalkSinceLastTransit();
if (useTransit) {
if (s.isAlightedLocal() || s.getNumBoardings() >= options.maxTransfers + 1) {
/* we have to walk to the destination from here */
if (euclidianDistance > remainingwalkDistance) {
return -1;
}
if (edgeWithReach != null) {
double reach = edgeWithReach.getReach();
if (depth > reach && euclidianDistance > reach) {
return -1;
}
}
return options.walkReluctance * euclidianDistance / speed;
} else {
/* we could walk to another transit stop or we could walk to the destination */
double distanceToNearestStop = sv.getDistanceToNearestTransitStop();
if (edgeWithReach != null) {
double reach = edgeWithReach.getReach();
//double transitReach = edgeWithReach.getTransitReach();
if (depth > reach && euclidianDistance > reach && distanceToNearestStop > reach) {
return -1;
}
}
int boardCost;
if (s.isOnboard()) {
boardCost = 0;
} else {
boardCost = options.getBoardCostLowerBound();
}
if (s.isEverBoarded()) {
boardCost += options.transferPenalty;
}
if (euclidianDistance < distanceToNearestStop) {
if (euclidianDistance > remainingwalkDistance) {
return -1;
}
return options.walkReluctance * euclidianDistance / speed;
} else {
double mandatoryWalkDistance = target.getDistanceToNearestTransitStop()
+ sv.getDistanceToNearestTransitStop();
if (mandatoryWalkDistance + s.getWalkDistance() > options.getMaxWalkDistance()) {
return -1;
}
double distance = (euclidianDistance - mandatoryWalkDistance) / maxSpeed
+ mandatoryWalkDistance * options.walkReluctance / speed
+ boardCost;
return Math.min(distance, options.walkReluctance * euclidianDistance
/ speed);
}
}
} else {
//non-transit case
if (edgeWithReach != null) {
double reach = edgeWithReach.getReach();
if (depth > reach && euclidianDistance > reach) {
return -1;
}
}
return options.walkReluctance * euclidianDistance / maxSpeed;
}
}
@Override
public double computeReverseWeight(State s, Vertex target) {
// from and to are interpreted in the direction of traversal
// so the edge actually leads from
Vertex sv = s.getVertex();
double euclidianDistance = distanceLibrary.fastDistance(sv.getCoordinate(),
target.getCoordinate());
double speed = options.getStreetSpeedUpperBound();
if (useTransit) {
if (s.isAlightedLocal()) {
if (euclidianDistance + s.getWalkDistance() > options.getMaxWalkDistance()) {
return -1;
}
return options.walkReluctance * euclidianDistance / speed;
} else {
int boardCost;
if (s.isOnboard()) {
boardCost = 0;
} else {
boardCost = options.getBoardCostLowerBound();
}
if (s.isEverBoarded()) {
boardCost += options.transferPenalty;
}
if (euclidianDistance < target.getDistanceToNearestTransitStop()) {
if (euclidianDistance + s.getWalkDistance() > options.getMaxWalkDistance()) {
return -1;
}
return options.walkReluctance * euclidianDistance
/ speed;
} else {
double mandatoryWalkDistance = target
.getDistanceToNearestTransitStop()
+ sv.getDistanceToNearestTransitStop();
if (mandatoryWalkDistance + s.getWalkDistance() > options.getMaxWalkDistance()) {
return -1;
}
double distance = (euclidianDistance - mandatoryWalkDistance) / maxSpeed
+ mandatoryWalkDistance * options.walkReluctance
/ speed + boardCost;
return Math.min(distance, options.walkReluctance
* euclidianDistance / speed);
}
}
} else {
return options.walkReluctance * euclidianDistance / maxSpeed;
}
}
public static double getMaxSpeed(RoutingRequest options) {
if (options.getModes().contains(TraverseMode.TRANSIT)) {
// assume that the max average transit speed over a hop is 10 m/s, which is roughly
// true in Portland and NYC, but *not* true on highways
return 10;
} else {
if (options.optimize == OptimizeType.QUICK) {
return options.getStreetSpeedUpperBound();
} else {
// assume that the best route is no more than 10 times better than
// the as-the-crow-flies flat base route.
return options.getStreetSpeedUpperBound() * 10;
}
}
}
@Override
public void reset() {
}
public DistanceLibrary getDistanceLibrary() {
return distanceLibrary;
}
public void setDistanceLibrary(DistanceLibrary distanceLibrary) {
this.distanceLibrary = distanceLibrary;
}
@Override
public void abort() {
// TODO Auto-generated method stub
}
}