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