Package org.geotools.geometry.iso.operation.overlay

Source Code of org.geotools.geometry.iso.operation.overlay.PolygonBuilder

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*   
*    (C) 2001-2006  Vivid Solutions
*    (C) 2001-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.geometry.iso.operation.overlay;

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

import org.geotools.geometry.iso.coordinate.EnvelopeImpl;
import org.geotools.geometry.iso.primitive.RingImpl;
import org.geotools.geometry.iso.primitive.RingImplUnsafe;
import org.geotools.geometry.iso.primitive.SurfaceImpl;
import org.geotools.geometry.iso.topograph2D.Coordinate;
import org.geotools.geometry.iso.topograph2D.DirectedEdge;
import org.geotools.geometry.iso.topograph2D.EdgeRing;
import org.geotools.geometry.iso.topograph2D.Envelope;
import org.geotools.geometry.iso.topograph2D.PlanarGraph;
import org.geotools.geometry.iso.topograph2D.util.CoordinateArrays;
import org.geotools.geometry.iso.util.Assert;
import org.geotools.geometry.iso.util.algorithm2D.CGAlgorithms;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.primitive.Ring;
import org.opengis.geometry.primitive.Surface;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
* Forms {@link Surface}s out of a graph of {@link DirectedEdge}s. The edges
* to use are marked as being in the result Area.
* <p>
*
*
*
*
* @source $URL$
*/
public class PolygonBuilder {
    final static int X = 0;
    final static int Y = 1;
    final static int Z = 2;
   
  //private FeatGeomFactoryImpl featGeomyFactory;
    private CoordinateReferenceSystem crs;

  private CGAlgorithms cga;

  // private List dirEdgeList;
  // private NodeMap nodes;
  private List shellList = new ArrayList();

  public PolygonBuilder(CoordinateReferenceSystem crs, CGAlgorithms cga) {
    this.crs = crs;
    this.cga = cga;
  }

  /**
   * Add a complete graph. The graph is assumed to contain one or more
   * polygons, possibly with holes.
   */
  public void add(PlanarGraph graph) {
    add(graph.getEdgeEnds(), graph.getNodes());
  }

  /**
   * Add a set of edges and nodes, which form a graph. The graph is assumed to
   * contain one or more polygons, possibly with holes.
   */
  public void add(Collection dirEdges, Collection nodes) {
    PlanarGraph.linkResultDirectedEdges(nodes);
    List maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
    List freeHoleList = new ArrayList();
    List edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, shellList,
        freeHoleList);
    this.sortShellsAndHoles(edgeRings, shellList, freeHoleList);
    this.placeFreeHoles(shellList, freeHoleList);
    // Assert: every hole on freeHoleList has a shell assigned to it
  }

  /**
   *
   * @return
   */
  public List getPolygons() {
    List resultPolyList = this.computeSurfaces(shellList);
    return resultPolyList;
  }

  /**
   * for all DirectedEdges in result, form them into MaximalEdgeRings
   */
  private List buildMaximalEdgeRings(Collection dirEdges) {
    List maxEdgeRings = new ArrayList();
    for (Iterator it = dirEdges.iterator(); it.hasNext();) {
      DirectedEdge de = (DirectedEdge) it.next();
      if (de.isInResult() && de.getLabel().isArea()) {
        // if this edge has not yet been processed
        if (de.getEdgeRing() == null) {
          MaximalEdgeRing er = new MaximalEdgeRing(de,
              crs, cga);
          maxEdgeRings.add(er);
          er.setInResult();
          // System.out.println("max node degree = " +
          // er.getMaxDegree());
        }
      }
    }
    return maxEdgeRings;
  }

  private List buildMinimalEdgeRings(List maxEdgeRings, List shellList,
      List freeHoleList) {
    List edgeRings = new ArrayList();
    for (Iterator it = maxEdgeRings.iterator(); it.hasNext();) {
      MaximalEdgeRing er = (MaximalEdgeRing) it.next();
      if (er.getMaxNodeDegree() > 2) {
        er.linkDirectedEdgesForMinimalEdgeRings();
        List minEdgeRings = er.buildMinimalRings();
        // at this point we can go ahead and attempt to place holes, if
        // this EdgeRing is a polygon
        EdgeRing shell = findShell(minEdgeRings);
        if (shell != null) {
          placePolygonHoles(shell, minEdgeRings);
          shellList.add(shell);
        } else {
          freeHoleList.addAll(minEdgeRings);
        }
      } else {
        edgeRings.add(er);
      }
    }
    return edgeRings;
  }

  /**
   * This method takes a list of MinimalEdgeRings derived from a
   * MaximalEdgeRing, and tests whether they form a Polygon. This is the case
   * if there is a single shell in the list. In this case the shell is
   * returned. The other possibility is that they are a series of connected
   * holes, in which case no shell is returned.
   *
   * @return the shell EdgeRing, if there is one
   * @return null, if all the rings are holes
   */
  private EdgeRing findShell(List minEdgeRings) {
    int shellCount = 0;
    EdgeRing shell = null;
    for (Iterator it = minEdgeRings.iterator(); it.hasNext();) {
      EdgeRing er = (MinimalEdgeRing) it.next();
      if (!er.isHole()) {
        shell = er;
        shellCount++;
      }
    }
    Assert.isTrue(shellCount <= 1,
        "found two shells in MinimalEdgeRing list");
    return shell;
  }

  /**
   * This method assigns the holes for a Polygon (formed from a list of
   * MinimalEdgeRings) to its shell. Determining the holes for a
   * MinimalEdgeRing polygon serves two purposes:
   * <ul>
   * <li>it is faster than using a point-in-polygon check later on.
   * <li>it ensures correctness, since if the PIP test was used the point
   * chosen might lie on the shell, which might return an incorrect result
   * from the PIP test
   * </ul>
   */
  private void placePolygonHoles(EdgeRing shell, List minEdgeRings) {
    for (Iterator it = minEdgeRings.iterator(); it.hasNext();) {
      MinimalEdgeRing er = (MinimalEdgeRing) it.next();
      if (er.isHole()) {
        er.setShell(shell);
      }
    }
  }

  /**
   * For all rings in the input list, determine whether the ring is a shell or
   * a hole and add it to the appropriate list. Due to the way the
   * DirectedEdges were linked, a ring is a shell if it is oriented CW, a hole
   * otherwise.
   */
  private void sortShellsAndHoles(List edgeRings, List shellList,
      List freeHoleList) {
    for (Iterator it = edgeRings.iterator(); it.hasNext();) {
      EdgeRing er = (EdgeRing) it.next();
      // er.setInResult();
      if (er.isHole()) {
        freeHoleList.add(er);
      } else {
        shellList.add(er);
      }
    }
  }

  /**
   * This method determines finds a containing shell for all holes which have
   * not yet been assigned to a shell. These "free" holes should all be
   * <b>properly</b> contained in their parent shells, so it is safe to use
   * the <code>findEdgeRingContaining</code> method. (This is the case
   * because any holes which are NOT properly contained (i.e. are connected to
   * their parent shell) would have formed part of a MaximalEdgeRing and been
   * handled in a previous step).
   */
  private void placeFreeHoles(List shellList, List freeHoleList) {
    for (Iterator it = freeHoleList.iterator(); it.hasNext();) {
      EdgeRing hole = (EdgeRing) it.next();
      // only place this hole if it doesn't yet have a shell
      if (hole.getShell() == null) {
        EdgeRing shell = findEdgeRingContaining(hole, shellList);
        Assert
            .isTrue(shell != null,
                "unable to assign hole to a shell");
        hole.setShell(shell);
      }
    }
  }

  /**
   * Find the innermost enclosing shell EdgeRing containing the argument
   * EdgeRing, if any. The innermost enclosing ring is the <i>smallest</i>
   * enclosing ring. The algorithm used depends on the fact that: <br>
   * ring A contains ring B if envelope(ring A) contains envelope(ring B)
   * <br>
   * This routine is only safe to use if the chosen point of the hole is known
   * to be properly contained in a shell (which is guaranteed to be the case
   * if the hole does not touch its shell)
   *
   * @return containing EdgeRing, if there is one
   * @return null if no containing EdgeRing is found
   */
  private EdgeRing findEdgeRingContaining(EdgeRing testEr, List shellList) {

    Ring testRing = testEr.getRing();
    org.geotools.geometry.iso.coordinate.EnvelopeImpl env = (EnvelopeImpl) testRing
        .getEnvelope();
    Envelope testEnv = new Envelope(env.getLowerCorner().getOrdinate(X), env
        .getUpperCorner().getOrdinate(X), env.getLowerCorner().getOrdinate(Y), env
        .getUpperCorner().getOrdinate(Y));
    // Take a point on the ring to do the point in ring test
    DirectPosition dp = testRing.getRepresentativePoint();
    Coordinate testPt = new Coordinate(dp.getCoordinate());

    EdgeRing minShell = null;
    Envelope minEnv = null;
    for (Iterator it = shellList.iterator(); it.hasNext();) {
      EdgeRing tryShell = (EdgeRing) it.next();
      Ring tryRing = tryShell.getRing();

      env = (EnvelopeImpl) tryRing.getEnvelope();
      Envelope tryEnv = new Envelope(env.getLowerCorner().getOrdinate(X), env
          .getUpperCorner().getOrdinate(X), env.getLowerCorner().getOrdinate(Y), env
          .getUpperCorner().getOrdinate(Y));

      if (minShell != null) {
        env = (EnvelopeImpl) minShell.getRing().getEnvelope();
        minEnv = new Envelope(env.getLowerCorner().getOrdinate(X), env
            .getUpperCorner().getOrdinate(X), env.getLowerCorner().getOrdinate(Y),
            env.getUpperCorner().getOrdinate(Y));
      }

      boolean isContained = false;
      if (tryEnv.contains(testEnv)
          && CGAlgorithms.isPointInRing(testPt, CoordinateArrays
              .toCoordinateArray( (((RingImplUnsafe) tryRing).asDirectPositions()) ))) {
        isContained = true;
      }
      // check if this new containing ring is smaller than the current
      // minimum ring
      if (isContained) {
        if (minShell == null || minEnv.contains(tryEnv)) {
          minShell = tryShell;
        }
      }
    }
    return minShell;
  }

  private List computeSurfaces(List shellList) {
    List resultPolyList = new ArrayList();
    // add Polygons for all shells
    for (Iterator it = shellList.iterator(); it.hasNext();) {
      EdgeRing er = (EdgeRing) it.next();

      SurfaceImpl poly = er.toPolygon();
      resultPolyList.add(poly);
    }
    return resultPolyList;
  }

  /**
   * Checks the current set of shells (with their associated holes) to see if
   * any of them contain the point.
   */
  public boolean containsPoint(Coordinate p) {
    for (Iterator it = shellList.iterator(); it.hasNext();) {
      EdgeRing er = (EdgeRing) it.next();
      if (er.containsPoint(p))
        return true;
    }
    return false;
  }

}
TOP

Related Classes of org.geotools.geometry.iso.operation.overlay.PolygonBuilder

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.