source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/sort/WayConnectionTypeCalculator.java@ 11886

Last change on this file since 11886 was 11886, checked in by Don-vip, 7 years ago

see #14635 - add a new unit test (not directly related to the bug)

  • Property svn:eol-style set to native
File size: 11.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation.sort;
3
4import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.BACKWARD;
5import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.FORWARD;
6import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.NONE;
7
8import java.util.ArrayList;
9import java.util.List;
10
11import org.openstreetmap.josm.data.osm.Node;
12import org.openstreetmap.josm.data.osm.RelationMember;
13import org.openstreetmap.josm.data.osm.Way;
14import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction;
15import org.openstreetmap.josm.tools.JosmRuntimeException;
16import org.openstreetmap.josm.tools.bugreport.BugReport;
17
18public class WayConnectionTypeCalculator {
19
20 private static final int UNCONNECTED = Integer.MIN_VALUE;
21
22 private List<RelationMember> members;
23
24 /**
25 * refresh the cache of member WayConnectionTypes
26 * @param members relation members
27 * @return way connections
28 */
29 public List<WayConnectionType> updateLinks(List<RelationMember> members) {
30 this.members = members;
31 final List<WayConnectionType> con = new ArrayList<>();
32
33 for (int i = 0; i < members.size(); ++i) {
34 con.add(null);
35 }
36
37 firstGroupIdx = 0;
38
39 lastForwardWay = UNCONNECTED;
40 lastBackwardWay = UNCONNECTED;
41 onewayBeginning = false;
42 WayConnectionType lastWct = null;
43
44 for (int i = 0; i < members.size(); ++i) {
45 try {
46 lastWct = updateLinksFor(con, lastWct, i);
47 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
48 int index = i;
49 throw BugReport.intercept(e).put("i", i).put("member", () -> members.get(index)).put("con", con);
50 }
51 }
52 makeLoopIfNeeded(con, members.size()-1);
53
54 return con;
55 }
56
57 private WayConnectionType updateLinksFor(final List<WayConnectionType> con, WayConnectionType lastWct, int i) {
58 final RelationMember m = members.get(i);
59 if (isNoHandleableWay(m)) {
60 if (i > 0) {
61 makeLoopIfNeeded(con, i-1);
62 }
63 con.set(i, new WayConnectionType());
64 firstGroupIdx = i;
65 } else {
66 WayConnectionType wct = computeNextWayConnection(con, lastWct, i, m);
67
68 if (!wct.linkPrev) {
69 if (i > 0) {
70 makeLoopIfNeeded(con, i-1);
71 }
72 firstGroupIdx = i;
73 }
74 return wct;
75 }
76 return lastWct;
77 }
78
79 private static boolean isNoHandleableWay(final RelationMember m) {
80 return !m.isWay() || m.getWay() == null || m.getWay().isIncomplete();
81 }
82
83 private WayConnectionType computeNextWayConnection(final List<WayConnectionType> con, WayConnectionType lastWct, int i,
84 final RelationMember m) {
85 WayConnectionType wct = new WayConnectionType(false);
86 wct.linkPrev = i > 0 && con.get(i-1) != null && con.get(i-1).isValid();
87 wct.direction = NONE;
88
89 if (RelationSortUtils.isOneway(m)) {
90 handleOneway(lastWct, i, wct);
91 }
92
93 if (wct.linkPrev) {
94 if (lastBackwardWay != UNCONNECTED && lastForwardWay != UNCONNECTED) {
95 determineOnewayConnectionType(con, m, i, wct);
96 if (!wct.linkPrev) {
97 firstGroupIdx = i;
98 }
99 }
100
101 if (lastWct != null && !RelationSortUtils.isOneway(m)) {
102 wct.direction = determineDirection(i-1, lastWct.direction, i);
103 wct.linkPrev = wct.direction != NONE;
104 }
105 }
106
107 if (!wct.linkPrev) {
108 wct.direction = determineDirectionOfFirst(i, m);
109 if (RelationSortUtils.isOneway(m)) {
110 wct.isOnewayLoopForwardPart = true;
111 lastForwardWay = i;
112 }
113 }
114
115 wct.linkNext = false;
116 if (lastWct != null) {
117 lastWct.linkNext = wct.linkPrev;
118 }
119 con.set(i, wct);
120 return wct;
121 }
122
123 private void handleOneway(WayConnectionType lastWct, int i, WayConnectionType wct) {
124 if (lastWct != null && lastWct.isOnewayTail) {
125 wct.isOnewayHead = true;
126 }
127 if (lastBackwardWay == UNCONNECTED && lastForwardWay == UNCONNECTED) { //Beginning of new oneway
128 wct.isOnewayHead = true;
129 lastForwardWay = i-1;
130 lastBackwardWay = i-1;
131 onewayBeginning = true;
132 }
133 }
134
135 private int firstGroupIdx;
136 private void makeLoopIfNeeded(final List<WayConnectionType> con, final int i) {
137 boolean loop = false;
138 if (i == firstGroupIdx) { //is primitive loop
139 loop = determineDirection(i, FORWARD, i) == FORWARD;
140 } else if (i >= 0) {
141 loop = determineDirection(i, con.get(i).direction, firstGroupIdx) == con.get(firstGroupIdx).direction;
142 }
143 if (loop) {
144 for (int j = firstGroupIdx; j <= i; ++j) {
145 con.get(j).isLoop = true;
146 }
147 }
148 }
149
150 private Direction determineDirectionOfFirst(final int i, final RelationMember m) {
151 Direction result = RelationSortUtils.roundaboutType(m);
152 if (result != NONE)
153 return result;
154
155 if (RelationSortUtils.isOneway(m)) {
156 if (RelationSortUtils.isBackward(m)) return BACKWARD;
157 else return FORWARD;
158 } else { /** guess the direction and see if it fits with the next member */
159 if (determineDirection(i, FORWARD, i+1) != NONE) return FORWARD;
160 if (determineDirection(i, BACKWARD, i+1) != NONE) return BACKWARD;
161 }
162 return NONE;
163 }
164
165 private int lastForwardWay;
166 private int lastBackwardWay;
167 private boolean onewayBeginning;
168
169 private void determineOnewayConnectionType(final List<WayConnectionType> con,
170 RelationMember m, int i, final WayConnectionType wct) {
171 Direction dirFW = determineDirection(lastForwardWay, con.get(lastForwardWay).direction, i);
172 Direction dirBW;
173 if (onewayBeginning) {
174 if (lastBackwardWay < 0) {
175 dirBW = determineDirection(firstGroupIdx, reverse(con.get(firstGroupIdx).direction), i, true);
176 } else {
177 dirBW = determineDirection(lastBackwardWay, con.get(lastBackwardWay).direction, i, true);
178 }
179
180 if (dirBW != NONE) {
181 onewayBeginning = false;
182 }
183 } else {
184 dirBW = determineDirection(lastBackwardWay, con.get(lastBackwardWay).direction, i, true);
185 }
186
187 if (RelationSortUtils.isOneway(m)) {
188 if (dirBW != NONE) {
189 wct.direction = dirBW;
190 lastBackwardWay = i;
191 wct.isOnewayLoopBackwardPart = true;
192 }
193 if (dirFW != NONE) {
194 wct.direction = dirFW;
195 lastForwardWay = i;
196 wct.isOnewayLoopForwardPart = true;
197 }
198 // Not connected to previous
199 if (dirFW == NONE && dirBW == NONE) {
200 wct.linkPrev = false;
201 if (RelationSortUtils.isOneway(m)) {
202 wct.isOnewayHead = true;
203 lastForwardWay = i-1;
204 lastBackwardWay = i-1;
205 } else {
206 lastForwardWay = UNCONNECTED;
207 lastBackwardWay = UNCONNECTED;
208 }
209 onewayBeginning = true;
210 }
211
212 if (dirFW != NONE && dirBW != NONE) { //End of oneway loop
213 if (i+1 < members.size() && determineDirection(i, dirFW, i+1) != NONE) {
214 wct.isOnewayLoopBackwardPart = false;
215 wct.direction = dirFW;
216 } else {
217 wct.isOnewayLoopForwardPart = false;
218 wct.direction = dirBW;
219 }
220
221 wct.isOnewayTail = true;
222 }
223
224 } else {
225 lastForwardWay = UNCONNECTED;
226 lastBackwardWay = UNCONNECTED;
227 if (dirFW == NONE || dirBW == NONE) {
228 wct.linkPrev = false;
229 }
230 }
231 }
232
233 private static Direction reverse(final Direction dir) {
234 if (dir == FORWARD) return BACKWARD;
235 if (dir == BACKWARD) return FORWARD;
236 return dir;
237 }
238
239 private Direction determineDirection(int refI, Direction refDirection, int k) {
240 return determineDirection(refI, refDirection, k, false);
241 }
242
243 /**
244 * Determines the direction of way {@code k} with respect to the way {@code ref_i}.
245 * The way {@code ref_i} is assumed to have the direction {@code ref_direction} and to be the predecessor of {@code k}.
246 *
247 * If both ways are not linked in any way, NONE is returned.
248 *
249 * Else the direction is given as follows:
250 * Let the relation be a route of oneway streets, and someone travels them in the given order.
251 * Direction is FORWARD if it is legal and BACKWARD if it is illegal to do so for the given way.
252 * @param refI way key
253 * @param refDirection direction of ref_i
254 * @param k successor of ref_i
255 * @param reversed if {@code true} determine reverse direction
256 * @return direction of way {@code k}
257 */
258 private Direction determineDirection(int refI, final Direction refDirection, int k, boolean reversed) {
259 if (members == null || refI < 0 || k < 0 || refI >= members.size() || k >= members.size() || refDirection == NONE)
260 return NONE;
261
262 final RelationMember mRef = members.get(refI);
263 final RelationMember m = members.get(k);
264 Way wayRef = null;
265 Way way = null;
266
267 if (mRef.isWay()) {
268 wayRef = mRef.getWay();
269 }
270 if (m.isWay()) {
271 way = m.getWay();
272 }
273
274 if (wayRef == null || way == null)
275 return NONE;
276
277 /** the list of nodes the way k can dock to */
278 List<Node> refNodes = new ArrayList<>();
279
280 switch (refDirection) {
281 case FORWARD:
282 refNodes.add(wayRef.lastNode());
283 break;
284 case BACKWARD:
285 refNodes.add(wayRef.firstNode());
286 break;
287 case ROUNDABOUT_LEFT:
288 case ROUNDABOUT_RIGHT:
289 refNodes = wayRef.getNodes();
290 break;
291 default: // Do nothing
292 }
293
294 for (Node n : refNodes) {
295 if (n == null) {
296 continue;
297 }
298 if (RelationSortUtils.roundaboutType(members.get(k)) != NONE) {
299 for (Node nn : way.getNodes()) {
300 if (n == nn)
301 return RelationSortUtils.roundaboutType(members.get(k));
302 }
303 } else if (RelationSortUtils.isOneway(m)) {
304 if (n == RelationNodeMap.firstOnewayNode(m) && !reversed) {
305 if (RelationSortUtils.isBackward(m))
306 return BACKWARD;
307 else
308 return FORWARD;
309 }
310 if (reversed && n == RelationNodeMap.lastOnewayNode(m)) {
311 if (RelationSortUtils.isBackward(m))
312 return FORWARD;
313 else
314 return BACKWARD;
315 }
316 } else {
317 if (n == way.firstNode())
318 return FORWARD;
319 if (n == way.lastNode())
320 return BACKWARD;
321 }
322 }
323 return NONE;
324 }
325}
Note: See TracBrowser for help on using the repository browser.