Revision 9178
trunk/libraries/libFMap/src/com/vividsolutions/jts/operation/overlay/SnapPolygonBuilder.java | ||
---|---|---|
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$ |
|
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 |
|
|
0 | 336 |
trunk/libraries/libFMap/src/com/vividsolutions/jts/operation/overlay/SnappingOverlayOperation.java | ||
---|---|---|
1 |
/* |
|
2 |
* Created on 12-sep-2006 by azabala |
|
3 |
* |
|
4 |
*/ |
|
5 |
package com.vividsolutions.jts.operation.overlay; |
|
6 |
|
|
7 |
import java.util.ArrayList; |
|
8 |
import java.util.Iterator; |
|
9 |
import java.util.List; |
|
10 |
|
|
11 |
import com.vividsolutions.jts.algorithm.CGAlgorithms; |
|
12 |
import com.vividsolutions.jts.algorithm.LineIntersector; |
|
13 |
import com.vividsolutions.jts.algorithm.PointLocator; |
|
14 |
import com.vividsolutions.jts.algorithms.SnapPointLocator; |
|
15 |
import com.vividsolutions.jts.geom.Coordinate; |
|
16 |
import com.vividsolutions.jts.geom.Geometry; |
|
17 |
import com.vividsolutions.jts.geom.GeometryFactory; |
|
18 |
import com.vividsolutions.jts.geom.Location; |
|
19 |
import com.vividsolutions.jts.geom.Point; |
|
20 |
import com.vividsolutions.jts.geomgraph.Depth; |
|
21 |
import com.vividsolutions.jts.geomgraph.DirectedEdge; |
|
22 |
import com.vividsolutions.jts.geomgraph.DirectedEdgeStar; |
|
23 |
import com.vividsolutions.jts.geomgraph.Edge; |
|
24 |
import com.vividsolutions.jts.geomgraph.EdgeList; |
|
25 |
import com.vividsolutions.jts.geomgraph.Label; |
|
26 |
import com.vividsolutions.jts.geomgraph.Node; |
|
27 |
import com.vividsolutions.jts.geomgraph.PlanarGraph; |
|
28 |
import com.vividsolutions.jts.geomgraph.Position; |
|
29 |
import com.vividsolutions.jts.geomgraph.SnapEdgeList; |
|
30 |
import com.vividsolutions.jts.geomgraph.SnappingGeometryGraph; |
|
31 |
import com.vividsolutions.jts.geomgraph.SnappingPlanarGraph; |
|
32 |
import com.vividsolutions.jts.io.ParseException; |
|
33 |
import com.vividsolutions.jts.operation.overlay.LineBuilder; |
|
34 |
import com.vividsolutions.jts.operation.overlay.OverlayNodeFactory; |
|
35 |
import com.vividsolutions.jts.operation.overlay.OverlayOp; |
|
36 |
import com.vividsolutions.jts.operation.overlay.PointBuilder; |
|
37 |
import com.vividsolutions.jts.operation.overlay.PolygonBuilder; |
|
38 |
import com.vividsolutions.jts.util.Assert; |
|
39 |
|
|
40 |
/** |
|
41 |
* TODO |
|
42 |
* El codigo esta lleno de coordinateA.distance(coordinateB) |
|
43 |
* <= snapTolerance; |
|
44 |
* |
|
45 |
* CAMBIAR POR SnapCoordinate, de forma que equals haga |
|
46 |
* esta comprobacin |
|
47 |
* |
|
48 |
* |
|
49 |
*/ |
|
50 |
public class SnappingOverlayOperation extends OverlayOp { |
|
51 |
|
|
52 |
protected final SnapPointLocator ptLocator = new SnapPointLocator(); |
|
53 |
protected GeometryFactory geomFact; |
|
54 |
protected Geometry resultGeom; |
|
55 |
|
|
56 |
protected LineIntersector li; |
|
57 |
|
|
58 |
protected double snapTolerance; |
|
59 |
|
|
60 |
|
|
61 |
/*Planar graph of the overlay operation*/ |
|
62 |
protected SnappingPlanarGraph graph; |
|
63 |
|
|
64 |
/*Geometry graph of each individual geometry*/ |
|
65 |
protected SnappingGeometryGraph[] arg; |
|
66 |
|
|
67 |
|
|
68 |
/*It saves all the new edges resulting from intersections of |
|
69 |
* edges of geometries A and B. It is a temporal repository, before |
|
70 |
* to save them in SnappingPlanarGraph*/ |
|
71 |
protected SnapEdgeList edgeList = null; |
|
72 |
|
|
73 |
|
|
74 |
/* |
|
75 |
* El resultado de una operacion de overlay puede contener |
|
76 |
* puntos, lineas y poligonos. |
|
77 |
* */ |
|
78 |
protected List resultPolyList = new ArrayList(); |
|
79 |
|
|
80 |
protected List resultLineList = new ArrayList(); |
|
81 |
|
|
82 |
protected List resultPointList = new ArrayList(); |
|
83 |
|
|
84 |
|
|
85 |
|
|
86 |
public static Geometry overlayOp(Geometry geom0, Geometry geom1, |
|
87 |
int opCode, double tolerance) { |
|
88 |
SnappingOverlayOperation gov = new SnappingOverlayOperation(geom0, |
|
89 |
geom1, tolerance); |
|
90 |
Geometry geomOv = gov.getResultGeometry(opCode); |
|
91 |
return geomOv; |
|
92 |
} |
|
93 |
|
|
94 |
|
|
95 |
|
|
96 |
public SnappingOverlayOperation(Geometry g0, Geometry g1, double tolerance) { |
|
97 |
super(g0, g1); |
|
98 |
graph = new SnappingPlanarGraph(new OverlayNodeFactory(), tolerance); |
|
99 |
arg = new SnappingGeometryGraph[2]; |
|
100 |
arg[0] = new SnappingGeometryGraph(tolerance, 0, g0); |
|
101 |
arg[1] = new SnappingGeometryGraph(tolerance, 1, g1); |
|
102 |
geomFact = g0.getFactory(); |
|
103 |
li = new SnapLineIntersector(tolerance); |
|
104 |
edgeList = new SnapEdgeList(tolerance); |
|
105 |
this.snapTolerance = tolerance; |
|
106 |
} |
|
107 |
|
|
108 |
public Geometry getResultGeometry(int funcCode) { |
|
109 |
computeOverlay(funcCode); |
|
110 |
return resultGeom; |
|
111 |
} |
|
112 |
|
|
113 |
|
|
114 |
public PlanarGraph getGraph() { |
|
115 |
return graph; |
|
116 |
} |
|
117 |
|
|
118 |
|
|
119 |
private boolean isReused = false; |
|
120 |
/** |
|
121 |
* Metodo de utilidad cuando vayamos a intersectar la geometria |
|
122 |
* g1 con varias geometrias (g2, g3, g4, .... , etc.) |
|
123 |
* |
|
124 |
* Los calculos basicos no se repiten para g1 |
|
125 |
* |
|
126 |
* */ |
|
127 |
|
|
128 |
|
|
129 |
public void setSecondGeometry(Geometry geometry){ |
|
130 |
Geometry g0 = arg[0].getGeometry(); |
|
131 |
if (g0.getPrecisionModel().compareTo(geometry.getPrecisionModel()) >= 0) |
|
132 |
setComputationPrecision(g0.getPrecisionModel()); |
|
133 |
else |
|
134 |
setComputationPrecision(geometry.getPrecisionModel()); |
|
135 |
graph = new SnappingPlanarGraph(new OverlayNodeFactory(), snapTolerance); |
|
136 |
|
|
137 |
|
|
138 |
/* |
|
139 |
*TODO |
|
140 |
*Deberiamos borrar todos los nodos del GeometryGraph, |
|
141 |
*o solo las intersecciones????? |
|
142 |
*De todos modos, aunque borrasemos los nodos, getBoundaryNodes |
|
143 |
*devolveria nodos cacheados |
|
144 |
* |
|
145 |
* TODO REVISAR |
|
146 |
* */ |
|
147 |
arg[0].clearIntersections(); |
|
148 |
|
|
149 |
|
|
150 |
|
|
151 |
arg[1] = new SnappingGeometryGraph(snapTolerance, 1, geometry); |
|
152 |
geomFact = g0.getFactory(); |
|
153 |
edgeList = new SnapEdgeList(snapTolerance); |
|
154 |
|
|
155 |
resultPolyList.clear(); |
|
156 |
resultLineList.clear(); |
|
157 |
resultPointList.clear(); |
|
158 |
|
|
159 |
resultGeom = null; |
|
160 |
|
|
161 |
isReused = true; |
|
162 |
} |
|
163 |
|
|
164 |
|
|
165 |
/* |
|
166 |
* ************************************************************ |
|
167 |
* METODO PRINCIPAL |
|
168 |
* ************************************************************ |
|
169 |
* */ |
|
170 |
private void computeOverlay(int opCode) { |
|
171 |
|
|
172 |
/* |
|
173 |
* Se copian los NODOS de las dos geometrias. |
|
174 |
* ESTO ES IMPORTANTE, PUES: |
|
175 |
* a) un punto origina un nodo. |
|
176 |
* b) una linea origina dos nodos |
|
177 |
* c) un poligono origina un nodo. |
|
178 |
* |
|
179 |
* */ |
|
180 |
copyPoints(0); |
|
181 |
copyPoints(1); |
|
182 |
if(! isReused) //esto solo se hace si no se ha calculado ya |
|
183 |
arg[0].computeSelfNodes(li, false); |
|
184 |
arg[1].computeSelfNodes(li, false); |
|
185 |
|
|
186 |
|
|
187 |
/* |
|
188 |
* Calcula las intersecciones. |
|
189 |
* Se supone que daran lugar a Nodes, ?NO? |
|
190 |
* Como resultado, cada Edge guardar? en sus EdgeIntersectionList |
|
191 |
* las intersecciones que hay en sus segmentos (EdgeIntersection). |
|
192 |
* |
|
193 |
* Estas intersecciones se representan por: |
|
194 |
* -segmento del edge en que ocurren. |
|
195 |
* -coordenada |
|
196 |
* -distancia al primer vertice del segmento |
|
197 |
* |
|
198 |
* ?OJO? COMO RESULTADO DE ESTO NO SE GENERAN EJES NUEVOS. |
|
199 |
* PARA HACER SNAP EN LAS INTERSECCIONES TENDRIAMOS QUE RETOCAR LA |
|
200 |
* CLASE EDGEINTERSECTIONLIST |
|
201 |
* |
|
202 |
* */ |
|
203 |
arg[0].computeEdgeIntersections(arg[1], li, true); |
|
204 |
/* |
|
205 |
* Ahora lo que se hace es: para cada Edge del grafo, |
|
206 |
* se parte (en funci?n de sus intersecciones) y se a?aden |
|
207 |
* los Edges fragmentados a la colecci?n baseSplitEdges |
|
208 |
* |
|
209 |
* */ |
|
210 |
|
|
211 |
List baseSplitEdges = new ArrayList(); |
|
212 |
arg[0].computeSplitEdges(baseSplitEdges); |
|
213 |
//TODO Quizas la clave est? tambien en la 2? geometria |
|
214 |
arg[1].computeSplitEdges(baseSplitEdges); |
|
215 |
|
|
216 |
|
|
217 |
/* |
|
218 |
* Edges resulting of A intersection B, that are in baseSplitEdges |
|
219 |
* Collection, are saved in EdgeList. |
|
220 |
* ?OJO? Si aparecen ejes repetidos, no se duplican (pero si |
|
221 |
* que se cambia su etiqueta) |
|
222 |
* */ |
|
223 |
//Se copian los nuevos Edges generados en EdgeList |
|
224 |
insertUniqueEdges(baseSplitEdges); |
|
225 |
|
|
226 |
|
|
227 |
/*Se etiquetan*/ |
|
228 |
computeLabelsFromDepths(); |
|
229 |
|
|
230 |
/* |
|
231 |
* Quita los Edges que hayan sufrido colapso dimensional |
|
232 |
* (en la documentac?on de JTS viene algo de esto) |
|
233 |
* */ |
|
234 |
replaceCollapsedEdges(); |
|
235 |
|
|
236 |
|
|
237 |
/* |
|
238 |
* Finalmente, se a?ade al SnappingPlanarGraph resultado los Edges |
|
239 |
* calculados como fruto de las intersecciones (contenidos en EdgeList). |
|
240 |
* |
|
241 |
* Aqu? se hace algo muy importante tambi?n: se a?aden nuevos nodos |
|
242 |
* al grafo (correspondientes con los extremos de los nuevos Edge |
|
243 |
* que no estuvieran ya en el grafo) |
|
244 |
* |
|
245 |
* */ |
|
246 |
graph.addEdges(edgeList.getEdges()); |
|
247 |
|
|
248 |
|
|
249 |
computeLabelling(); |
|
250 |
labelIncompleteNodes(); |
|
251 |
|
|
252 |
/** |
|
253 |
* The ordering of building the result Geometries is important. Areas |
|
254 |
* must be built before lines, which must be built before points. This |
|
255 |
* is so that lines which are covered by areas are not included |
|
256 |
* explicitly, and similarly for points. |
|
257 |
*/ |
|
258 |
findResultAreaEdges(opCode); |
|
259 |
cancelDuplicateResultEdges(); |
|
260 |
|
|
261 |
|
|
262 |
//TODO Todos los builders deber?n usar los metodos snap de locator |
|
263 |
SnapPolygonBuilder polyBuilder = new SnapPolygonBuilder(geomFact, cga); |
|
264 |
polyBuilder.add(graph); |
|
265 |
resultPolyList = polyBuilder.getPolygons(); |
|
266 |
|
|
267 |
LineBuilder lineBuilder = new LineBuilder(this, geomFact, ptLocator); |
|
268 |
resultLineList = lineBuilder.build(opCode); |
|
269 |
|
|
270 |
PointBuilder pointBuilder = new PointBuilder(this, geomFact, ptLocator){ |
|
271 |
public List build(int opCode) |
|
272 |
{ |
|
273 |
for (Iterator nodeit = getGraph().getNodes().iterator(); nodeit.hasNext(); ) { |
|
274 |
Node n = (Node) nodeit.next(); |
|
275 |
|
|
276 |
// filter out nodes which are known to be in the result |
|
277 |
if (n.isInResult()) |
|
278 |
continue; |
|
279 |
// if an incident edge is in the result, then the node coordinate is included already |
|
280 |
if (n.isIncidentEdgeInResult()) |
|
281 |
continue; |
|
282 |
if (n.getEdges().getDegree() == 0 || opCode == OverlayOp.INTERSECTION) { |
|
283 |
|
|
284 |
/** |
|
285 |
* For nodes on edges, only INTERSECTION can result in edge nodes being included even |
|
286 |
* if none of their incident edges are included |
|
287 |
*/ |
|
288 |
Label label = n.getLabel(); |
|
289 |
if (SnappingOverlayOperation.checkLabelLocation(label.getLocation(0), |
|
290 |
label.getLocation(1), opCode)) { |
|
291 |
filterCoveredNodeToPoint(n); |
|
292 |
} |
|
293 |
} |
|
294 |
} |
|
295 |
return resultPointList; |
|
296 |
} |
|
297 |
|
|
298 |
|
|
299 |
|
|
300 |
private void filterCoveredNodeToPoint(Node n) |
|
301 |
{ |
|
302 |
Coordinate coord = n.getCoordinate(); |
|
303 |
if (! isCoveredByLA(coord)) { |
|
304 |
Point pt = geomFact.createPoint(coord); |
|
305 |
resultPointList.add(pt); |
|
306 |
} |
|
307 |
} |
|
308 |
}; |
|
309 |
resultPointList = pointBuilder.build(opCode); |
|
310 |
|
|
311 |
// gather the results from all calculations into a single Geometry for |
|
312 |
// the result set |
|
313 |
resultGeom = computeGeometry(resultPointList, resultLineList, |
|
314 |
resultPolyList); |
|
315 |
} |
|
316 |
|
|
317 |
|
|
318 |
/** |
|
319 |
* This method will handle arguments of Location.NONE correctly |
|
320 |
* |
|
321 |
* @return true if the locations correspond to the opCode |
|
322 |
*/ |
|
323 |
public static boolean isResultOfOp(int loc0, int loc1, int opCode) |
|
324 |
{ |
|
325 |
if (loc0 == Location.BOUNDARY) loc0 = Location.INTERIOR; |
|
326 |
if (loc1 == Location.BOUNDARY) loc1 = Location.INTERIOR; |
|
327 |
switch (opCode) { |
|
328 |
case INTERSECTION: |
|
329 |
return loc0 == Location.INTERIOR |
|
330 |
&& loc1 == Location.INTERIOR; |
|
331 |
case UNION: |
|
332 |
return loc0 == Location.INTERIOR |
|
333 |
|| loc1 == Location.INTERIOR; |
|
334 |
case DIFFERENCE: |
|
335 |
return loc0 == Location.INTERIOR |
|
336 |
&& loc1 != Location.INTERIOR; |
|
337 |
case SYMDIFFERENCE: |
|
338 |
return ( loc0 == Location.INTERIOR && loc1 != Location.INTERIOR) |
|
339 |
|| ( loc0 != Location.INTERIOR && loc1 == Location.INTERIOR); |
|
340 |
} |
|
341 |
return false; |
|
342 |
} |
|
343 |
|
|
344 |
public static boolean isResultOfOp(Label label, int opCode) |
|
345 |
{ |
|
346 |
int loc0 = label.getLocation(0); |
|
347 |
int loc1 = label.getLocation(1); |
|
348 |
return isResultOfOp(loc0, loc1, opCode); |
|
349 |
} |
|
350 |
|
|
351 |
|
|
352 |
//TODO Quitar esto de aqui |
|
353 |
|
|
354 |
public static boolean checkLabelLocation(int loc0, int loc1, int opCode) |
|
355 |
{ |
|
356 |
if (loc0 == Location.BOUNDARY) loc0 = Location.INTERIOR; |
|
357 |
if (loc1 == Location.BOUNDARY) loc1 = Location.INTERIOR; |
|
358 |
switch (opCode) { |
|
359 |
case INTERSECTION: |
|
360 |
return loc0 == Location.INTERIOR |
|
361 |
&& loc1 == Location.INTERIOR; |
|
362 |
case UNION: |
|
363 |
return loc0 == Location.INTERIOR |
|
364 |
|| loc1 == Location.INTERIOR; |
|
365 |
case DIFFERENCE: |
|
366 |
return loc0 == Location.INTERIOR |
|
367 |
&& loc1 != Location.INTERIOR; |
|
368 |
case SYMDIFFERENCE: |
|
369 |
return ( loc0 == Location.INTERIOR && loc1 != Location.INTERIOR) |
|
370 |
|| ( loc0 != Location.INTERIOR && loc1 == Location.INTERIOR); |
|
371 |
} |
|
372 |
return false; |
|
373 |
} |
|
374 |
|
|
375 |
private void insertUniqueEdges(List edges) { |
|
376 |
for (Iterator i = edges.iterator(); i.hasNext();) { |
|
377 |
Edge e = (Edge) i.next(); |
|
378 |
insertUniqueEdge(e); |
|
379 |
} |
|
380 |
} |
|
381 |
|
|
382 |
/** |
|
383 |
* Insert an edge from one of the noded input graphs. Checks edges that are |
|
384 |
* inserted to see if an identical edge already exists. If so, the edge is |
|
385 |
* not inserted, but its label is merged with the existing edge. |
|
386 |
*/ |
|
387 |
protected void insertUniqueEdge(Edge e) { |
|
388 |
|
|
389 |
//TODO Crear una clase SnapEdge y SnapEdgeList puede ser necesario??? |
|
390 |
//creo que si pq SnapEdgeList mantiene una cache que no considera snap |
|
391 |
Edge existingEdge = edgeList.findEqualEdge(e); |
|
392 |
// If an identical edge already exists, simply update its label |
|
393 |
if (existingEdge != null) { |
|
394 |
Label existingLabel = existingEdge.getLabel(); |
|
395 |
Label labelToMerge = e.getLabel(); |
|
396 |
|
|
397 |
// check if new edge is in reverse direction to existing edge |
|
398 |
// if so, must flip the label before merging it |
|
399 |
if (!existingEdge.isPointwiseEqual(e)) { |
|
400 |
labelToMerge = new Label(e.getLabel()); |
|
401 |
labelToMerge.flip(); |
|
402 |
} |
|
403 |
Depth depth = existingEdge.getDepth(); |
|
404 |
// if this is the first duplicate found for this edge, initialize |
|
405 |
// the depths |
|
406 |
if (depth.isNull()) { |
|
407 |
depth.add(existingLabel); |
|
408 |
} |
|
409 |
depth.add(labelToMerge); |
|
410 |
existingLabel.merge(labelToMerge); |
|
411 |
} else { // no matching existing edge was found |
|
412 |
// add this new edge to the list of edges in this graph |
|
413 |
edgeList.add(e); |
|
414 |
} |
|
415 |
} |
|
416 |
|
|
417 |
|
|
418 |
/** |
|
419 |
* Update the labels for edges according to their depths. For each edge, the |
|
420 |
* depths are first normalized. Then, if the depths for the edge are equal, |
|
421 |
* this edge must have collapsed into a line edge. If the depths are not |
|
422 |
* equal, update the label with the locations corresponding to the depths |
|
423 |
* (i.e. a depth of 0 corresponds to a Location of EXTERIOR, a depth of 1 |
|
424 |
* corresponds to INTERIOR) |
|
425 |
*/ |
|
426 |
private void computeLabelsFromDepths() { |
|
427 |
for (Iterator it = edgeList.iterator(); it.hasNext();) { |
|
428 |
Edge e = (Edge) it.next(); |
|
429 |
Label lbl = e.getLabel(); |
|
430 |
Depth depth = e.getDepth(); |
|
431 |
/* |
|
432 |
* Only check edges for which there were duplicates, since these are |
|
433 |
* the only ones which might be the result of dimensional collapses. |
|
434 |
*/ |
|
435 |
if (!depth.isNull()) { |
|
436 |
depth.normalize(); |
|
437 |
for (int i = 0; i < 2; i++) { |
|
438 |
if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) { |
|
439 |
/** |
|
440 |
* if the depths are equal, this edge is the result of |
|
441 |
* the dimensional collapse of two or more edges. It has |
|
442 |
* the same location on both sides of the edge, so it |
|
443 |
* has collapsed to a line. |
|
444 |
*/ |
|
445 |
if (depth.getDelta(i) == 0) { |
|
446 |
lbl.toLine(i); |
|
447 |
} else { |
|
448 |
/** |
|
449 |
* This edge may be the result of a dimensional |
|
450 |
* collapse, but it still has different locations on |
|
451 |
* both sides. The label of the edge must be updated |
|
452 |
* to reflect the resultant side locations indicated |
|
453 |
* by the depth values. |
|
454 |
*/ |
|
455 |
Assert |
|
456 |
.isTrue(!depth.isNull(i, Position.LEFT), |
|
457 |
"depth of LEFT side has not been initialized"); |
|
458 |
lbl.setLocation(i, Position.LEFT, depth |
|
459 |
.getLocation(i, Position.LEFT)); |
|
460 |
Assert |
|
461 |
.isTrue(!depth.isNull(i, Position.RIGHT), |
|
462 |
"depth of RIGHT side has not been initialized"); |
|
463 |
lbl.setLocation(i, Position.RIGHT, depth |
|
464 |
.getLocation(i, Position.RIGHT)); |
|
465 |
} |
|
466 |
} |
|
467 |
} |
|
468 |
} |
|
469 |
} |
|
470 |
} |
|
471 |
|
|
472 |
/** |
|
473 |
* If edges which have undergone dimensional collapse are found, replace |
|
474 |
* them with a new edge which is a L edge |
|
475 |
*/ |
|
476 |
private void replaceCollapsedEdges() { |
|
477 |
List newEdges = new ArrayList(); |
|
478 |
for (Iterator it = edgeList.iterator(); it.hasNext();) { |
|
479 |
Edge e = (Edge) it.next(); |
|
480 |
if (e.isCollapsed()) { |
|
481 |
// Debug.print(e); |
|
482 |
it.remove(); |
|
483 |
newEdges.add(e.getCollapsedEdge()); |
|
484 |
} |
|
485 |
} |
|
486 |
edgeList.addAll(newEdges); |
|
487 |
} |
|
488 |
|
|
489 |
/** |
|
490 |
* Copy all nodes from an arg geometry into this graph. The node label in |
|
491 |
* the arg geometry overrides any previously computed label for that |
|
492 |
* argIndex. (E.g. a node may be an intersection node with a previously |
|
493 |
* computed label of BOUNDARY, but in the original arg Geometry it is |
|
494 |
* actually in the interior due to the Boundary Determination Rule) |
|
495 |
*/ |
|
496 |
private void copyPoints(int argIndex) { |
|
497 |
for (Iterator i = arg[argIndex].getNodeIterator(); i.hasNext();) { |
|
498 |
Node graphNode = (Node) i.next(); |
|
499 |
Node newNode = graph.addNode(graphNode.getCoordinate()); |
|
500 |
newNode.setLabel(argIndex, graphNode.getLabel().getLocation( |
|
501 |
argIndex)); |
|
502 |
} |
|
503 |
} |
|
504 |
|
|
505 |
/** |
|
506 |
* Compute initial labelling for all DirectedEdges at each node. In this |
|
507 |
* step, DirectedEdges will acquire a complete labelling (i.e. one with |
|
508 |
* labels for both Geometries) only if they are incident on a node which has |
|
509 |
* edges for both Geometries |
|
510 |
*/ |
|
511 |
private void computeLabelling() { |
|
512 |
for (Iterator nodeit = graph.getNodes().iterator(); nodeit.hasNext();) { |
|
513 |
Node node = (Node) nodeit.next(); |
|
514 |
node.getEdges().computeLabelling(arg); |
|
515 |
} |
|
516 |
mergeSymLabels(); |
|
517 |
updateNodeLabelling(); |
|
518 |
|
|
519 |
} |
|
520 |
|
|
521 |
/** |
|
522 |
* For nodes which have edges from only one Geometry incident on them, the |
|
523 |
* previous step will have left their dirEdges with no labelling for the |
|
524 |
* other Geometry. However, the sym dirEdge may have a labelling for the |
|
525 |
* other Geometry, so merge the two labels. |
|
526 |
*/ |
|
527 |
private void mergeSymLabels() { |
|
528 |
for (Iterator nodeit = graph.getNodes().iterator(); nodeit.hasNext();) { |
|
529 |
Node node = (Node) nodeit.next(); |
|
530 |
((DirectedEdgeStar) node.getEdges()).mergeSymLabels(); |
|
531 |
} |
|
532 |
} |
|
533 |
|
|
534 |
private void updateNodeLabelling() { |
|
535 |
// update the labels for nodes |
|
536 |
// The label for a node is updated from the edges incident on it |
|
537 |
// (Note that a node may have already been labelled |
|
538 |
// because it is a point in one of the input geometries) |
|
539 |
for (Iterator nodeit = graph.getNodes().iterator(); nodeit.hasNext();) { |
|
540 |
Node node = (Node) nodeit.next(); |
|
541 |
Label lbl = ((DirectedEdgeStar) node.getEdges()).getLabel(); |
|
542 |
Label otherLbl = node.getLabel(); |
|
543 |
otherLbl.merge(lbl); |
|
544 |
} |
|
545 |
} |
|
546 |
|
|
547 |
/** |
|
548 |
* Incomplete nodes are nodes whose labels are incomplete. (e.g. the |
|
549 |
* location for one Geometry is null). These are either isolated nodes, or |
|
550 |
* nodes which have edges from only a single Geometry incident on them. |
|
551 |
* |
|
552 |
* Isolated nodes are found because nodes in one graph which don't intersect |
|
553 |
* nodes in the other are not completely labelled by the initial process of |
|
554 |
* adding nodes to the nodeList. To complete the labelling we need to check |
|
555 |
* for nodes that lie in the interior of edges, and in the interior of |
|
556 |
* areas. |
|
557 |
* <p> |
|
558 |
* When each node labelling is completed, the labelling of the incident |
|
559 |
* edges is updated, to complete their labelling as well. |
|
560 |
*/ |
|
561 |
private void labelIncompleteNodes() { |
|
562 |
for (Iterator ni = graph.getNodes().iterator(); ni.hasNext();) { |
|
563 |
Node n = (Node) ni.next(); |
|
564 |
Label label = n.getLabel(); |
|
565 |
if (n.isIsolated()) { |
|
566 |
if (label.isNull(0)) |
|
567 |
labelIncompleteNode(n, 0); |
|
568 |
else |
|
569 |
labelIncompleteNode(n, 1); |
|
570 |
} |
|
571 |
// now update the labelling for the DirectedEdges incident on this |
|
572 |
// node |
|
573 |
((DirectedEdgeStar) n.getEdges()).updateLabelling(label); |
|
574 |
} |
|
575 |
} |
|
576 |
|
|
577 |
/** |
|
578 |
* Label an isolated node with its relationship to the target geometry. |
|
579 |
*/ |
|
580 |
private void labelIncompleteNode(Node n, int targetIndex) { |
|
581 |
//TODO Ver si el pointLocator deberia snapear |
|
582 |
Coordinate coord = n.getCoordinate(); |
|
583 |
Geometry geom = arg[targetIndex].getGeometry(); |
|
584 |
int loc = ptLocator.locate(coord, geom, snapTolerance); |
|
585 |
n.getLabel().setLocation(targetIndex, loc); |
|
586 |
} |
|
587 |
|
|
588 |
/** |
|
589 |
* Find all edges whose label indicates that they are in the result area(s), |
|
590 |
* according to the operation being performed. Since we want polygon shells |
|
591 |
* to be oriented CW, choose dirEdges with the interior of the result on the |
|
592 |
* RHS. Mark them as being in the result. Interior Area edges are the result |
|
593 |
* of dimensional collapses. They do not form part of the result area |
|
594 |
* boundary. |
|
595 |
*/ |
|
596 |
private void findResultAreaEdges(int opCode) { |
|
597 |
for (Iterator it = graph.getEdgeEnds().iterator(); it.hasNext();) { |
|
598 |
DirectedEdge de = (DirectedEdge) it.next(); |
|
599 |
// mark all dirEdges with the appropriate label |
|
600 |
Label label = de.getLabel(); |
|
601 |
if (label.isArea() |
|
602 |
&& !de.isInteriorAreaEdge() |
|
603 |
&& isResultOfOp(label.getLocation(0, Position.RIGHT), label |
|
604 |
.getLocation(1, Position.RIGHT), opCode)) { |
|
605 |
de.setInResult(true); |
|
606 |
// Debug.print("in result "); Debug.println(de); |
|
607 |
} |
|
608 |
} |
|
609 |
} |
|
610 |
|
|
611 |
/** |
|
612 |
* If both a dirEdge and its sym are marked as being in the result, cancel |
|
613 |
* them out. |
|
614 |
*/ |
|
615 |
private void cancelDuplicateResultEdges() { |
|
616 |
// remove any dirEdges whose sym is also included |
|
617 |
// (they "cancel each other out") |
|
618 |
for (Iterator it = graph.getEdgeEnds().iterator(); it.hasNext();) { |
|
619 |
DirectedEdge de = (DirectedEdge) it.next(); |
|
620 |
DirectedEdge sym = de.getSym(); |
|
621 |
if (de.isInResult() && sym.isInResult()) { |
|
622 |
de.setInResult(false); |
|
623 |
sym.setInResult(false); |
|
624 |
// Debug.print("cancelled "); Debug.println(de); |
|
625 |
// Debug.println(sym); |
|
626 |
} |
|
627 |
} |
|
628 |
} |
|
629 |
|
|
630 |
/** |
|
631 |
* This method is used to decide if a point node should be included in the |
|
632 |
* result or not. |
|
633 |
* |
|
634 |
* @return true if the coord point is covered by a result Line or Area |
|
635 |
* geometry |
|
636 |
*/ |
|
637 |
public boolean isCoveredByLA(Coordinate coord) { |
|
638 |
if (isCovered(coord, resultLineList)) |
|
639 |
return true; |
|
640 |
if (isCovered(coord, resultPolyList)) |
|
641 |
return true; |
|
642 |
return false; |
|
643 |
} |
|
644 |
|
|
645 |
/** |
|
646 |
* This method is used to decide if an L edge should be included in the |
|
647 |
* result or not. |
|
648 |
* |
|
649 |
* @return true if the coord point is covered by a result Area geometry |
|
650 |
*/ |
|
651 |
public boolean isCoveredByA(Coordinate coord) { |
|
652 |
if (isCovered(coord, resultPolyList)) |
|
653 |
return true; |
|
654 |
return false; |
|
655 |
} |
|
656 |
|
|
657 |
/** |
|
658 |
* @return true if the coord is located in the interior or boundary of a |
|
659 |
* geometry in the list. |
|
660 |
*/ |
|
661 |
private boolean isCovered(Coordinate coord, List geomList) { |
|
662 |
for (Iterator it = geomList.iterator(); it.hasNext();) { |
|
663 |
Geometry geom = (Geometry) it.next(); |
|
664 |
int loc = ptLocator.locate(coord, geom, snapTolerance); |
|
665 |
if (loc != Location.EXTERIOR) |
|
666 |
return true; |
|
667 |
} |
|
668 |
return false; |
|
669 |
} |
|
670 |
|
|
671 |
private Geometry computeGeometry(List resultPointList, List resultLineList, |
|
672 |
List resultPolyList) { |
|
673 |
List geomList = new ArrayList(); |
|
674 |
// element geometries of the result are always in the order P,L,A |
|
675 |
geomList.addAll(resultPointList); |
|
676 |
geomList.addAll(resultLineList); |
|
677 |
geomList.addAll(resultPolyList); |
|
678 |
// build the most specific geometry possible |
|
679 |
return geomFact.buildGeometry(geomList); |
|
680 |
} |
|
681 |
|
|
682 |
|
|
683 |
public static void main(String[] args){ |
|
684 |
GeometryFactory factory = new GeometryFactory(); |
|
685 |
com.vividsolutions.jts.io.WKTReader reader = new com.vividsolutions.jts.io.WKTReader(factory); |
|
686 |
Geometry a, b, c, d; |
|
687 |
try { |
|
688 |
|
|
689 |
//Snap en un extremo y en un vertice intermedio |
|
690 |
a = reader.read("LINESTRING(0.001 0.001, 5.001 5.001)"); |
|
691 |
b = reader.read("LINESTRING(2.1 -3, 0.0 -0.001, -2.22 4.88, 10.0 10.0, 5.002 5.002)"); |
|
692 |
System.out.println(SnappingOverlayOperation.overlayOp(a, b, OverlayOp.INTERSECTION, 0.01)); |
|
693 |
// //snap mediante l?neas paralelas |
|
694 |
c = reader.read("LINESTRING(0 0, 5 0, 10 0.001)"); |
|
695 |
d = reader.read("LINESTRING(0.001 0.01, 5.001 0.002, 10.001 0.002)"); |
|
696 |
long t0 = System.currentTimeMillis(); |
|
697 |
System.out.println(SnappingOverlayOperation.overlayOp(c, d, OverlayOp.INTERSECTION, 0.1)); |
|
698 |
long t1 = System.currentTimeMillis(); |
|
699 |
System.out.println(OverlayOp.overlayOp(c, d, OverlayOp.INTERSECTION)); |
|
700 |
long t2 = System.currentTimeMillis(); |
|
701 |
System.out.println("Con snap: "+(t1-t0)+" ms"); |
|
702 |
System.out.println("Sin snap: "+(t2-t1)+" ms"); |
|
703 |
|
|
704 |
d = reader.read("LINESTRING(0 0, 5 0, 10 0.001)"); |
|
705 |
System.out.println(OverlayOp.overlayOp(c, d, OverlayOp.INTERSECTION)); |
|
706 |
|
|
707 |
//lineas paralelas a una distancia superior a la de snap |
|
708 |
//(para comprobar el criterio de paralelismo en LineIntersector |
|
709 |
c = reader.read("LINESTRING(0 0, 5 0, 10 0.001)"); |
|
710 |
d = reader.read("LINESTRING(0 0.11, 5 0.12, 10 0.14)"); |
|
711 |
System.out.println(SnappingOverlayOperation.overlayOp(c, d, OverlayOp.INTERSECTION, 0.001)); |
|
712 |
// |
|
713 |
c = reader.read("LINESTRING(1 0, 3 2)"); |
|
714 |
d = reader.read("LINESTRING(3.05 2.01, 5 1.25, 0.25 1.75)"); |
|
715 |
System.out.println(OverlayOp.overlayOp(c, d, OverlayOp.INTERSECTION)); |
|
716 |
System.out.println((SnappingOverlayOperation.overlayOp(c, d, OverlayOp.INTERSECTION, 0.1))); |
|
717 |
// |
|
718 |
// |
|
719 |
d = reader.read("LINESTRING(3 2, 5 1.25, 0.25 1.75)"); |
|
720 |
System.out.println(OverlayOp.overlayOp(c, d, OverlayOp.INTERSECTION)); |
|
721 |
System.out.println((SnappingOverlayOperation.overlayOp(c, d, OverlayOp.INTERSECTION, 0.1))); |
|
722 |
|
|
723 |
//Que un pol?gono est? cerrado o no no es snap, sino regla topologica |
|
724 |
|
|
725 |
//TODO CON POLIGONOS EST? DANDO PROBLEMAS. HABRA QUE REVISAR LINEAS Y POLIGONOS |
|
726 |
// c = reader.read("POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))"); |
|
727 |
// d = reader.read("POLYGON((-0.01 0, 3 8, 6 6 , -0.01 0))"); |
|
728 |
|
|
729 |
c = reader.read("POLYGON((5 0, 5 5, 10 5, 10 0, 5 0))"); |
|
730 |
d = reader.read("POLYGON((4 3, 4.99 3.5, 10.01 3.5, 12 3, 4 3))"); |
|
731 |
|
|
732 |
|
|
733 |
|
|
734 |
//REVISI?N TOPOLOGICA |
|
735 |
/* |
|
736 |
* Un aspecto esencial de la topologia en JTS es el etiquetado. |
|
737 |
* Todo Eje del grafo asociado a un pol?gono tiene una etiqueta o Label, |
|
738 |
* con tres valores posibles para la izquierda, derecha y encima del poligono |
|
739 |
* (EXTERIOR, BOUNDARY, INTERIOR) |
|
740 |
* |
|
741 |
* Por tanto, si la orientaci?n no es la correcta, todo se va al traste |
|
742 |
* (pues las cosas se invierten especularmente) |
|
743 |
* */ |
|
744 |
if(CGAlgorithms.isCCW(c.getCoordinates())){ |
|
745 |
System.out.println("Anillo exterior de poligono en orden incorrecto"); |
|
746 |
System.out.println(c.toText()); |
|
747 |
System.exit(-2); |
|
748 |
} |
|
749 |
if(CGAlgorithms.isCCW(d.getCoordinates())){ |
|
750 |
System.out.println("Anillo exterior de poligono en orden incorrecto"); |
|
751 |
System.out.println(d.toText()); |
|
752 |
System.exit(-2); |
|
753 |
} |
|
754 |
|
|
755 |
System.out.println((SnappingOverlayOperation.overlayOp(c, d, OverlayOp.INTERSECTION, 0.1))); |
|
756 |
|
|
757 |
Geometry pol1 = reader.read("POLYGON((0 0, -5 0, -10 5, 0 10, 10 5, 5 0, 0 0))"); |
|
758 |
Geometry pol2 = reader.read("POLYGON((10.01 0, 5 5, 5 10, 10 10, 10.01 0))"); |
|
759 |
|
|
760 |
System.out.println((SnappingOverlayOperation.overlayOp(pol1, pol2, OverlayOp.INTERSECTION, 0.1))); |
|
761 |
System.out.println((OverlayOp.overlayOp(pol1, pol2, OverlayOp.INTERSECTION))); |
|
762 |
|
|
763 |
|
|
764 |
} catch (ParseException e) { |
|
765 |
e.printStackTrace(); |
|
766 |
} |
|
767 |
|
|
768 |
|
|
769 |
} |
|
770 |
|
|
771 |
} |
|
0 | 772 |
trunk/libraries/libFMap/src/com/vividsolutions/jts/operation/overlay/SnappingOverlayOperationTest.java | ||
---|---|---|
1 |
/* |
|
2 |
* Created on 03-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$ |
|
47 |
* $Log$ |
|
48 |
* Revision 1.1 2006-12-04 19:30:23 azabala |
|
49 |
* *** empty log message *** |
|
50 |
* |
|
51 |
* Revision 1.1 2006/10/19 16:06:48 azabala |
|
52 |
* *** empty log message *** |
|
53 |
* |
|
54 |
* Revision 1.1 2006/10/17 18:25:53 azabala |
|
55 |
* *** empty log message *** |
|
56 |
* |
|
57 |
* Revision 1.3 2006/10/10 18:50:57 azabala |
|
58 |
* *** empty log message *** |
|
59 |
* |
|
60 |
* Revision 1.2 2006/10/09 19:10:56 azabala |
|
61 |
* First version in CVS |
|
62 |
* |
|
63 |
* Revision 1.1 2006/10/05 19:20:57 azabala |
|
64 |
* first version in cvs |
|
65 |
* |
|
66 |
* |
|
67 |
*/ |
|
68 |
package com.vividsolutions.jts.operation.overlay; |
|
69 |
|
|
70 |
import junit.framework.TestCase; |
|
71 |
|
|
72 |
import com.vividsolutions.jts.geom.Coordinate; |
|
73 |
import com.vividsolutions.jts.geom.Geometry; |
|
74 |
import com.vividsolutions.jts.geom.GeometryCollection; |
|
75 |
import com.vividsolutions.jts.geom.GeometryFactory; |
|
76 |
import com.vividsolutions.jts.geom.Polygon; |
|
77 |
import com.vividsolutions.jts.operation.overlay.OverlayOp; |
|
78 |
|
|
79 |
|
|
80 |
public class SnappingOverlayOperationTest extends TestCase { |
|
81 |
|
|
82 |
//test 1 |
|
83 |
Geometry a,b; |
|
84 |
GeometryFactory factory ; |
|
85 |
com.vividsolutions.jts.io.WKTReader reader; |
|
86 |
|
|
87 |
//test 2 |
|
88 |
Geometry c,d; |
|
89 |
|
|
90 |
//test 4 |
|
91 |
Geometry f; |
|
92 |
|
|
93 |
//test 5 |
|
94 |
Geometry g, h; |
|
95 |
|
|
96 |
|
|
97 |
//test 6 |
|
98 |
Geometry pol1, pol2; |
|
99 |
|
|
100 |
public static void main(String[] args) { |
|
101 |
} |
|
102 |
|
|
103 |
public SnappingOverlayOperationTest(String name) { |
|
104 |
super(name); |
|
105 |
} |
|
106 |
|
|
107 |
|
|
108 |
protected void setUp() throws Exception { |
|
109 |
super.setUp(); |
|
110 |
factory = new GeometryFactory(); |
|
111 |
reader = new com.vividsolutions.jts.io.WKTReader(factory); |
|
112 |
a = reader.read("LINESTRING(0.001 0.001, 5.001 5.001)"); |
|
113 |
b = reader.read("LINESTRING(2.1 -3, 0.0 -0.001, -2.22 4.88, 10.0 10.0, 5.002 5.002)"); |
|
114 |
|
|
115 |
c = reader.read("LINESTRING(0 0, 5 0, 10 0.001)"); |
|
116 |
d = reader.read("LINESTRING(0 0.01, 5 0.002, 10 0.002)"); |
|
117 |
|
|
118 |
f = reader.read("LINESTRING(0 0.11, 5 0.12, 10 0.14)"); |
|
119 |
|
|
120 |
|
|
121 |
g = reader.read("LINESTRING(1 0, 3 2)"); |
|
122 |
h = reader.read("LINESTRING(3.05 2.01, 5 1.25, 0.25 1.75)"); |
|
123 |
|
|
124 |
|
|
125 |
pol1 = reader.read("POLYGON((0 0, -5 0, -10 5, 0 10, 10 5, 5 0, 0 0))"); |
|
126 |
pol2 = reader.read("POLYGON((10.01 0, 5 5, 5 10, 10 10, 10.01 0))"); |
|
127 |
} |
|
128 |
|
|
129 |
|
|
130 |
|
|
131 |
protected void tearDown() throws Exception { |
|
132 |
super.tearDown(); |
|
133 |
factory = null; |
|
134 |
reader = null; |
|
135 |
a = null; |
|
136 |
b = null; |
|
137 |
} |
|
138 |
|
|
139 |
/* |
|
140 |
* Test method for 'com.iver.cit.gvsig.fmap.topology.SnappingOverlayOperation.SnappingOverlayOperation(Geometry, Geometry, double)' |
|
141 |
*/ |
|
142 |
public void testSnappingOverlayOperation() { |
|
143 |
//test 1: dos lineas que intersectan en dos puntos: uno Nodo y otro vertice |
|
144 |
Geometry geom = SnappingOverlayOperation.overlayOp(a, b, |
|
145 |
OverlayOp.INTERSECTION, 0.01); |
|
146 |
assertTrue(geom.toString().equals("MULTIPOINT (0.001 0.001, 5.001 5.001)")); |
|
147 |
|
|
148 |
//test 2: dos lineas paralelas separadas por una distancia inferior a la |
|
149 |
//de snap |
|
150 |
geom = SnappingOverlayOperation.overlayOp(c, d, |
|
151 |
OverlayOp.INTERSECTION, 0.1); |
|
152 |
|
|
153 |
assertTrue(geom.toString().equals("MULTILINESTRING ((0 0, 5 0), (5 0, 10 0.001))")); |
|
154 |
|
|
155 |
//test 3: identicas l?neas pero reducimos la distancia de snap |
|
156 |
geom = SnappingOverlayOperation.overlayOp(c, d, |
|
157 |
OverlayOp.INTERSECTION, 0.0001); |
|
158 |
assertTrue(geom.toString().equals("GEOMETRYCOLLECTION EMPTY")); |
|
159 |
|
|
160 |
//test 4: identicas l?neas, pero se separan un poco mas |
|
161 |
geom = SnappingOverlayOperation.overlayOp(c, f, OverlayOp.INTERSECTION, 0.1); |
|
162 |
assertTrue(geom.toString().equals("GEOMETRYCOLLECTION EMPTY")); |
|
163 |
|
|
164 |
|
|
165 |
//test 5: interseccion de dos lineas, la solucion son dos puntos: |
|
166 |
//uno con snap y el otro normal. Presentan cambios de orientacion |
|
167 |
geom = SnappingOverlayOperation.overlayOp(g, h, OverlayOp.INTERSECTION, 0.1); |
|
168 |
assertTrue(geom instanceof GeometryCollection); |
|
169 |
assertTrue(geom.toString().equals("MULTIPOINT (2.511904761904762 1.5119047619047619, 3 2)")); |
|
170 |
|
|
171 |
geom = SnappingOverlayOperation.overlayOp(pol1, |
|
172 |
pol2, |
|
173 |
OverlayOp.INTERSECTION, |
|
174 |
0.01); |
|
175 |
assertTrue(geom instanceof Polygon); |
|
176 |
assertTrue(geom.toString().equals("POLYGON ((5 7.5, 10 5, 7.502497502497502 2.5024975024975022, 5 5, 5 7.5))")); |
|
177 |
|
|
178 |
|
|
179 |
|
|
180 |
|
|
181 |
|
|
182 |
} |
|
183 |
|
|
184 |
} |
|
185 |
|
|
0 | 186 |
trunk/libraries/libFMap/src/com/vividsolutions/jts/operation/overlay/NonRobustSnapLineIntersector.java | ||
---|---|---|
1 |
/* |
|
2 |
* Created on 28-sep-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$ |
|
47 |
* $Log$ |
|
48 |
* Revision 1.1 2006-12-04 19:30:23 azabala |
|
49 |
* *** empty log message *** |
|
50 |
* |
|
51 |
* Revision 1.1 2006/10/19 16:06:48 azabala |
|
52 |
* *** empty log message *** |
|
53 |
* |
|
54 |
* Revision 1.1 2006/10/17 18:25:53 azabala |
|
55 |
* *** empty log message *** |
|
56 |
* |
|
57 |
* Revision 1.1 2006/10/05 19:20:57 azabala |
|
58 |
* first version in cvs |
|
59 |
* |
|
60 |
* Revision 1.1 2006/10/02 19:06:56 azabala |
|
61 |
* *** empty log message *** |
|
62 |
* |
|
63 |
* |
|
64 |
*/ |
|
65 |
package com.vividsolutions.jts.operation.overlay; |
|
66 |
|
|
67 |
|
|
68 |
import com.vividsolutions.jts.algorithm.NonRobustLineIntersector; |
|
69 |
|
|
70 |
public class NonRobustSnapLineIntersector extends NonRobustLineIntersector { |
|
71 |
|
|
72 |
/** |
|
73 |
* @return true if both numbers are positive or if both numbers are negative. |
|
74 |
* Returns false if both numbers are zero. |
|
75 |
*/ |
|
76 |
public static boolean isSameSignAndNonZero(double a, double b) { |
|
77 |
if (a == 0 || b == 0) { |
|
78 |
return false; |
|
79 |
} |
|
80 |
return (a < 0 && b < 0) || (a > 0 && b > 0); |
|
81 |
} |
|
82 |
|
|
83 |
|
|
84 |
public NonRobustSnapLineIntersector() { |
|
85 |
} |
|
86 |
|
|
87 |
|
|
88 |
|
|
89 |
|
|
90 |
|
|
91 |
|
|
92 |
|
|
93 |
|
|
94 |
|
|
95 |
} |
|
96 |
|
|
0 | 97 |
trunk/libraries/libFMap/src/com/vividsolutions/jts/operation/overlay/SnapLineIntersector.java | ||
---|---|---|
1 |
/* |
|
2 |
* Created on 27-sep-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$ |
|
47 |
* $Log$ |
|
48 |
* Revision 1.1 2006-12-04 19:30:23 azabala |
|
49 |
* *** empty log message *** |
|
50 |
* |
|
51 |
* Revision 1.1 2006/10/19 16:06:48 azabala |
|
52 |
* *** empty log message *** |
|
53 |
* |
|
54 |
* Revision 1.1 2006/10/17 18:25:53 azabala |
|
55 |
* *** empty log message *** |
|
56 |
* |
Also available in: Unified diff