Class MCIndexPointSnapper

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12  
 13 package org.locationtech.jts.noding.snapround;
 14  
 15 import org.locationtech.jts.geom.Envelope;
 16 import org.locationtech.jts.index.ItemVisitor;
 17 import org.locationtech.jts.index.SpatialIndex;
 18 import org.locationtech.jts.index.chain.MonotoneChain;
 19 import org.locationtech.jts.index.chain.MonotoneChainSelectAction;
 20 import org.locationtech.jts.index.strtree.STRtree;
 21 import org.locationtech.jts.noding.NodedSegmentString;
 22 import org.locationtech.jts.noding.SegmentString;
 23  
 24 /**
 25  * "Snaps" all {@link SegmentString}s in a {@link SpatialIndex} containing
 26  * {@link MonotoneChain}s to a given {@link HotPixel}.
 27  *
 28  * @version 1.7
 29  */
 30 public class MCIndexPointSnapper
 31 {
 32   //public static final int nSnaps = 0;
 33  
 34   private STRtree index;
 35  
 36   public MCIndexPointSnapper(SpatialIndex index) {
 37     this.index = (STRtree) index;
 38   }
 39  
 40   /**
 41    * Snaps (nodes) all interacting segments to this hot pixel.
 42    * The hot pixel may represent a vertex of an edge,
 43    * in which case this routine uses the optimization
 44    * of not noding the vertex itself
 45    *
 46    * @param hotPixel the hot pixel to snap to
 47    * @param parentEdge the edge containing the vertex, if applicable, or <code>null</code>
 48    * @param hotPixelVertexIndex the index of the hotPixel vertex, if applicable, or -1
 49    * @return <code>true</code> if a node was added for this pixel
 50    */
 51   public boolean snap(HotPixel hotPixel, SegmentString parentEdge, int hotPixelVertexIndex)
 52   {
 53     final Envelope pixelEnv = hotPixel.getSafeEnvelope();
 54     final HotPixelSnapAction hotPixelSnapAction = new HotPixelSnapAction(hotPixel, parentEdge, hotPixelVertexIndex);
 55  
 56     index.query(pixelEnv, new ItemVisitor() {
 57       public void visitItem(Object item) {
 58         MonotoneChain testChain = (MonotoneChain) item;
 59         testChain.select(pixelEnv, hotPixelSnapAction);
 60       }
 61     }
 62     );
 63     return hotPixelSnapAction.isNodeAdded();
 64   }
 65  
 66   public boolean snap(HotPixel hotPixel)
 67   {
 68     return snap(hotPixel, null, -1);
 69   }
 70  
 71   public static class HotPixelSnapAction
 72       extends MonotoneChainSelectAction
 73   {
 74     private HotPixel hotPixel;
 75     private SegmentString parentEdge;
 76     // is -1 if hotPixel is not a vertex
 77     private int hotPixelVertexIndex;
 78     private boolean isNodeAdded = false;
 79  
 80     public HotPixelSnapAction(HotPixel hotPixel, SegmentString parentEdge, int hotPixelVertexIndex)
 81     {
 82       this.hotPixel = hotPixel;
 83       this.parentEdge = parentEdge;
 84       this.hotPixelVertexIndex = hotPixelVertexIndex;
 85     }
 86  
 87     /**
 88      * Reports whether the HotPixel caused a node to be added in any target
 89      * segmentString (including its own). If so, the HotPixel must be added as a
 90      * node as well.
 91      * 
 92      * @return true if a node was added in any target segmentString.
 93      */
 94     public boolean isNodeAdded() {
 95       return isNodeAdded;
 96     }
 97  
 98     /**
 99      * Check if a segment of the monotone chain intersects
100      * the hot pixel vertex and introduce a snap node if so.
101      * Optimized to avoid noding segments which
102      * contain the vertex (which otherwise 
103      * would cause every vertex to be noded).
104      */
105     public void select(MonotoneChain mc, int startIndex)
106     {
107         NodedSegmentString ss = (NodedSegmentString) mc.getContext();
108       /**
109        * Check to avoid snapping a hotPixel vertex to the same vertex.
110        * This method is called for segments which intersects the 
111        * hot pixel,
112        * so need to check if either end of the segment is equal to the hot pixel
113        * and if so, do not snap.
114        * 
115        * Sep 22 2012 - MD - currently do need to snap to every vertex,
116        * since otherwise the testCollapse1 test in SnapRoundingTest fails.
117        */
118       if (parentEdge == ss) {
119         // exit if hotpixel is equal to endpoint of target segment
120         if (startIndex == hotPixelVertexIndex
121             || startIndex + 1 == hotPixelVertexIndex)
122           return;
123       }
124       // snap and record if a node was created
125       isNodeAdded |= hotPixel.addSnappedNode(ss, startIndex);
126     }
127  
128   }
129  
130 }
131