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.HashSet; 007import java.util.Map; 008import java.util.Set; 009 010import org.openstreetmap.josm.Main; 011import org.openstreetmap.josm.command.Command; 012import org.openstreetmap.josm.data.osm.OsmPrimitive; 013import org.openstreetmap.josm.data.osm.Relation; 014import org.openstreetmap.josm.data.osm.RelationMember; 015import org.openstreetmap.josm.data.osm.Way; 016import org.openstreetmap.josm.data.validation.Severity; 017import org.openstreetmap.josm.data.validation.Test; 018import org.openstreetmap.josm.data.validation.TestError; 019import org.openstreetmap.josm.gui.progress.ProgressMonitor; 020 021/** 022 * Checks for untagged ways 023 * 024 * @author frsantos 025 */ 026public class UntaggedWay extends Test { 027 028 /** Empty way error */ 029 protected static final int EMPTY_WAY = 301; 030 /** Untagged way error */ 031 protected static final int UNTAGGED_WAY = 302; 032 /** Unnamed way error */ 033 protected static final int UNNAMED_WAY = 303; 034 /** One node way error */ 035 protected static final int ONE_NODE_WAY = 304; 036 /** Unnamed junction error */ 037 protected static final int UNNAMED_JUNCTION = 305; 038 /** Untagged, but commented way error */ 039 protected static final int COMMENTED_WAY = 306; 040 041 private Set<Way> waysUsedInRelations; 042 043 /** Ways that must have a name */ 044 protected static final Set<String> NAMED_WAYS = new HashSet<>(); 045 static { 046 NAMED_WAYS.add("motorway"); 047 NAMED_WAYS.add("trunk"); 048 NAMED_WAYS.add("primary"); 049 NAMED_WAYS.add("secondary"); 050 NAMED_WAYS.add("tertiary"); 051 NAMED_WAYS.add("residential"); 052 NAMED_WAYS.add("pedestrian"); 053 } 054 055 /** Whitelist of roles allowed to reference an untagged way */ 056 protected static final Set<String> WHITELIST = new HashSet<>(); 057 static { 058 WHITELIST.add("outer"); 059 WHITELIST.add("inner"); 060 WHITELIST.add("perimeter"); 061 WHITELIST.add("edge"); 062 WHITELIST.add("outline"); 063 } 064 065 /** 066 * Constructor 067 */ 068 public UntaggedWay() { 069 super(tr("Untagged, empty and one node ways"), 070 tr("This test checks for untagged, empty and one node ways.")); 071 } 072 073 @Override 074 public void visit(Way w) { 075 if (!w.isUsable()) 076 return; 077 078 Map<String, String> tags = w.getKeys(); 079 if (!tags.isEmpty()) { 080 String highway = tags.get("highway"); 081 if (highway != null && NAMED_WAYS.contains(highway) && !tags.containsKey("name") && !tags.containsKey("ref") 082 && !"yes".equals(tags.get("noname"))) { 083 boolean isJunction = false; 084 boolean hasName = false; 085 for (String key : tags.keySet()) { 086 hasName = key.startsWith("name:") || key.endsWith("_name") || key.endsWith("_ref"); 087 if (hasName) { 088 break; 089 } 090 if ("junction".equals(key)) { 091 isJunction = true; 092 break; 093 } 094 } 095 096 if (!hasName && !isJunction) { 097 errors.add(new TestError(this, Severity.WARNING, tr("Unnamed ways"), UNNAMED_WAY, w)); 098 } else if (isJunction) { 099 errors.add(new TestError(this, Severity.OTHER, tr("Unnamed junction"), UNNAMED_JUNCTION, w)); 100 } 101 } 102 } 103 104 if (!w.isTagged() && !waysUsedInRelations.contains(w)) { 105 if (w.hasKeys()) { 106 errors.add(new TestError(this, Severity.WARNING, tr("Untagged ways (commented)"), COMMENTED_WAY, w)); 107 } else { 108 errors.add(new TestError(this, Severity.WARNING, tr("Untagged ways"), UNTAGGED_WAY, w)); 109 } 110 } 111 112 if (w.getNodesCount() == 0) { 113 errors.add(new TestError(this, Severity.ERROR, tr("Empty ways"), EMPTY_WAY, w)); 114 } else if (w.getNodesCount() == 1) { 115 errors.add(new TestError(this, Severity.ERROR, tr("One node ways"), ONE_NODE_WAY, w)); 116 } 117 } 118 119 @Override 120 public void startTest(ProgressMonitor monitor) { 121 super.startTest(monitor); 122 waysUsedInRelations = new HashSet<>(); 123 for (Relation r : Main.main.getCurrentDataSet().getRelations()) { 124 if (r.isUsable()) { 125 for (RelationMember m : r.getMembers()) { 126 if (r.isMultipolygon() || WHITELIST.contains(m.getRole())) { 127 OsmPrimitive member = m.getMember(); 128 if (member instanceof Way && member.isUsable() && !member.isTagged()) { 129 waysUsedInRelations.add((Way) member); 130 } 131 } 132 } 133 } 134 } 135 } 136 137 @Override 138 public void endTest() { 139 waysUsedInRelations = null; 140 super.endTest(); 141 } 142 143 @Override 144 public boolean isFixable(TestError testError) { 145 if (testError.getTester() instanceof UntaggedWay) 146 return testError.getCode() == EMPTY_WAY 147 || testError.getCode() == ONE_NODE_WAY; 148 149 return false; 150 } 151 152 @Override 153 public Command fixError(TestError testError) { 154 return deletePrimitivesIfNeeded(testError.getPrimitives()); 155 } 156 157 @Override 158 public boolean isPrimitiveUsable(OsmPrimitive p) { 159 return p.isUsable(); 160 } 161}