Package org.geotools.graph.util.graph

Source Code of org.geotools.graph.util.graph.GraphFuser$EdgeMerger

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2003-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.util.graph;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.geotools.graph.build.GraphBuilder;
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.GraphWalker;
import org.geotools.graph.traverse.basic.BasicGraphTraversal;
import org.geotools.graph.traverse.basic.SourceGraphIterator;
import org.geotools.graph.traverse.standard.DepthFirstTopologicalIterator;
import org.geotools.graph.traverse.standard.NoBifurcationIterator;

/**
* Removes all nodes of degree 2 from a graph. The following are examples
* of graphs being fused.<BR>
* <BR>
* <IMG src="doc-files/fuse_0.gif"/><BR>
* <BR>
* <IMG src="doc-files/fuse_1.gif"/><BR>
* <BR>
* <IMG src="doc-files/fuse_2.gif"/><BR>
* <BR>
* When a node of degree 2 is removed from tan unfused graph, the two edges
* is is adjacent to must be merged into a single edge. More generally if n
* adjacent nodes of degree 2 are removed, n+1 edges must be merged into a
* single edge. This change in graph structure has an effect on the entities
* modelled by the graph. Since each edge models a single object, replacing
* multiple edges with a single edge results in an inconsistet model. Therefore
* an EdgeMerger is used to merge the objects represented by the multiple edges
* into a single object. This new object becomes the underlying object of the
* merged edge.
*
* @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net
*
*
*
* @source $URL$
*/
public class GraphFuser {

  /** the graph being fused **/
  private Graph m_graph;
 
  /** the builder used to modify the graph being fused **/
  private GraphBuilder m_builder;
 
  /** the edge merger **/
  private EdgeMerger m_merger;
 
  /** traversal used during the graph fuse **/
  private GraphTraversal m_traversal;
 
  /** walker used in the traversal during graph fuse **/
  private GraphWalker m_walker;
 
  /** counter used to track number of unvisited nodes of degree 2 **/
  private int m_ndegree2;
 
  /** collection of node sets to fuse **/
  private ArrayList m_sets;
 
  /** individual node set **/
  private ArrayList m_nodes;
 
  /**
   * Constructs a GraphFuser.
   *
   * @param graph Graph to fuse.
   * @param builder GraphBuilder used to fuse graph.
   * @param merger Used to merge edges.
   */
  public GraphFuser(Graph graph, GraphBuilder builder, EdgeMerger merger) {
    m_graph = graph;
    m_builder = builder;
    m_merger = merger;
  }
 
  /**
   * Performs the fuse.
   *
   * @return True if the fuse was successful, otherwise false.
   */
  public boolean fuse() {
    //create walker for first stage
    // if the walker sees a node of degree 2 it adds it to the current
    // set of nodes, else it starts a new set
    m_walker = new GraphWalker() {
      public int visit(Graphable element, GraphTraversal traversal) {
        Node node = (Node)element;
       
        //if the node is not of degree 2, start a new set
        if (node.getDegree() != 2) {
          finish()
        }
        else {
          //add node to current set
          m_nodes.add(node);
          m_ndegree2--;
        }
      
        return(GraphTraversal.CONTINUE);
      }

      public void finish() {
        //no need to recreate if empty
        if (!m_nodes.isEmpty()) {
          m_sets.add(m_nodes);
          m_nodes = new ArrayList();
        }
      }
    };
   
    //perform a topological depth first traversal
    m_traversal = new BasicGraphTraversal(
      m_graph, m_walker, new DepthFirstTopologicalIterator()
    );
   
    //initialise set and node collections
    m_sets = new ArrayList();
    m_nodes = new ArrayList();
   
 
    m_ndegree2 = m_graph.getNodesOfDegree(2).size();
    if (m_ndegree2 == 0) return(true); // nothing to fuse
   
    m_traversal.init();
   
    //reset edge visited flags
    m_graph.visitNodes(
      new GraphVisitor() {
        public int visit(Graphable component) {
          component.setVisited(false);
          return 0;
        }
      }
    );
   
    //perform the traversal
    m_traversal.traverse();
   
    //if all nodes of degree 2 have been visited, we are finished
    if (m_ndegree2 > 0) {
   
      //if there are still nodes of degree 2 that havent been visited, it means
      // that the graph has a cycle and that the remaining degree 2 nodes are
      // internal to the cycle, so the strategy for the second stage is to
      // find all unvisited nodes of degree 2 that are not visited and start
      // a no bifurcation traversal from them
      Iterator sources = m_graph.queryNodes(
        new GraphVisitor() {
          public int visit(Graphable component) {
            Node node = (Node)component;
            if (!node.isVisited() && node.getDegree() == 2) {
              //check for adjacent node of degree > 2
              for (Iterator itr = node.getRelated(); itr.hasNext(); ) {
                Node rel = (Node)itr.next();
                if (rel.getDegree() > 2) return(Graph.PASS_AND_CONTINUE);
              }
            }
            return(Graph.FAIL_QUERY);
          }
        }
      ).iterator();
     
      //if the query returned no nodes, it means that all the cycle is
      // disconnected from the rest of graph, so just pick any node of degree 2
      if (!sources.hasNext()) {
        sources = m_graph.queryNodes(
          new GraphVisitor() {
            public int visit(Graphable component) {
              if (!component.isVisited()) return(Graph.PASS_AND_STOP);
              return(Graph.FAIL_QUERY);
            }
          }
        ).iterator()
      }
 
      //create stage 2 walker, simple add any nodes visited nodes to the
      // current node set
      m_walker = new GraphWalker() {
        public int visit(Graphable element, GraphTraversal traversal) {
          m_ndegree2--;
          m_nodes.add(element);
          return(GraphTraversal.CONTINUE);
        }
 
        public void finish() {
          m_sets.add(m_nodes);
          m_nodes = new ArrayList();
        }
      };
      m_traversal.setWalker(m_walker);
      m_traversal.setIterator(new NoBifurcationIterator());
     
      //clear current node list
      m_nodes = new ArrayList();
           
      Node source = null;
      while(sources.hasNext()) {
        while(sources.hasNext()) {
          source = (Node)sources.next();
          if (source.isVisited()) continue;
 
          ((SourceGraphIterator)m_traversal.getIterator()).setSource(source);
          m_traversal.traverse();
        }
      }
    }
   
    // should be zero, if not something wierd not accounted for
    if(m_ndegree2 == 0) {
     
      //build the fused graph
      for(Iterator sitr = m_sets.iterator(); sitr.hasNext();) {
        ArrayList nodes = (ArrayList)sitr.next();
        ArrayList edges = new ArrayList();
        Node first = null; //node of degree != 2 adjacent to first node in set
        Node last = null; //node of degree != 2 adjacent to last node in set
        
        if (nodes.size() == 1) {
          //set first and last to be related nodes
          Iterator related = ((Node)nodes.get(0)).getRelated();
          first = (Node)related.next();
          last = (Node)related.next();
         
          edges.addAll(((Node)nodes.get(0)).getEdges());
        }
        else {
         
          //get the node of degree != 2 adjacent to first node in set
          Node node = (Node)nodes.get(0);
          Iterator rel = node.getRelated();
          first = (Node)rel.next();
          if (first.equals(nodes.get(1))) first = (Node)rel.next();
        
          //get the node of degree != 2 adjacent to last node in set
          node = (Node)nodes.get(nodes.size()-1);
          rel = node.getRelated();
          last = (Node)rel.next();
          if (last.equals(nodes.get(nodes.size()-2))) last = (Node)rel.next();
         
          //check to see that the first node is not of degree 2, if it is we
          // have a set of nodes that forms a cycle with no bifurcations
          // set first to point to node at index 1, and last index x, also
          // remove node at index 0 from node set so it doesn't get removed
          if (first.getDegree() == 2) {
            first = (Node)nodes.get(1);
            last = (Node)nodes.get(0);
            first = last = (Node)nodes.get(0);
           
            //remove first node from list so that it doesn't get deleted
            nodes.remove(0);
          }
         
          //add edge between first node in set, and the node of degree != 2
          // that is adjacent to it
          edges.add(first.getEdge((Node)nodes.get(0)));
         
          //add middle edges
          for (int i = 1; i < nodes.size(); i++) {
            Node curr = (Node)nodes.get(i);
            Node prev = (Node)nodes.get(i-1);
            edges.add(curr.getEdge(prev));
          }
        
          //add edge between last node in set, and the node of degree != 2
          // that is adjacent to it
          edges.add(last.getEdge((Node)nodes.get(nodes.size()-1)));
        }
       
        //merge the underlying objects of the edges into a single object
        Object obj = m_merger.merge(edges);
       
        //remove the old nodes from the graph
        m_builder.removeNodes(nodes);
       
        //add merged object to the generator
       
        //create the new edge
        Edge newEdge = m_builder.buildEdge(first, last);
       
        //set the underlying object to be the merged object we created
        m_merger.setMergedObject(newEdge, obj, edges);
       
        //add the edge to the builder
        m_builder.addEdge(newEdge);
      }
   
      return(true)
    }
    return(false)
  }
 
//  /**
//   * Sets the object for the newly created edge.
//   *
//   * @param newEdge The edge created to represented the merged object.
//   * @param merged The merged object.
//   */
//  public void setObject(Edge newEdge, Object merged) {
//    newEdge.setObject(merged);
//  }
 
  /**
   * Merges the underlying objects represented by a number of edges into a
   * single object.
   *   
   * @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net
   *
   */
  public static interface EdgeMerger {
   
    /**
     * Creates a single object from collection of underlying objects represented
     * by a collection of edges.
     *
     * @param edges A collection of edges.
     *
     * @return A single object.
     */
    public Object merge(List edges);
   
    /**
     * Sets the object for the edge created to represented the merged object.
     *
     * @param newEdge The edge created to represent the merged object.
     * @param merged The merged object.
     * @param edges The original edges that were merged
     */
    public void setMergedObject(Edge newEdge, Object merged, List edges);
  }
 
}
TOP

Related Classes of org.geotools.graph.util.graph.GraphFuser$EdgeMerger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.