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 }