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

Last change on this file since 9244 was 9244, checked in by simon04, 8 years ago

see #12111 - Detect when "to" and "from" also in no_u_turn restriction relations

Those will be reported as informational warning messages.

  • Property svn:eol-style set to native
File size: 8.6 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.Collections;
9import java.util.List;
10
11import org.openstreetmap.josm.data.osm.Node;
12import org.openstreetmap.josm.data.osm.OsmPrimitive;
13import org.openstreetmap.josm.data.osm.Relation;
14import org.openstreetmap.josm.data.osm.RelationMember;
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;
19
20/**
21 * Checks if turnrestrictions are valid
22 * @since 3669
23 */
24public class TurnrestrictionTest extends Test {
25
26 protected static final int NO_VIA = 1801;
27 protected static final int NO_FROM = 1802;
28 protected static final int NO_TO = 1803;
29 protected static final int MORE_VIA = 1804;
30 protected static final int MORE_FROM = 1805;
31 protected static final int MORE_TO = 1806;
32 protected static final int UNKNOWN_ROLE = 1807;
33 protected static final int UNKNOWN_TYPE = 1808;
34 protected static final int FROM_VIA_NODE = 1809;
35 protected static final int TO_VIA_NODE = 1810;
36 protected static final int FROM_VIA_WAY = 1811;
37 protected static final int TO_VIA_WAY = 1812;
38 protected static final int MIX_VIA = 1813;
39 protected static final int UNCONNECTED_VIA = 1814;
40 protected static final int SUPERFLUOUS = 1815;
41 protected static final int FROM_EQUALS_TO = 1816;
42
43 /**
44 * Constructs a new {@code TurnrestrictionTest}.
45 */
46 public TurnrestrictionTest() {
47 super(tr("Turnrestrictions"), tr("This test checks if turnrestrictions are valid."));
48 }
49
50 @Override
51 public void visit(Relation r) {
52 if (!"restriction".equals(r.get("type")))
53 return;
54
55 Way fromWay = null;
56 Way toWay = null;
57 List<OsmPrimitive> via = new ArrayList<>();
58
59 boolean morefrom = false;
60 boolean moreto = false;
61 boolean morevia = false;
62 boolean mixvia = false;
63
64 /* find the "from", "via" and "to" elements */
65 for (RelationMember m : r.getMembers()) {
66 if (m.getMember().isIncomplete())
67 return;
68
69 List<OsmPrimitive> l = new ArrayList<>();
70 l.add(r);
71 l.add(m.getMember());
72 if (m.isWay()) {
73 Way w = m.getWay();
74 if (w.getNodesCount() < 2) {
75 continue;
76 }
77
78 switch (m.getRole()) {
79 case "from":
80 if (fromWay != null) {
81 morefrom = true;
82 } else {
83 fromWay = w;
84 }
85 break;
86 case "to":
87 if (toWay != null) {
88 moreto = true;
89 } else {
90 toWay = w;
91 }
92 break;
93 case "via":
94 if (!via.isEmpty() && via.get(0) instanceof Node) {
95 mixvia = true;
96 } else {
97 via.add(w);
98 }
99 break;
100 default:
101 errors.add(new TestError(this, Severity.WARNING, tr("Unknown role"), UNKNOWN_ROLE,
102 l, Collections.singletonList(m)));
103 }
104 } else if (m.isNode()) {
105 Node n = m.getNode();
106 if ("via".equals(m.getRole())) {
107 if (!via.isEmpty()) {
108 if (via.get(0) instanceof Node) {
109 morevia = true;
110 } else {
111 mixvia = true;
112 }
113 } else {
114 via.add(n);
115 }
116 } else {
117 errors.add(new TestError(this, Severity.WARNING, tr("Unknown role"), UNKNOWN_ROLE,
118 l, Collections.singletonList(m)));
119 }
120 } else {
121 errors.add(new TestError(this, Severity.WARNING, tr("Unknown member type"), UNKNOWN_TYPE,
122 l, Collections.singletonList(m)));
123 }
124 }
125 if (morefrom) {
126 errors.add(new TestError(this, Severity.ERROR, tr("More than one \"from\" way found"), MORE_FROM, r));
127 }
128 if (moreto) {
129 errors.add(new TestError(this, Severity.ERROR, tr("More than one \"to\" way found"), MORE_TO, r));
130 }
131 if (morevia) {
132 errors.add(new TestError(this, Severity.ERROR, tr("More than one \"via\" node found"), MORE_VIA, r));
133 }
134 if (mixvia) {
135 errors.add(new TestError(this, Severity.ERROR, tr("Cannot mix node and way for role \"via\""), MIX_VIA, r));
136 }
137
138 if (fromWay == null) {
139 errors.add(new TestError(this, Severity.ERROR, tr("No \"from\" way found"), NO_FROM, r));
140 return;
141 }
142 if (toWay == null) {
143 errors.add(new TestError(this, Severity.ERROR, tr("No \"to\" way found"), NO_TO, r));
144 return;
145 }
146 if (fromWay.equals(toWay)) {
147 errors.add(new TestError(this, r.hasTag("restriction", "no_u_turn") ? Severity.OTHER : Severity.WARNING,
148 tr("\"from\" way equals \"to\" way"), FROM_EQUALS_TO, r));
149 }
150 if (via.isEmpty()) {
151 errors.add(new TestError(this, Severity.ERROR, tr("No \"via\" node or way found"), NO_VIA, r));
152 return;
153 }
154
155 if (via.get(0) instanceof Node) {
156 final Node viaNode = (Node) via.get(0);
157 final Way viaPseudoWay = new Way();
158 viaPseudoWay.addNode(viaNode);
159 checkIfConnected(fromWay, viaPseudoWay,
160 tr("The \"from\" way does not start or end at a \"via\" node."), FROM_VIA_NODE);
161 if (toWay.isOneway() != 0 && viaNode.equals(toWay.lastNode(true))) {
162 errors.add(new TestError(this, Severity.WARNING, tr("Superfluous turnrestriction as \"to\" way is oneway"), SUPERFLUOUS, r));
163 return;
164 }
165 checkIfConnected(viaPseudoWay, toWay,
166 tr("The \"to\" way does not start or end at a \"via\" node."), TO_VIA_NODE);
167 } else {
168 // check if consecutive ways are connected: from/via[0], via[i-1]/via[i], via[last]/to
169 checkIfConnected(fromWay, (Way) via.get(0),
170 tr("The \"from\" and the first \"via\" way are not connected."), FROM_VIA_WAY);
171 if (via.size() > 1) {
172 for (int i = 1; i < via.size(); i++) {
173 Way previous = (Way) via.get(i - 1);
174 Way current = (Way) via.get(i);
175 checkIfConnected(previous, current,
176 tr("The \"via\" ways are not connected."), UNCONNECTED_VIA);
177 }
178 }
179 if (toWay.isOneway() != 0 && ((Way) via.get(via.size() - 1)).isFirstLastNode(toWay.lastNode(true))) {
180 errors.add(new TestError(this, Severity.WARNING, tr("Superfluous turnrestriction as \"to\" way is oneway"), SUPERFLUOUS, r));
181 return;
182 }
183 checkIfConnected((Way) via.get(via.size() - 1), toWay,
184 tr("The last \"via\" and the \"to\" way are not connected."), TO_VIA_WAY);
185 }
186 }
187
188 private static boolean isFullOneway(Way w) {
189 return w.isOneway() != 0 && !"no".equals(w.get("oneway:bicycle"));
190 }
191
192 private void checkIfConnected(Way previous, Way current, String msg, int code) {
193 boolean c;
194 if (isFullOneway(previous) && isFullOneway(current)) {
195 // both oneways: end/start node must be equal
196 c = previous.lastNode(true).equals(current.firstNode(true));
197 } else if (isFullOneway(previous)) {
198 // previous way is oneway: end of previous must be start/end of current
199 c = current.isFirstLastNode(previous.lastNode(true));
200 } else if (isFullOneway(current)) {
201 // current way is oneway: start of current must be start/end of previous
202 c = previous.isFirstLastNode(current.firstNode(true));
203 } else {
204 // otherwise: start/end of previous must be start/end of current
205 c = current.isFirstLastNode(previous.firstNode()) || current.isFirstLastNode(previous.lastNode());
206 }
207 if (!c) {
208 errors.add(new TestError(this, Severity.ERROR, msg, code, Arrays.asList(previous, current)));
209 }
210 }
211}
Note: See TracBrowser for help on using the repository browser.