Revision 41098 trunk/org.gvsig.desktop/org.gvsig.desktop.plugin/org.gvsig.app/org.gvsig.app.mainplugin/src/main/java/org/gvsig/app/project/documents/view/toolListeners/snapping/snappers/IntersectionPointSnapper.java
IntersectionPointSnapper.java | ||
---|---|---|
23 | 23 |
*/ |
24 | 24 |
package org.gvsig.app.project.documents.view.toolListeners.snapping.snappers; |
25 | 25 |
|
26 |
import java.awt.geom.PathIterator; |
|
27 | 26 |
import java.awt.geom.Point2D; |
27 |
import java.util.ArrayList; |
|
28 |
import java.util.Iterator; |
|
28 | 29 |
import java.util.List; |
29 | 30 |
|
31 |
import com.vividsolutions.jts.geom.GeometryFactory; |
|
32 |
import com.vividsolutions.jts.geom.MultiPoint; |
|
33 |
import com.vividsolutions.jts.geom.Point; |
|
34 |
|
|
30 | 35 |
import org.slf4j.Logger; |
31 | 36 |
import org.slf4j.LoggerFactory; |
32 | 37 |
|
33 | 38 |
import org.gvsig.fmap.geom.Geometry; |
34 |
import org.gvsig.fmap.geom.operation.GeometryOperationException;
|
|
35 |
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
|
|
39 |
import org.gvsig.fmap.geom.aggregate.MultiCurve;
|
|
40 |
import org.gvsig.fmap.geom.aggregate.MultiSurface;
|
|
36 | 41 |
import org.gvsig.fmap.geom.primitive.Curve; |
42 |
import org.gvsig.fmap.geom.primitive.Envelope; |
|
43 |
import org.gvsig.fmap.geom.primitive.Surface; |
|
37 | 44 |
import org.gvsig.fmap.mapcontrol.PrimitivesDrawer; |
38 | 45 |
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapperGeometriesVectorial; |
39 | 46 |
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.impl.AbstractSnapper; |
... | ... | |
49 | 56 |
implements ISnapperGeometriesVectorial { |
50 | 57 |
private static final Logger LOG = LoggerFactory.getLogger(IntersectionPointSnapper.class); |
51 | 58 |
|
52 |
private static int maxPointsGeom = 1000; |
|
53 |
private List<Geometry> geometries; |
|
54 |
private static long lastLogTime = 0; |
|
59 |
public static final int MAX_GEOMETRIES_IN_RELAXED_LIST = 30; |
|
60 |
public static final int MAX_GEOMETRIES_WITHIN_TOLERANCE = 5; |
|
61 |
|
|
62 |
private List relaxedList = null; |
|
63 |
private GeometryFactory jtsGeoFact = null; |
|
64 |
private long errorMsgCount = 0; |
|
55 | 65 |
|
56 |
public Point2D getSnapPoint(Point2D point, Geometry geom, |
|
57 |
double tolerance, Point2D lastPointEntered) { |
|
58 |
if (!(geom instanceof Curve)){ |
|
59 |
return null; |
|
60 |
} |
|
61 |
Point2D result = null; |
|
62 |
|
|
63 |
if (geometries == null) { |
|
66 |
public Point2D getSnapPoint( |
|
67 |
Point2D point, |
|
68 |
Geometry geom, |
|
69 |
double tolerance, |
|
70 |
Point2D lastPointEntered) { |
|
71 |
|
|
72 |
if (geom == null) { |
|
64 | 73 |
return null; |
65 | 74 |
} |
66 |
|
|
67 |
for (int i = 0; i < geometries.size(); i++) { |
|
68 |
Point2D r = intersects(geom, geometries.get(i), point, tolerance); |
|
69 |
|
|
70 |
if (r != null) { |
|
71 |
result = r; |
|
72 |
} |
|
75 |
|
|
76 |
if (relaxedList == null |
|
77 |
|| relaxedList.size() < 2 /* We need 2 to intersect */ |
|
78 |
|| relaxedList.size() > MAX_GEOMETRIES_IN_RELAXED_LIST) { |
|
79 |
/* |
|
80 |
* If there are a lot of geometries, we assume the user |
|
81 |
* is not interested in snapping now. The relaxed list |
|
82 |
* might be long because the spatial index used perhaps |
|
83 |
* is "lazy" so the value of MAX_GEOMETRIES_IN_RELAXED_LIST |
|
84 |
* has to be not very small |
|
85 |
*/ |
|
86 |
return null; |
|
73 | 87 |
} |
74 |
|
|
75 |
return result; |
|
76 |
} |
|
77 |
|
|
78 |
private Point2D intersects(Geometry geometry1, Geometry geometry2, Point2D point, |
|
79 |
double tolerance) { |
|
80 |
|
|
81 |
//If there is a topology error don't intersects |
|
82 |
if ((geometry1 == null) || (geometry2 == null)){ |
|
83 |
return null; |
|
84 |
} |
|
85 |
|
|
86 |
if (hasMoreThanVertices(geometry1, maxPointsGeom) |
|
87 |
|| hasMoreThanVertices(geometry2, maxPointsGeom)) { |
|
88 |
|
|
89 |
return null; |
|
90 |
} |
|
91 |
|
|
92 |
Geometry geometry; |
|
88 |
|
|
89 |
List<Geometry> refinedList = null; |
|
93 | 90 |
try { |
94 |
geometry = geometry1.intersection(geometry2); |
|
95 |
if ((geometry != null) && (geometry.getType() == Geometry.TYPES.POINT)){ |
|
96 |
return geometry.getHandlers(Geometry.SELECTHANDLER)[0].getPoint(); |
|
91 |
refinedList = refineList(relaxedList, point, tolerance); |
|
92 |
} catch (Exception e1) { |
|
93 |
errorMsgCount++; |
|
94 |
if (errorMsgCount % 100 == 0) { |
|
95 |
/* |
|
96 |
* Prevents too many lines in logger. If this happens, |
|
97 |
* it will happen many times, so we'll see it in the log file. |
|
98 |
*/ |
|
99 |
LOG.info("Error while refining list: " + e1.getMessage()); |
|
100 |
errorMsgCount = 0; |
|
97 | 101 |
} |
98 |
} catch (GeometryOperationNotSupportedException e) { |
|
99 |
LOG.error("Unable to intersect these geometries", e); |
|
100 |
} catch (GeometryOperationException e) { |
|
101 |
LOG.error("Unable to intersect these geometries", e); |
|
102 |
} catch (Exception e) { |
|
102 |
return null; |
|
103 |
} |
|
104 |
|
|
105 |
if (refinedList.size() > MAX_GEOMETRIES_WITHIN_TOLERANCE) { |
|
103 | 106 |
/* |
104 |
* Sometimes there is a JTS TopologyException. |
|
105 |
* The cause is unknown because it's difficult to |
|
106 |
* reproduce, but probably caused by extremely similar |
|
107 |
* geometries. This is unlikely to cause functionality |
|
108 |
* problems, so we'll only log it once every 5 seconds |
|
109 |
* (otherwise the user can see performance issues because |
|
110 |
* this exception is thrown many times in those strange |
|
111 |
* cases) |
|
107 |
* This refined list contains the geometries that are |
|
108 |
* close to the current mouse. Again we use an upper limit |
|
109 |
* to prevent the case where the user is in a zoom where |
|
110 |
* he is not interested in snapping. |
|
112 | 111 |
*/ |
113 |
long curr_time = System.currentTimeMillis(); |
|
114 |
// This ensures not more than one log every 5 seconds |
|
115 |
if (curr_time - lastLogTime > 5000) { |
|
116 |
LOG.info("Error while intersecting: " + e.getMessage()); |
|
117 |
lastLogTime = curr_time; |
|
112 |
return null; |
|
113 |
} |
|
114 |
|
|
115 |
List<Point> interPoints = getInterPoints(refinedList); |
|
116 |
|
|
117 |
if (interPoints.size() == 0) { |
|
118 |
return null; |
|
119 |
} |
|
120 |
|
|
121 |
com.vividsolutions.jts.geom.Geometry jtsg = null; |
|
122 |
try { |
|
123 |
jtsg = (com.vividsolutions.jts.geom.Geometry) geom.invokeOperation("toJTS", null); |
|
124 |
} catch (Exception e) { |
|
125 |
errorMsgCount++; |
|
126 |
if (errorMsgCount % 100 == 0) { |
|
127 |
/* |
|
128 |
* Prevents too many lines in logger. If this happens, |
|
129 |
* it will happen many times, so we'll see it in the log file. |
|
130 |
*/ |
|
131 |
LOG.info("Error while refining list: " + e.getMessage()); |
|
132 |
errorMsgCount = 0; |
|
118 | 133 |
} |
119 |
} |
|
120 |
|
|
121 |
return null; |
|
134 |
return null; |
|
135 |
} |
|
136 |
|
|
137 |
double zerodist = 0.001 * tolerance; |
|
138 |
int n = interPoints.size(); |
|
139 |
Point po = null; |
|
140 |
for (int i=0; i<n; i++) { |
|
141 |
po = interPoints.get(i); |
|
142 |
if (point.distance(po.getX(), po.getY()) < tolerance) { |
|
143 |
// This is a candidate, now we check that it belongs to the |
|
144 |
// current geometry |
|
145 |
if (jtsg.distance(po) < zerodist) { |
|
146 |
return new Point2D.Double(po.getX(), po.getY()); |
|
147 |
} |
|
148 |
} |
|
149 |
} |
|
150 |
return null; |
|
122 | 151 |
} |
123 | 152 |
|
153 |
|
|
154 |
private List<Geometry> refineList( |
|
155 |
List geomlist, |
|
156 |
Point2D point, |
|
157 |
double tolerance) throws Exception { |
|
158 |
|
|
159 |
List<Geometry> resp = new ArrayList<Geometry>(); |
|
160 |
if (geomlist == null || geomlist.size() == 0) { |
|
161 |
return resp; |
|
162 |
} |
|
163 |
|
|
164 |
double point_tol_north = point.getY() + tolerance; |
|
165 |
double point_tol_south = point.getY() - tolerance; |
|
166 |
double point_tol_east = point.getX() + tolerance; |
|
167 |
double point_tol_west = point.getX() - tolerance; |
|
168 |
|
|
169 |
Geometry item = null; |
|
170 |
Envelope env = null; |
|
171 |
Iterator iter = geomlist.iterator(); |
|
172 |
while (iter.hasNext()) { |
|
173 |
item = (Geometry) iter.next(); |
|
174 |
|
|
175 |
if (item.intersects( |
|
176 |
point_tol_west, point_tol_south, |
|
177 |
point_tol_east - point_tol_west, |
|
178 |
point_tol_north - point_tol_south)) { |
|
179 |
|
|
180 |
resp.add(item); |
|
181 |
} |
|
182 |
} |
|
183 |
return resp; |
|
184 |
} |
|
185 |
|
|
186 |
|
|
124 | 187 |
public void draw(PrimitivesDrawer primitivesDrawer, Point2D pPixels) { |
125 | 188 |
primitivesDrawer.setColor(getColor()); |
126 | 189 |
|
... | ... | |
139 | 202 |
} |
140 | 203 |
|
141 | 204 |
public void setGeometries(List geoms) { |
142 |
this.geometries = geoms;
|
|
205 |
relaxedList = leaveUsefulGeometries(geoms);
|
|
143 | 206 |
} |
144 | 207 |
|
208 |
private List<Point> getInterPoints(List geomes) { |
|
209 |
|
|
210 |
List<Point> resp = new ArrayList<Point>(); |
|
211 |
List borders = getJTSBorders(geomes); |
|
212 |
com.vividsolutions.jts.geom.Geometry jtsg1 = null; |
|
213 |
com.vividsolutions.jts.geom.Geometry jtsg2 = null; |
|
214 |
com.vividsolutions.jts.geom.Geometry jtsinter = null; |
|
215 |
int n = borders.size(); |
|
216 |
Point jtsp = null; |
|
217 |
MultiPoint jtsmp = null; |
|
218 |
|
|
219 |
for (int i=0; i<n; i++) { |
|
220 |
for (int j=0; j<i; j++) { |
|
221 |
if (i != j) { |
|
222 |
jtsg1 = (com.vividsolutions.jts.geom.Geometry) borders.get(i); |
|
223 |
jtsg2 = (com.vividsolutions.jts.geom.Geometry) borders.get(j); |
|
224 |
jtsinter = jtsg1.intersection(jtsg2); |
|
225 |
if (jtsinter instanceof Point) { |
|
226 |
jtsp = (Point) jtsinter; |
|
227 |
resp.add(jtsp); |
|
228 |
} else { |
|
229 |
if (jtsinter instanceof MultiPoint) { |
|
230 |
|
|
231 |
jtsmp = (MultiPoint) jtsinter; |
|
232 |
int m = jtsmp.getNumGeometries(); |
|
233 |
for (int k=0; k<m; k++) { |
|
234 |
jtsp = (Point) jtsmp.getGeometryN(k); |
|
235 |
resp.add(jtsp); |
|
236 |
} |
|
237 |
} |
|
238 |
} |
|
239 |
} |
|
240 |
} |
|
241 |
} |
|
242 |
return resp; |
|
243 |
} |
|
145 | 244 |
|
146 |
|
|
147 |
/** |
|
148 |
* Tells whether geometry has more than n vertices using its path |
|
149 |
* iterator |
|
150 |
* @param geom |
|
151 |
* @param n |
|
152 |
* @return |
|
153 |
*/ |
|
154 |
private static boolean hasMoreThanVertices(Geometry geom, int n) { |
|
245 |
|
|
246 |
private List getJTSBorders(List gvsigGeoms) { |
|
155 | 247 |
|
156 |
PathIterator piter = geom.getPathIterator(null); |
|
157 |
if (piter == null) { |
|
158 |
return false; |
|
159 |
} |
|
248 |
List resp = new ArrayList(); |
|
249 |
com.vividsolutions.jts.geom.Geometry jtsborder = null; |
|
160 | 250 |
|
161 |
if (n < 1) { |
|
162 |
return true; |
|
251 |
if (gvsigGeoms != null && gvsigGeoms.size() > 0) { |
|
252 |
|
|
253 |
Iterator iter = gvsigGeoms.iterator(); |
|
254 |
Object itemobj = null; |
|
255 |
|
|
256 |
while (iter.hasNext()) { |
|
257 |
itemobj = iter.next(); |
|
258 |
if (itemobj instanceof Geometry) { |
|
259 |
jtsborder = getJTSBorder((Geometry) itemobj); |
|
260 |
if (jtsborder != null) { |
|
261 |
resp.add(jtsborder); |
|
262 |
} |
|
263 |
} |
|
264 |
} |
|
163 | 265 |
} |
266 |
return resp; |
|
267 |
} |
|
164 | 268 |
|
165 |
int cnt = n; |
|
166 |
while (!piter.isDone()) { |
|
167 |
piter.next(); |
|
168 |
cnt--; |
|
169 |
if (cnt == 0) { |
|
170 |
return true; |
|
269 |
private com.vividsolutions.jts.geom.Geometry getJTSBorder(Geometry geom) { |
|
270 |
|
|
271 |
com.vividsolutions.jts.geom.Geometry resp = null; |
|
272 |
|
|
273 |
if (geom instanceof Curve || geom instanceof MultiCurve) { |
|
274 |
|
|
275 |
try { |
|
276 |
resp = (com.vividsolutions.jts.geom.Geometry) |
|
277 |
geom.invokeOperation("toJTS", null); |
|
278 |
} catch (Exception e) { |
|
279 |
errorMsgCount++; |
|
280 |
if (errorMsgCount % 100 == 0) { |
|
281 |
/* |
|
282 |
* Prevents too many lines in logger. If this happens, |
|
283 |
* it will happen many times, so we'll see it in the log file. |
|
284 |
*/ |
|
285 |
LOG.info("Error while refining list: " + e.getMessage()); |
|
286 |
errorMsgCount = 0; |
|
287 |
} |
|
288 |
return null; |
|
171 | 289 |
} |
290 |
return resp; |
|
291 |
|
|
292 |
} else { |
|
293 |
if (geom instanceof Surface || geom instanceof MultiSurface) { |
|
294 |
|
|
295 |
try { |
|
296 |
resp = (com.vividsolutions.jts.geom.Geometry) |
|
297 |
geom.invokeOperation("toJTS", null); |
|
298 |
return resp.getBoundary(); |
|
299 |
} catch (Exception e) { |
|
300 |
|
|
301 |
errorMsgCount++; |
|
302 |
if (errorMsgCount % 100 == 0) { |
|
303 |
/* |
|
304 |
* Prevents too many lines in logger. If this happens, |
|
305 |
* it will happen many times, so we'll see it in the log file. |
|
306 |
*/ |
|
307 |
LOG.info("Error while refining list: " + e.getMessage()); |
|
308 |
errorMsgCount = 0; |
|
309 |
} |
|
310 |
return null; |
|
311 |
} |
|
312 |
|
|
313 |
} else { |
|
314 |
return null; |
|
315 |
} |
|
172 | 316 |
} |
173 |
return false; |
|
317 |
|
|
174 | 318 |
} |
319 |
|
|
320 |
|
|
321 |
private List<Geometry> leaveUsefulGeometries(List geoms) { |
|
322 |
|
|
323 |
List resp = new ArrayList(); |
|
324 |
if (geoms == null || geoms.size() == 0) { |
|
325 |
return resp; |
|
326 |
} |
|
327 |
|
|
328 |
Object item = null; |
|
329 |
Iterator iter = geoms.iterator(); |
|
330 |
while (iter.hasNext()) { |
|
331 |
item = iter.next(); |
|
332 |
if (item instanceof Curve || item instanceof Surface |
|
333 |
|| item instanceof MultiCurve || item instanceof MultiSurface) { |
|
334 |
resp.add(item); |
|
335 |
} |
|
336 |
} |
|
337 |
return resp; |
|
338 |
} |
|
339 |
|
|
175 | 340 |
} |
Also available in: Unified diff