Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / vividsolutions / jts / operation / overlay / SnapPolygonBuilder.java @ 9178

History | View | Annotate | Download (10.7 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 9178 2006-12-04 19:30:23Z azabala $
47
* $Log$
48
* Revision 1.1  2006-12-04 19:30:23  azabala
49
* *** empty log message ***
50
*
51
* Revision 1.2  2006/10/17 18:25:53  azabala
52
* *** empty log message ***
53
*
54
* Revision 1.1  2006/10/09 19:10:56  azabala
55
* First version in CVS
56
*
57
*
58
*/
59
package com.vividsolutions.jts.operation.overlay;
60

    
61
import java.util.ArrayList;
62
import java.util.Collection;
63
import java.util.Iterator;
64
import java.util.List;
65

    
66
import com.vividsolutions.jts.algorithm.CGAlgorithms;
67
import com.vividsolutions.jts.geom.Coordinate;
68
import com.vividsolutions.jts.geom.Envelope;
69
import com.vividsolutions.jts.geom.GeometryFactory;
70
import com.vividsolutions.jts.geom.LinearRing;
71
import com.vividsolutions.jts.geom.Polygon;
72
import com.vividsolutions.jts.geomgraph.DirectedEdge;
73
import com.vividsolutions.jts.geomgraph.EdgeRing;
74
import com.vividsolutions.jts.geomgraph.SnappingPlanarGraph;
75
import com.vividsolutions.jts.operation.overlay.MaximalEdgeRing;
76
import com.vividsolutions.jts.operation.overlay.PolygonBuilder;
77
import com.vividsolutions.jts.util.Assert;
78

    
79
public class SnapPolygonBuilder extends PolygonBuilder {
80

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

    
87
          public SnapPolygonBuilder(GeometryFactory geometryFactory, 
88
                          CGAlgorithms cga)
89
          {
90
                super(geometryFactory, cga);
91
            this.geometryFactory = geometryFactory;
92
            this.cga = cga;
93
          }
94

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

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

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

    
126

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

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

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

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

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

    
332

    
333

    
334
}
335