source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/UnclosedWays.java

Last change on this file was 18521, checked in by taylor.smock, 21 months ago

Fix #22225: Don't warn about sport=running on unclosed ways

  • Property svn:eol-style set to native
File size: 8.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
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.util.Arrays;
8import java.util.Collections;
9import java.util.HashSet;
10import java.util.Set;
11import java.util.stream.Collectors;
12
13import org.openstreetmap.josm.data.osm.OsmUtils;
14import org.openstreetmap.josm.data.osm.Relation;
15import org.openstreetmap.josm.data.osm.Way;
16import org.openstreetmap.josm.data.validation.Severity;
17import org.openstreetmap.josm.data.validation.Test;
18import org.openstreetmap.josm.data.validation.TestError;
19import org.openstreetmap.josm.gui.mappaint.ElemStyles;
20
21/**
22 * Check area type ways for errors
23 *
24 * @author stoecker
25 * @since 3669
26 */
27public class UnclosedWays extends Test {
28
29 /**
30 * Constructs a new {@code UnclosedWays} test.
31 */
32 public UnclosedWays() {
33 super(tr("Unclosed Ways"), tr("This tests if ways which should be circular are closed."));
34 }
35
36 /**
37 * A check performed by UnclosedWays test.
38 * @since 6390
39 */
40 private static class UnclosedWaysCheck {
41 /** The unique numeric code for this check */
42 public final int code;
43 /** The OSM key checked */
44 public final String key;
45 /** The English message */
46 private final String engMessage;
47 /** The special values, to be ignored if ignore is set to true; to be considered only if ignore is set to false */
48 private final Set<String> specialValues;
49 /** The boolean indicating if special values must be ignored or considered only */
50 private final boolean ignore;
51
52 /**
53 * Constructs a new {@code UnclosedWaysCheck}.
54 * @param code The unique numeric code for this check
55 * @param key The OSM key checked
56 * @param engMessage The English message
57 */
58 UnclosedWaysCheck(int code, String key, String engMessage) {
59 this(code, key, engMessage, Collections.<String>emptySet());
60 }
61
62 /**
63 * Constructs a new {@code UnclosedWaysCheck}.
64 * @param code The unique numeric code for this check
65 * @param key The OSM key checked
66 * @param engMessage The English message
67 * @param ignoredValues The ignored values.
68 */
69 UnclosedWaysCheck(int code, String key, String engMessage, Set<String> ignoredValues) {
70 this(code, key, engMessage, ignoredValues, true);
71 }
72
73 /**
74 * Constructs a new {@code UnclosedWaysCheck}.
75 * @param code The unique numeric code for this check
76 * @param key The OSM key checked
77 * @param engMessage The English message
78 * @param specialValues The special values, to be ignored if ignore is set to true; to be considered only if ignore is set to false
79 * @param ignore indicates if special values must be ignored or considered only
80 */
81 UnclosedWaysCheck(int code, String key, String engMessage, Set<String> specialValues, boolean ignore) {
82 this.code = code;
83 this.key = key;
84 this.engMessage = engMessage;
85 this.specialValues = specialValues;
86 this.ignore = ignore;
87 }
88
89 /**
90 * Returns the test error of the given way, if any.
91 * @param w The way to check
92 * @param test parent test
93 * @return The test error if the way is erroneous, {@code null} otherwise
94 */
95 public final TestError getTestError(Way w, UnclosedWays test) {
96 String value = w.get(key);
97 if (isValueErroneous(value)) {
98 final Severity severity;
99 // see #20455: raise severity to error when we are sure that tag key must describe an area
100 if ((ignore && !specialValues.isEmpty()) || "boundary".equals(key)) {
101 severity = Severity.WARNING;
102 } else {
103 severity = Severity.ERROR;
104 }
105 return TestError.builder(test, severity, code)
106 .message(tr("Unclosed way"), engMessage, engMessage.contains("{0}") ? new Object[]{value} : new Object[]{})
107 .primitives(w)
108 .highlight(Arrays.asList(w.firstNode(), w.lastNode()))
109 .build();
110 }
111 return null;
112 }
113
114 protected boolean isValueErroneous(String value) {
115 return value != null && ignore != specialValues.contains(value);
116 }
117 }
118
119 /**
120 * A check performed by UnclosedWays test where the key is treated as boolean.
121 * @since 6390
122 */
123 private static final class UnclosedWaysBooleanCheck extends UnclosedWaysCheck {
124
125 /**
126 * Constructs a new {@code UnclosedWaysBooleanCheck}.
127 * @param code The unique numeric code for this check
128 * @param key The OSM key checked
129 * @param engMessage The English message
130 */
131 UnclosedWaysBooleanCheck(int code, String key, String engMessage) {
132 super(code, key, engMessage);
133 }
134
135 @Override
136 protected boolean isValueErroneous(String value) {
137 Boolean btest = OsmUtils.getOsmBoolean(value);
138 // Not a strict boolean comparison to handle building=house like a building=yes
139 return (btest != null && btest) || (btest == null && value != null);
140 }
141 }
142
143 private static final UnclosedWaysCheck[] checks = {
144 // CHECKSTYLE.OFF: SingleSpaceSeparator
145 // list contains natural tag allowed on unclosed ways as well as those only allowed on nodes to avoid
146 // duplicate warnings
147 new UnclosedWaysCheck(1101, "natural", marktr("natural type {0}"),
148 new HashSet<>(Arrays.asList("arete", "bay", "cave", "cliff", "coastline", "earth_bank", "gorge", "gully",
149 "mountain_range", "peak", "ridge", "saddle", "strait", "tree", "tree_row", "valley", "volcano"))),
150
151 new UnclosedWaysCheck(1102, "landuse", marktr("landuse type {0}")),
152 new UnclosedWaysCheck(1103, "amenity", marktr("amenity type {0}"),
153 new HashSet<>(Arrays.asList("bench", "bicycle_parking", "weighbridge"))),
154 new UnclosedWaysCheck(1104, "sport", marktr("sport type {0}"),
155 new HashSet<>(Arrays.asList("water_slide", "climbing", "skiing", "toboggan", "bobsleigh", "karting", "motor", "motocross",
156 "cycling", "running"))),
157 new UnclosedWaysCheck(1105, "tourism", marktr("tourism type {0}"),
158 new HashSet<>(Arrays.asList("attraction", "artwork"))),
159 new UnclosedWaysCheck(1106, "shop", marktr("shop type {0}")),
160 new UnclosedWaysCheck(1107, "leisure", marktr("leisure type {0}"),
161 new HashSet<>(Arrays.asList("track", "slipway", "barefoot"))),
162 new UnclosedWaysCheck(1108, "waterway", marktr("waterway type {0}"),
163 new HashSet<>(Arrays.asList("riverbank")), false),
164 new UnclosedWaysCheck(1109, "boundary", marktr("boundary type {0}")),
165 new UnclosedWaysCheck(1110, "area:highway", marktr("area:highway type {0}")),
166 new UnclosedWaysCheck(1111, "place", marktr("place type {0}")),
167 new UnclosedWaysBooleanCheck(1120, "building", marktr("building")),
168 new UnclosedWaysBooleanCheck(1130, "area", marktr("area")),
169 // 1131: Area style way is not closed
170 // CHECKSTYLE.ON: SingleSpaceSeparator
171 };
172
173 /**
174 * Returns the set of checked OSM keys.
175 * @return The set of checked OSM keys.
176 * @since 6390
177 */
178 public Set<String> getCheckedKeys() {
179 return Arrays.stream(checks).map(c -> c.key).collect(Collectors.toSet());
180 }
181
182 @Override
183 public void visit(Way w) {
184
185 if (!w.isUsable() || w.isArea())
186 return;
187
188 for (UnclosedWaysCheck c : checks) {
189 if ("boundary".equals(c.key) && w.referrers(Relation.class).anyMatch(Relation::isMultipolygon))
190 return;
191 TestError error = c.getTestError(w, this);
192 if (error != null) {
193 errors.add(error);
194 return;
195 }
196 }
197 // code 1131: other area style ways
198 if (ElemStyles.hasOnlyAreaElements(w) && !w.getNodes().isEmpty()) {
199 errors.add(TestError.builder(this, Severity.WARNING, 1131)
200 .message(tr("Unclosed way"), marktr("Area style way is not closed"), new Object())
201 .primitives(w)
202 .highlight(Arrays.asList(w.firstNode(), w.lastNode()))
203 .build());
204 }
205 }
206}
Note: See TracBrowser for help on using the repository browser.