source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/TurnrestrictionTest.java@ 15003

Last change on this file since 15003 was 15003, checked in by GerdP, 6 years ago

fix #17567: apply 17567-rephrase.patch

  • Property svn:eol-style set to native
File size: 12.9 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.tr;
5
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.List;
9
10import org.openstreetmap.josm.data.osm.Node;
11import org.openstreetmap.josm.data.osm.OsmPrimitive;
12import org.openstreetmap.josm.data.osm.Relation;
13import org.openstreetmap.josm.data.osm.RelationMember;
14import org.openstreetmap.josm.data.osm.Way;
15import org.openstreetmap.josm.data.validation.Severity;
16import org.openstreetmap.josm.data.validation.Test;
17import org.openstreetmap.josm.data.validation.TestError;
18
19/**
20 * Checks if turnrestrictions are valid
21 * @since 3669
22 */
23public class TurnrestrictionTest extends Test {
24
25 protected static final int NO_VIA = 1801;
26 protected static final int NO_FROM = 1802;
27 protected static final int NO_TO = 1803;
28 protected static final int MORE_VIA = 1804;
29 protected static final int MORE_FROM = 1805;
30 protected static final int MORE_TO = 1806;
31 protected static final int UNEXPECTED_ROLE = 1807;
32 protected static final int UNEXPECTED_TYPE = 1808;
33 protected static final int FROM_VIA_NODE = 1809;
34 protected static final int TO_VIA_NODE = 1810;
35 protected static final int FROM_VIA_WAY = 1811;
36 protected static final int TO_VIA_WAY = 1812;
37 protected static final int MIX_VIA = 1813;
38 protected static final int UNCONNECTED_VIA = 1814;
39 protected static final int SUPERFLUOUS = 1815;
40 protected static final int FROM_EQUALS_TO = 1816;
41 protected static final int UNKNOWN_RESTRICTION = 1817;
42 protected static final int TO_CLOSED_WAY = 1818;
43 protected static final int FROM_CLOSED_WAY = 1819;
44
45 private static final List<String> SUPPORTED_RESTRICTIONS = Arrays.asList(
46 "no_right_turn", "no_left_turn", "no_u_turn", "no_straight_on",
47 "only_right_turn", "only_left_turn", "only_straight_on",
48 "no_entry", "no_exit"
49 );
50
51 /**
52 * Constructs a new {@code TurnrestrictionTest}.
53 */
54 public TurnrestrictionTest() {
55 super(tr("Turnrestrictions"), tr("This test checks if turnrestrictions are valid."));
56 }
57
58 @Override
59 public void visit(Relation r) {
60 if (!r.hasTag("type", "restriction"))
61 return;
62
63 if (!r.hasTag("restriction", SUPPORTED_RESTRICTIONS)) {
64 errors.add(TestError.builder(this, Severity.ERROR, UNKNOWN_RESTRICTION)
65 .message(tr("Unknown restriction"))
66 .primitives(r)
67 .build());
68 return;
69 }
70
71 Way fromWay = null;
72 Way toWay = null;
73 List<OsmPrimitive> via = new ArrayList<>();
74
75 boolean morefrom = false;
76 boolean moreto = false;
77 boolean morevia = false;
78 boolean mixvia = false;
79
80 /* find the "from", "via" and "to" elements */
81 for (RelationMember m : r.getMembers()) {
82 if (m.getMember().isIncomplete())
83 return;
84
85 List<OsmPrimitive> l = new ArrayList<>();
86 l.add(r);
87 l.add(m.getMember());
88 if (m.isWay()) {
89 Way w = m.getWay();
90 if (w.getNodesCount() < 2) {
91 continue;
92 }
93
94 switch (m.getRole()) {
95 case "from":
96 if (fromWay != null) {
97 morefrom = true;
98 } else {
99 fromWay = w;
100 }
101 break;
102 case "to":
103 if (toWay != null) {
104 moreto = true;
105 } else {
106 toWay = w;
107 }
108 break;
109 case "via":
110 if (!via.isEmpty() && via.get(0) instanceof Node) {
111 mixvia = true;
112 } else {
113 via.add(w);
114 }
115 break;
116 default:
117 errors.add(TestError.builder(this, Severity.WARNING, UNEXPECTED_ROLE)
118 .message(tr("Unexpected role ''{0}'' in restriction", m.getRole()))
119 .primitives(l)
120 .highlight(m.getMember())
121 .build());
122 }
123 } else if (m.isNode()) {
124 Node n = m.getNode();
125 if ("via".equals(m.getRole())) {
126 if (!via.isEmpty()) {
127 if (via.get(0) instanceof Node) {
128 morevia = true;
129 } else {
130 mixvia = true;
131 }
132 } else {
133 via.add(n);
134 }
135 } else {
136 errors.add(TestError.builder(this, Severity.WARNING, UNEXPECTED_ROLE)
137 .message(tr("Unexpected role ''{0}'' in restriction", m.getRole()))
138 .primitives(l)
139 .highlight(m.getMember())
140 .build());
141 }
142 } else {
143 errors.add(TestError.builder(this, Severity.WARNING, UNEXPECTED_TYPE)
144 .message(tr("Unexpected member type in restriction"))
145 .primitives(l)
146 .highlight(m.getMember())
147 .build());
148 }
149 }
150 if (morefrom) {
151 errors.add(TestError.builder(this, Severity.ERROR, MORE_FROM)
152 .message(tr("More than one \"from\" way found"))
153 .primitives(r)
154 .build());
155 return;
156 }
157 if (moreto) {
158 errors.add(TestError.builder(this, Severity.ERROR, MORE_TO)
159 .message(tr("More than one \"to\" way found"))
160 .primitives(r)
161 .build());
162 return;
163 }
164 if (morevia) {
165 errors.add(TestError.builder(this, Severity.ERROR, MORE_VIA)
166 .message(tr("More than one \"via\" node found"))
167 .primitives(r)
168 .build());
169 return;
170 }
171 if (mixvia) {
172 errors.add(TestError.builder(this, Severity.ERROR, MIX_VIA)
173 .message(tr("Cannot mix node and way for role \"via\""))
174 .primitives(r)
175 .build());
176 return;
177 }
178
179 if (fromWay == null) {
180 errors.add(TestError.builder(this, Severity.ERROR, NO_FROM)
181 .message(tr("No \"from\" way found"))
182 .primitives(r)
183 .build());
184 return;
185 } else if (fromWay.isClosed()) {
186 errors.add(TestError.builder(this, Severity.ERROR, FROM_CLOSED_WAY)
187 .message(tr("\"from\" way is a closed way"))
188 .primitives(r)
189 .highlight(fromWay)
190 .build());
191 return;
192 }
193
194 if (toWay == null) {
195 errors.add(TestError.builder(this, Severity.ERROR, NO_TO)
196 .message(tr("No \"to\" way found"))
197 .primitives(r)
198 .build());
199 return;
200 } else if (toWay.isClosed()) {
201 errors.add(TestError.builder(this, Severity.ERROR, TO_CLOSED_WAY)
202 .message(tr("\"to\" way is a closed way"))
203 .primitives(r)
204 .highlight(toWay)
205 .build());
206 return;
207 }
208 if (fromWay.equals(toWay)) {
209 Severity severity = r.hasTag("restriction", "no_u_turn") ? Severity.OTHER : Severity.WARNING;
210 errors.add(TestError.builder(this, severity, FROM_EQUALS_TO)
211 .message(tr("\"from\" way equals \"to\" way"))
212 .primitives(r)
213 .build());
214 }
215 if (via.isEmpty()) {
216 errors.add(TestError.builder(this, Severity.ERROR, NO_VIA)
217 .message(tr("No \"via\" node or way found"))
218 .primitives(r)
219 .build());
220 return;
221 }
222
223 if (via.get(0) instanceof Node) {
224 final Node viaNode = (Node) via.get(0);
225 if (isFullOneway(toWay) && viaNode.equals(toWay.lastNode(true))) {
226 errors.add(TestError.builder(this, Severity.WARNING, SUPERFLUOUS)
227 .message(tr("Superfluous turnrestriction as \"to\" way is oneway"))
228 .primitives(r)
229 .highlight(toWay)
230 .build());
231 return;
232 }
233 if (isFullOneway(fromWay) && viaNode.equals(fromWay.firstNode(true))) {
234 errors.add(TestError.builder(this, Severity.WARNING, SUPERFLUOUS)
235 .message(tr("Superfluous turnrestriction as \"from\" way is oneway"))
236 .primitives(r)
237 .highlight(fromWay)
238 .build());
239 return;
240 }
241 final Way viaPseudoWay = new Way();
242 viaPseudoWay.addNode(viaNode);
243 checkIfConnected(r, fromWay, viaPseudoWay,
244 tr("The \"from\" way does not start or end at a \"via\" node."), FROM_VIA_NODE);
245 checkIfConnected(r, viaPseudoWay, toWay,
246 tr("The \"to\" way does not start or end at a \"via\" node."), TO_VIA_NODE);
247 } else {
248 if (isFullOneway(toWay) && ((Way) via.get(via.size() - 1)).isFirstLastNode(toWay.lastNode(true))) {
249 errors.add(TestError.builder(this, Severity.WARNING, SUPERFLUOUS)
250 .message(tr("Superfluous turnrestriction as \"to\" way is oneway"))
251 .primitives(r)
252 .highlight(toWay)
253 .build());
254 return;
255 }
256 if (isFullOneway(fromWay) && ((Way) via.get(0)).isFirstLastNode(fromWay.firstNode(true))) {
257 errors.add(TestError.builder(this, Severity.WARNING, SUPERFLUOUS)
258 .message(tr("Superfluous turnrestriction as \"from\" way is oneway"))
259 .primitives(r)
260 .highlight(fromWay)
261 .build());
262 return;
263 }
264 // check if consecutive ways are connected: from/via[0], via[i-1]/via[i], via[last]/to
265 checkIfConnected(r, fromWay, (Way) via.get(0),
266 tr("The \"from\" and the first \"via\" way are not connected."), FROM_VIA_WAY);
267 if (via.size() > 1) {
268 for (int i = 1; i < via.size(); i++) {
269 Way previous = (Way) via.get(i - 1);
270 Way current = (Way) via.get(i);
271 checkIfConnected(r, previous, current,
272 tr("The \"via\" ways are not connected."), UNCONNECTED_VIA);
273 }
274 }
275 checkIfConnected(r, (Way) via.get(via.size() - 1), toWay,
276 tr("The last \"via\" and the \"to\" way are not connected."), TO_VIA_WAY);
277 }
278 }
279
280 private static boolean isFullOneway(Way w) {
281 return w.isOneway() != 0 && !w.hasTag("oneway:bicycle", "no");
282 }
283
284 private void checkIfConnected(Relation r, Way previous, Way current, String msg, int code) {
285 boolean c;
286 if (isFullOneway(previous) && isFullOneway(current)) {
287 // both oneways: end/start node must be equal
288 c = previous.lastNode(true).equals(current.firstNode(true));
289 } else if (isFullOneway(previous)) {
290 // previous way is oneway: end of previous must be start/end of current
291 c = current.isFirstLastNode(previous.lastNode(true));
292 } else if (isFullOneway(current)) {
293 // current way is oneway: start of current must be start/end of previous
294 c = previous.isFirstLastNode(current.firstNode(true));
295 } else {
296 // otherwise: start/end of previous must be start/end of current
297 c = current.isFirstLastNode(previous.firstNode()) || current.isFirstLastNode(previous.lastNode());
298 }
299 if (!c) {
300 List<OsmPrimitive> hilite = new ArrayList<>();
301 if (previous.getNodesCount() == 1 && previous.isNew())
302 hilite.add(previous.firstNode());
303 else
304 hilite.add(previous);
305 if (current.getNodesCount() == 1 && current.isNew())
306 hilite.add(current.firstNode());
307 else
308 hilite.add(current);
309 List<OsmPrimitive> primitives = new ArrayList<>();
310 primitives.add(r);
311 primitives.addAll(hilite);
312 errors.add(TestError.builder(this, Severity.ERROR, code)
313 .message(msg)
314 .primitives(primitives)
315 .highlight(hilite)
316 .build());
317 }
318 }
319}
Note: See TracBrowser for help on using the repository browser.