source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/CrossingWays.java@ 12958

Last change on this file since 12958 was 12958, checked in by Don-vip, 7 years ago

fix #14645 - distinguish crossing ways validator error subtypes to properly ignore the selected group only

  • Property svn:eol-style set to native
File size: 11.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.geom.Point2D;
7import java.util.ArrayList;
8import java.util.HashMap;
9import java.util.List;
10import java.util.Map;
11import java.util.Objects;
12
13import org.openstreetmap.josm.data.coor.EastNorth;
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.Relation;
16import org.openstreetmap.josm.data.osm.Way;
17import org.openstreetmap.josm.data.osm.WaySegment;
18import org.openstreetmap.josm.data.validation.OsmValidator;
19import org.openstreetmap.josm.data.validation.Severity;
20import org.openstreetmap.josm.data.validation.Test;
21import org.openstreetmap.josm.data.validation.TestError;
22import org.openstreetmap.josm.data.validation.util.ValUtil;
23import org.openstreetmap.josm.gui.progress.ProgressMonitor;
24import org.openstreetmap.josm.tools.Logging;
25
26/**
27 * Tests if there are segments that crosses in the same layer
28 *
29 * @author frsantos
30 */
31public abstract class CrossingWays extends Test {
32
33 static final String HIGHWAY = "highway";
34 static final String RAILWAY = "railway";
35 static final String WATERWAY = "waterway";
36
37 /** All way segments, grouped by cells */
38 private final Map<Point2D, List<WaySegment>> cellSegments = new HashMap<>(1000);
39 /** The already detected ways in error */
40 private final Map<List<Way>, List<WaySegment>> seenWays = new HashMap<>(50);
41
42 private final int code;
43
44 /**
45 * General crossing ways test.
46 */
47 public static class Ways extends CrossingWays {
48
49 protected static final int CROSSING_WAYS = 601;
50
51 /**
52 * Constructs a new crossing {@code Ways} test.
53 */
54 public Ways() {
55 super(tr("Crossing ways"), CROSSING_WAYS);
56 }
57
58 @Override
59 public boolean isPrimitiveUsable(OsmPrimitive w) {
60 return super.isPrimitiveUsable(w)
61 && !isProposedOrAbandoned(w)
62 && (isHighway(w)
63 || w.hasKey(WATERWAY)
64 || isRailway(w)
65 || isCoastline(w)
66 || isBuilding(w));
67 }
68
69 @Override
70 boolean ignoreWaySegmentCombination(Way w1, Way w2) {
71 if (w1 == w2)
72 return false;
73 if (!Objects.equals(getLayer(w1), getLayer(w2))) {
74 return true;
75 }
76 if (w1.hasKey(HIGHWAY) && w2.hasKey(HIGHWAY) && !Objects.equals(w1.get("level"), w2.get("level"))) {
77 return true;
78 }
79 if (isSubwayOrTramOrRazed(w2)) {
80 return true;
81 }
82 if (isCoastline(w1) != isCoastline(w2)) {
83 return true;
84 }
85 if ((w1.hasTag(WATERWAY, "river", "stream", "canal", "drain", "ditch") && w2.hasTag(WATERWAY, "riverbank"))
86 || (w2.hasTag(WATERWAY, "river", "stream", "canal", "drain", "ditch") && w1.hasTag(WATERWAY, "riverbank"))) {
87 return true;
88 }
89 return isProposedOrAbandoned(w2);
90 }
91
92 @Override
93 String createMessage(Way w1, Way w2) {
94 if (isBuilding(w1)) {
95 return tr("Crossing buildings");
96 } else if (w1.hasKey(WATERWAY) && w2.hasKey(WATERWAY)) {
97 return tr("Crossing waterways");
98 } else if ((w1.hasKey(HIGHWAY) && w2.hasKey(WATERWAY))
99 || (w2.hasKey(HIGHWAY) && w1.hasKey(WATERWAY))) {
100 return tr("Crossing waterway/highway");
101 } else {
102 return tr("Crossing ways");
103 }
104 }
105 }
106
107 /**
108 * Crossing boundaries ways test.
109 */
110 public static class Boundaries extends CrossingWays {
111
112 protected static final int CROSSING_BOUNDARIES = 602;
113
114 /**
115 * Constructs a new crossing {@code Boundaries} test.
116 */
117 public Boundaries() {
118 super(tr("Crossing boundaries"), CROSSING_BOUNDARIES);
119 }
120
121 @Override
122 public boolean isPrimitiveUsable(OsmPrimitive p) {
123 return super.isPrimitiveUsable(p) && p.hasKey("boundary")
124 && (!(p instanceof Relation) || (((Relation) p).isMultipolygon() && !((Relation) p).hasIncompleteMembers()));
125 }
126
127 @Override
128 boolean ignoreWaySegmentCombination(Way w1, Way w2) {
129 return !Objects.equals(w1.get("boundary"), w2.get("boundary"));
130 }
131
132 @Override
133 String createMessage(Way w1, Way w2) {
134 return tr("Crossing boundaries");
135 }
136
137 @Override
138 public void visit(Relation r) {
139 for (Way w : r.getMemberPrimitives(Way.class)) {
140 visit(w);
141 }
142 }
143 }
144
145 /**
146 * Crossing barriers ways test.
147 */
148 public static class Barrier extends CrossingWays {
149
150 protected static final int CROSSING_BARRIERS = 603;
151
152 /**
153 * Constructs a new crossing {@code Barrier} test.
154 */
155 public Barrier() {
156 super(tr("Crossing barriers"), CROSSING_BARRIERS);
157 }
158
159 @Override
160 public boolean isPrimitiveUsable(OsmPrimitive p) {
161 return super.isPrimitiveUsable(p) && p.hasKey("barrier");
162 }
163
164 @Override
165 boolean ignoreWaySegmentCombination(Way w1, Way w2) {
166 return !Objects.equals(getLayer(w1), getLayer(w2));
167 }
168
169 @Override
170 String createMessage(Way w1, Way w2) {
171 return tr("Crossing barriers");
172 }
173 }
174
175 /**
176 * Self crossing ways test (for all the rest)
177 */
178 public static class SelfCrossing extends CrossingWays {
179
180 protected static final int CROSSING_SELF = 604;
181
182 CrossingWays.Ways normalTest = new Ways();
183 CrossingWays.Barrier barrierTest = new Barrier();
184 CrossingWays.Boundaries boundariesTest = new Boundaries();
185
186 /**
187 * Constructs a new SelfIntersection test.
188 */
189 public SelfCrossing() {
190 super(tr("Self crossing"), CROSSING_SELF);
191 }
192
193 @Override
194 public boolean isPrimitiveUsable(OsmPrimitive p) {
195 return super.isPrimitiveUsable(p) && !(normalTest.isPrimitiveUsable(p) || barrierTest.isPrimitiveUsable(p)
196 || boundariesTest.isPrimitiveUsable(p));
197 }
198
199 @Override
200 boolean ignoreWaySegmentCombination(Way w1, Way w2) {
201 return w1 != w2; // should not happen
202 }
203
204 @Override
205 String createMessage(Way w1, Way w2) {
206 return tr("Self-crossing ways");
207 }
208 }
209
210 /**
211 * Constructs a new {@code CrossingWays} test.
212 * @param title The test title
213 * @param code The test code
214 * @since 12958
215 */
216 public CrossingWays(String title, int code) {
217 super(title, tr("This test checks if two roads, railways, waterways or buildings crosses in the same layer, " +
218 "but are not connected by a node."));
219 this.code = code;
220 }
221
222 @Override
223 public void startTest(ProgressMonitor monitor) {
224 super.startTest(monitor);
225 cellSegments.clear();
226 seenWays.clear();
227 }
228
229 @Override
230 public void endTest() {
231 super.endTest();
232 cellSegments.clear();
233 seenWays.clear();
234 }
235
236 static String getLayer(OsmPrimitive w) {
237 String layer1 = w.get("layer");
238 if ("0".equals(layer1)) {
239 layer1 = null; // 0 is default value for layer.
240 }
241 return layer1;
242 }
243
244 static boolean isCoastline(OsmPrimitive w) {
245 return w.hasTag("natural", "water", "coastline") || w.hasTag("landuse", "reservoir");
246 }
247
248 static boolean isHighway(OsmPrimitive w) {
249 return w.hasTagDifferent(HIGHWAY, "rest_area", "services");
250 }
251
252 static boolean isRailway(OsmPrimitive w) {
253 return w.hasKey(RAILWAY) && !isSubwayOrTramOrRazed(w);
254 }
255
256 static boolean isSubwayOrTramOrRazed(OsmPrimitive w) {
257 return w.hasTag(RAILWAY, "subway", "tram", "razed");
258 }
259
260 static boolean isProposedOrAbandoned(OsmPrimitive w) {
261 return w.hasTag(HIGHWAY, "proposed") || w.hasTag(RAILWAY, "proposed", "abandoned");
262 }
263
264 abstract boolean ignoreWaySegmentCombination(Way w1, Way w2);
265
266 abstract String createMessage(Way w1, Way w2);
267
268 @Override
269 public void visit(Way w) {
270 if (this instanceof SelfCrossing) {
271 // free memory, we are not interested in previous ways
272 cellSegments.clear();
273 seenWays.clear();
274 }
275
276 int nodesSize = w.getNodesCount();
277 for (int i = 0; i < nodesSize - 1; i++) {
278 final WaySegment es1 = new WaySegment(w, i);
279 final EastNorth en1 = es1.getFirstNode().getEastNorth();
280 final EastNorth en2 = es1.getSecondNode().getEastNorth();
281 if (en1 == null || en2 == null) {
282 Logging.warn("Crossing ways test skipped "+es1);
283 continue;
284 }
285 for (List<WaySegment> segments : getSegments(cellSegments, en1, en2)) {
286 for (WaySegment es2 : segments) {
287 List<Way> prims;
288 List<WaySegment> highlight;
289
290 if (!es1.intersects(es2) || ignoreWaySegmentCombination(es1.way, es2.way)) {
291 continue;
292 }
293
294 prims = new ArrayList<>();
295 prims.add(es1.way);
296 if (es1.way != es2.way)
297 prims.add(es2.way);
298 if ((highlight = seenWays.get(prims)) == null) {
299 highlight = new ArrayList<>();
300 highlight.add(es1);
301 highlight.add(es2);
302
303 final String message = createMessage(es1.way, es2.way);
304 errors.add(TestError.builder(this, Severity.WARNING, code)
305 .message(message)
306 .primitives(prims)
307 .highlightWaySegments(highlight)
308 .build());
309 seenWays.put(prims, highlight);
310 } else {
311 highlight.add(es1);
312 highlight.add(es2);
313 }
314 }
315 segments.add(es1);
316 }
317 }
318 }
319
320 /**
321 * Returns all the cells this segment crosses. Each cell contains the list
322 * of segments already processed
323 * @param cellSegments map with already collected way segments
324 * @param n1 The first EastNorth
325 * @param n2 The second EastNorth
326 * @return A list with all the cells the segment crosses
327 */
328 public static List<List<WaySegment>> getSegments(Map<Point2D, List<WaySegment>> cellSegments, EastNorth n1, EastNorth n2) {
329 List<List<WaySegment>> cells = new ArrayList<>();
330 for (Point2D cell : ValUtil.getSegmentCells(n1, n2, OsmValidator.getGridDetail())) {
331 cells.add(cellSegments.computeIfAbsent(cell, k -> new ArrayList<>()));
332 }
333 return cells;
334 }
335}
Note: See TracBrowser for help on using the repository browser.