/* * The JTS Topology Suite is a collection of Java classes that * implement the fundamental operations required to validate a given * geo-spatial data set to a known topological specification. * * Copyright (C) 2001 Vivid Solutions * * 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; either * version 2.1 of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.revolsys.geometry.geomgraph; /** * @version 1.7 */ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import com.revolsys.geometry.algorithm.CGAlgorithms; import com.revolsys.geometry.algorithm.CGAlgorithmsDD; import com.revolsys.geometry.model.Location; import com.revolsys.geometry.model.Point; /** * The computation of the <code>IntersectionMatrix</code> relies on the use of a structure * called a "topology graph". The topology graph contains nodes and edges * corresponding to the nodes and line segments of a <code>Geometry</code>. Each * node and edge in the graph is labeled with its topological location relative to * the source geometry. * <P> * Note that there is no requirement that points of self-intersection be a vertex. * Thus to obtain a correct topology graph, <code>Geometry</code>s must be * self-noded before constructing their graphs. * <P> * Two fundamental operations are supported by topology graphs: * <UL> * <LI>Computing the intersections between all the edges and nodes of a single graph * <LI>Computing the intersections between the edges and nodes of two different graphs * </UL> * * @version 1.7 */ public class PlanarGraph { /** * For nodes in the Collection, link the DirectedEdges at the node that are in the result. * This allows clients to link only a subset of nodes in the graph, for * efficiency (because they know that only a subset is of interest). */ public static void linkResultDirectedEdges(final Collection<Node> nodes) { for (final Node node : nodes) { ((DirectedEdgeStar)node.getEdges()).linkResultDirectedEdges(); } } protected List<DirectedEdge> edgeEndList = new ArrayList<>(); protected List<Edge> edges = new ArrayList<>(); private final NodeMap nodes; public PlanarGraph() { this(new NodeFactory()); } public PlanarGraph(final NodeFactory nodeFact) { this.nodes = new NodeMap(nodeFact); } public void add(final DirectedEdge e) { this.nodes.add(e); this.edgeEndList.add(e); } /** * Add a set of edges to the graph. For each edge two DirectedEdges * will be created. DirectedEdges are NOT linked by this method. */ public void addEdges(final List<Edge> edgesToAdd) { // Construct a newll the nodes for the edges for (final Edge e : edgesToAdd) { this.edges.add(e); final DirectedEdge de1 = new DirectedEdge(e, true); final DirectedEdge de2 = new DirectedEdge(e, false); de1.setSym(de2); de2.setSym(de1); add(de1); add(de2); } } public Node addNode(final Node node) { return this.nodes.addNode(node); } public Node addNode(final Point coord) { return this.nodes.addNode(coord); } void debugPrint(final Object o) { System.out.print(o); } void debugPrintln(final Object o) { System.out.println(o); } public Iterable<Edge> edges() { return this.edges; } /** * @return the node if found; null otherwise */ public Node find(final Point coord) { return this.nodes.find(coord); } /** * Returns the edge whose first two coordinates are p0 and p1 * * @return the edge, if found * <code>null</code> if the edge was not found */ public Edge findEdge(final Point p0, final Point p1) { for (int i = 0; i < this.edges.size(); i++) { final Edge e = this.edges.get(i); if (p0.equals(e.getPoint(0)) && p1.equals(e.getPoint(1))) { return e; } } return null; } /** * Returns the EdgeEnd which has edge e as its base edge * (MD 18 Feb 2002 - this should return a pair of edges) * * @return the edge, if found * <code>null</code> if the edge was not found */ public EdgeEnd findEdgeEnd(final Edge e) { for (final Object element : getEdgeEnds()) { final EdgeEnd ee = (EdgeEnd)element; if (ee.getEdge() == e) { return ee; } } return null; } /** * Returns the edge which starts at p0 and whose first segment is * parallel to p1 * * @return the edge, if found * <code>null</code> if the edge was not found */ public Edge findEdgeInSameDirection(final Point p0, final Point p1) { for (int i = 0; i < this.edges.size(); i++) { final Edge e = this.edges.get(i); if (matchInSameDirection(p0, p1, e.getPoint(0), e.getPoint(1))) { return e; } if (matchInSameDirection(p0, p1, e.getPoint(e.getVertexCount() - 1), e.getPoint(e.getVertexCount() - 2))) { return e; } } return null; } public Collection<DirectedEdge> getEdgeEnds() { return this.edgeEndList; } public Iterator<Edge> getEdgeIterator() { return this.edges.iterator(); } public Iterator<Node> getNodeIterator() { return this.nodes.iterator(); } public NodeMap getNodeMap() { return this.nodes; } public Collection<Node> getNodes() { return this.nodes.values(); } protected void insertEdge(final Edge e) { this.edges.add(e); } public boolean isBoundaryNode(final int geomIndex, final Point coord) { final Node node = this.nodes.find(coord); if (node == null) { return false; } final Label label = node.getLabel(); if (label != null && label.getLocation(geomIndex) == Location.BOUNDARY) { return true; } return false; } /** * Link the DirectedEdges at the nodes of the graph. * This allows clients to link only a subset of nodes in the graph, for * efficiency (because they know that only a subset is of interest). */ public void linkAllDirectedEdges() { for (final Object element : this.nodes) { final Node node = (Node)element; ((DirectedEdgeStar)node.getEdges()).linkAllDirectedEdges(); } } /** * Link the DirectedEdges at the nodes of the graph. * This allows clients to link only a subset of nodes in the graph, for * efficiency (because they know that only a subset is of interest). */ public void linkResultDirectedEdges() { for (final Object element : this.nodes) { final Node node = (Node)element; ((DirectedEdgeStar)node.getEdges()).linkResultDirectedEdges(); } } /** * The coordinate pairs match if they define line segments lying in the same direction. * E.g. the segments are parallel and in the same quadrant * (as opposed to parallel and opposite!). */ private boolean matchInSameDirection(final Point p0, final Point p1, final Point ep0, final Point ep1) { if (!p0.equals(ep0)) { return false; } if (CGAlgorithmsDD.orientationIndex(p0, p1, ep1) == CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) == Quadrant.quadrant(ep0, ep1)) { return true; } return false; } }