source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/UnconnectedWays.java@ 3671

Last change on this file since 3671 was 3671, checked in by bastiK, 14 years ago

adapt coding style (to some degree); there shouldn't be any semantic changes in this commit

  • Property svn:eol-style set to native
File size: 14.2 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.geom.Area;
7import java.awt.geom.Line2D;
8import java.awt.geom.Point2D;
9import java.util.ArrayList;
10import java.util.Arrays;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.coor.EastNorth;
21import org.openstreetmap.josm.data.coor.LatLon;
22import org.openstreetmap.josm.data.osm.BBox;
23import org.openstreetmap.josm.data.osm.DataSet;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmUtils;
26import org.openstreetmap.josm.data.osm.QuadBuckets;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.data.validation.Severity;
29import org.openstreetmap.josm.data.validation.Test;
30import org.openstreetmap.josm.data.validation.TestError;
31import org.openstreetmap.josm.gui.preferences.ValidatorPreference;
32import org.openstreetmap.josm.gui.progress.ProgressMonitor;
33
34/**
35 * Tests if there are segments that crosses in the same layer
36 *
37 * @author frsantos
38 */
39public class UnconnectedWays extends Test {
40
41 protected static int UNCONNECTED_WAYS = 1301;
42 protected static final String PREFIX = ValidatorPreference.PREFIX + "." + UnconnectedWays.class.getSimpleName();
43
44 Set<MyWaySegment> ways;
45 Set<Node> endnodes; // nodes at end of way
46 Set<Node> endnodes_highway; // nodes at end of way
47 Set<Node> middlenodes; // nodes in middle of way
48 Set<Node> othernodes; // nodes appearing at least twice
49 //NodeSearchCache nodecache;
50 QuadBuckets<Node> nodecache;
51 Area ds_area;
52 DataSet ds;
53
54 double mindist;
55 double minmiddledist;
56
57 /**
58 * Constructor
59 */
60 public UnconnectedWays() {
61 super(tr("Unconnected ways."),
62 tr("This test checks if a way has an endpoint very near to another way."));
63 }
64
65 @Override
66 public void startTest(ProgressMonitor monitor) {
67 super.startTest(monitor);
68 ways = new HashSet<MyWaySegment>();
69 endnodes = new HashSet<Node>();
70 endnodes_highway = new HashSet<Node>();
71 middlenodes = new HashSet<Node>();
72 othernodes = new HashSet<Node>();
73 mindist = Main.pref.getDouble(PREFIX + ".node_way_distance", 10.0)/6378135.0;
74 minmiddledist = Main.pref.getDouble(PREFIX + ".way_way_distance", 0.0)/6378135.0;
75 this.ds = Main.main.getCurrentDataSet();
76 this.ds_area = ds.getDataSourceArea();
77 }
78
79 @Override
80 public void endTest() {
81 //Area a = Main.ds.getDataSourceArea();
82 Map<Node, Way> map = new HashMap<Node, Way>();
83 //long last = -1;
84 for (int iter = 0; iter < 1; iter++) {
85 //last = System.currentTimeMillis();
86 long last_print = -1;
87 int nr = 0;
88 Collection<MyWaySegment> tmp_ways = ways;
89 for (MyWaySegment s : tmp_ways) {
90 nr++;
91 long now = System.currentTimeMillis();
92 if (now - last_print > 200) {
93 //System.err.println("processing segment nr: " + nr + " of " + ways.size());
94 last_print = now;
95 }
96 for (Node en : s.nearbyNodes(mindist)) {
97 if (en == null || !s.highway || !endnodes_highway.contains(en)) {
98 continue;
99 }
100 if ("turning_circle".equals(en.get("highway"))
101 || "bus_stop".equals(en.get("highway"))
102 || OsmUtils.isTrue(en.get("noexit"))
103 || en.hasKey("barrier")) {
104 continue;
105 }
106 // There's a small false-positive here. Imagine an intersection
107 // like a 't'. If the top part of the 't' is short enough, it
108 // will trigger the node at the very top of the 't' to be unconnected
109 // to the way that "crosses" the 't'. We should probably check that
110 // the ways to which 'en' belongs are not connected to 's.w'.
111 map.put(en, s.w);
112 }
113 }
114 //System.out.println("p1 elapsed: " + (System.currentTimeMillis()-last));
115 //last = System.currentTimeMillis();
116 }
117 for (Map.Entry<Node, Way> error : map.entrySet()) {
118 errors.add(new TestError(this, Severity.WARNING,
119 tr("Way end node near other highway"),
120 UNCONNECTED_WAYS,
121 Arrays.asList(error.getKey(), error.getValue())));
122 }
123 map.clear();
124 for (MyWaySegment s : ways) {
125 for (Node en : s.nearbyNodes(mindist)) {
126 if (endnodes_highway.contains(en) && !s.highway && !s.isArea()) {
127 map.put(en, s.w);
128 } else if (endnodes.contains(en) && !s.isArea()) {
129 map.put(en, s.w);
130 }
131 }
132 }
133 //System.out.println("p2 elapsed: " + (System.currentTimeMillis()-last));
134 //last = System.currentTimeMillis();
135 for (Map.Entry<Node, Way> error : map.entrySet()) {
136 errors.add(new TestError(this, Severity.WARNING,
137 tr("Way end node near other way"),
138 UNCONNECTED_WAYS,
139 Arrays.asList(error.getKey(), error.getValue())));
140 }
141 /* the following two use a shorter distance */
142 if (minmiddledist > 0.0) {
143 map.clear();
144 for (MyWaySegment s : ways) {
145 for (Node en : s.nearbyNodes(minmiddledist)) {
146 if (!middlenodes.contains(en)) {
147 continue;
148 }
149 map.put(en, s.w);
150 }
151 }
152 //System.out.println("p3 elapsed: " + (System.currentTimeMillis()-last));
153 //last = System.currentTimeMillis();
154 for (Map.Entry<Node, Way> error : map.entrySet()) {
155 errors.add(new TestError(this, Severity.OTHER,
156 tr("Way node near other way"),
157 UNCONNECTED_WAYS,
158 Arrays.asList(error.getKey(), error.getValue())));
159 }
160 map.clear();
161 for (MyWaySegment s : ways) {
162 for (Node en : s.nearbyNodes(minmiddledist)) {
163 if (!othernodes.contains(en)) {
164 continue;
165 }
166 map.put(en, s.w);
167 }
168 }
169 //System.out.println("p4 elapsed: " + (System.currentTimeMillis()-last));
170 //last = System.currentTimeMillis();
171 for (Map.Entry<Node, Way> error : map.entrySet()) {
172 errors.add(new TestError(this, Severity.OTHER,
173 tr("Connected way end node near other way"),
174 UNCONNECTED_WAYS,
175 Arrays.asList(error.getKey(), error.getValue())));
176 }
177 }
178 ways = null;
179 endnodes = null;
180 super.endTest();
181 //System.out.println("p99 elapsed: " + (System.currentTimeMillis()-last));
182 //last = System.currentTimeMillis();
183 }
184
185 private class MyWaySegment {
186 private final Line2D line;
187 public final Way w;
188 public final boolean isAbandoned;
189 public final boolean isBoundary;
190 public final boolean highway;
191 private final double len;
192 private Set<Node> nearbyNodeCache;
193 double nearbyNodeCacheDist = -1.0;
194 final Node n1;
195 final Node n2;
196
197 public MyWaySegment(Way w, Node n1, Node n2) {
198 this.w = w;
199 String railway = w.get("railway");
200 String highway = w.get("highway");
201 this.isAbandoned = "abandoned".equals(railway) || OsmUtils.isTrue(w.get("disused"));
202 this.highway = (highway != null || railway != null) && !isAbandoned;
203 this.isBoundary = !this.highway && "administrative".equals(w.get("boundary"));
204 line = new Line2D.Double(n1.getEastNorth().east(), n1.getEastNorth().north(),
205 n2.getEastNorth().east(), n2.getEastNorth().north());
206 len = line.getP1().distance(line.getP2());
207 this.n1 = n1;
208 this.n2 = n2;
209 }
210
211 public boolean nearby(Node n, double dist) {
212 if (w == null) {
213 Main.debug("way null");
214 return false;
215 }
216 if (w.containsNode(n))
217 return false;
218 EastNorth coord = n.getEastNorth();
219 if (coord == null)
220 return false;
221 Point2D p = new Point2D.Double(coord.east(), coord.north());
222 if (line.getP1().distance(p) > len+dist)
223 return false;
224 if (line.getP2().distance(p) > len+dist)
225 return false;
226 return line.ptSegDist(p) < dist;
227 }
228
229 public List<LatLon> getBounds(double fudge) {
230 double x1 = n1.getCoor().lon();
231 double x2 = n2.getCoor().lon();
232 if (x1 > x2) {
233 double tmpx = x1;
234 x1 = x2;
235 x2 = tmpx;
236 }
237 double y1 = n1.getCoor().lat();
238 double y2 = n2.getCoor().lat();
239 if (y1 > y2) {
240 double tmpy = y1;
241 y1 = y2;
242 y2 = tmpy;
243 }
244 LatLon topLeft = new LatLon(y2+fudge, x1-fudge);
245 LatLon botRight = new LatLon(y1-fudge, x2+fudge);
246 List<LatLon> ret = new ArrayList<LatLon>();
247 ret.add(topLeft);
248 ret.add(botRight);
249 return ret;
250 }
251
252 public Collection<Node> nearbyNodes(double dist) {
253 // If you're looking for nodes that are farther
254 // away that we looked for last time, the cached
255 // result is no good
256 if (dist > nearbyNodeCacheDist) {
257 //if (nearbyNodeCacheDist != -1)
258 // System.out.println("destroyed MyWaySegment nearby node cache:" + dist + " > " + nearbyNodeCacheDist);
259 nearbyNodeCache = null;
260 }
261 if (nearbyNodeCache != null) {
262 // If we've cached an aread greater than the
263 // one now being asked for...
264 if (nearbyNodeCacheDist > dist) {
265 //System.out.println("had to trim MyWaySegment nearby node cache.");
266 // Used the cached result and trim out
267 // the nodes that are not in the smaller
268 // area, but keep the old larger cache.
269 Set<Node> trimmed = new HashSet<Node>(nearbyNodeCache);
270 for (Node n : new HashSet<Node>(nearbyNodeCache)) {
271 if (!nearby(n, dist)) {
272 trimmed.remove(n);
273 }
274 }
275 return trimmed;
276 }
277 return nearbyNodeCache;
278 }
279 /*
280 * We know that any point near the line must be at
281 * least as close as the other end of the line, plus
282 * a little fudge for the distance away ('dist').
283 */
284
285 // This needs to be a hash set because the searches
286 // overlap a bit and can return duplicate nodes.
287 nearbyNodeCache = null;
288 List<LatLon> bounds = this.getBounds(dist);
289 List<Node> found_nodes = ds.searchNodes(new BBox(bounds.get(0), bounds.get(1)));
290 if (found_nodes == null)
291 return Collections.emptySet();
292
293 for (Node n : found_nodes) {
294 if (!nearby(n, dist) ||
295 (ds_area != null && !ds_area.contains(n.getCoor()))) {
296 continue;
297 }
298 // It is actually very rare for us to find a node
299 // so defer as much of the work as possible, like
300 // allocating the hash set
301 if (nearbyNodeCache == null) {
302 nearbyNodeCache = new HashSet<Node>();
303 }
304 nearbyNodeCache.add(n);
305 }
306 nearbyNodeCacheDist = dist;
307 if (nearbyNodeCache == null) {
308 nearbyNodeCache = Collections.emptySet();
309 }
310 return nearbyNodeCache;
311 }
312
313 public boolean isArea() {
314 return w.hasKey("landuse")
315 || w.hasKey("leisure")
316 || w.hasKey("amenity")
317 || w.hasKey("building");
318 }
319 }
320
321 List<MyWaySegment> getWaySegments(Way w) {
322 List<MyWaySegment> ret = new ArrayList<MyWaySegment>();
323 if (!w.isUsable()
324 || w.hasKey("barrier")
325 || "cliff".equals(w.get("natural")))
326 return ret;
327
328 int size = w.getNodesCount();
329 if (size < 2)
330 return ret;
331 for (int i = 1; i < size; ++i) {
332 if(i < size-1) {
333 addNode(w.getNode(i), middlenodes);
334 }
335 MyWaySegment ws = new MyWaySegment(w, w.getNode(i-1), w.getNode(i));
336 if (ws.isBoundary || ws.isAbandoned) {
337 continue;
338 }
339 ret.add(ws);
340 }
341 return ret;
342 }
343
344 @Override
345 public void visit(Way w) {
346 ways.addAll(getWaySegments(w));
347 Set<Node> set = endnodes;
348 if (w.hasKey("highway") || w.hasKey("railway")) {
349 set = endnodes_highway;
350 }
351 addNode(w.firstNode(), set);
352 addNode(w.lastNode(), set);
353 }
354
355 @Override
356 public void visit(Node n) {
357 }
358
359 private void addNode(Node n, Set<Node> s) {
360 boolean m = middlenodes.contains(n);
361 boolean e = endnodes.contains(n);
362 boolean eh = endnodes_highway.contains(n);
363 boolean o = othernodes.contains(n);
364 if (!m && !e && !o && !eh) {
365 s.add(n);
366 } else if (!o) {
367 othernodes.add(n);
368 if (e) {
369 endnodes.remove(n);
370 } else if (eh) {
371 endnodes_highway.remove(n);
372 } else {
373 middlenodes.remove(n);
374 }
375 }
376 }
377}
Note: See TracBrowser for help on using the repository browser.