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 |
}
|