source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java@ 6863

Last change on this file since 6863 was 6863, checked in by simon04, 10 years ago

see #9545 fix #9725 - Validator: add warning "Route scheme (public_transport or legacy) is unspecified"

  • Property svn:eol-style set to native
File size: 11.0 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.text.MessageFormat;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.HashMap;
11import java.util.HashSet;
12import java.util.LinkedList;
13import java.util.Set;
14
15import org.openstreetmap.josm.command.Command;
16import org.openstreetmap.josm.command.DeleteCommand;
17import org.openstreetmap.josm.data.osm.Node;
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.data.osm.Relation;
20import org.openstreetmap.josm.data.osm.RelationMember;
21import org.openstreetmap.josm.data.osm.Way;
22import org.openstreetmap.josm.data.validation.Severity;
23import org.openstreetmap.josm.data.validation.Test;
24import org.openstreetmap.josm.data.validation.TestError;
25import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference;
26import org.openstreetmap.josm.gui.tagging.TaggingPreset;
27import org.openstreetmap.josm.gui.tagging.TaggingPresetItem;
28import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Role;
29import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Key;
30import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Roles;
31import org.openstreetmap.josm.gui.tagging.TaggingPresetType;
32
33/**
34 * Check for wrong relations
35 *
36 */
37public class RelationChecker extends Test {
38
39 protected static final int ROLE_UNKNOWN = 1701;
40 protected static final int ROLE_EMPTY = 1702;
41 protected static final int WRONG_TYPE = 1703;
42 protected static final int HIGH_COUNT = 1704;
43 protected static final int LOW_COUNT = 1705;
44 protected static final int ROLE_MISSING = 1706;
45 protected static final int RELATION_UNKNOWN = 1707;
46 protected static final int RELATION_EMPTY = 1708;
47
48 /**
49 * Error message used to group errors related to role problems.
50 * @since 6731
51 */
52 public static final String ROLE_VERIF_PROBLEM_MSG = tr("Role verification problem");
53
54 /**
55 * Constructor
56 */
57 public RelationChecker() {
58 super(tr("Relation checker"),
59 tr("This plugin checks for errors in relations."));
60 }
61
62 @Override
63 public void initialize() {
64 initializePresets();
65 }
66
67 private static Collection<TaggingPreset> relationpresets = new LinkedList<TaggingPreset>();
68
69 /**
70 * Reads the presets data.
71 *
72 */
73 public void initializePresets() {
74 Collection<TaggingPreset> presets = TaggingPresetPreference.taggingPresets;
75 if (presets != null) {
76 for (TaggingPreset p : presets) {
77 for (TaggingPresetItem i : p.data) {
78 if (i instanceof Roles) {
79 relationpresets.add(p);
80 break;
81 }
82 }
83 }
84 }
85 }
86
87 private static class RoleInfo {
88 private int total = 0;
89 private Collection<Node> nodes = new LinkedList<Node>();
90 private Collection<Way> ways = new LinkedList<Way>();
91 private Collection<Way> openways = new LinkedList<Way>();
92 private Collection<Relation> relations = new LinkedList<Relation>();
93 }
94
95 @SuppressWarnings("unchecked")
96 @Override
97 public void visit(Relation n) {
98 LinkedList<Role> allroles = new LinkedList<Role>();
99 for (TaggingPreset p : relationpresets) {
100 boolean matches = true;
101 Roles r = null;
102 for (TaggingPresetItem i : p.data) {
103 if (i instanceof Key) {
104 Key k = (Key) i;
105 if (!k.value.equals(n.get(k.key))) {
106 matches = false;
107 break;
108 }
109 } else if (i instanceof Roles) {
110 r = (Roles) i;
111 }
112 }
113 if (matches && r != null) {
114 allroles.addAll(r.roles);
115 }
116 }
117 if (allroles.isEmpty() && n.hasTag("type", "route")
118 && n.hasTag("route", "train", "subway", "monorail", "tram", "bus", "trolleybus", "aerialway", "ferry")) {
119 errors.add(new TestError(this, Severity.WARNING,
120 tr("Route scheme (public_transport or legacy) is unspecified. Add {0}", "public_transport:version"),
121 RELATION_UNKNOWN, n));
122 } else if (allroles.isEmpty()) {
123 errors.add( new TestError(this, Severity.WARNING, tr("Relation type is unknown"),
124 RELATION_UNKNOWN, n) );
125 } else {
126 HashMap<String,RoleInfo> map = new HashMap<String, RoleInfo>();
127 for (RelationMember m : n.getMembers()) {
128 String s = "";
129 if (m.hasRole()) {
130 s = m.getRole();
131 }
132 RoleInfo ri = map.get(s);
133 if (ri == null) {
134 ri = new RoleInfo();
135 }
136 ri.total++;
137 if (m.isRelation()) {
138 ri.relations.add(m.getRelation());
139 } else if(m.isWay()) {
140 ri.ways.add(m.getWay());
141 if (!m.getWay().isClosed()) {
142 ri.openways.add(m.getWay());
143 }
144 }
145 else if (m.isNode()) {
146 ri.nodes.add(m.getNode());
147 }
148 map.put(s, ri);
149 }
150 if(map.isEmpty()) {
151 errors.add( new TestError(this, Severity.ERROR, tr("Relation is empty"),
152 RELATION_EMPTY, n) );
153 } else {
154 LinkedList<String> done = new LinkedList<String>();
155 for (Role r : allroles) {
156 done.add(r.key);
157 String keyname = r.key;
158 if ("".equals(keyname)) {
159 keyname = tr("<empty>");
160 }
161 RoleInfo ri = map.get(r.key);
162 long count = (ri == null) ? 0 : ri.total;
163 long vc = r.getValidCount(count);
164 if (count != vc) {
165 if (count == 0) {
166 String s = marktr("Role {0} missing");
167 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
168 tr(s, keyname), MessageFormat.format(s, keyname), ROLE_MISSING, n));
169 }
170 else if (vc > count) {
171 String s = marktr("Number of {0} roles too low ({1})");
172 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
173 tr(s, keyname, count), MessageFormat.format(s, keyname, count), LOW_COUNT, n));
174 } else {
175 String s = marktr("Number of {0} roles too high ({1})");
176 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
177 tr(s, keyname, count), MessageFormat.format(s, keyname, count), HIGH_COUNT, n));
178 }
179 }
180 if (ri != null) {
181 if (r.types != null) {
182 Set<OsmPrimitive> wrongTypes = new HashSet<OsmPrimitive>();
183 if (!r.types.contains(TaggingPresetType.WAY)) {
184 wrongTypes.addAll(r.types.contains(TaggingPresetType.CLOSEDWAY) ? ri.openways : ri.ways);
185 }
186 if (!r.types.contains(TaggingPresetType.NODE)) {
187 wrongTypes.addAll(ri.nodes);
188 }
189 if (!r.types.contains(TaggingPresetType.RELATION)) {
190 wrongTypes.addAll(ri.relations);
191 }
192 if (!wrongTypes.isEmpty()) {
193 String s = marktr("Member for role {0} of wrong type");
194 LinkedList<OsmPrimitive> highlight = new LinkedList<OsmPrimitive>(wrongTypes);
195 highlight.addFirst(n);
196 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
197 tr(s, keyname), MessageFormat.format(s, keyname), WRONG_TYPE,
198 highlight, wrongTypes));
199 }
200 }
201 if (r.memberExpression != null) {
202 Set<OsmPrimitive> notMatching = new HashSet<OsmPrimitive>();
203 for (Collection<OsmPrimitive> c : Arrays.asList(new Collection[]{ri.nodes, ri.ways, ri.relations})) {
204 for (OsmPrimitive p : c) {
205 if (p.isUsable() && !r.memberExpression.match(p)) {
206 notMatching.add(p);
207 }
208 }
209 }
210 if (!notMatching.isEmpty()) {
211 String s = marktr("Member for role ''{0}'' does not match ''{1}''");
212 LinkedList<OsmPrimitive> highlight = new LinkedList<OsmPrimitive>(notMatching);
213 highlight.addFirst(n);
214 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
215 tr(s, keyname, r.memberExpression), MessageFormat.format(s, keyname, r.memberExpression), WRONG_TYPE,
216 highlight, notMatching));
217 }
218 }
219 }
220 }
221 for (String key : map.keySet()) {
222 if (!done.contains(key)) {
223 if (key.length() > 0) {
224 String s = marktr("Role {0} unknown");
225 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
226 tr(s, key), MessageFormat.format(s, key), ROLE_UNKNOWN, n));
227 } else {
228 String s = marktr("Empty role found");
229 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
230 tr(s), s, ROLE_EMPTY, n));
231 }
232 }
233 }
234 }
235 }
236 }
237
238 @Override
239 public Command fixError(TestError testError) {
240 if (isFixable(testError)) {
241 return new DeleteCommand(testError.getPrimitives());
242 }
243 return null;
244 }
245
246 @Override
247 public boolean isFixable(TestError testError) {
248 Collection<? extends OsmPrimitive> primitives = testError.getPrimitives();
249 return testError.getCode() == RELATION_EMPTY && !primitives.isEmpty() && primitives.iterator().next().isNew();
250 }
251}
Note: See TracBrowser for help on using the repository browser.