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

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

fix #14132 - MapCSS crossing operator must check layer attribute first

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