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

Last change on this file since 8269 was 8269, checked in by simon04, 9 years ago

fix #11172 - Crossing ways: ignore highway=rest_area and highway=services

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