/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.transit_data_federation.impl.otp;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.onebusaway.geospatial.model.CoordinatePoint;
import org.onebusaway.geospatial.services.SphericalGeometryLibrary;
import org.onebusaway.transit_data_federation.impl.otp.graph.TransitVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.tp.HasPathStateVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.tp.TPState;
import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry;
import org.onebusaway.transit_data_federation.services.tripplanner.HubNode;
import org.onebusaway.transit_data_federation.services.tripplanner.TransferNode;
import org.opentripplanner.routing.algorithm.strategies.RemainingWeightHeuristic;
import org.opentripplanner.routing.core.EdgeNarrative;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseOptions;
import org.opentripplanner.routing.core.Vertex;
public class TPRemainingWeightHeuristicImpl implements RemainingWeightHeuristic {
private TraverseOptions _options;
private boolean _useTransit;
/**
* assume that the max average transit speed over a hop is 10 m/s, which is so
* far true for New York and Portland
*/
private double _maxTransitSpeed = 10.0;
@Override
public void reset() {
_options = null;
_useTransit = false;
}
@Override
public double computeInitialWeight(State s, Vertex target) {
_options = s.getOptions();
_useTransit = _options.getModes().getTransit();
double maxSpeed = getMaxSpeed();
return distance(s.getVertex(), target) / maxSpeed;
}
@Override
public double computeForwardWeight(State s, Vertex target) {
EdgeNarrative narrative = s.getBackEdgeNarrative();
Vertex v = narrative.getToVertex();
return computeWeight(s, target, v);
}
@Override
public double computeReverseWeight(State s, Vertex target) {
EdgeNarrative narrative = s.getBackEdgeNarrative();
Vertex v = narrative.getFromVertex();
return computeWeight(s, target, v);
}
/****
* Private Methods
****/
private double computeWeight(State state, Vertex target, Vertex v) {
if (v instanceof HasPathStateVertex) {
HasPathStateVertex tpV = (HasPathStateVertex) v;
TPState pathState = tpV.getPathState();
TransferNode node = pathState.getNode();
boolean isFromSourceStop = tpV.isDeparture() ^ pathState.isReverse();
CoordinatePoint dest = new CoordinatePoint(target.getY(), target.getX());
Set<TransferNode> visitedNodes = new HashSet<TransferNode>();
return getWeightForTransferNode(null, node, isFromSourceStop, dest,
visitedNodes);
}
double distanceEstimate = distance(v, target);
double maxSpeed = getMaxSpeedForCurrentState(state, v);
return distanceEstimate / maxSpeed;
}
private double getWeightForTransferNode(TransferNode parentNode,
TransferNode node, boolean isFromSourceStop, CoordinatePoint target,
Set<TransferNode> visitedNodes) {
visitedNodes.add(node);
double w = 0;
if (isFromSourceStop) {
if (parentNode != null) {
StopEntry fromStop = parentNode.getToStop();
StopEntry toStop = node.getFromStop();
double transferWeight = computeTransferWeight(
fromStop.getStopLocation(), toStop.getStopLocation());
w += transferWeight;
}
StopEntry fromStop = node.getFromStop();
StopEntry toStop = node.getToStop();
int transitWeight = computeTransitWeight(fromStop.getStopLocation(),
toStop.getStopLocation());
w += transitWeight;
}
/**
* What's our best option?
*/
double minOption = node.getMinRemainingWeight();
if (minOption < 0) {
minOption = Double.POSITIVE_INFINITY;
/**
* We could exit if we're allowed, walking to our destination
*/
if (node.isExitAllowed()) {
StopEntry toStop = node.getToStop();
double transferWeight = computeTransferWeight(toStop.getStopLocation(),
target);
minOption = Math.min(transferWeight, minOption);
}
/**
* Or we could transfer to another transfer pattern
*/
Collection<TransferNode> transfers = node.getTransfers();
for (TransferNode subTree : transfers) {
if (!visitedNodes.contains(subTree)) {
double subWeight = getWeightForTransferNode(node, subTree, true,
target, visitedNodes);
minOption = Math.min(subWeight, minOption);
}
}
Collection<HubNode> hubs = node.getHubs();
for (HubNode hubNode : hubs) {
StopEntry hubStop = hubNode.getHubStop();
CoordinatePoint hubLocation = hubStop.getStopLocation();
double transferWeight = computeTransferWeight(
node.getToStop().getStopLocation(), hubLocation);
double transitWeight = computeTransitWeight(hubLocation, target);
double subWeight = transferWeight + transitWeight;
minOption = Math.min(subWeight, minOption);
}
node.setMinRemainingWeight(minOption);
}
w += minOption;
return w;
}
private double getMaxSpeed() {
if (!_useTransit)
return _options.speed;
return _maxTransitSpeed;
}
private double getMaxSpeedForCurrentState(State state, Vertex v) {
/**
* If we can't use transit at all, just use our walking speed
*/
if (!_useTransit)
return _options.speed;
/**
* If we've ever boarded a transit vehicle, but are now off transit, we can
* guarantee that we'll never get back on transit. Thus, we can assume
* walking speed as our max velocity.
*/
if (state.isEverBoarded() && !(v instanceof TransitVertex))
return _options.speed;
return _maxTransitSpeed;
}
private int computeTransitWeight(CoordinatePoint from, CoordinatePoint to) {
double transitDistance = distance(from.getLat(), from.getLon(),
to.getLat(), to.getLon());
return (int) (transitDistance / _maxTransitSpeed);
}
private double computeTransferWeight(CoordinatePoint source,
CoordinatePoint dest) {
double walkDistance = distance(source.getLat(), source.getLon(),
dest.getLat(), dest.getLon());
int walkTime = (int) (walkDistance / _options.speed);
return ItineraryWeightingLibrary.computeTransferWeight(walkTime, _options);
}
private double distance(Vertex a, Vertex b) {
return distance(a.getY(), a.getX(), b.getY(), b.getX());
}
private static final double distance(double lat1, double lon1, double lat2,
double lon2) {
return SphericalGeometryLibrary.distanceFaster(lat1, lon2, lat2, lon2);
}
}