001 // License: GPL. See LICENSE file for details.
002 package org.openstreetmap.josm.data.validation.tests;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.util.HashSet;
007 import java.util.Map;
008 import java.util.Set;
009
010 import org.openstreetmap.josm.Main;
011 import org.openstreetmap.josm.command.Command;
012 import org.openstreetmap.josm.data.osm.OsmPrimitive;
013 import org.openstreetmap.josm.data.osm.Relation;
014 import org.openstreetmap.josm.data.osm.RelationMember;
015 import org.openstreetmap.josm.data.osm.Way;
016 import org.openstreetmap.josm.data.validation.Severity;
017 import org.openstreetmap.josm.data.validation.Test;
018 import org.openstreetmap.josm.data.validation.TestError;
019 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
020
021 /**
022 * Checks for untagged ways
023 *
024 * @author frsantos
025 */
026 public 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 public static final Set<String> NAMED_WAYS = new HashSet<String>();
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 public static final Set<String> WHITELIST = new HashSet<String>();
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)) {
082 if (!tags.containsKey("name") && !tags.containsKey("ref")) {
083 boolean isRoundabout = false;
084 boolean hasName = false;
085 for (String key : w.keySet()) {
086 hasName = key.startsWith("name:") || key.endsWith("_name") || key.endsWith("_ref");
087 if (hasName) {
088 break;
089 }
090 if (key.equals("junction")) {
091 isRoundabout = w.get("junction").equals("roundabout");
092 break;
093 }
094 }
095
096 if (!hasName && !isRoundabout) {
097 errors.add(new TestError(this, Severity.WARNING, tr("Unnamed ways"), UNNAMED_WAY, w));
098 } else if (isRoundabout) {
099 errors.add(new TestError(this, Severity.WARNING, tr("Unnamed junction"), UNNAMED_JUNCTION, w));
100 }
101 }
102 }
103 }
104
105 if (!w.isTagged() && !waysUsedInRelations.contains(w)) {
106 if (w.hasKeys()) {
107 errors.add(new TestError(this, Severity.WARNING, tr("Untagged ways (commented)"), COMMENTED_WAY, w));
108 } else {
109 errors.add(new TestError(this, Severity.WARNING, tr("Untagged ways"), UNTAGGED_WAY, w));
110 }
111 }
112
113 if (w.getNodesCount() == 0) {
114 errors.add(new TestError(this, Severity.ERROR, tr("Empty ways"), EMPTY_WAY, w));
115 } else if (w.getNodesCount() == 1) {
116 errors.add(new TestError(this, Severity.ERROR, tr("One node ways"), ONE_NODE_WAY, w));
117 }
118 }
119
120 @Override
121 public void startTest(ProgressMonitor monitor) {
122 super.startTest(monitor);
123 waysUsedInRelations = new HashSet<Way>();
124 for (Relation r : Main.main.getCurrentDataSet().getRelations()) {
125 if (r.isUsable()) {
126 for (RelationMember m : r.getMembers()) {
127 if (r.isMultipolygon() || WHITELIST.contains(m.getRole())) {
128 OsmPrimitive member = m.getMember();
129 if (member != null && member instanceof Way && member.isUsable() && !member.isTagged()) {
130 waysUsedInRelations.add((Way)member);
131 }
132 }
133 }
134 }
135 }
136 }
137
138 @Override
139 public void endTest() {
140 waysUsedInRelations = null;
141 super.endTest();
142 }
143
144 @Override
145 public boolean isFixable(TestError testError) {
146 if (testError.getTester() instanceof UntaggedWay)
147 return testError.getCode() == EMPTY_WAY
148 || testError.getCode() == ONE_NODE_WAY;
149
150 return false;
151 }
152
153 @Override
154 public Command fixError(TestError testError) {
155 return deletePrimitivesIfNeeded(testError.getPrimitives());
156 }
157 }