Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / vividsolutions / jts / operation / overlay / SnapPolygonBuilder.java @ 38047

History | View | Annotate | Download (10.6 KB)

1
/*
2
 * Created on 09-oct-2006
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 *
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
21
 *
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 *
34
 *    or
35
 *
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 *
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
/* CVS MESSAGES:
45
*
46
* $Id: SnapPolygonBuilder.java 38047 2012-03-01 17:46:39Z amaneiro $
47
* $Log$
48
* Revision 1.2  2007-03-06 17:08:59  caballero
49
* Exceptions
50
*
51
* Revision 1.1  2006/12/04 19:30:23  azabala
52
* *** empty log message ***
53
*
54
* Revision 1.2  2006/10/17 18:25:53  azabala
55
* *** empty log message ***
56
*
57
* Revision 1.1  2006/10/09 19:10:56  azabala
58
* First version in CVS
59
*
60
*
61
*/
62
package com.vividsolutions.jts.operation.overlay;
63

    
64
import java.util.ArrayList;
65
import java.util.Collection;
66
import java.util.Iterator;
67
import java.util.List;
68

    
69
import com.vividsolutions.jts.algorithm.CGAlgorithms;
70
import com.vividsolutions.jts.geom.Coordinate;
71
import com.vividsolutions.jts.geom.Envelope;
72
import com.vividsolutions.jts.geom.GeometryFactory;
73
import com.vividsolutions.jts.geom.LinearRing;
74
import com.vividsolutions.jts.geom.Polygon;
75
import com.vividsolutions.jts.geomgraph.DirectedEdge;
76
import com.vividsolutions.jts.geomgraph.EdgeRing;
77
import com.vividsolutions.jts.geomgraph.SnappingPlanarGraph;
78
import com.vividsolutions.jts.util.Assert;
79

    
80
public class SnapPolygonBuilder extends PolygonBuilder {
81

    
82
         private GeometryFactory geometryFactory;
83
          //private List dirEdgeList;
84
          //private NodeMap nodes;
85
          private List shellList        = new ArrayList();
86

    
87
          public SnapPolygonBuilder(GeometryFactory geometryFactory)
88
          {
89
                super(geometryFactory);
90
            this.geometryFactory = geometryFactory;
91
          }
92

    
93
          /**
94
           * Add a complete graph.
95
           * The graph is assumed to contain one or more polygons,
96
           * possibly with holes.
97
           */
98
          public void add(SnappingPlanarGraph graph)
99
          {
100
            add(graph.getEdgeEnds(), graph.getNodes());
101
          }
102

    
103
          /**
104
           * Add a set of edges and nodes, which form a graph.
105
           * The graph is assumed to contain one or more polygons,
106
           * possibly with holes.
107
           */
108
          public void add(Collection dirEdges, Collection nodes)
109
          {
110
                SnappingPlanarGraph.linkResultDirectedEdges(nodes);
111
            List maxEdgeRings = buildMaximalEdgeRings(dirEdges);
112
            List freeHoleList = new ArrayList();
113
            List edgeRings = buildMinimalEdgeRings(maxEdgeRings, shellList, freeHoleList);
114
            sortShellsAndHoles(edgeRings, shellList, freeHoleList);
115
            placeFreeHoles(shellList, freeHoleList);
116
          }
117

    
118
          public List getPolygons()
119
          {
120
            List resultPolyList = computePolygons(shellList);
121
            return resultPolyList;
122
          }
123

    
124

    
125
          /**
126
           * for all DirectedEdges in result, form them into MaximalEdgeRings
127
           */
128
          private List buildMaximalEdgeRings(Collection dirEdges)
129
          {
130
            List maxEdgeRings     = new ArrayList();
131
            for (Iterator it = dirEdges.iterator(); it.hasNext(); ) {
132
              DirectedEdge de = (DirectedEdge) it.next();
133
              if (de.isInResult() && de.getLabel().isArea() ) {
134
                // if this edge has not yet been processed
135
                if (de.getEdgeRing() == null) {
136
                  MaximalEdgeRing er = new MaximalEdgeRing(de, geometryFactory);
137
                  maxEdgeRings.add(er);
138
                  er.setInResult();
139
//        System.out.println("max node degree = " + er.getMaxDegree());
140
                }
141
              }
142
            }
143
            return maxEdgeRings;
144
          }
145

    
146
          private List buildMinimalEdgeRings(List maxEdgeRings, List shellList, List freeHoleList)
147
          {
148
            List edgeRings = new ArrayList();
149
            for (Iterator it = maxEdgeRings.iterator(); it.hasNext(); ) {
150
              MaximalEdgeRing er = (MaximalEdgeRing) it.next();
151
              if (er.getMaxNodeDegree() > 2) {
152
                er.linkDirectedEdgesForMinimalEdgeRings();
153
                List minEdgeRings = er.buildMinimalRings();
154
                // at this point we can go ahead and attempt to place holes, if this EdgeRing is a polygon
155
                EdgeRing shell = findShell(minEdgeRings);
156
                if (shell != null) {
157
                  placePolygonHoles(shell, minEdgeRings);
158
                  shellList.add(shell);
159
                }
160
                else {
161
                  freeHoleList.addAll(minEdgeRings);
162
                }
163
              }
164
              else {
165
                edgeRings.add(er);
166
              }
167
            }
168
            return edgeRings;
169
          }
170

    
171
          /**
172
           * This method takes a list of MinimalEdgeRings derived from a MaximalEdgeRing,
173
           * and tests whether they form a Polygon.  This is the case if there is a single shell
174
           * in the list.  In this case the shell is returned.
175
           * The other possibility is that they are a series of connected holes, in which case
176
           * no shell is returned.
177
           *
178
           * @return the shell EdgeRing, if there is one
179
           * @return null, if all the rings are holes
180
           */
181
          private EdgeRing findShell(List minEdgeRings)
182
          {
183
            int shellCount = 0;
184
            EdgeRing shell = null;
185
            for (Iterator it = minEdgeRings.iterator(); it.hasNext(); ) {
186
              EdgeRing er = (MinimalEdgeRing) it.next();
187
              if (! er.isHole()) {
188
                shell = er;
189
                shellCount++;
190
              }
191
            }
192
            Assert.isTrue(shellCount <= 1, "found two shells in MinimalEdgeRing list");
193
            return shell;
194
          }
195
          /**
196
           * This method assigns the holes for a Polygon (formed from a list of
197
           * MinimalEdgeRings) to its shell.
198
           * Determining the holes for a MinimalEdgeRing polygon serves two purposes:
199
           * <ul>
200
           * <li>it is faster than using a point-in-polygon check later on.
201
           * <li>it ensures correctness, since if the PIP test was used the point
202
           * chosen might lie on the shell, which might return an incorrect result from the
203
           * PIP test
204
           * </ul>
205
           */
206
          private void placePolygonHoles(EdgeRing shell, List minEdgeRings)
207
          {
208
            for (Iterator it = minEdgeRings.iterator(); it.hasNext(); ) {
209
              MinimalEdgeRing er = (MinimalEdgeRing) it.next();
210
              if (er.isHole()) {
211
                er.setShell(shell);
212
              }
213
            }
214
          }
215
          /**
216
           * For all rings in the input list,
217
           * determine whether the ring is a shell or a hole
218
           * and add it to the appropriate list.
219
           * Due to the way the DirectedEdges were linked,
220
           * a ring is a shell if it is oriented CW, a hole otherwise.
221
           */
222
          private void sortShellsAndHoles(List edgeRings, List shellList, List freeHoleList)
223
          {
224
            for (Iterator it = edgeRings.iterator(); it.hasNext(); ) {
225
              EdgeRing er = (EdgeRing) it.next();
226
//              er.setInResult();
227
              if (er.isHole() ) {
228
                freeHoleList.add(er);
229
              }
230
              else {
231
                shellList.add(er);
232
              }
233
            }
234
          }
235
          /**
236
           * This method determines finds a containing shell for all holes
237
           * which have not yet been assigned to a shell.
238
           * These "free" holes should
239
           * all be <b>properly</b> contained in their parent shells, so it is safe to use the
240
           * <code>findEdgeRingContaining</code> method.
241
           * (This is the case because any holes which are NOT
242
           * properly contained (i.e. are connected to their
243
           * parent shell) would have formed part of a MaximalEdgeRing
244
           * and been handled in a previous step).
245
           */
246
          private void placeFreeHoles(List shellList, List freeHoleList)
247
          {
248
            for (Iterator it = freeHoleList.iterator(); it.hasNext(); ) {
249
              EdgeRing hole = (EdgeRing) it.next();
250
              // only place this hole if it doesn't yet have a shell
251
              if (hole.getShell() == null) {
252
                EdgeRing shell = findEdgeRingContaining(hole, shellList);
253
                Assert.isTrue(shell != null, "unable to assign hole to a shell");
254
                hole.setShell(shell);
255
              }
256
            }
257
          }
258
          /**
259
           * Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any.
260
           * The innermost enclosing ring is the <i>smallest</i> enclosing ring.
261
           * The algorithm used depends on the fact that:
262
           * <br>
263
           *  ring A contains ring B iff envelope(ring A) contains envelope(ring B)
264
           * <br>
265
           * This routine is only safe to use if the chosen point of the hole
266
           * is known to be properly contained in a shell
267
           * (which is guaranteed to be the case if the hole does not touch its shell)
268
           *
269
           * @return containing EdgeRing, if there is one
270
           * @return null if no containing EdgeRing is found
271
           */
272
          
273
          
274
          //TODO Estudiar como meter el snapping
275
          private EdgeRing findEdgeRingContaining(EdgeRing testEr, List shellList)
276
          {
277
            LinearRing testRing = testEr.getLinearRing();
278
            Envelope testEnv = testRing.getEnvelopeInternal();
279
            Coordinate testPt = testRing.getCoordinateN(0);
280

    
281
            EdgeRing minShell = null;
282
            Envelope minEnv = null;
283
            for (Iterator it = shellList.iterator(); it.hasNext(); ) {
284
              EdgeRing tryShell = (EdgeRing) it.next();
285
              LinearRing tryRing = tryShell.getLinearRing();
286
              Envelope tryEnv = tryRing.getEnvelopeInternal();
287
              if (minShell != null) minEnv = minShell.getLinearRing().getEnvelopeInternal();
288
              boolean isContained = false;
289
              if (tryEnv.contains(testEnv)
290
                  && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates()) )
291
                isContained = true;
292
              // check if this new containing ring is smaller than the current minimum ring
293
              if (isContained) {
294
                if (minShell == null
295
                    || minEnv.contains(tryEnv)) {
296
                  minShell = tryShell;
297
                }
298
              }
299
            }
300
            return minShell;
301
          }
302
          private List computePolygons(List shellList)
303
          {
304
            List resultPolyList   = new ArrayList();
305
            // add Polygons for all shells
306
            for (Iterator it = shellList.iterator(); it.hasNext(); ) {
307
              EdgeRing er = (EdgeRing) it.next();
308
              Polygon poly = er.toPolygon(geometryFactory);
309
              resultPolyList.add(poly);
310
            }
311
            return resultPolyList;
312
          }
313

    
314
          /**
315
           * Checks the current set of shells (with their associated holes) to
316
           * see if any of them contain the point.
317
           */
318
          
319
          //TODO METER SNAPPING
320
          public boolean containsPoint(Coordinate p)
321
          {
322
            for (Iterator it = shellList.iterator(); it.hasNext(); ) {
323
              EdgeRing er = (EdgeRing) it.next();
324
              if (er.containsPoint(p))
325
               return true;
326
            }
327
            return false;
328
          }
329

    
330

    
331

    
332
}
333