// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source:
// /cvs/distapps/openmap/src/openmap/com/bbn/openmap/tools/roads/Route.java,v
// $
// $RCSfile: Route.java,v $
// $Revision: 1.5 $
// $Date: 2005/12/09 21:09:11 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.tools.roads;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.logging.Logger;
import com.bbn.openmap.proj.GreatCircle;
import com.bbn.openmap.proj.coords.LatLonPoint;
public class Route implements Cloneable, Serializable {
private static float MSEC_PER_HOUR = 3600000.0f;
private String name = null;
Road[] roads;
private boolean startWithFirstIntersection;
// private boolean endWithFirstIntersection;
transient Logger logger = Logger.getLogger(this.getClass().getName());
static class NodeInfo {
Intersection intersection;
Road bestRoad = null;
double time;
double crowsPathHours;
NodeInfo(Intersection intersection, Road road, double time, double crowsPathHours) {
this.intersection = intersection;
this.bestRoad = road;
this.time = time;
this.crowsPathHours = crowsPathHours;
}
}
public synchronized static Route getBestRoute(Intersection from, Intersection to,
double bestConvoySpeed, double worstConvoySpeed) {
Hashtable marks = new Hashtable();
boolean haveRoute = false;
LatLonPoint toLoc = to.getLocation();
double toLat = toLoc.getY();
double toLon = toLoc.getX();
LatLonPoint fromLoc = from.getLocation();
double fromLat = fromLoc.getY();
double fromLon = fromLoc.getX();
double timeLimitBase = GreatCircle.sphericalDistance(toLat, toLon, fromLat, fromLon)
/ worstConvoySpeed;
double bestTime = Double.MAX_VALUE;
for (float snakeFactor = 1.0f; snakeFactor < 40f; snakeFactor *= 2f) {
// if (logger.isLoggable(Level.INFO))
// logger.info ("Snake factor " + snakeFactor);
double timeLimit = timeLimitBase * snakeFactor;
Vector toDo = new Vector();
toDo.addElement(from);
marks.clear();
marks.put(from, new NodeInfo(from, null, 0.0f, 0.0f));
while (!toDo.isEmpty()) {
Vector newToDo = new Vector();
for (Enumeration e = toDo.elements(); e.hasMoreElements();) {
Intersection thisIntersection = (Intersection) e.nextElement();
NodeInfo thisInfo = (NodeInfo) marks.get(thisIntersection);
// System.out.println ("examining " +
// thisIntersection);
for (Enumeration e2 = thisIntersection.getRoads(); e2.hasMoreElements();) {
Road road = (Road) e2.nextElement();
// System.out.println (" - road " + road);
double roadTime = road.getTraverseHours();
double newTime = thisInfo.time + roadTime;
if (newTime > timeLimit)
continue;
Intersection nextIntersection = road.getOtherIntersection(thisIntersection);
// System.out.println (" - next inter " +
// nextIntersection);
NodeInfo nextInfo = (NodeInfo) marks.get(nextIntersection);
if (nextInfo == null) {
LatLonPoint nextLoc = nextIntersection.getLocation();
double crowsPathDistance = GreatCircle.sphericalDistance(toLat, toLon, nextLoc.getY(), nextLoc.getX());
double crowsPathHours = crowsPathDistance / bestConvoySpeed;
nextInfo = new NodeInfo(nextIntersection, road, newTime, crowsPathHours);
marks.put(nextIntersection, nextInfo);
if (newTime + nextInfo.crowsPathHours > bestTime)
continue;
newToDo.addElement(nextIntersection);
// System.out.println (" - best road for "
// + nextIntersection + " is " + road);
} else if (nextInfo.time > newTime) {
if (!nextInfo.intersection.equals(nextIntersection)) {
System.err.println("huh? lookup of " + nextIntersection
+ " gets node info with inter " + nextInfo.intersection);
}
nextInfo.time = newTime;
nextInfo.bestRoad = road;
// System.out.println (" - (redo) best
// road for " + nextIntersection + " is "
// + road);
if (newTime + nextInfo.crowsPathHours > bestTime)
continue;
newToDo.addElement(nextIntersection);
} else {
continue;
}
if (nextIntersection == to) {
// System.err.println ("found end " + to);
bestTime = nextInfo.time;
haveRoute = true;
}
}
}
toDo = newToDo;
}
if (haveRoute)
break;
}
Vector roadVector = new Vector();
Route result = null;
if (haveRoute) {
for (NodeInfo info = (NodeInfo) marks.get(to); result == null;) {
roadVector.addElement(info.bestRoad);
// System.err.println ("adding Road #" + (i++) + " - "
// + info.bestRoad);
Intersection prevIntersection = info.bestRoad.getOtherIntersection(info.intersection);
if (prevIntersection == from)
result = new Route(roadVector, info.bestRoad.getFirstIntersection() == prevIntersection);
else
info = (NodeInfo) marks.get(prevIntersection);
}
}
marks = null;
return result;
}
public Route(String name, Road[] roads, boolean startWithFirstIntersection) {
this.name = name;
this.roads = roads;
this.startWithFirstIntersection = startWithFirstIntersection;
}
private Route(Vector roadVector, boolean startWithFirstIntersection) {
int nRoads = roadVector.size();
roads = new Road[nRoads];
for (int i = 0; i < nRoads; i++)
roads[i] = (Road) roadVector.elementAt(nRoads - 1 - i);
this.startWithFirstIntersection = startWithFirstIntersection;
}
public Object clone() {
return new Route(null, roads, startWithFirstIntersection);
}
public String getName() {
return name;
}
public void setName(String newName) {
name = newName;
}
public Road[] getRoads() {
return roads;
}
public int getBlockedRoadCount() {
int blockedRoadCount = 0;
for (int i = 0; i < roads.length; i++) {
if (roads[i].isBlocked())
blockedRoadCount++;
}
return blockedRoadCount;
}
public void unblockBlockedRoads() {
for (int i = 0; i < roads.length; i++) {
if (roads[i].isBlocked())
roads[i].unblock();
}
}
public Intersection getOriginIntersection() {
if (startWithFirstIntersection)
return roads[0].getFirstIntersection();
else
return roads[0].getSecondIntersection();
}
public Intersection getDestinationIntersection() {
Intersection x = getOriginIntersection();
for (int i = 0; i < roads.length; i++) {
x = roads[i].getOtherIntersection(x);
}
return x;
}
public long getTravelTime() {
double hours = 0.0f;
for (int i = 0; i < roads.length; i++) {
Road road = roads[i];
double roadLength = road.getLengthInKilometers();
double convoySpeed = road.getRoadClass().getConvoySpeed();
double timeToTraverse = roadLength / convoySpeed;
hours += timeToTraverse;
}
return (long) (hours * MSEC_PER_HOUR);
}
public LatLonPoint location(long time) {
double hours = time / MSEC_PER_HOUR;
Intersection from = (startWithFirstIntersection ? roads[0].getFirstIntersection()
: roads[0].getSecondIntersection());
for (int i = 0; i < roads.length; i++) {
Road road = roads[i];
boolean forward = road.getFirstIntersection() == from;
double roadLength = road.getLengthInKilometers();
double convoySpeed = road.getRoadClass().getConvoySpeed();
double timeToTraverse = roadLength / convoySpeed;
if (timeToTraverse > hours) {
float fraction = (float) hours / (float) timeToTraverse;
if (!forward)
fraction = 1.0f - fraction;
return road.getLocationAtKilometer(roadLength * fraction);
}
hours -= timeToTraverse;
if (forward)
from = road.getSecondIntersection();
else
from = road.getFirstIntersection();
}
return from.getLocation();
}
}