Class FastNodingValidator

  1  
  2 /*
  3  * Copyright (c) 2016 Vivid Solutions.
  4  *
  5  * All rights reserved. This program and the accompanying materials
  6  * are made available under the terms of the Eclipse Public License 2.0
  7  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  8  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  9  * and the Eclipse Distribution License is available at
 10  *
 11  * http://www.eclipse.org/org/documents/edl-v10.php.
 12  */
 13 package org.locationtech.jts.noding;
 14  
 15 import java.util.Collection;
 16 import java.util.List;
 17  
 18 import org.locationtech.jts.algorithm.LineIntersector;
 19 import org.locationtech.jts.algorithm.RobustLineIntersector;
 20 import org.locationtech.jts.geom.Coordinate;
 21 import org.locationtech.jts.geom.TopologyException;
 22 import org.locationtech.jts.io.WKTWriter;
 23  
 24  
 25 /**
 26  * Validates that a collection of {@link SegmentString}s is correctly noded.
 27  * Indexing is used to improve performance.
 28  * By default validation stops after a single 
 29  * non-noded intersection is detected. 
 30  * Alternatively, it can be requested to detect all intersections
 31  * by using {@link #setFindAllIntersections(boolean)}.
 32  * <p>
 33  * The validator does not check for topology collapse situations
 34  * (e.g. where two segment strings are fully co-incident).
 35  * <p> 
 36  * The validator checks for the following situations which indicated incorrect noding:
 37  * <ul>
 38  * <li>Proper intersections between segments (i.e. the intersection is interior to both segments)
 39  * <li>Intersections at an interior vertex (i.e. with an endpoint or another interior vertex)
 40  * </ul>
 41  * <p>
 42  * The client may either test the {@link #isValid()} condition, 
 43  * or request that a suitable {@link TopologyException} be thrown.
 44  *
 45  * @version 1.7
 46  * 
 47  * @see NodingIntersectionFinder
 48  */
 49 public class FastNodingValidator 
 50 {
 51   /**
 52    * Gets a list of all intersections found.
 53    * Intersections are represented as {@link Coordinate}s.
 54    * List is empty if none were found.
 55    * 
 56    * @param segStrings a collection of SegmentStrings
 57    * @return a list of Coordinate
 58    */
 59   public static List computeIntersections(Collection segStrings)
 60   {
 61     FastNodingValidator nv = new FastNodingValidator(segStrings);
 62     nv.setFindAllIntersections(true);
 63     nv.isValid();
 64     return nv.getIntersections();
 65   }
 66   
 67   private LineIntersector li = new RobustLineIntersector();
 68  
 69   private Collection segStrings;
 70   private boolean findAllIntersections = false;
 71   private NodingIntersectionFinder segInt = null;
 72   private boolean isValid = true;
 73   
 74   /**
 75    * Creates a new noding validator for a given set of linework.
 76    * 
 77    * @param segStrings a collection of {@link SegmentString}s
 78    */
 79   public FastNodingValidator(Collection segStrings)
 80   {
 81     this.segStrings = segStrings;
 82   }
 83  
 84   public void setFindAllIntersections(boolean findAllIntersections)
 85   {
 86     this.findAllIntersections = findAllIntersections;
 87   }
 88   
 89   /**
 90    * Gets a list of all intersections found.
 91    * Intersections are represented as {@link Coordinate}s.
 92    * List is empty if none were found.
 93    * 
 94    * @return a list of Coordinate
 95    */
 96   public List getIntersections()
 97   {
 98     return segInt.getIntersections();
 99   }
100  
101   /**
102    * Checks for an intersection and 
103    * reports if one is found.
104    * 
105    * @return true if the arrangement contains an interior intersection
106    */
107   public boolean isValid()
108   {
109       execute();
110       return isValid;
111   }
112   
113   /**
114    * Returns an error message indicating the segments containing
115    * the intersection.
116    * 
117    * @return an error message documenting the intersection location
118    */
119   public String getErrorMessage()
120   {
121       if (isValid) return "no intersections found";
122       
123         Coordinate[] intSegs = segInt.getIntersectionSegments();
124     return "found non-noded intersection between "
125         + WKTWriter.toLineString(intSegs[0], intSegs[1])
126         + " and "
127         + WKTWriter.toLineString(intSegs[2], intSegs[3]);
128   }
129   
130   /**
131    * Checks for an intersection and throws
132    * a TopologyException if one is found.
133    *
134    * @throws TopologyException if an intersection is found
135    */
136   public void checkValid()
137   {
138       execute();
139       if (! isValid)
140           throw new TopologyException(getErrorMessage(), segInt.getIntersection());
141   }
142  
143   private void execute()
144   {
145       if (segInt != null
146           return;
147     checkInteriorIntersections();
148   }
149  
150   private void checkInteriorIntersections()
151   {
152       /**
153        * MD - It may even be reliable to simply check whether 
154        * end segments (of SegmentStrings) have an interior intersection,
155        * since noding should have split any true interior intersections already.
156        */
157       isValid = true;
158       segInt = new NodingIntersectionFinder(li);
159     segInt.setFindAllIntersections(findAllIntersections);
160       MCIndexNoder noder = new MCIndexNoder();
161       noder.setSegmentIntersector(segInt);
162       noder.computeNodes(segStrings);
163       if (segInt.hasIntersection()) {
164           isValid = false;
165           return;
166       }
167   }
168   
169 }
170