source: josm/trunk/test/unit/org/openstreetmap/josm/gui/dialogs/relation/sort/WayConnectionTypeCalculatorTest.java@ 16886

Last change on this file since 16886 was 16886, checked in by simon04, 4 years ago

fix #19633 - Route relations with split start do not show links as expected in the relation editor (patch by matthijskooijman)

  • Property svn:eol-style set to native
File size: 14.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation.sort;
3
4import java.io.IOException;
5import java.io.InputStream;
6import java.nio.file.Files;
7import java.nio.file.Paths;
8import java.util.ArrayList;
9import java.util.Arrays;
10import java.util.Collections;
11import java.util.HashSet;
12import java.util.List;
13
14import org.junit.Assert;
15import org.junit.Before;
16import org.junit.Rule;
17import org.junit.Test;
18import org.openstreetmap.josm.data.osm.DataSet;
19import org.openstreetmap.josm.data.osm.Node;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.Relation;
22import org.openstreetmap.josm.data.osm.Way;
23import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
24import org.openstreetmap.josm.io.IllegalDataException;
25import org.openstreetmap.josm.io.OsmReader;
26import org.openstreetmap.josm.testutils.JOSMTestRules;
27
28import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
29
30/**
31 * Unit tests of {@link WayConnectionTypeCalculator} class.
32 */
33public class WayConnectionTypeCalculatorTest {
34
35 private RelationSorter sorter = new RelationSorter();
36 private WayConnectionTypeCalculator wayConnectionTypeCalculator = new WayConnectionTypeCalculator();
37 private DataSet testDataset;
38
39 /**
40 * Use Mercator projection
41 */
42 @Rule
43 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
44 public JOSMTestRules test = new JOSMTestRules().preferences().projection();
45
46 /**
47 * Load the test data set
48 * @throws IllegalDataException if an error was found while parsing the data
49 * @throws IOException in case of I/O error
50 */
51 @Before
52 public void loadData() throws IllegalDataException, IOException {
53 if (testDataset == null) {
54 try (InputStream fis = Files.newInputStream(Paths.get("nodist/data/relation_sort.osm"))) {
55 testDataset = OsmReader.parseDataSet(fis, NullProgressMonitor.INSTANCE);
56 }
57 }
58 }
59
60 private Relation getRelation(String testType) {
61 return testDataset.getRelations().stream().filter(r -> testType.equals(r.get("test"))).findFirst().orElse(null);
62 }
63
64 private String getConnections(List<WayConnectionType> connections) {
65 String[] result = new String[connections.size()];
66 for (int i = 0; i < result.length; i++) {
67 WayConnectionType wc = connections.get(i);
68
69 if (wc.isValid()) {
70 StringBuilder sb = new StringBuilder();
71 if (wc.isLoop) {
72 sb.append("L");
73 }
74 if (wc.isOnewayLoopForwardPart) {
75 sb.append("FP");
76 }
77 if (wc.isOnewayLoopBackwardPart) {
78 sb.append("BP");
79 }
80 if (wc.isOnewayHead) {
81 sb.append("H");
82 }
83 if (wc.isOnewayTail) {
84 sb.append("T");
85 }
86
87 if (sb.length() > 0) {
88 sb.append(" ");
89 }
90 sb.append(wc.direction);
91 result[i] = sb.toString();
92
93 } else {
94 result[i] = "I";
95 }
96
97 }
98 return Arrays.toString(result);
99 }
100
101 @Test
102 public void testEmpty() {
103 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(new ArrayList<>()));
104 Assert.assertEquals("[]", actual);
105 }
106
107 // This cluster of tests checks the rendering before and after
108 // sorting of a few relations. Initially, these relations are
109 // intentionally not sorted to ensure the sorting has some work.
110
111 @Test
112 public void testGeneric() {
113 Relation relation = getRelation("generic");
114 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(relation.getMembers()));
115 Assert.assertEquals("[NONE, NONE, FORWARD, FORWARD, NONE, NONE, NONE, I, I]", actual);
116 actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
117 Assert.assertEquals("[FORWARD, FORWARD, FORWARD, FORWARD, BACKWARD, BACKWARD, NONE, I, I]", actual);
118 }
119
120 @Test
121 public void testAssociatedStreet() {
122 Relation relation = getRelation("associatedStreet");
123 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(relation.getMembers()));
124 Assert.assertEquals("[NONE, I, I, I, NONE, I]", actual);
125 actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
126 Assert.assertEquals("[FORWARD, FORWARD, I, I, I, I]", actual);
127 }
128
129 @Test
130 public void testLoop() {
131 Relation relation = getRelation("loop");
132 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(relation.getMembers()));
133 Assert.assertEquals("[FPH FORWARD, FP FORWARD, NONE, FPH FORWARD, NONE, FPH FORWARD, NONE]", actual);
134 //TODO Sorting doesn't work well in this case
135 actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
136 Assert.assertEquals("[BACKWARD, BACKWARD, BACKWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, BPT BACKWARD]", actual);
137 }
138
139 // The following cluster of tests checks various configurations
140 // involving split / dual carriageway routes (i.e. containing
141 // members with role=forward or role=backward). Again, these are
142 // intentionally not sorted.
143
144 @Test
145 public void testThreeLoopsEndsLoop() {
146 Relation relation = getRelation("three-loops-ends-loop");
147 // Check the first way before sorting, otherwise the sorter
148 // might pick a different loop starting point than expected below
149 Assert.assertEquals("t5w1", relation.getMembers().get(0).getMember().get("name"));
150 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
151 String expected = "[" +
152 "L FORWARD, LFPH FORWARD, LFP FORWARD, LFP FORWARD, LBP BACKWARD, LBP BACKWARD, LBPT BACKWARD, " +
153 "L FORWARD, LFPH FORWARD, LFP FORWARD, LFP FORWARD, LBP BACKWARD, LBP BACKWARD, LBPT BACKWARD, " +
154 "LFPH FORWARD, LFP FORWARD, LFP FORWARD, LBP BACKWARD, LBP BACKWARD, LBPT BACKWARD, " +
155 "L FORWARD, L FORWARD" +
156 "]";
157 Assert.assertEquals(expected, actual);
158 }
159
160 @Test
161 public void testThreeLoopsEndsWay() {
162 Relation relation = getRelation("three-loops-ends-way");
163 // Check the first way before sorting, otherwise the sorter
164 // might sort in reverse compared to what is expected below
165 Assert.assertEquals("t5w1", relation.getMembers().get(0).getMember().get("name"));
166 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
167 String expected = "[" +
168 "FORWARD, FPH FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, BPT BACKWARD, " +
169 "FORWARD, FPH FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, BPT BACKWARD, " +
170 "FPH FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, BPT BACKWARD, " +
171 "FORWARD" +
172 "]";
173 Assert.assertEquals(expected, actual);
174 }
175
176 @Test
177 public void testThreeLoopsEndsNode() {
178 Relation relation = getRelation("three-loops-ends-node");
179 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
180 String expected = "[" +
181 "FPH FORWARD, BP BACKWARD, BP BACKWARD, BP BACKWARD, BP BACKWARD, BPT BACKWARD, " +
182 "FORWARD, FPH FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, BPT BACKWARD, " +
183 "FPH FORWARD, FP FORWARD, FP FORWARD, FP FORWARD, FP FORWARD, BPT BACKWARD" +
184 "]";
185 Assert.assertEquals(expected, actual);
186 }
187
188 @Test
189 public void testOneLoopEndsSplit() {
190 Relation relation = getRelation("one-loop-ends-split");
191 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
192 String expected = "[" +
193 "FP FORWARD, FP FORWARD, BP BACKWARD, BPT BACKWARD, " +
194 "FORWARD, FPH FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, BPT BACKWARD, " +
195 "FPH FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD" +
196 "]";
197 Assert.assertEquals(expected, actual);
198 }
199
200 @Test
201 public void testNoLoopEndsSplit() {
202 Relation relation = getRelation("no-loop-ends-split");
203 // TODO: This is not yet sorted properly, so this route is
204 // presorted in the data file
205 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(relation.getMembers()));
206 String expected = "[" +
207 "FP FORWARD, FP FORWARD, BP BACKWARD, BPT BACKWARD, " +
208 "FPH FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD" +
209 "]";
210 Assert.assertEquals(expected, actual);
211 }
212
213 @Test
214 public void testIncompleteLoops() {
215 Relation relation = getRelation("incomplete-loops");
216 // TODO: This is not yet sorted perfectly (might not be possible)
217 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
218 String expected = "[" +
219 "FORWARD, FPH FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, " +
220 "FORWARD, FPH FORWARD, FP FORWARD, FP FORWARD, FP FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, " +
221 "BACKWARD, FPH FORWARD, FP FORWARD, FP FORWARD" +
222 "]";
223 Assert.assertEquals(expected, actual);
224 }
225
226 @Test
227 public void testParallelOneWay() {
228 Relation relation = getRelation("parallel-oneway");
229 // TODO: This is not always sorted properly, only when the right
230 // way is already at the top, so check that
231 Assert.assertEquals("t6w1a", relation.getMembers().get(0).getMember().get("name"));
232 String actual = getConnections(wayConnectionTypeCalculator.updateLinks(sorter.sortMembers(relation.getMembers())));
233 String expected = "[" +
234 "FP FORWARD, FP FORWARD, FP FORWARD, BP BACKWARD, BP BACKWARD, BP BACKWARD" +
235 "]";
236 Assert.assertEquals(expected, actual);
237 }
238
239 private void reverseWay(Way way) {
240 List<Node> nodes = way.getNodes();
241 Collections.reverse(nodes);
242 way.removeNodes(new HashSet<>(nodes));
243 for (Node node : nodes) {
244 way.addNode(node);
245 }
246 }
247
248 /**
249 * Test directional {@link WayConnectionTypeCalculator#computeNextWayConnection}
250 */
251 @Test
252 public void testDirectionsOnewaysOnly() {
253 Relation relation = getRelation("direction");
254
255 // Check with only one wrong oneway
256 List<WayConnectionType> returned = wayConnectionTypeCalculator.updateLinks(relation.getMembers());
257 for (int i = 0; i < 4; i++) {
258 Assert.assertTrue(returned.get(i).onewayFollowsPrevious);
259 Assert.assertTrue(returned.get(i).onewayFollowsNext);
260 }
261
262 Assert.assertTrue(returned.get(4).onewayFollowsPrevious);
263 Assert.assertFalse(returned.get(4).onewayFollowsNext);
264
265 Assert.assertFalse(returned.get(5).onewayFollowsPrevious);
266 Assert.assertFalse(returned.get(5).onewayFollowsNext);
267
268 Assert.assertFalse(returned.get(6).onewayFollowsPrevious);
269 Assert.assertTrue(returned.get(6).onewayFollowsNext);
270
271 // Reverse the last oneway
272 OsmPrimitive way7 = relation.getMemberPrimitivesList().get(6);
273 if (way7 instanceof Way) {
274 Way way = (Way) way7;
275 reverseWay(way);
276 returned = wayConnectionTypeCalculator.updateLinks(relation.getMembers());
277 for (int i = 0; i < 4; i++) {
278 Assert.assertTrue(returned.get(i).onewayFollowsPrevious);
279 Assert.assertTrue(returned.get(i).onewayFollowsNext);
280 }
281
282 Assert.assertTrue(returned.get(4).onewayFollowsPrevious);
283 Assert.assertFalse(returned.get(4).onewayFollowsNext);
284
285 Assert.assertFalse(returned.get(5).onewayFollowsPrevious);
286 Assert.assertTrue(returned.get(5).onewayFollowsNext);
287
288 Assert.assertTrue(returned.get(6).onewayFollowsPrevious);
289 Assert.assertTrue(returned.get(6).onewayFollowsNext);
290 reverseWay(way);
291 }
292
293 // Reverse the wrong oneway
294 OsmPrimitive way6 = relation.getMemberPrimitivesList().get(5);
295 if (way6 instanceof Way) {
296 Way way = (Way) way6;
297 reverseWay(way);
298 returned = wayConnectionTypeCalculator.updateLinks(relation.getMembers());
299 for (int i = 0; i < 7; i++) {
300 Assert.assertTrue(returned.get(i).onewayFollowsPrevious);
301 Assert.assertTrue(returned.get(i).onewayFollowsNext);
302 }
303 }
304
305 // Reverse everything
306 for (Way way : relation.getMemberPrimitives(Way.class)) {
307 reverseWay(way);
308 }
309 returned = wayConnectionTypeCalculator.updateLinks(relation.getMembers());
310 for (int i = 0; i < 7; i++) {
311 Assert.assertTrue(returned.get(i).onewayFollowsPrevious);
312 Assert.assertTrue(returned.get(i).onewayFollowsNext);
313 }
314 }
315
316 /**
317 * Test directional {@link WayConnectionTypeCalculator#computeNextWayConnection}
318 */
319 @Test
320 public void testDirectionsOnewayMix() {
321 Relation relation = getRelation("direction");
322
323 // Remove the oneway in the wrong direction
324 OsmPrimitive osm = relation.getMemberPrimitivesList().get(5);
325 osm.remove("oneway");
326 List<WayConnectionType> returned = wayConnectionTypeCalculator.updateLinks(relation.getMembers());
327 for (WayConnectionType type : returned) {
328 Assert.assertTrue(type.onewayFollowsNext);
329 Assert.assertTrue(type.onewayFollowsPrevious);
330 }
331
332 // Check with a oneway=-1 tag without reversing the way
333 osm.put("oneway", "-1");
334 returned = wayConnectionTypeCalculator.updateLinks(relation.getMembers());
335 for (WayConnectionType type : returned) {
336 Assert.assertTrue(type.onewayFollowsNext);
337 Assert.assertTrue(type.onewayFollowsPrevious);
338 }
339
340 // Check with oneways that converge onto a two-way
341 // TODO figure out a way to find this situation?
342 osm.remove("oneway");
343 OsmPrimitive way7 = relation.getMemberPrimitivesList().get(6);
344 way7.put("oneway", "-1");
345 returned = wayConnectionTypeCalculator.updateLinks(relation.getMembers());
346 for (int i = 0; i < returned.size() - 1; i++) {
347 WayConnectionType type = returned.get(i);
348 Assert.assertTrue(type.onewayFollowsNext);
349 Assert.assertTrue(type.onewayFollowsPrevious);
350 }
351 Assert.assertTrue(returned.get(6).onewayFollowsNext);
352 Assert.assertFalse(returned.get(6).onewayFollowsPrevious);
353 }
354}
Note: See TracBrowser for help on using the repository browser.