Ticket #18364: 18364.1.patch
| File 18364.1.patch, 37.2 KB (added by , 6 years ago) |
|---|
-
src/org/openstreetmap/josm/data/validation/OsmValidator.java
60 60 import org.openstreetmap.josm.data.validation.tests.PublicTransportRouteTest; 61 61 import org.openstreetmap.josm.data.validation.tests.RelationChecker; 62 62 import org.openstreetmap.josm.data.validation.tests.RightAngleBuildingTest; 63 import org.openstreetmap.josm.data.validation.tests.RoutingIslandsTest; 63 64 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay; 64 65 import org.openstreetmap.josm.data.validation.tests.SharpAngles; 65 66 import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays; … … 150 151 PublicTransportRouteTest.class, // 3600 .. 3699 151 152 RightAngleBuildingTest.class, // 3700 .. 3799 152 153 SharpAngles.class, // 3800 .. 3899 154 RoutingIslandsTest.class, // 3900 .. 3999 153 155 }; 154 156 155 157 /** -
src/org/openstreetmap/josm/data/validation/tests/RoutingIslandsTest.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.validation.tests; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.util.ArrayList; 7 import java.util.Arrays; 8 import java.util.Collection; 9 import java.util.Collections; 10 import java.util.HashMap; 11 import java.util.HashSet; 12 import java.util.List; 13 import java.util.Map; 14 import java.util.Set; 15 import java.util.function.BiPredicate; 16 import java.util.stream.Collectors; 17 18 import org.openstreetmap.josm.data.osm.Node; 19 import org.openstreetmap.josm.data.osm.OsmPrimitive; 20 import org.openstreetmap.josm.data.osm.Relation; 21 import org.openstreetmap.josm.data.osm.TagMap; 22 import org.openstreetmap.josm.data.osm.Way; 23 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; 24 import org.openstreetmap.josm.data.validation.Severity; 25 import org.openstreetmap.josm.data.validation.Test; 26 import org.openstreetmap.josm.data.validation.TestError; 27 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 28 import org.openstreetmap.josm.spi.preferences.Config; 29 import org.openstreetmap.josm.tools.Access; 30 import org.openstreetmap.josm.tools.Pair; 31 32 /** 33 * A test for routing islands 34 * 35 * @author Taylor Smock 36 * @since xxx 37 */ 38 public class RoutingIslandsTest extends Test { 39 40 private static final Map<Integer, Severity> SEVERITY_MAP = new HashMap<>(); 41 /** The code for the routing island validation test */ 42 public static final int ROUTING_ISLAND = 3900; 43 static { 44 SEVERITY_MAP.put(ROUTING_ISLAND, Severity.OTHER); 45 } 46 /** 47 * This is mostly as a sanity check, and to avoid infinite recursion (shouldn't 48 * happen, but still) 49 */ 50 private static final int MAX_LOOPS = 1000; 51 private Set<Way> potentialWays; 52 53 /** 54 * Constructs a new {@code RightAngleBuildingTest} test. 55 */ 56 public RoutingIslandsTest() { 57 super(tr("Routing islands"), tr("Checks for roads that cannot be reached or left.")); 58 super.setPartialSelection(false); 59 } 60 61 @Override 62 public void startTest(ProgressMonitor monitor) { 63 super.startTest(monitor); 64 potentialWays = new HashSet<>(); 65 } 66 67 @Override 68 public void endTest() { 69 for (String mode : Access.getTransportModes()) { 70 progressMonitor.setCustomText(mode); 71 runTest(mode); 72 } 73 super.endTest(); 74 } 75 76 /** 77 * TODO Turn restrictions 78 */ 79 80 /** 81 * May connect to: 1) A road leaving the fully downloaded BBox 2) A 82 * dock/ferry/other waterway loading point 3) An aeroport 83 */ 84 85 @Override 86 public void visit(Way way) { 87 if ((ValidatorPrefHelper.PREF_OTHER.get() || ValidatorPrefHelper.PREF_OTHER_UPLOAD.get() 88 || !Severity.OTHER.equals(SEVERITY_MAP.get(ROUTING_ISLAND))) 89 && way.hasKey("highway") && way.isUsable() 90 && way.getNodes().parallelStream().anyMatch(node -> way.getDataSet().getDataSourceBounds() 91 .parallelStream().anyMatch(source -> source.contains(node.getCoor())))) 92 potentialWays.add(way); 93 } 94 95 private void runTest(String currentTransportMode) { 96 Set<Way> incomingWays = new HashSet<>(); 97 Set<Way> outgoingWays = new HashSet<>(); 98 for (Way way : potentialWays) { 99 if (way.isUsable() && way.isOutsideDownloadArea()) { 100 Node firstNode = firstNode(way, currentTransportMode); 101 Node lastNode = lastNode(way, currentTransportMode); 102 if (isOneway(way, currentTransportMode) != 0 103 && firstNode != null && firstNode.isOutsideDownloadArea()) 104 incomingWays.add(way); 105 if (isOneway(way, currentTransportMode) != 0 106 && lastNode != null && lastNode.isOutsideDownloadArea()) { 107 outgoingWays.add(way); 108 } 109 if (isOneway(way, currentTransportMode) == 0 && firstNode != null // Don't need to test lastNode 110 && (way.firstNode().isOutsideDownloadArea() || way.lastNode().isOutsideDownloadArea())) { 111 incomingWays.add(way); 112 outgoingWays.add(way); 113 } 114 } 115 } 116 Set<Way> toIgnore = potentialWays.parallelStream() 117 .filter(way -> incomingWays.contains(way) || outgoingWays.contains(way)) 118 .filter(way -> !Access.getPositiveAccessValues().contains( 119 getDefaultAccessTags(way).getOrDefault(currentTransportMode, Access.AccessTags.NO.getKey()))) 120 .collect(Collectors.toSet()); 121 incomingWays.removeAll(toIgnore); 122 outgoingWays.removeAll(toIgnore); 123 124 Set<Way> toCheck = potentialWays.parallelStream() 125 .filter(way -> !incomingWays.contains(way) && !outgoingWays.contains(way)).collect(Collectors.toSet()); 126 checkForUnconnectedWays(incomingWays, outgoingWays, currentTransportMode); 127 List<Pair<String, Set<Way>>> problematic = collectConnected(potentialWays.parallelStream() 128 .filter(way -> !incomingWays.contains(way) || !outgoingWays.contains(way)) 129 .filter(way -> Access.getPositiveAccessValues().contains( 130 getDefaultAccessTags(way).getOrDefault(currentTransportMode, Access.AccessTags.NO.getKey()))) 131 .collect(Collectors.toSet())).parallelStream() 132 .map(way -> new Pair<>((incomingWays.containsAll(way) ? "outgoing" : "incoming"), way)) 133 .collect(Collectors.toList()); 134 createErrors(problematic, currentTransportMode); 135 } 136 137 private static List<Set<Way>> collectConnected(Collection<Way> ways) { 138 ArrayList<Set<Way>> collected = new ArrayList<>(); 139 ArrayList<Way> listOfWays = new ArrayList<>(ways); 140 final int maxLoop = Config.getPref().getInt("validator.routingislands.maxrecursion", MAX_LOOPS); 141 for (int i = 0; i < listOfWays.size(); i++) { 142 Way initial = listOfWays.get(i); 143 Set<Way> connected = new HashSet<>(); 144 connected.add(initial); 145 int loopCounter = 0; 146 while (!getConnected(connected) && loopCounter < maxLoop) { 147 loopCounter++; 148 } 149 if (listOfWays.removeAll(connected)) 150 i--; // NOSONAR not an issue -- this ensures that everything is accounted for, only 151 // triggers when ways removed 152 collected.add(connected); 153 } 154 return collected; 155 } 156 157 private static boolean getConnected(Collection<Way> ways) { 158 TagMap defaultAccess = getDefaultAccessTags(ways.iterator().next()); 159 return ways.addAll(ways.parallelStream().flatMap(way -> way.getNodes().parallelStream()) 160 .flatMap(node -> node.getReferrers().parallelStream()).filter(Way.class::isInstance) 161 .map(Way.class::cast).filter(way -> getDefaultAccessTags(way).equals(defaultAccess)) 162 .collect(Collectors.toSet())); 163 } 164 165 private void createErrors(List<Pair<String, Set<Way>>> problematic, String mode) { 166 for (Pair<String, Set<Way>> ways : problematic) { 167 errors.add(TestError 168 .builder(this, SEVERITY_MAP.getOrDefault(ROUTING_ISLAND, Severity.OTHER), ROUTING_ISLAND) 169 .message(tr("Routing island"), "{0}: {1}", tr(ways.a), mode == null ? "default" : mode) 170 .primitives(ways.b).build()); 171 potentialWays.removeAll(ways.b); 172 } 173 } 174 175 /** 176 * Check for unconnected ways 177 * 178 * @param incoming The current incoming ways (will be modified) 179 * @param outgoing The current outgoing ways (will be modified) 180 * @param currentTransportMode The transport mode we are investigating (may be 181 * {@code null}) 182 */ 183 public static void checkForUnconnectedWays(Collection<Way> incoming, 184 Collection<Way> outgoing, String currentTransportMode) { 185 int loopCount = 0; 186 int maxLoops = Config.getPref().getInt("validator.routingislands.maxrecursion", MAX_LOOPS); 187 do { 188 loopCount++; 189 } while (loopCount <= maxLoops && getWaysFor(incoming, currentTransportMode, 190 (way, oldWay) -> oldWay.containsNode(firstNode(way, currentTransportMode)))); 191 loopCount = 0; 192 do { 193 loopCount++; 194 } while (loopCount <= maxLoops && getWaysFor(outgoing, currentTransportMode, 195 (way, oldWay) -> oldWay.containsNode(lastNode(way, currentTransportMode)))); 196 } 197 198 private static boolean getWaysFor(Collection<Way> directional, String currentTransportMode, 199 BiPredicate<Way, Way> predicate) { 200 Set<Way> toAdd = new HashSet<>(); 201 for (Way way : directional) { 202 for (Node node : way.getNodes()) { 203 Set<Way> referrers = node.getReferrers(true).parallelStream().filter(Way.class::isInstance) 204 .map(Way.class::cast).filter(tWay -> !directional.contains(tWay)).collect(Collectors.toSet()); 205 for (Way tWay : referrers) { 206 if (isOneway(tWay, currentTransportMode) == 0 || predicate.test(tWay, way) 207 || tWay.hasKey("junction")) { 208 toAdd.add(tWay); 209 } 210 } 211 } 212 } 213 return directional.addAll(toAdd); 214 } 215 216 /** 217 * Check if I can get to way to from way from (currently doesn't work with via 218 * ways) 219 * 220 * @param from The from way 221 * @param to The to way 222 * @param currentTransportMode The specific transport mode to check 223 * @return {@code true} if the to way can be accessed from the from way TODO 224 * clean up and work with via ways 225 */ 226 public static boolean checkAccessibility(Way from, Way to, String currentTransportMode) { 227 boolean isAccessible = true; 228 229 List<Relation> relations = from.getReferrers().parallelStream().distinct().filter(Relation.class::isInstance) 230 .map(Relation.class::cast).filter(relation -> "restriction".equals(relation.get("type"))) 231 .collect(Collectors.toList()); 232 for (Relation relation : relations) { 233 if (((relation.hasKey("except") && relation.get("except").contains(currentTransportMode)) 234 || (currentTransportMode == null || currentTransportMode.trim().isEmpty())) 235 && relation.getMembersFor(Collections.singleton(from)).parallelStream() 236 .anyMatch(member -> "from".equals(member.getRole())) 237 && relation.getMembersFor(Collections.singleton(to)).parallelStream() 238 .anyMatch(member -> "to".equals(member.getRole()))) { 239 isAccessible = false; 240 } 241 } 242 243 return isAccessible; 244 } 245 246 /** 247 * Check if a node connects to the outside world 248 * 249 * @param node The node to check 250 * @return true if outside download area, connects to an aeroport, or a water 251 * transport 252 */ 253 public static Boolean outsideConnections(Node node) { 254 boolean outsideConnections = false; 255 if (node.isOutsideDownloadArea() || node.hasTag("amenity", "parking_entrance", "parking", "parking_space", 256 "motorcycle_parking", "ferry_terminal")) 257 outsideConnections = true; 258 return outsideConnections; 259 } 260 261 /** 262 * Check if a way is oneway for a specific transport type 263 * 264 * @param way The way to look at 265 * @param transportType The specific transport type 266 * @return See {@link Way#isOneway} (but may additionally return {@code null} if 267 * the transport type cannot route down that way) 268 */ 269 public static Integer isOneway(Way way, String transportType) { 270 if (transportType == null || transportType.trim().isEmpty()) { 271 return way.isOneway(); 272 } 273 String forward = transportType.concat(":forward"); 274 String backward = transportType.concat(":backward"); 275 boolean possibleForward = "yes".equals(way.get(forward)) 276 || (!way.hasKey(forward) && way.isOneway() != -1); 277 boolean possibleBackward = "yes".equals(way.get(backward)) 278 || (!way.hasKey(backward) && way.isOneway() != 1); 279 if (possibleForward && !possibleBackward) { 280 return 1; 281 } else if (!possibleForward && possibleBackward) { 282 return -1; 283 } else if (!possibleBackward) { 284 return null; 285 } 286 return 0; 287 } 288 289 /** 290 * Get the first node of a way respecting the oneway for a transport type 291 * 292 * @param way The way to get the node from 293 * @param transportType The transport type 294 * @return The first node for the specified transport type, or null if it is not 295 * routable 296 */ 297 public static Node firstNode(Way way, String transportType) { 298 Integer oneway = isOneway(way, transportType); 299 Node node = (Integer.valueOf(-1).equals(oneway)) ? way.lastNode() : way.firstNode(); 300 Way tWay = new Way(way); 301 tWay.clearOsmMetadata(); 302 303 Map<String, String> accessValues = getDefaultAccessTags(way); 304 boolean accessible = Access.getPositiveAccessValues() 305 .contains(accessValues.getOrDefault(transportType, Access.AccessTags.NO.getKey())); 306 return (transportType == null || accessible) ? node : null; 307 308 } 309 310 /** 311 * Get the last node of a way respecting the oneway for a transport type 312 * 313 * @param way The way to get the node from 314 * @param transportType The transport type 315 * @return The last node for the specified transport type, or the last node of 316 * the way, or null if it is not routable 317 */ 318 public static Node lastNode(Way way, String transportType) { 319 Integer oneway = isOneway(way, transportType); 320 Node node = (Integer.valueOf(-1).equals(oneway)) ? way.firstNode() : way.lastNode(); 321 Map<String, String> accessValues = getDefaultAccessTags(way); 322 boolean accessible = Access.getPositiveAccessValues() 323 .contains(accessValues.getOrDefault(transportType, Access.AccessTags.NO.getKey())); 324 return (transportType == null || accessible) ? node : null; 325 } 326 327 /** 328 * Get the default access tags for a primitive 329 * 330 * @param primitive The primitive to get access tags for 331 * @return The map of access tags to access 332 */ 333 public static TagMap getDefaultAccessTags(OsmPrimitive primitive) { 334 TagMap access = new TagMap(); 335 if (primitive.hasKey("highway")) { 336 TagMap tags = primitive.getKeys(); 337 String highway = primitive.get("highway"); 338 339 if (tags.containsKey("sidewalk") && !tags.get("sidewalk").equals(Access.AccessTags.NO.getKey())) { 340 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); 341 } 342 343 if ("residential".equals(highway)) { 344 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 345 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); 346 tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.YES.getKey()); 347 } else if (Arrays.asList("service", "unclassified", "tertiary", "tertiary_link").contains(highway)) { 348 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 349 } else if (Arrays.asList("secondary", "secondary_link").contains(highway)) { 350 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 351 } else if (Arrays.asList("primary", "primary_link").contains(highway)) { 352 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 353 tags.putIfAbsent(Access.AccessTags.HGV.getKey(), Access.AccessTags.YES.getKey()); 354 } else if (Arrays.asList("motorway", "trunk", "motorway_link", "trunk_link").contains(highway)) { 355 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 356 tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.NO.getKey()); 357 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.NO.getKey()); 358 } else if ("steps".equals(highway)) { 359 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); 360 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); 361 } else if ("path".equals(highway)) { 362 tags.putIfAbsent(Access.AccessTags.MOTOR_VEHICLE.getKey(), Access.AccessTags.NO.getKey()); 363 tags.putIfAbsent(Access.AccessTags.EMERGENCY.getKey(), Access.AccessTags.DESTINATION.getKey()); 364 } else if ("footway".equals(highway)) { 365 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.DESIGNATED.getKey()); 366 } else if ("bus_guideway".equals(highway)) { 367 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); 368 tags.putIfAbsent(Access.AccessTags.BUS.getKey(), Access.AccessTags.DESIGNATED.getKey()); 369 } else if ("road".equals(highway)) { // Don't expect these to be routable 370 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); 371 } else { 372 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.YES.getKey()); 373 } 374 375 tags.putAll(Access.expandAccessValues(tags)); 376 377 for (String direction : Arrays.asList("", "forward:", "backward:")) { 378 Access.getTransportModes().parallelStream().map(direction::concat).filter(tags::containsKey) 379 .forEach(mode -> access.put(mode, tags.get(direction.concat(mode)))); 380 } 381 } 382 return access; 383 } 384 385 /** 386 * Get the error level for a test 387 * 388 * @param test The integer value of the test error 389 * @return The severity for the test 390 */ 391 public static Severity getErrorLevel(int test) { 392 return SEVERITY_MAP.get(test); 393 } 394 395 /** 396 * Set the error level for a test 397 * 398 * @param test The integer value of the test error 399 * @param severity The new severity for the test 400 */ 401 public static void setErrorLevel(int test, Severity severity) { 402 SEVERITY_MAP.put(test, severity); 403 } 404 } -
test/unit/org/openstreetmap/josm/data/validation/tests/RoutingIslandsTestTest.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.validation.tests; 3 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertFalse; 6 import static org.junit.Assert.assertNull; 7 import static org.junit.Assert.assertSame; 8 import static org.junit.Assert.assertTrue; 9 10 import java.util.Arrays; 11 import java.util.Collections; 12 import java.util.HashSet; 13 import java.util.Set; 14 import java.util.stream.Collectors; 15 16 import org.junit.Rule; 17 import org.junit.Test; 18 import org.openstreetmap.josm.TestUtils; 19 import org.openstreetmap.josm.data.Bounds; 20 import org.openstreetmap.josm.data.DataSource; 21 import org.openstreetmap.josm.data.coor.LatLon; 22 import org.openstreetmap.josm.data.osm.DataSet; 23 import org.openstreetmap.josm.data.osm.Node; 24 import org.openstreetmap.josm.data.osm.OsmPrimitive; 25 import org.openstreetmap.josm.data.osm.Way; 26 import org.openstreetmap.josm.data.validation.Severity; 27 import org.openstreetmap.josm.spi.preferences.Config; 28 import org.openstreetmap.josm.testutils.JOSMTestRules; 29 30 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 31 32 /** 33 * Test class for {@link RoutingIslandsTest} 34 * 35 * @author Taylor Smock 36 * @since xxx 37 */ 38 public class RoutingIslandsTestTest { 39 /** 40 * Setup test. 41 */ 42 @Rule 43 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") 44 public JOSMTestRules rule = new JOSMTestRules().projection().preferences(); 45 46 /** 47 * Test method for {@link RoutingIslandsTest#RoutingIslandsTest()} and the 48 * testing apparatus 49 */ 50 @Test 51 public void testRoutingIslandsTest() { 52 RoutingIslandsTest.setErrorLevel(RoutingIslandsTest.ROUTING_ISLAND, Severity.WARNING); 53 RoutingIslandsTest test = new RoutingIslandsTest(); 54 test.startTest(null); 55 test.endTest(); 56 assertTrue(test.getErrors().isEmpty()); 57 58 DataSet ds = new DataSet(); 59 60 Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(1, 1))); 61 Way way2 = TestUtils.newWay("highway=residential", new Node(new LatLon(-1, 0)), way1.firstNode()); 62 addToDataSet(ds, way1); 63 addToDataSet(ds, way2); 64 65 ds.addDataSource(new DataSource(new Bounds(0, 0, 1, 1), "openstreetmap.org")); 66 67 test.clear(); 68 test.startTest(null); 69 test.visit(ds.allPrimitives()); 70 test.endTest(); 71 assertTrue(test.getErrors().isEmpty()); 72 73 ds.addDataSource(new DataSource(new Bounds(-5, -5, 5, 5), "openstreetmap.org")); 74 test.clear(); 75 test.startTest(null); 76 test.visit(ds.allPrimitives()); 77 test.endTest(); 78 assertEquals(1, test.getErrors().size()); 79 assertEquals(2, test.getErrors().get(0).getPrimitives().size()); 80 81 ds.clear(); 82 way1 = TestUtils.newWay("highway=motorway oneway=yes", new Node(new LatLon(39.1156655, -108.5465434)), 83 new Node(new LatLon(39.1157251, -108.5496874)), new Node(new LatLon(39.11592, -108.5566841))); 84 way2 = TestUtils.newWay("highway=motorway oneway=yes", new Node(new LatLon(39.1157244, -108.55674)), 85 new Node(new LatLon(39.1155548, -108.5496901)), new Node(new LatLon(39.1154827, -108.5462431))); 86 addToDataSet(ds, way1); 87 addToDataSet(ds, way2); 88 ds.addDataSource( 89 new DataSource(new Bounds(new LatLon(39.1136949, -108.558445), new LatLon(39.117242, -108.5489166)), 90 "openstreetmap.org")); 91 test.clear(); 92 test.startTest(null); 93 test.visit(ds.allPrimitives()); 94 test.endTest(); 95 assertEquals(2, test.getErrors().size()); 96 Way way3 = TestUtils.newWay("highway=motorway oneway=no", way1.getNode(1), way2.getNode(1)); 97 addToDataSet(ds, way3); 98 test.clear(); 99 test.startTest(null); 100 test.visit(ds.allPrimitives()); 101 test.endTest(); 102 assertEquals(2, test.getErrors().size()); 103 104 Node tNode = new Node(new LatLon(39.1158845, -108.5599312)); 105 addToDataSet(ds, tNode); 106 way1.addNode(tNode); 107 tNode = new Node(new LatLon(39.115723, -108.5599239)); 108 addToDataSet(ds, tNode); 109 way2.addNode(0, tNode); 110 test.clear(); 111 test.startTest(null); 112 test.visit(ds.allPrimitives()); 113 test.endTest(); 114 assertTrue(test.getErrors().isEmpty()); 115 } 116 117 /** 118 * Test roundabouts 119 */ 120 @Test 121 public void testRoundabouts() { 122 RoutingIslandsTest test = new RoutingIslandsTest(); 123 Way roundabout = TestUtils.newWay("highway=residential junction=roundabout oneway=yes", 124 new Node(new LatLon(39.119582, -108.5262686)), new Node(new LatLon(39.1196494, -108.5260935)), 125 new Node(new LatLon(39.1197572, -108.5260784)), new Node(new LatLon(39.1197929, -108.526391)), 126 new Node(new LatLon(39.1196595, -108.5264047))); 127 roundabout.addNode(roundabout.firstNode()); // close it up 128 DataSet ds = new DataSet(); 129 addToDataSet(ds, roundabout); 130 ds.addDataSource( 131 new DataSource(new Bounds(new LatLon(39.1182025, -108.527574), new LatLon(39.1210588, -108.5251112)), 132 "openstreetmap.org")); 133 Way incomingFlare = TestUtils.newWay("highway=residential oneway=yes", 134 new Node(new LatLon(39.1196377, -108.5257567)), roundabout.getNode(3)); 135 addToDataSet(ds, incomingFlare); 136 Way outgoingFlare = TestUtils.newWay("highway=residential oneway=yes", roundabout.getNode(2), 137 incomingFlare.firstNode()); 138 addToDataSet(ds, outgoingFlare); 139 140 Way outgoingRoad = TestUtils.newWay("highway=residential", incomingFlare.firstNode(), 141 new Node(new LatLon(39.1175184, -108.5219623))); 142 addToDataSet(ds, outgoingRoad); 143 144 test.startTest(null); 145 test.visit(ds.allPrimitives()); 146 test.endTest(); 147 assertTrue(test.getErrors().isEmpty()); 148 } 149 150 /** 151 * Test method for {@link RoutingIslandsTest#checkForUnconnectedWays}. 152 */ 153 @Test 154 public void testCheckForUnconnectedWaysIncoming() { 155 RoutingIslandsTest.checkForUnconnectedWays(Collections.emptySet(), Collections.emptySet(), null); 156 Way way1 = TestUtils.newWay("highway=residential oneway=yes", new Node(new LatLon(0, 0)), 157 new Node(new LatLon(1, 1))); 158 Set<Way> incomingSet = new HashSet<>(); 159 DataSet ds = new DataSet(); 160 way1.getNodes().forEach(ds::addPrimitive); 161 ds.addPrimitive(way1); 162 incomingSet.add(way1); 163 RoutingIslandsTest.checkForUnconnectedWays(incomingSet, Collections.emptySet(), null); 164 assertEquals(1, incomingSet.size()); 165 assertSame(way1, incomingSet.iterator().next()); 166 167 Way way2 = TestUtils.newWay("highway=residential", way1.firstNode(), new Node(new LatLon(-1, -2))); 168 way2.getNodes().parallelStream().filter(node -> node.getDataSet() == null).forEach(ds::addPrimitive); 169 ds.addPrimitive(way2); 170 171 RoutingIslandsTest.checkForUnconnectedWays(incomingSet, Collections.emptySet(), null); 172 assertEquals(2, incomingSet.size()); 173 assertTrue(incomingSet.parallelStream().allMatch(way -> Arrays.asList(way1, way2).contains(way))); 174 175 Way way3 = TestUtils.newWay("highway=residential", way2.lastNode(), new Node(new LatLon(-2, -1))); 176 way3.getNodes().parallelStream().filter(node -> node.getDataSet() == null).forEach(ds::addPrimitive); 177 ds.addPrimitive(way3); 178 179 incomingSet.clear(); 180 incomingSet.add(way1); 181 RoutingIslandsTest.checkForUnconnectedWays(incomingSet, Collections.emptySet(), null); 182 assertEquals(3, incomingSet.size()); 183 assertTrue(incomingSet.parallelStream().allMatch(way -> Arrays.asList(way1, way2, way3).contains(way))); 184 185 Config.getPref().putInt("validator.routingislands.maxrecursion", 1); 186 incomingSet.clear(); 187 incomingSet.add(way1); 188 RoutingIslandsTest.checkForUnconnectedWays(incomingSet, Collections.emptySet(), null); 189 assertEquals(2, incomingSet.size()); 190 assertTrue(incomingSet.parallelStream().allMatch(way -> Arrays.asList(way1, way2).contains(way))); 191 } 192 193 /** 194 * Test method for {@link RoutingIslandsTest#checkForUnconnectedWays}. 195 */ 196 @Test 197 public void testCheckForUnconnectedWaysOutgoing() { 198 RoutingIslandsTest.checkForUnconnectedWays(Collections.emptySet(), Collections.emptySet(), null); 199 Way way1 = TestUtils.newWay("highway=residential oneway=yes", new Node(new LatLon(0, 0)), 200 new Node(new LatLon(1, 1))); 201 Set<Way> outgoingSet = new HashSet<>(); 202 DataSet ds = new DataSet(); 203 way1.getNodes().forEach(ds::addPrimitive); 204 ds.addPrimitive(way1); 205 outgoingSet.add(way1); 206 RoutingIslandsTest.checkForUnconnectedWays(Collections.emptySet(), outgoingSet, null); 207 assertEquals(1, outgoingSet.size()); 208 assertSame(way1, outgoingSet.iterator().next()); 209 210 Way way2 = TestUtils.newWay("highway=residential", way1.firstNode(), new Node(new LatLon(-1, -2))); 211 way2.getNodes().parallelStream().filter(node -> node.getDataSet() == null).forEach(ds::addPrimitive); 212 ds.addPrimitive(way2); 213 214 RoutingIslandsTest.checkForUnconnectedWays(Collections.emptySet(), outgoingSet, null); 215 assertEquals(2, outgoingSet.size()); 216 assertTrue(outgoingSet.parallelStream().allMatch(way -> Arrays.asList(way1, way2).contains(way))); 217 218 Way way3 = TestUtils.newWay("highway=residential", way2.lastNode(), new Node(new LatLon(-2, -1))); 219 way3.getNodes().parallelStream().filter(node -> node.getDataSet() == null).forEach(ds::addPrimitive); 220 ds.addPrimitive(way3); 221 222 outgoingSet.clear(); 223 outgoingSet.add(way1); 224 RoutingIslandsTest.checkForUnconnectedWays(Collections.emptySet(), outgoingSet, null); 225 assertEquals(3, outgoingSet.size()); 226 assertTrue(outgoingSet.parallelStream().allMatch(way -> Arrays.asList(way1, way2, way3).contains(way))); 227 228 Config.getPref().putInt("validator.routingislands.maxrecursion", 1); 229 outgoingSet.clear(); 230 outgoingSet.add(way1); 231 RoutingIslandsTest.checkForUnconnectedWays(Collections.emptySet(), outgoingSet, null); 232 assertEquals(2, outgoingSet.size()); 233 assertTrue(outgoingSet.parallelStream().allMatch(way -> Arrays.asList(way1, way2).contains(way))); 234 } 235 236 /** 237 * Test method for {@link RoutingIslandsTest#outsideConnections(Node)}. 238 */ 239 @Test 240 public void testOutsideConnections() { 241 Node node = new Node(new LatLon(0, 0)); 242 DataSet ds = new DataSet(node); 243 ds.addDataSource(new DataSource(new Bounds(-0.1, -0.1, -0.01, -0.01), "Test bounds")); 244 node.setOsmId(1, 1); 245 assertTrue(RoutingIslandsTest.outsideConnections(node)); 246 ds.addDataSource(new DataSource(new Bounds(-0.1, -0.1, 0.1, 0.1), "Test bounds")); 247 assertFalse(RoutingIslandsTest.outsideConnections(node)); 248 node.put("amenity", "parking_entrance"); 249 assertTrue(RoutingIslandsTest.outsideConnections(node)); 250 } 251 252 /** 253 * Test method for {@link RoutingIslandsTest#isOneway(Way, String)}. 254 */ 255 @Test 256 public void testIsOneway() { 257 Way way = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(1, 1))); 258 assertEquals(Integer.valueOf(0), RoutingIslandsTest.isOneway(way, null)); 259 assertEquals(Integer.valueOf(0), RoutingIslandsTest.isOneway(way, " ")); 260 way.put("oneway", "yes"); 261 assertEquals(Integer.valueOf(1), RoutingIslandsTest.isOneway(way, null)); 262 assertEquals(Integer.valueOf(1), RoutingIslandsTest.isOneway(way, " ")); 263 way.put("oneway", "-1"); 264 assertEquals(Integer.valueOf(-1), RoutingIslandsTest.isOneway(way, null)); 265 assertEquals(Integer.valueOf(-1), RoutingIslandsTest.isOneway(way, " ")); 266 267 way.put("vehicle:forward", "yes"); 268 assertEquals(Integer.valueOf(0), RoutingIslandsTest.isOneway(way, "vehicle")); 269 way.put("vehicle:backward", "no"); 270 assertEquals(Integer.valueOf(1), RoutingIslandsTest.isOneway(way, "vehicle")); 271 way.put("vehicle:forward", "no"); 272 assertNull(RoutingIslandsTest.isOneway(way, "vehicle")); 273 way.put("vehicle:backward", "yes"); 274 assertEquals(Integer.valueOf(-1), RoutingIslandsTest.isOneway(way, "vehicle")); 275 276 way.put("oneway", "yes"); 277 way.remove("vehicle:backward"); 278 way.remove("vehicle:forward"); 279 assertEquals(Integer.valueOf(1), RoutingIslandsTest.isOneway(way, "vehicle")); 280 way.remove("oneway"); 281 assertEquals(Integer.valueOf(0), RoutingIslandsTest.isOneway(way, "vehicle")); 282 283 way.put("oneway", "-1"); 284 assertEquals(Integer.valueOf(-1), RoutingIslandsTest.isOneway(way, "vehicle")); 285 } 286 287 /** 288 * Test method for {@link RoutingIslandsTest#firstNode(Way, String)}. 289 */ 290 @Test 291 public void testFirstNode() { 292 Way way = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(1, 1))); 293 assertEquals(way.firstNode(), RoutingIslandsTest.firstNode(way, null)); 294 way.put("oneway", "yes"); 295 assertEquals(way.firstNode(), RoutingIslandsTest.firstNode(way, null)); 296 way.put("oneway", "-1"); 297 assertEquals(way.lastNode(), RoutingIslandsTest.firstNode(way, null)); 298 299 way.put("vehicle:forward", "yes"); 300 assertEquals(way.firstNode(), RoutingIslandsTest.firstNode(way, "vehicle")); 301 way.put("vehicle:backward", "no"); 302 assertEquals(way.firstNode(), RoutingIslandsTest.firstNode(way, "vehicle")); 303 way.put("vehicle:forward", "no"); 304 assertEquals(way.firstNode(), RoutingIslandsTest.firstNode(way, "vehicle")); 305 way.put("vehicle:backward", "yes"); 306 assertEquals(way.lastNode(), RoutingIslandsTest.firstNode(way, "vehicle")); 307 } 308 309 /** 310 * Test method for {@link RoutingIslandsTest#lastNode(Way, String)}. 311 */ 312 @Test 313 public void testLastNode() { 314 Way way = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(1, 1))); 315 assertEquals(way.lastNode(), RoutingIslandsTest.lastNode(way, null)); 316 way.put("oneway", "yes"); 317 assertEquals(way.lastNode(), RoutingIslandsTest.lastNode(way, null)); 318 way.put("oneway", "-1"); 319 assertEquals(way.firstNode(), RoutingIslandsTest.lastNode(way, null)); 320 321 way.put("vehicle:forward", "yes"); 322 assertEquals(way.lastNode(), RoutingIslandsTest.lastNode(way, "vehicle")); 323 way.put("vehicle:backward", "no"); 324 assertEquals(way.lastNode(), RoutingIslandsTest.lastNode(way, "vehicle")); 325 way.put("vehicle:forward", "no"); 326 assertEquals(way.lastNode(), RoutingIslandsTest.lastNode(way, "vehicle")); 327 way.put("vehicle:backward", "yes"); 328 assertEquals(way.firstNode(), RoutingIslandsTest.lastNode(way, "vehicle")); 329 } 330 331 /** 332 * Test with a way that by default does not give access to motor vehicles 333 */ 334 @Test 335 public void testNoAccessWay() { 336 Way i70w = TestUtils.newWay("highway=motorway hgv=designated", new Node(new LatLon(39.1058104, -108.5258586)), 337 new Node(new LatLon(39.1052235, -108.5293733))); 338 Way i70e = TestUtils.newWay("highway=motorway hgv=designated", new Node(new LatLon(39.1049905, -108.5293074)), 339 new Node(new LatLon(39.1055829, -108.5257975))); 340 Way testPath = TestUtils.newWay("highway=footway", i70w.lastNode(true), i70e.firstNode(true)); 341 DataSet ds = new DataSet(); 342 Arrays.asList(i70w, i70e, testPath).forEach(way -> addToDataSet(ds, way)); 343 344 RoutingIslandsTest test = new RoutingIslandsTest(); 345 test.startTest(null); 346 test.visit(ds.allPrimitives()); 347 test.endTest(); 348 } 349 350 private static void addToDataSet(DataSet ds, OsmPrimitive primitive) { 351 if (primitive instanceof Way) { 352 ((Way) primitive).getNodes().parallelStream().distinct().filter(node -> node.getDataSet() == null) 353 .forEach(ds::addPrimitive); 354 } 355 if (primitive.getDataSet() == null) 356 ds.addPrimitive(primitive); 357 Long id = Math.max(ds.allPrimitives().parallelStream().mapToLong(prim -> prim.getId()).max().orElse(0L), 0L); 358 for (OsmPrimitive osm : ds.allPrimitives().parallelStream().filter(prim -> prim.getUniqueId() < 0) 359 .collect(Collectors.toList())) { 360 id++; 361 osm.setOsmId(id, 1); 362 } 363 } 364 }
