/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library 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;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.graph.traverse.standard;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import org.geotools.graph.structure.Edge;
import org.geotools.graph.structure.Graph;
import org.geotools.graph.structure.GraphVisitor;
import org.geotools.graph.structure.Graphable;
import org.geotools.graph.structure.Node;
import org.geotools.graph.traverse.GraphTraversal;
import org.geotools.graph.traverse.basic.SourceGraphIterator;
import org.geotools.graph.util.PriorityQueue;
/**
* Iterates over the nodes of a graph in pattern using <B>Dijkstra's
* Shortest Path Algorithm</B>. A Dijkstra iteration returns nodes
* in an order of increasing cost relative to a specified node
* (the source node of the iteration).<BR>
* <BR>
* In a Dijsktra iteration, a <B>weight</B> is associated with each edge
* and a <B>cost</B> with each node. The iteration operates by maintaining
* two sets of nodes. The first the set of nodes whose final cost is known, and
* the second is the set of nodes whose final cost is unknown.
* Initially, every node except for the source node has a cost of infinity, and
* resides in the unkown set. The source node has a cost of zero, and is
* is a member of the known set.<BR>
* <BR>
* The iteration operatates as follows:<BR>
* <PRE>
* sn = source node of iteration
* N = set of all nodes
* K = set of nodes with known cost = {sn}
* U = set of nodes with unknown cost = N - K
*
* cost(sn) = 0
* for each node $un in U
* cost(un) = infinity
*
* while(|U| > 0)
* for each node n in K
* find a node un in U that relates to n
* if cost(n) + weight(n,un) < cost(un)
* cost(un) = cost(n) + weight(n,un)
*
* ln = node with least cost in U
* remove ln from U
* add ln to K
*
* return ln as next node in iteration
* </PRE>
* The following is an illustration of the algorithm. Edge weights are labelled
* in blue and the final node costs are labelled in red.<BR>
* <IMG src="doc-files/dijkstra.gif"/>
* <BR>
* The nodes are returned in order of increasing cost which yields the sequence
* A,C,B,D,E,F,G,H,I.<BR>
*
* @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net
*
* @source $URL$
*/
public class DijkstraIterator extends SourceGraphIterator {
/** compares two internal nodes used by the iteration by comparing costs **/
private static Comparator<DijkstraNode> comparator = new Comparator<DijkstraNode>() {
public int compare(DijkstraNode n1, DijkstraNode n2) {
return(n1.cost < n2.cost ? -1 : n1.cost > n2.cost ? 1 : 0);
}
};
/** provides weights for edges in the graph **/
protected EdgeWeighter weighter;
/** provides weights for nodes in the graph **/
protected NodeWeighter nweighter;
/** priority queue to manage active nodes **/
protected PriorityQueue queue;
/** map of graph node to internal dijkstra node **/
protected HashMap<Graphable,DijkstraNode> nodemap;
/**
* Constructs a new Dijkstra iterator which uses the specided EdgeWeighter.
*
* @param weighter Calculates weights for edges in the graph being iterated
* over.
*/
public DijkstraIterator(EdgeWeighter weighter) {
this(weighter,null);
}
/**
* Constructs a new Dijkstra iterator which uses the specided EdgeWeighter and
* NodeWeighter
*
* @param weighter Calculates weights for edges in the graph being iterated
* over.
* @param nweighter Calculates weights for nodes in the graph being iterated
* over.
*/
public DijkstraIterator(EdgeWeighter weighter, NodeWeighter nweighter) {
this.weighter = weighter;
this.nweighter = nweighter;
}
/**
* Builds internal priority queue to manage node costs.
*
* @see org.geotools.graph.traverse.GraphIterator#init(Graph)
*/
public void init(Graph graph, GraphTraversal traversal) {
//initialize data structures
nodemap = new HashMap<Graphable,DijkstraNode>();
queue = new PriorityQueue(comparator);
queue.init(graph.getNodes().size());
//place nodes into priority queue
graph.visitNodes(
new GraphVisitor() {
public int visit(Graphable component) {
//create a dijkstra node with infinite cost
DijkstraNode dn = new DijkstraNode((Node)component, Double.MAX_VALUE);
//create the mapping
nodemap.put(component, dn);
//source component gets a cost of 0
if (component == getSource()) dn.cost = 0d;
//place into priority queue
queue.insert(dn);
return 0;
}
}
);
}
/**
* Returns the next node in the priority queue. If the next node coming out
* of the queue has infinite cost, then the node is not adjacent to any nodes
* in the set of nodes with known costs. This situation will end the traversal
* every other node will also have infinite cost. This usally is the result of
* a disconnected graph.
*
* @see org.geotools.graph.traverse.GraphIterator#next()
*/
public Graphable next(GraphTraversal traversal) {
if (queue.isEmpty()) return(null);
DijkstraNode next = (DijkstraNode)queue.extract();
//check cost of node, if cost == infinity then return null
// because no node in the visited set ever updated the node
// since it is at the top of the heap it means no more nodes
// in the visited set will be visited
if (next.cost == Double.MAX_VALUE) return(null);
return(next.node);
}
/**
* Looks for adjacent nodes to the current node which are in the adjacent
* node and updates costs.
*
* @see org.geotools.graph.traverse.GraphIterator#cont(Graphable)
*/
public void cont(Graphable current, GraphTraversal traversal) {
DijkstraNode currdn = (DijkstraNode)nodemap.get(current);
for (Iterator itr = getRelated(current); itr.hasNext();) {
Node related = (Node)itr.next();
if (!traversal.isVisited(related)) {
DijkstraNode reldn = (DijkstraNode)nodemap.get(related);
//calculate cost from current node to related node
double cost = weighter.getWeight(currdn.node.getEdge(related))
+ currdn.cost;
//calculate the cost of going through the node
if ( nweighter != null ) {
double ncost = 0d;
if(currdn.parent != null) {
Edge e1 = currdn.parent.node.getEdge(currdn.node);
Edge e2 = currdn.node.getEdge(related);
ncost = nweighter.getWeight(currdn.node,e1,e2);
}
}
//if cost less than current cost of related node, update
if (cost < reldn.cost) {
reldn.cost = cost;
reldn.parent = currdn;
queue.update(reldn);
}
}
}
}
/**
* Kills the branch of the traversal by not updating the cost of any
* adjacent nodes.
*
* @see org.geotools.graph.traverse.GraphIterator#killBranch(Graphable)
*/
public void killBranch(Graphable current, GraphTraversal traversal) {
//do nothing
}
/**
* Returns the internal cost of a node which has been calculated by the
* iterator.
*
* @param component The component whose cost to return.
*
* @return The cost associated with the component.
*/
public double getCost(Graphable component) {
return(((DijkstraNode)nodemap.get(component)).cost);
}
/**
* Returns the last node in the known set to update the node. The iteration
* operates by nodes in the known set updating the cost of nodes in the
* unknown set. Each time an update occurs, the known node is set as the
* parent of the unkown node.
*
* @param component The node whose parent to return (child)
*
* @return The parent, or null if the method is supplied the source of the
* iteration.
*/
public Graphable getParent(Graphable component) {
if (component.equals(getSource())) return(null);
DijkstraNode dn = (DijkstraNode)nodemap.get(component);
if (dn == null || dn.parent == null) return(null);
return(dn.parent.node);
//return(((DijkstraNode)m_nodemap.get(component)).parent.node);
}
protected PriorityQueue getQueue() {
return(queue);
}
protected Iterator getRelated(Graphable current) {
return(current.getRelated());
}
/**
* Supplies a weight for each edge in the graph to be used by the iteration
* when calculating node costs.
*
* @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net
*
*/
public static interface EdgeWeighter {
/**
* Returns the weight for the associated edge.
*
* @param e The edge whose weight to return.
*
* @return The weight of the edge.
*/
public double getWeight(Edge e);
}
/**
* Supplies a weight for each pair of adjacent edges.
*
* @author Sandeep Kumar Jakkaraju sandeepkumar@iitbombay.org
*
*/
public static interface NodeWeighter {
/**
* Returns the weight for a node, with respect to two adjecent edges.
*
* @param n The node.
* @param e1 First edge.
* @param e2 Second edge.
*
* @return The weight associated with traversing through the node from
* the first edge to the second.
*/
public double getWeight(Node n, Edge e1, Edge e2);
}
/**
* Internal data structure used to track node costs, and parent nodes.
*
* @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net
*
*/
protected static class DijkstraNode {
/** underlying graph node **/
public Node node;
/** cost associated with the node **/
public double cost;
/** last node to update the cost the the underlying graph node **/
public DijkstraNode parent;
/**
* Constructs a new Dijsktra node.
*
* @param node Underling node in graph being iterated over.
* @param cost Initial cost of node.
*/
public DijkstraNode(Node node, double cost) {
this.node = node;
this.cost = cost;
}
}
}