All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vividsolutions.jts.geomgraph.EdgeEndStar Maven / Gradle / Ivy

Go to download

The JTS Topology Suite is an API for modelling and manipulating 2-dimensional linear geometry. It provides numerous geometric predicates and functions. JTS conforms to the Simple Features Specification for SQL published by the Open GIS Consortium.

There is a newer version: 1.13
Show newest version



/*
 * 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.vividsolutions.jts.geomgraph;

import java.io.PrintStream;
import java.util.*;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.algorithm.*;
import com.vividsolutions.jts.algorithm.locate.SimplePointInAreaLocator;
import com.vividsolutions.jts.util.*;

/**
 * A EdgeEndStar is an ordered list of EdgeEnds around a node.
 * They are maintained in CCW order (starting with the positive x-axis) around the node
 * for efficient lookup and topology building.
 *
 * @version 1.7
 */
abstract public class EdgeEndStar
{

  /**
   * A map which maintains the edges in sorted order around the node
   */
  protected Map edgeMap = new TreeMap();
  /**
   * A list of all outgoing edges in the result, in CCW order
   */
  protected List edgeList;
  /**
   * The location of the point for this star in Geometry i Areas
   */
  private int[] ptInAreaLocation = { Location.NONE, Location.NONE };

  public EdgeEndStar()
  {

  }

  /**
   * Insert a EdgeEnd into this EdgeEndStar
   */
  abstract public void insert(EdgeEnd e);

  /**
   * Insert an EdgeEnd into the map, and clear the edgeList cache,
   * since the list of edges has now changed
   */
  protected void insertEdgeEnd(EdgeEnd e, Object obj)
  {
    edgeMap.put(e, obj);
    edgeList = null;  // edge list has changed - clear the cache
  }

  /**
   * @return the coordinate for the node this star is based at
   */
  public Coordinate getCoordinate()
  {
    Iterator it = iterator();
    if (! it.hasNext()) return null;
    EdgeEnd e = (EdgeEnd) it.next();
    return e.getCoordinate();
  }
  public int getDegree()
  {
    return edgeMap.size();
  }

  /**
   * Iterator access to the ordered list of edges is optimized by
   * copying the map collection to a list.  (This assumes that
   * once an iterator is requested, it is likely that insertion into
   * the map is complete).
   */
  public Iterator iterator()
  {
    return getEdges().iterator();
  }
  public List getEdges()
  {
    if (edgeList == null) {
      edgeList = new ArrayList(edgeMap.values());
    }
    return edgeList;
  }
  public EdgeEnd getNextCW(EdgeEnd ee)
  {
    getEdges();
    int i = edgeList.indexOf(ee);
    int iNextCW = i - 1;
    if (i == 0)
      iNextCW = edgeList.size() - 1;
    return (EdgeEnd) edgeList.get(iNextCW);
  }

  public void computeLabelling(GeometryGraph[] geomGraph)
  {
    computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
    // Propagate side labels  around the edges in the star
    // for each parent Geometry
//Debug.print(this);
    propagateSideLabels(0);
//Debug.print(this);
//Debug.printIfWatch(this);
    propagateSideLabels(1);
//Debug.print(this);
//Debug.printIfWatch(this);

    /**
     * If there are edges that still have null labels for a geometry
     * this must be because there are no area edges for that geometry incident on this node.
     * In this case, to label the edge for that geometry we must test whether the
     * edge is in the interior of the geometry.
     * To do this it suffices to determine whether the node for the edge is in the interior of an area.
     * If so, the edge has location INTERIOR for the geometry.
     * In all other cases (e.g. the node is on a line, on a point, or not on the geometry at all) the edge
     * has the location EXTERIOR for the geometry.
     * 

* Note that the edge cannot be on the BOUNDARY of the geometry, since then * there would have been a parallel edge from the Geometry at this node also labelled BOUNDARY * and this edge would have been labelled in the previous step. *

* This code causes a problem when dimensional collapses are present, since it may try and * determine the location of a node where a dimensional collapse has occurred. * The point should be considered to be on the EXTERIOR * of the polygon, but locate() will return INTERIOR, since it is passed * the original Geometry, not the collapsed version. * * If there are incident edges which are Line edges labelled BOUNDARY, * then they must be edges resulting from dimensional collapses. * In this case the other edges can be labelled EXTERIOR for this Geometry. * * MD 8/11/01 - NOT TRUE! The collapsed edges may in fact be in the interior of the Geometry, * which means the other edges should be labelled INTERIOR for this Geometry. * Not sure how solve this... Possibly labelling needs to be split into several phases: * area label propagation, symLabel merging, then finally null label resolution. */ boolean[] hasDimensionalCollapseEdge = { false, false }; for (Iterator it = iterator(); it.hasNext(); ) { EdgeEnd e = (EdgeEnd) it.next(); Label label = e.getLabel(); for (int geomi = 0; geomi < 2; geomi++) { if (label.isLine(geomi) && label.getLocation(geomi) == Location.BOUNDARY) hasDimensionalCollapseEdge[geomi] = true; } } //Debug.print(this); for (Iterator it = iterator(); it.hasNext(); ) { EdgeEnd e = (EdgeEnd) it.next(); Label label = e.getLabel(); //Debug.println(e); for (int geomi = 0; geomi < 2; geomi++) { if (label.isAnyNull(geomi)) { int loc = Location.NONE; if (hasDimensionalCollapseEdge[geomi]) { loc = Location.EXTERIOR; } else { Coordinate p = e.getCoordinate(); loc = getLocation(geomi, p, geomGraph); } label.setAllLocationsIfNull(geomi, loc); } } //Debug.println(e); } //Debug.print(this); //Debug.printIfWatch(this); } private void computeEdgeEndLabels(BoundaryNodeRule boundaryNodeRule) { // Compute edge label for each EdgeEnd for (Iterator it = iterator(); it.hasNext(); ) { EdgeEnd ee = (EdgeEnd) it.next(); ee.computeLabel(boundaryNodeRule); } } private int getLocation(int geomIndex, Coordinate p, GeometryGraph[] geom) { // compute location only on demand if (ptInAreaLocation[geomIndex] == Location.NONE) { ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry()); } return ptInAreaLocation[geomIndex]; } public boolean isAreaLabelsConsistent(GeometryGraph geomGraph) { computeEdgeEndLabels(geomGraph.getBoundaryNodeRule()); return checkAreaLabelsConsistent(0); } private boolean checkAreaLabelsConsistent(int geomIndex) { // Since edges are stored in CCW order around the node, // As we move around the ring we move from the right to the left side of the edge List edges = getEdges(); // if no edges, trivially consistent if (edges.size() <= 0) return true; // initialize startLoc to location of last L side (if any) int lastEdgeIndex = edges.size() - 1; Label startLabel = ((EdgeEnd) edges.get(lastEdgeIndex)).getLabel(); int startLoc = startLabel.getLocation(geomIndex, Position.LEFT); Assert.isTrue(startLoc != Location.NONE, "Found unlabelled area edge"); int currLoc = startLoc; for (Iterator it = iterator(); it.hasNext(); ) { EdgeEnd e = (EdgeEnd) it.next(); Label label = e.getLabel(); // we assume that we are only checking a area Assert.isTrue(label.isArea(geomIndex), "Found non-area edge"); int leftLoc = label.getLocation(geomIndex, Position.LEFT); int rightLoc = label.getLocation(geomIndex, Position.RIGHT); //System.out.println(leftLoc + " " + rightLoc); //Debug.print(this); // check that edge is really a boundary between inside and outside! if (leftLoc == rightLoc) { return false; } // check side location conflict //Assert.isTrue(rightLoc == currLoc, "side location conflict " + locStr); if (rightLoc != currLoc) { //Debug.print(this); return false; } currLoc = leftLoc; } return true; } void propagateSideLabels(int geomIndex) { // Since edges are stored in CCW order around the node, // As we move around the ring we move from the right to the left side of the edge int startLoc = Location.NONE ; // initialize loc to location of last L side (if any) //System.out.println("finding start location"); for (Iterator it = iterator(); it.hasNext(); ) { EdgeEnd e = (EdgeEnd) it.next(); Label label = e.getLabel(); if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) != Location.NONE) startLoc = label.getLocation(geomIndex, Position.LEFT); } // no labelled sides found, so no labels to propagate if (startLoc == Location.NONE) return; int currLoc = startLoc; for (Iterator it = iterator(); it.hasNext(); ) { EdgeEnd e = (EdgeEnd) it.next(); Label label = e.getLabel(); // set null ON values to be in current location if (label.getLocation(geomIndex, Position.ON) == Location.NONE) label.setLocation(geomIndex, Position.ON, currLoc); // set side labels (if any) if (label.isArea(geomIndex)) { int leftLoc = label.getLocation(geomIndex, Position.LEFT); int rightLoc = label.getLocation(geomIndex, Position.RIGHT); // if there is a right location, that is the next location to propagate if (rightLoc != Location.NONE) { //Debug.print(rightLoc != currLoc, this); if (rightLoc != currLoc) throw new TopologyException("side location conflict", e.getCoordinate()); if (leftLoc == Location.NONE) { Assert.shouldNeverReachHere("found single null side (at " + e.getCoordinate() + ")"); } currLoc = leftLoc; } else { /** RHS is null - LHS must be null too. * This must be an edge from the other geometry, which has no location * labelling for this geometry. This edge must lie wholly inside or outside * the other geometry (which is determined by the current location). * Assign both sides to be the current location. */ Assert.isTrue(label.getLocation(geomIndex, Position.LEFT) == Location.NONE, "found single null side"); label.setLocation(geomIndex, Position.RIGHT, currLoc); label.setLocation(geomIndex, Position.LEFT, currLoc); } } } } public int findIndex(EdgeEnd eSearch) { iterator(); // force edgelist to be computed for (int i = 0; i < edgeList.size(); i++ ) { EdgeEnd e = (EdgeEnd) edgeList.get(i); if (e == eSearch) return i; } return -1; } public void print(PrintStream out) { System.out.println("EdgeEndStar: " + getCoordinate()); for (Iterator it = iterator(); it.hasNext(); ) { EdgeEnd e = (EdgeEnd) it.next(); e.print(out); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy