=====
Graph
=====
The ``gt-graph`` package defines the concept of a graph (or network) made up of
GeoTools Features.
**Maven**::
org.geotools
gt-graph
${geotools.version}
Graph Model
-----------
The graph module provides a convenient, flexible and efficient API for graph construction and query.
In additional to generic Graph support, it implements directed and undirected networks.
This code was originally developed at Refractions Research and has been used in Jump before being
ported to GeoTools.
**Use of GeoTools**
The Graph module makes use of concepts and (classes) from the GeoTools core:
* ``Feature`` - atomic unit of geographic information
* ``FeatureType`` - keeps track of what attributes each Feature can hold
* ``FeatureID`` - a unique id associated with each Feature (must start with
a non-numeric character)
In addition to the Feature API from core, the graph module makes use of relationships.
Usually relationships are based on spatial comparisons between features, although you
may be able to quickly establish a relationship using your feature attributes.
* Graph constructed from ``LineStrings`` based on "shared end points"
Example a road network used for navigation.
* Graph constructed from ``Polygons`` based on "touches"
Example graph of watersheds used for ground water analysis.
Graph
-----
.. image:: /images/graph_model.PNG
The basic layout of a Graph is a collection of edges joined together at nodes.
**Graph**
The core graph data structure is a collection of nodes and edges, a range of methods are available
allowing access using visitors.
**Node**
Represents a node in the graph.
**Edge**
Represents the graph edge between two nodes; with a range of methods to both access and compare.
**Graphable**
We store the following common information for both nodes and edges:
* visited: Used during traversal to mark visited components
* count: In a similar fashion count is stored
* object: Used to store the Feature used during creation.
Depending on the graph constructed you may end up storing the
features along the edges (say for a road network) or at each
node (say for a watershed network).
Graph Access
^^^^^^^^^^^^
A Graph supports simple direct access is supported using:
+ ``Graph.getNodes()``
+ ``Graph.getEdges()``
You can also pass in a visitor in order to traverse the graph contents:
+ ``Graph.visitNodes( visitor )``
+ ``Graph.visitEdges( visitor )``
The visitor can also control when to stop the process using:
* ``Graph.queryNodes( visitor )``
* ``Graph.queryEdges( visitor )``
To provide control your visitor can indicate when it wants to stop, backtrack or continue using:
* ``GraphTraversal.CONTINUE``
* ``GraphTraversal.KILL_BRANCH``
* ``GraphTraversal.STOP``
* ``GraphTraversal.SUSPEND``
Graph Traversal
^^^^^^^^^^^^^^^
An alternative to direct access is the configure a **GraphTraversal** using:
* **GraphIterator**: iterator specifying the order in which to visit components of
the graph during the traversal.
* **GraphWalker**: walker being iterated over the graph (usually to accomplish a specific goal)
GeoTools provides out of the box implementations for many common problems: finding the
shortest path, partition a graph into sections, or visiting all the elements.
Directed Graph
^^^^^^^^^^^^^^
We also have a straight extension of these ideas to represent a directed graph in which each edge has a direction in which
it can be traversed. This is the difference between considering each edge a simple connection vs thinking of each edge as
an arrow.
.. image:: /images/directed_graph_model.PNG
Building
--------
GraphBuilder
^^^^^^^^^^^^
At a low level graph creation is handled using a ``GraphBuilder``. We have a range
of implementations available. The important point is that you control them by
calling ``buildNode``, and ``buildEdge`` repeatedly allowing it to build up an internal
representation of your Graph.
When you are satisfied with the result you can call ``getGraph()`` to retrieve
the result.
.. image:: /images/graph_builder.PNG
Example
'''''''
1. Building a Line network:
.. literalinclude:: /../src/main/java/org/geotools/graph/GraphExamples.java
:language: java
:start-after: // graphExample start
:end-before: // graphExample end
2. To make use of your graph we will use a ``GraphVisitor``:
The following ``OrphanVistor`` is called for "each" ``GraphComponent`` where it
has a chance to check if the ``GraphComponent`` is an orphan (i.e. has no
relationships) or not.
.. literalinclude:: /../src/main/java/org/geotools/graph/GraphExamples.java
:language: java
:start-after: // visitor example start
:end-before: // visitor example end
For those familiar with the Builder Pattern (GOF Design Patterns) this will look familiar.
GraphGenerator
^^^^^^^^^^^^^^
The other approach is we have a number of generators which will automatically create a
Graph for you based on information you feed in. The ``GraphGenerators`` use a ``GraphBuilder``
to build up each node and edge internally; so you will need to be careful to construct them
with the correct builder for the problem you are wishing to solve.
.. image:: /images/graph_generator.PNG
Each one of these implementations is set up to handle different kinds of data (``Features``, ``LineStrings``, etc...)
so please be sure to read the javadocs.
Building Graph from a FeatureCollection
'''''''''''''''''''''''''''''''''''''''
We have a number of generators that can be used to process a feature collection in different
ways in order to build up an appropriate Graph.
* This example can be used if you want to build a graph from a feature collection made up of linear
features::
// get a feature collection somehow
SimpleFeatureCollection fCollection = featureSource.getFeatures();
//create a linear graph generate
LineStringGraphGenerator lineStringGen = new LineStringGraphGenerator();
//wrap it in a feature graph generator
FeatureGraphGenerator featureGen = new FeatureGraphGenerator( lineStringGen );
//throw all the features into the graph generator
FeatureIterator iter = fCollection.features();
try {
while(iter.hasNext()){
Feature feature = iter.next();
featureGen.add( feature );
}
} finally {
iter.close();
}
Graph graph = featureGen.getGraph()
Building Graph from Line Segments
'''''''''''''''''''''''''''''''''
* This example can be used to build a graph from just a set of line segments::
//we have some line segments
LineSegment[] lines = ...
//create the graph generator
BasicLineGraphGenerator graphGen = new BasicLineGraphGenerator();
//add the lines to the graph
for ( int i = 0; i < lines.length; i++ ) {
graphGen.add( lines[i] );
}
Graph graph = graphGen.getGraph()
Building a FeatureCollection from your Graph
--------------------------------------------
Once the graph is built each, ``edge.getObject()`` will hold the original
feature used to built it.
You can traverse your graph and build up ``FeatureCollection`` as you go.::
SimpleFeatureCollection features = new DefaultFeatureCollection();
for ( Iterator e = graph.getEdges().iterator(); e.hasNext(); ) {
Edge edge = (Edge) e.next();
SimpleFeature feature = (SimpleFeature) e.getObject();
features.add( feature );
}
Shortest Path
-------------
We have a number of ways to calculate the shortest path between two nodes:
* The class **DijkstraShortestPathFinder** can be used to calculate a path using Dijkstra's Shortest Path algorithm.::
//reference to a graph, already built
Graph graph = ...see above...
//find a source node (usually your user chooses one)
Node start = ..
// create a strategy for weighting edges in the graph
// in this case we are using geometry length
DijkstraIterator.EdgeWeigter weighter = new DijkstraIterator.EdgeWeighter() {
public double getWeight(Edge e) {
SimpleFeature feature = (SimpleFeature) e.getObject();
Geometry geometry = (Geometry) feature.getDefaultGeometry();
return gometry.getLength();
}
}
// Create GraphWalker - in this case DijkstraShortestPathFinder
DijkstraShortestPathFinder pf = new DijkstraShortestPathFinder( graph, start, weighter );
pf.calculate();
//find some destinations to calculate paths to
List destinations = ...
//calculate the paths
for ( Iterator d = destinations.iterator(); d.hasNext(); ) {
Node destination = (Node) d.next();
Path path = pf.getPath( destination );
//do something with the path
}
* **AStarShortestPathFinder** can be used in a similar fashion (and is often quicker)