001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.validation.tests; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.Arrays; 007import java.util.List; 008 009import org.openstreetmap.josm.data.osm.Node; 010import org.openstreetmap.josm.data.osm.OsmPrimitive; 011import org.openstreetmap.josm.data.osm.Relation; 012import org.openstreetmap.josm.data.osm.Way; 013import org.openstreetmap.josm.data.validation.Severity; 014import org.openstreetmap.josm.data.validation.Test; 015import org.openstreetmap.josm.data.validation.TestError; 016import org.openstreetmap.josm.gui.mappaint.ElemStyles; 017import org.openstreetmap.josm.tools.Predicates; 018import org.openstreetmap.josm.tools.Utils; 019 020/** 021 * Checks for ways connected to areas. 022 * @since 4682 023 */ 024public class WayConnectedToArea extends Test { 025 026 /** 027 * Constructs a new {@code WayConnectedToArea} test. 028 */ 029 public WayConnectedToArea() { 030 super(tr("Way connected to Area"), tr("Checks for ways connected to areas.")); 031 } 032 033 @Override 034 public void visit(Way w) { 035 if (!w.isUsable() || w.isClosed() || !w.hasKey("highway")) { 036 return; 037 } 038 039 boolean hasway = false; 040 List<OsmPrimitive> r = w.firstNode().getReferrers(); 041 for (OsmPrimitive p : r) { 042 if (p != w && p.hasKey("highway")) { 043 hasway = true; 044 break; 045 } 046 } 047 if (!hasway) { 048 for (OsmPrimitive p : r) { 049 testForError(w, w.firstNode(), p); 050 } 051 } 052 hasway = false; 053 r = w.lastNode().getReferrers(); 054 for (OsmPrimitive p : r) { 055 if (p != w && p.hasKey("highway")) { 056 hasway = true; 057 break; 058 } 059 } 060 if (!hasway) { 061 for (OsmPrimitive p : r) { 062 testForError(w, w.lastNode(), p); 063 } 064 } 065 } 066 067 private void testForError(Way w, Node wayNode, OsmPrimitive p) { 068 if (wayNode.isOutsideDownloadArea()) { 069 return; 070 } else if (Utils.exists(wayNode.getReferrers(), Predicates.hasTag("route", "ferry"))) { 071 return; 072 } else if (isArea(p)) { 073 addPossibleError(w, wayNode, p, p); 074 } else { 075 for (OsmPrimitive r : p.getReferrers()) { 076 if (r instanceof Relation 077 && r.hasTag("type", "multipolygon") 078 && isArea(r)) { 079 addPossibleError(w, wayNode, p, r); 080 break; 081 } 082 } 083 } 084 } 085 086 private static boolean isArea(OsmPrimitive p) { 087 return (p.hasKey("landuse") || p.hasKey("natural")) 088 && ElemStyles.hasAreaElemStyle(p, false); 089 } 090 091 private void addPossibleError(Way w, Node wayNode, OsmPrimitive p, OsmPrimitive area) { 092 // Avoid "legal" cases (see #10655) 093 if (w.hasKey("highway") && wayNode.hasTag("leisure", "slipway") && area.hasTag("natural", "water")) { 094 return; 095 } 096 errors.add(new TestError(this, Severity.WARNING, 097 tr("Way terminates on Area"), 2301, 098 Arrays.asList(w, p), 099 Arrays.asList(wayNode))); 100 } 101}