Ticket #18364: 18364.4.patch
File 18364.4.patch, 72.0 KB (added by , 4 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.marktr; 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 7 import java.util.ArrayList; 8 import java.util.Arrays; 9 import java.util.Collection; 10 import java.util.Collections; 11 import java.util.HashMap; 12 import java.util.HashSet; 13 import java.util.List; 14 import java.util.Map; 15 import java.util.Set; 16 import java.util.function.BiPredicate; 17 import java.util.stream.Collectors; 18 19 import org.openstreetmap.josm.data.osm.Node; 20 import org.openstreetmap.josm.data.osm.OsmPrimitive; 21 import org.openstreetmap.josm.data.osm.Relation; 22 import org.openstreetmap.josm.data.osm.TagMap; 23 import org.openstreetmap.josm.data.osm.Way; 24 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; 25 import org.openstreetmap.josm.data.validation.Severity; 26 import org.openstreetmap.josm.data.validation.Test; 27 import org.openstreetmap.josm.data.validation.TestError; 28 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 29 import org.openstreetmap.josm.spi.preferences.Config; 30 import org.openstreetmap.josm.tools.Access; 31 import org.openstreetmap.josm.tools.Pair; 32 33 /** 34 * A test for routing islands 35 * 36 * @author Taylor Smock 37 * @since xxx 38 */ 39 public class RoutingIslandsTest extends Test { 40 41 private static final Map<Integer, Severity> SEVERITY_MAP = new HashMap<>(); 42 /** The code for the routing island validation test */ 43 public static final int ROUTING_ISLAND = 3900; 44 /** The code for ways that are not connected to other ways, and are routable */ 45 public static final int LONELY_WAY = ROUTING_ISLAND + 1; 46 static { 47 SEVERITY_MAP.put(ROUTING_ISLAND, Severity.OTHER); 48 SEVERITY_MAP.put(LONELY_WAY, Severity.ERROR); 49 } 50 51 private static final String HIGHWAY = "highway"; 52 private static final String WATERWAY = "waterway"; 53 54 /** 55 * This is mostly as a sanity check, and to avoid infinite recursion (shouldn't 56 * happen, but still) 57 */ 58 private static final int MAX_LOOPS = 1000; 59 /** Highways to check for routing connectivity */ 60 private Set<Way> potentialHighways; 61 /** Waterways to check for routing connectivity */ 62 private Set<Way> potentialWaterways; 63 64 /** 65 * Constructs a new {@code RightAngleBuildingTest} test. 66 */ 67 public RoutingIslandsTest() { 68 super(tr("Routing islands"), tr("Checks for roads that cannot be reached or left.")); 69 super.setPartialSelection(false); 70 } 71 72 @Override 73 public void startTest(ProgressMonitor monitor) { 74 super.startTest(monitor); 75 potentialHighways = new HashSet<>(); 76 potentialWaterways = new HashSet<>(); 77 } 78 79 @Override 80 public void endTest() { 81 Access.AccessTags.getByTransportType(Access.AccessTags.LAND_TRANSPORT_TYPE).parallelStream().forEach(mode -> { 82 runTest(mode.getKey(), potentialHighways); 83 progressMonitor.setCustomText(mode.getKey()); 84 }); 85 Access.AccessTags.getByTransportType(Access.AccessTags.WATER_TRANSPORT_TYPE).parallelStream().forEach(mode -> { 86 progressMonitor.setCustomText(mode.getKey()); 87 runTest(mode.getKey(), potentialWaterways); 88 }); 89 super.endTest(); 90 } 91 92 @Override 93 public void visit(Way way) { 94 if (way.isUsable() && way.getNodes().parallelStream().anyMatch(node -> way.getDataSet().getDataSourceBounds() 95 .parallelStream().anyMatch(source -> source.contains(node.getCoor())))) { 96 if ((way.hasKey(HIGHWAY) || way.hasKey(WATERWAY)) 97 && way.getNodes().parallelStream().flatMap(node -> node.getReferrers().parallelStream()).distinct() 98 .allMatch(way::equals) 99 && way.getNodes().parallelStream().noneMatch(Node::isOutsideDownloadArea)) { 100 errors.add(TestError.builder(this, SEVERITY_MAP.get(LONELY_WAY), LONELY_WAY).primitives(way) 101 .message(tr("MapWithAI (experimental)"), marktr("Routable way not connected to other ways")) 102 .build()); 103 } else if ((ValidatorPrefHelper.PREF_OTHER.get() || ValidatorPrefHelper.PREF_OTHER_UPLOAD.get() 104 || !Severity.OTHER.equals(SEVERITY_MAP.get(ROUTING_ISLAND)))) { 105 if (way.hasKey(HIGHWAY)) { 106 potentialHighways.add(way); 107 } else if (way.hasKey(WATERWAY)) { 108 potentialWaterways.add(way); 109 } 110 } 111 } 112 } 113 114 private void runTest(String currentTransportMode, Collection<Way> potentialWays) { 115 Set<Way> incomingWays = new HashSet<>(); 116 Set<Way> outgoingWays = new HashSet<>(); 117 findConnectedWays(currentTransportMode, potentialWays, incomingWays, outgoingWays); 118 Collection<Way> realPotentialWays = (incomingWays.isEmpty() || outgoingWays.isEmpty()) 119 ? expandNetwork(currentTransportMode, potentialWays) 120 : potentialWays; 121 122 if (incomingWays.isEmpty() || outgoingWays.isEmpty()) { 123 findConnectedWays(currentTransportMode, realPotentialWays, incomingWays, outgoingWays); 124 } 125 runGenericTest(currentTransportMode, realPotentialWays, incomingWays, outgoingWays); 126 127 } 128 129 /** 130 * Expand a network from an initial selection 131 * 132 * @param currentTransportMode The current transport mode 133 * @param initial The initial collection of ways 134 * @return An expanded collection of ways, which should be all connected ways 135 * that allow the current transport mode. 136 */ 137 private static Collection<Way> expandNetwork(String currentTransportMode, Collection<Way> initial) { 138 Collection<Way> connected = initial.parallelStream().flatMap(way -> way.getNodes().parallelStream()) 139 .flatMap(node -> node.getReferrers().parallelStream()).filter(Way.class::isInstance) 140 .map(Way.class::cast).distinct().collect(Collectors.toSet()); 141 if (connected.containsAll(initial) && initial.containsAll(connected)) { 142 return connected; 143 } 144 return expandNetwork(currentTransportMode, connected); 145 } 146 147 /** 148 * This test is run when there are known incoming/outgoing ways 149 * 150 * @param currentTransportMode The current transport mode 151 * @param potentialWays The ways to check 152 * @param incomingWays The incoming ways 153 * @param outgoingWays The outgoing ways 154 */ 155 private void runGenericTest(String currentTransportMode, Collection<Way> potentialWays, 156 Collection<Way> incomingWays, Collection<Way> outgoingWays) { 157 Set<Way> toIgnore = potentialWays.parallelStream() 158 .filter(way -> incomingWays.contains(way) || outgoingWays.contains(way)) 159 .filter(way -> !Access.getPositiveAccessValues().contains( 160 getDefaultAccessTags(way).getOrDefault(currentTransportMode, Access.AccessTags.NO.getKey()))) 161 .collect(Collectors.toSet()); 162 incomingWays.removeAll(toIgnore); 163 outgoingWays.removeAll(toIgnore); 164 165 checkForUnconnectedWays(incomingWays, outgoingWays, currentTransportMode); 166 List<Pair<String, Set<Way>>> problematic = collectConnected(potentialWays.parallelStream() 167 .filter(way -> !incomingWays.contains(way) || !outgoingWays.contains(way)) 168 .filter(way -> Access.getPositiveAccessValues().contains( 169 getDefaultAccessTags(way).getOrDefault(currentTransportMode, Access.AccessTags.NO.getKey()))) 170 .collect(Collectors.toSet())) 171 .parallelStream() 172 .map(way -> new Pair<>( 173 (incomingWays.containsAll(way) ? marktr("outgoing") : marktr("incoming")), way)) 174 .collect(Collectors.toList()); 175 createErrors(problematic, currentTransportMode); 176 } 177 178 /** 179 * Find ways that may be connected to the wider network 180 * 181 * @param currentTransportMode The current mode of transport 182 * @param potentialWays The ways to check for connections 183 * @param incomingWays A collection that will have incoming ways after 184 * this method is called 185 * @param outgoingWays A collection that will have outgoing ways after 186 * this method is called 187 */ 188 private static void findConnectedWays(String currentTransportMode, Collection<Way> potentialWays, 189 Collection<Way> incomingWays, Collection<Way> outgoingWays) { 190 potentialWays.stream().filter(Way::isUsable).filter(Way::isOutsideDownloadArea).forEach(way -> { 191 Node firstNode = firstNode(way, currentTransportMode); 192 Node lastNode = lastNode(way, currentTransportMode); 193 Integer isOneway = isOneway(way, currentTransportMode); 194 if (firstNode != null && firstNode.isOutsideDownloadArea()) { 195 incomingWays.add(way); 196 } 197 if (lastNode != null && lastNode.isOutsideDownloadArea()) { 198 outgoingWays.add(way); 199 } 200 if (isOneway == 0 && firstNode != null && lastNode != null 201 && (firstNode.isOutsideDownloadArea() || lastNode.isOutsideDownloadArea())) { 202 incomingWays.add(way); 203 outgoingWays.add(way); 204 } 205 }); 206 } 207 208 /** 209 * Take a collection of ways and modify it so that it is a list of connected 210 * ways 211 * 212 * @param ways A collection of ways that may or may not be connected 213 * @return a list of sets of ways that are connected 214 */ 215 private static List<Set<Way>> collectConnected(Collection<Way> ways) { 216 ArrayList<Set<Way>> collected = new ArrayList<>(); 217 ArrayList<Way> listOfWays = new ArrayList<>(ways); 218 final int maxLoop = Config.getPref().getInt("validator.routingislands.maxrecursion", MAX_LOOPS); 219 for (int i = 0; i < listOfWays.size(); i++) { 220 Way initial = listOfWays.get(i); 221 Set<Way> connected = new HashSet<>(); 222 connected.add(initial); 223 int loopCounter = 0; 224 while (!getConnected(connected) && loopCounter < maxLoop) { 225 loopCounter++; 226 } 227 if (listOfWays.removeAll(connected)) { 228 /* 229 * Not an issue -- this ensures that everything is accounted for, only triggers 230 * when ways removed 231 */ 232 i--; // NOSONAR 233 } 234 collected.add(connected); 235 } 236 return collected; 237 } 238 239 private static boolean getConnected(Collection<Way> ways) { 240 TagMap defaultAccess = getDefaultAccessTags(ways.iterator().next()); 241 return ways.addAll(ways.parallelStream().flatMap(way -> way.getNodes().parallelStream()) 242 .flatMap(node -> node.getReferrers().parallelStream()).filter(Way.class::isInstance) 243 .map(Way.class::cast).filter(way -> getDefaultAccessTags(way).equals(defaultAccess)) 244 .collect(Collectors.toSet())); 245 } 246 247 /** 248 * Create errors for a problematic way 249 * 250 * @param problematic The set of problematic ways (Pairs are 251 * <incoming/outgoing, Set<Connected ways with same 252 * issue>>) 253 * @param mode The transport mode 254 */ 255 private void createErrors(List<Pair<String, Set<Way>>> problematic, String mode) { 256 for (Pair<String, Set<Way>> ways : problematic) { 257 errors.add( 258 TestError.builder(this, SEVERITY_MAP.getOrDefault(ROUTING_ISLAND, Severity.OTHER), ROUTING_ISLAND) 259 .message(tr("MapWithAI (experimental)"), marktr("Routing island"), "{1}: {0}", tr(ways.a), 260 mode == null ? marktr("default") : mode) 261 .primitives(ways.b).build()); 262 } 263 } 264 265 /** 266 * Check for unconnected ways 267 * 268 * @param incoming The current incoming ways (will be modified) 269 * @param outgoing The current outgoing ways (will be modified) 270 * @param currentTransportMode The transport mode we are investigating (may be 271 * {@code null}) 272 */ 273 public static void checkForUnconnectedWays(Collection<Way> incoming, Collection<Way> outgoing, 274 String currentTransportMode) { 275 int loopCount = 0; 276 int maxLoops = Config.getPref().getInt("validator.routingislands.maxrecursion", MAX_LOOPS); 277 do { 278 loopCount++; 279 } while (loopCount <= maxLoops && getWaysFor(incoming, currentTransportMode, 280 (way, oldWay) -> oldWay.containsNode(firstNode(way, currentTransportMode)) 281 && checkAccessibility(oldWay, way, currentTransportMode))); 282 loopCount = 0; 283 do { 284 loopCount++; 285 } while (loopCount <= maxLoops && getWaysFor(outgoing, currentTransportMode, 286 (way, oldWay) -> oldWay.containsNode(lastNode(way, currentTransportMode)) 287 && checkAccessibility(oldWay, way, currentTransportMode))); 288 } 289 290 private static boolean getWaysFor(Collection<Way> directional, String currentTransportMode, 291 BiPredicate<Way, Way> predicate) { 292 Set<Way> toAdd = new HashSet<>(); 293 for (Way way : directional) { 294 for (Node node : way.getNodes()) { 295 Set<Way> referrers = node.getReferrers(true).parallelStream().filter(Way.class::isInstance) 296 .map(Way.class::cast).filter(tWay -> !directional.contains(tWay)).collect(Collectors.toSet()); 297 for (Way tWay : referrers) { 298 if (isOneway(tWay, currentTransportMode) == 0 || predicate.test(tWay, way) || tWay.isClosed()) { 299 toAdd.add(tWay); 300 } 301 } 302 } 303 } 304 return directional.addAll(toAdd); 305 } 306 307 /** 308 * Check if I can get to way to from way from (currently doesn't work with via 309 * ways) 310 * 311 * @param from The from way 312 * @param to The to way 313 * @param currentTransportMode The specific transport mode to check 314 * @return {@code true} if the to way can be accessed from the from way TODO 315 * clean up and work with via ways 316 */ 317 public static boolean checkAccessibility(Way from, Way to, String currentTransportMode) { 318 boolean isAccessible = true; 319 320 List<Relation> relations = from.getReferrers().parallelStream().distinct().filter(Relation.class::isInstance) 321 .map(Relation.class::cast).filter(relation -> "restriction".equals(relation.get("type"))) 322 .collect(Collectors.toList()); 323 for (Relation relation : relations) { 324 if (((relation.hasKey("except") && relation.get("except").contains(currentTransportMode)) 325 || (currentTransportMode == null || currentTransportMode.trim().isEmpty())) 326 && relation.getMembersFor(Collections.singleton(from)).parallelStream() 327 .anyMatch(member -> "from".equals(member.getRole())) 328 && relation.getMembersFor(Collections.singleton(to)).parallelStream() 329 .anyMatch(member -> "to".equals(member.getRole()))) { 330 isAccessible = false; 331 } 332 } 333 return isAccessible; 334 } 335 336 /** 337 * Check if a node connects to the outside world 338 * 339 * @param node The node to check 340 * @return true if outside download area, connects to an aeroport, or a water 341 * transport 342 */ 343 public static Boolean outsideConnections(Node node) { 344 boolean outsideConnections = false; 345 if (node.isOutsideDownloadArea() || node.hasTag("amenity", "parking_entrance", "parking", "parking_space", 346 "motorcycle_parking", "ferry_terminal")) { 347 outsideConnections = true; 348 } 349 return outsideConnections; 350 } 351 352 /** 353 * Check if a way is oneway for a specific transport type 354 * 355 * @param way The way to look at 356 * @param transportType The specific transport type 357 * @return See {@link Way#isOneway} (but may additionally return {@code null} if 358 * the transport type cannot route down that way) 359 */ 360 public static Integer isOneway(Way way, String transportType) { 361 if (transportType == null || transportType.trim().isEmpty()) { 362 return way.isOneway(); 363 } 364 String forward = transportType.concat(":forward"); 365 String backward = transportType.concat(":backward"); 366 boolean possibleForward = "yes".equals(way.get(forward)) || (!way.hasKey(forward) && way.isOneway() != -1); 367 boolean possibleBackward = "yes".equals(way.get(backward)) || (!way.hasKey(backward) && way.isOneway() != 1); 368 if (transportType.equals(Access.AccessTags.FOOT.getKey()) && !"footway".equals(way.get(HIGHWAY)) 369 && !way.hasTag("foot:forward") && !way.hasTag("foot:backward")) { 370 /* 371 * Foot is almost never oneway, especially on generic road types. There are some 372 * cases on mountain paths. 373 */ 374 return 0; 375 } 376 if (possibleForward && !possibleBackward) { 377 return 1; 378 } else if (!possibleForward && possibleBackward) { 379 return -1; 380 } else if (!possibleBackward) { 381 return null; 382 } 383 return 0; 384 } 385 386 /** 387 * Get the first node of a way respecting the oneway for a transport type 388 * 389 * @param way The way to get the node from 390 * @param transportType The transport type 391 * @return The first node for the specified transport type, or null if it is not 392 * routable 393 */ 394 public static Node firstNode(Way way, String transportType) { 395 Integer oneway = isOneway(way, transportType); 396 Node node = (Integer.valueOf(-1).equals(oneway)) ? way.lastNode() : way.firstNode(); 397 398 Map<String, String> accessValues = getDefaultAccessTags(way); 399 boolean accessible = Access.getPositiveAccessValues() 400 .contains(accessValues.getOrDefault(transportType, Access.AccessTags.NO.getKey())); 401 return (transportType == null || accessible) ? node : null; 402 } 403 404 /** 405 * Get the last node of a way respecting the oneway for a transport type 406 * 407 * @param way The way to get the node from 408 * @param transportType The transport type 409 * @return The last node for the specified transport type, or the last node of 410 * the way, or null if it is not routable 411 */ 412 public static Node lastNode(Way way, String transportType) { 413 Integer oneway = isOneway(way, transportType); 414 Node node = (Integer.valueOf(-1).equals(oneway)) ? way.firstNode() : way.lastNode(); 415 Map<String, String> accessValues = getDefaultAccessTags(way); 416 boolean accessible = Access.getPositiveAccessValues() 417 .contains(accessValues.getOrDefault(transportType, Access.AccessTags.NO.getKey())); 418 return (transportType == null || accessible) ? node : null; 419 } 420 421 /** 422 * Get the default access tags for a primitive 423 * 424 * @param primitive The primitive to get access tags for 425 * @return The map of access tags to access 426 */ 427 public static TagMap getDefaultAccessTags(OsmPrimitive primitive) { 428 TagMap access = new TagMap(); 429 final TagMap tags; 430 if (primitive.hasKey(HIGHWAY)) { 431 tags = getDefaultHighwayAccessTags(primitive.getKeys()); 432 } else if (primitive.hasKey(WATERWAY)) { 433 tags = getDefaultWaterwayAccessTags(primitive.getKeys()); 434 } else { 435 tags = new TagMap(); 436 } 437 tags.putAll(Access.expandAccessValues(tags)); 438 439 for (String direction : Arrays.asList("", "forward:", "backward:")) { 440 Access.getTransportModes().parallelStream().map(direction::concat).filter(tags::containsKey) 441 .forEach(mode -> access.put(mode, tags.get(direction.concat(mode)))); 442 } 443 return access; 444 } 445 446 private static TagMap getDefaultWaterwayAccessTags(TagMap tags) { 447 if ("river".equals(tags.get(WATERWAY))) { 448 tags.putIfAbsent("boat", Access.AccessTags.YES.getKey()); 449 } 450 return tags; 451 } 452 453 private static TagMap getDefaultHighwayAccessTags(TagMap tags) { 454 String highway = tags.get(HIGHWAY); 455 456 if (tags.containsKey("sidewalk") && !tags.get("sidewalk").equals(Access.AccessTags.NO.getKey())) { 457 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); 458 } 459 460 if (tags.keySet().parallelStream() 461 .anyMatch(str -> str.contains("cycleway") && !Access.AccessTags.NO.getKey().equals(tags.get(str)))) { 462 tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.YES.getKey()); 463 } 464 465 if ("residential".equals(highway)) { 466 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 467 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); 468 tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.YES.getKey()); 469 } else if (Arrays.asList("service", "unclassified", "tertiary", "tertiary_link").contains(highway)) { 470 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 471 } else if (Arrays.asList("secondary", "secondary_link").contains(highway)) { 472 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 473 } else if (Arrays.asList("primary", "primary_link").contains(highway)) { 474 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 475 tags.putIfAbsent(Access.AccessTags.HGV.getKey(), Access.AccessTags.YES.getKey()); 476 } else if (Arrays.asList("motorway", "trunk", "motorway_link", "trunk_link").contains(highway)) { 477 tags.putIfAbsent(Access.AccessTags.VEHICLE.getKey(), Access.AccessTags.YES.getKey()); 478 tags.putIfAbsent(Access.AccessTags.BICYCLE.getKey(), Access.AccessTags.NO.getKey()); 479 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.NO.getKey()); 480 } else if ("steps".equals(highway)) { 481 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); 482 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.YES.getKey()); 483 } else if ("path".equals(highway)) { 484 tags.putIfAbsent(Access.AccessTags.MOTOR_VEHICLE.getKey(), Access.AccessTags.NO.getKey()); 485 tags.putIfAbsent(Access.AccessTags.EMERGENCY.getKey(), Access.AccessTags.DESTINATION.getKey()); 486 } else if ("footway".equals(highway)) { 487 tags.putIfAbsent(Access.AccessTags.FOOT.getKey(), Access.AccessTags.DESIGNATED.getKey()); 488 } else if ("bus_guideway".equals(highway)) { 489 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); 490 tags.putIfAbsent(Access.AccessTags.BUS.getKey(), Access.AccessTags.DESIGNATED.getKey()); 491 } else if ("road".equals(highway)) { // Don't expect these to be routable 492 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.NO.getKey()); 493 } else { 494 tags.putIfAbsent(Access.AccessTags.ACCESS_KEY.getKey(), Access.AccessTags.YES.getKey()); 495 } 496 return tags; 497 } 498 499 /** 500 * Get the error level for a test 501 * 502 * @param test The integer value of the test error 503 * @return The severity for the test 504 */ 505 public static Severity getErrorLevel(int test) { 506 return SEVERITY_MAP.get(test); 507 } 508 509 /** 510 * Set the error level for a test 511 * 512 * @param test The integer value of the test error 513 * @param severity The new severity for the test 514 */ 515 public static void setErrorLevel(int test, Severity severity) { 516 SEVERITY_MAP.put(test, severity); 517 } 518 } -
src/org/openstreetmap/josm/tools/Access.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import java.util.ArrayList; 5 import java.util.Arrays; 6 import java.util.Collection; 7 import java.util.Collections; 8 import java.util.Comparator; 9 import java.util.HashMap; 10 import java.util.HashSet; 11 import java.util.List; 12 import java.util.Map; 13 import java.util.Map.Entry; 14 import java.util.Objects; 15 import java.util.Set; 16 import java.util.stream.Collectors; 17 18 import org.openstreetmap.josm.data.osm.OsmPrimitive; 19 20 /** 21 * Access tag related utilities 22 * 23 * @author Taylor Smock 24 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:access">Key:access</a> 25 * @since xxx 26 */ 27 public final class Access { 28 /** 29 * Holds access tags to avoid typos 30 */ 31 public enum AccessTags { 32 /** Air, land, and sea */ 33 ALL_TRANSPORT_TYPE("all"), 34 35 /** 36 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:access">Key:access</a> 37 */ 38 ACCESS_KEY("access", ALL_TRANSPORT_TYPE), 39 40 // Access tag values 41 /** 42 * @see <a href= 43 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dyes">Tag:access%3Dyes</a> 44 */ 45 YES("yes"), 46 /** 47 * @see <a href= 48 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dofficial">Tag:access%3Dofficial</a> 49 */ 50 OFFICIAL("official"), 51 /** 52 * @see <a href= 53 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Ddesignated">Tag:access%3Ddesignated</a> 54 */ 55 DESIGNATED("designated"), 56 /** 57 * @see <a href= 58 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Ddestination">Tag:access%3Ddestination</a> 59 */ 60 DESTINATION("destination"), 61 /** 62 * @see <a href= 63 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Ddelivery">Tag:access%3Ddelivery</a> 64 */ 65 DELIVERY("delivery"), 66 /** 67 * @see <a href= 68 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dcustomers">Tag:access%3Dcustomers</a> 69 */ 70 CUSTOMERS("customers"), 71 /** 72 * @see <a href= 73 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dpermissive">Tag:access%3Dpermissive</a> 74 */ 75 PERMISSIVE("permissive"), 76 /** 77 * @see <a href= 78 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dagricultural">Tag:access%3Dagricultural</a> 79 */ 80 AGRICULTURAL("agricultural"), 81 /** 82 * @see <a href= 83 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dforestry">Tag:access%3Dforestry</a> 84 */ 85 FORESTRY("forestry"), 86 /** 87 * @see <a href= 88 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dprivate">Tag:access%3Dprivate</a> 89 */ 90 PRIVATE("private"), 91 /** 92 * @see <a href= 93 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Dno">Tag:access%3Dno</a> 94 */ 95 NO("no"), 96 /** 97 * @see <a href= 98 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Ddiscouraged">Tag:access%3Ddiscouraged</a> 99 */ 100 DISCOURAGED("discouraged"), 101 /** 102 * @see <a href= 103 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Duse_sidepath">Tag:access%3Duse_sidepath</a> 104 */ 105 USE_SIDEPATH("use_sidepath"), 106 /** 107 * @see <a href= 108 * "https://wiki.openstreetmap.org/wiki/Tag:access%3Ddismount">Tag:access%3Ddismount</a> 109 */ 110 DISMOUNT("dismount"), 111 // Land 112 /** Land transport types */ 113 LAND_TRANSPORT_TYPE("land", ALL_TRANSPORT_TYPE), 114 /** 115 * @see <a href= 116 * "https://wiki.openstreetmap.org/wiki/Key:vehicle">Key:vehicle</a> 117 */ 118 VEHICLE("vehicle", LAND_TRANSPORT_TYPE), 119 /** 120 * @see <a href= 121 * "https://wiki.openstreetmap.org/wiki/Key:motor_vehicle">Key:motor_vehicle</a> 122 */ 123 MOTOR_VEHICLE("motor_vehicle", LAND_TRANSPORT_TYPE), 124 /** 125 * @see <a href= 126 * "https://wiki.openstreetmap.org/wiki/Key:trailer">Key:trailer</a> 127 */ 128 TRAILER("trailer", LAND_TRANSPORT_TYPE), 129 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:foot">Key:foot</a> */ 130 FOOT("foot", LAND_TRANSPORT_TYPE), 131 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:ski">Key:ski</a> */ 132 SKI("ski", LAND_TRANSPORT_TYPE), 133 /** 134 * @see <a href= 135 * "https://wiki.openstreetmap.org/wiki/Key:inline_skates">Key:inline_skates</a> 136 */ 137 INLINE_SKATES("inline_skates", LAND_TRANSPORT_TYPE), 138 /** 139 * @see <a href= 140 * "https://wiki.openstreetmap.org/wiki/Key:ice_skates">Key:ice_skates</a> 141 */ 142 ICE_SKATES("ice_skates", LAND_TRANSPORT_TYPE), 143 /** 144 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:horse">Key:horse</a> 145 */ 146 HORSE("horse", LAND_TRANSPORT_TYPE), 147 /** 148 * @see <a href= 149 * "https://wiki.openstreetmap.org/wiki/Key:bicycle">Key:bicycle</a> 150 */ 151 BICYCLE("bicycle", LAND_TRANSPORT_TYPE), 152 /** 153 * @see <a href= 154 * "https://wiki.openstreetmap.org/wiki/Key:carriage">Key:carriage</a> 155 */ 156 CARRIAGE("carriage", LAND_TRANSPORT_TYPE), 157 /** 158 * @see <a href= 159 * "https://wiki.openstreetmap.org/wiki/Key:caravan">Key:caravan</a> 160 */ 161 CARAVAN("caravan", LAND_TRANSPORT_TYPE), 162 /** 163 * @see <a href= 164 * "https://wiki.openstreetmap.org/wiki/Key:motorcycle">Key:motorcycle</a> 165 */ 166 MOTORCYCLE("motorcycle", LAND_TRANSPORT_TYPE), 167 /** 168 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:moped">Key:moped</a> 169 */ 170 MOPED("moped", LAND_TRANSPORT_TYPE), 171 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:mofa">Key:mofa</a> */ 172 MOFA("mofa", LAND_TRANSPORT_TYPE), 173 /** 174 * @see <a href= 175 * "https://wiki.openstreetmap.org/wiki/Key:motorcar">Key:motorcar</a> 176 */ 177 MOTORCAR("motorcar", LAND_TRANSPORT_TYPE), 178 /** 179 * @see <a href= 180 * "https://wiki.openstreetmap.org/wiki/Key:motorhome">Key:motorhome</a> 181 */ 182 MOTORHOME("motorhome", LAND_TRANSPORT_TYPE), 183 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:psv">Key:psv</a> */ 184 PSV("psv", LAND_TRANSPORT_TYPE), 185 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:bus">Key:bus</a> */ 186 BUS("bus", LAND_TRANSPORT_TYPE), 187 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:taxi">Key:taxi</a> */ 188 TAXI("taxi", LAND_TRANSPORT_TYPE), 189 /** 190 * @see <a href= 191 * "https://wiki.openstreetmap.org/wiki/Key:tourist_bus">Key:tourist_bus</a> 192 */ 193 TOURIST_BUS("tourist_bus", LAND_TRANSPORT_TYPE), 194 /** 195 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:goods">Key:goods</a> 196 */ 197 GOODS("goods", LAND_TRANSPORT_TYPE), 198 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:hgv">Key:hgv</a> */ 199 HGV("hgv", LAND_TRANSPORT_TYPE), 200 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:atv">Key:atv</a> */ 201 ATV("atv", LAND_TRANSPORT_TYPE), 202 /** 203 * @see <a href= 204 * "https://wiki.openstreetmap.org/wiki/Key:snowmobile">Key:snowmobile</a> 205 */ 206 SNOWMOBILE("snowmobile", LAND_TRANSPORT_TYPE), 207 /** 208 * @see <a href= 209 * "https://wiki.openstreetmap.org/wiki/Key:hgv_articulated">Key:hgv_articulated</a> 210 */ 211 HGV_ARTICULATED("hgv_articulated", LAND_TRANSPORT_TYPE), 212 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:ski">Key:ski</a> */ 213 SKI_NORDIC("ski:nordic", LAND_TRANSPORT_TYPE), 214 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:ski">Key:ski</a> */ 215 SKI_ALPINE("ski:alpine", LAND_TRANSPORT_TYPE), 216 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:ski">Key:ski</a> */ 217 SKI_TELEMARK("ski:telemark", LAND_TRANSPORT_TYPE), 218 /** 219 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:coach">Key:coach</a> 220 */ 221 COACH("coach", LAND_TRANSPORT_TYPE), 222 /** 223 * @see <a href= 224 * "https://wiki.openstreetmap.org/wiki/Key:golf_cart">Key:golf_cart</a> 225 */ 226 GOLF_CART("golf_cart", LAND_TRANSPORT_TYPE), 227 /** 228 * @see <a href= 229 * "https://wiki.openstreetmap.org/wiki/Key:minibus">Key:minibus</a> 230 */ 231 MINIBUS("minibus", LAND_TRANSPORT_TYPE), 232 /** 233 * @see <a href= 234 * "https://wiki.openstreetmap.org/wiki/Key:share_taxi">Key:share_taxi</a> 235 */ 236 SHARE_TAXI("share_taxi", LAND_TRANSPORT_TYPE), 237 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:hov">Key:hov</a> */ 238 HOV("hov", LAND_TRANSPORT_TYPE), 239 /** 240 * @see <a href= 241 * "https://wiki.openstreetmap.org/wiki/Key:car_sharing">Key:car_sharing</a> 242 */ 243 CAR_SHARING("car_sharing", LAND_TRANSPORT_TYPE), 244 /** 245 * Routers should default to {@code yes}, regardless of higher access rules, 246 * assuming it is navigatible by vehicle 247 * 248 * @see <a href= 249 * "https://wiki.openstreetmap.org/wiki/Key:emergency">Key:emergency</a> 250 */ 251 EMERGENCY("emergency", LAND_TRANSPORT_TYPE), 252 /** 253 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:hazmat">Key:hazmat</a> 254 */ 255 HAZMAT("hazmat", LAND_TRANSPORT_TYPE), 256 /** 257 * @see <a href= 258 * "https://wiki.openstreetmap.org/wiki/Key:disabled">Key:disabled</a> 259 */ 260 DISABLED("disabled", LAND_TRANSPORT_TYPE), 261 262 // Water 263 /** Water transport type */ 264 WATER_TRANSPORT_TYPE("water", ALL_TRANSPORT_TYPE), 265 /** 266 * @see <a href= 267 * "https://wiki.openstreetmap.org/wiki/Key:swimming">Key:swimming</a> 268 */ 269 SWIMMING("swimming", WATER_TRANSPORT_TYPE), 270 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:boat">Key:boat</a> */ 271 BOAT("boat", WATER_TRANSPORT_TYPE), 272 /** 273 * @see <a href= 274 * "https://wiki.openstreetmap.org/wiki/Key:fishing_vessel">Key:fishing_vessel</a> 275 */ 276 FISHING_VESSEL("fishing_vessel", WATER_TRANSPORT_TYPE), 277 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:ship">Key:ship</a> */ 278 SHIP("ship", WATER_TRANSPORT_TYPE), 279 /** 280 * @see <a href= 281 * "https://wiki.openstreetmap.org/wiki/Key:motorboat">Key:motorboat</a> 282 */ 283 MOTORBOAT("motorboat", WATER_TRANSPORT_TYPE), 284 /** 285 * @see <a href= 286 * "https://wiki.openstreetmap.org/wiki/Key:sailboat">Key:sailboat</a> 287 */ 288 SAILBOAT("sailboat", WATER_TRANSPORT_TYPE), 289 /** 290 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:canoe">Key:canoe</a> 291 */ 292 CANOE("canoe", WATER_TRANSPORT_TYPE), 293 /** 294 * @see <a href= 295 * "https://wiki.openstreetmap.org/wiki/Key:passenger">Key:passenger</a> 296 */ 297 PASSENGER("passenger", WATER_TRANSPORT_TYPE), 298 /** 299 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:cargo">Key:cargo</a> 300 */ 301 CARGO("cargo", WATER_TRANSPORT_TYPE), 302 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:isps">Key:isps</a> */ 303 ISPS("isps", WATER_TRANSPORT_TYPE), 304 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:bulk">Key:bulk</a> */ 305 BULK("bulk", WATER_TRANSPORT_TYPE), 306 /** 307 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:tanker">Key:tanker</a> 308 */ 309 TANKER("tanker", WATER_TRANSPORT_TYPE), 310 /** 311 * @see <a href= 312 * "https://wiki.openstreetmap.org/wiki/Key:container">Key:container</a> 313 */ 314 CONTAINER("container", WATER_TRANSPORT_TYPE), 315 /** @see <a href="https://wiki.openstreetmap.org/wiki/Key:imdg">Key:imdg</a> */ 316 IMDG("imdg", WATER_TRANSPORT_TYPE), 317 /** 318 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:tanker">Key:tanker</a> 319 */ 320 TANKER_GAS("tanker:gas", WATER_TRANSPORT_TYPE), 321 /** 322 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:tanker">Key:tanker</a> 323 */ 324 TANKER_OIL("tanker:oil", WATER_TRANSPORT_TYPE), 325 /** 326 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:tanker">Key:tanker</a> 327 */ 328 TANKER_CHEMICAL("tanker:chemical", WATER_TRANSPORT_TYPE), 329 /** 330 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:tanker">Key:tanker</a> 331 */ 332 TANKER_SINGLEHULL("tanker:singlehull", WATER_TRANSPORT_TYPE), 333 334 // Trains 335 /** Rail transport type */ 336 RAIL_TRANSPORT_TYPE("rail", ALL_TRANSPORT_TYPE), 337 /** 338 * @see <a href="https://wiki.openstreetmap.org/wiki/Key:train">Key:train</a> 339 */ 340 TRAIN("train", RAIL_TRANSPORT_TYPE); 341 342 private String key; 343 private AccessTags type; 344 345 AccessTags(String key) { 346 this.key = key; 347 this.type = null; 348 } 349 350 AccessTags(String key, AccessTags type) { 351 this.key = key; 352 this.type = type; 353 } 354 355 /** 356 * @return The key for the enum 357 */ 358 public String getKey() { 359 return key; 360 } 361 362 /** 363 * @return The AccessTags transport type 364 * (RAIL_TRANSPORT_TYPE/WATER_TRANSPORT_TYPE/etc) 365 */ 366 public AccessTags getTransportType() { 367 return type; 368 } 369 370 /** 371 * Check if this is a parent transport type (air/sea/water/all) 372 * 373 * @param potentialDescendant The AccessTags that we want to check 374 * @return true if valueOf is a child transport type of this 375 */ 376 public boolean parentOf(AccessTags potentialDescendant) { 377 AccessTags tmp = potentialDescendant; 378 while (tmp != null && tmp != this) { 379 tmp = tmp.getTransportType(); 380 } 381 return tmp == this; 382 } 383 384 /** 385 * Get the enum that matches the mode 386 * 387 * @param childrenMode The mode to get the access tag 388 * @return The AccessTags enum that matches the childrenMode, or null 389 */ 390 public static AccessTags get(String childrenMode) { 391 for (AccessTags value : values()) { 392 if (value.getKey().equalsIgnoreCase(childrenMode)) { 393 return value; 394 } 395 } 396 return null; 397 } 398 399 /** 400 * Get access tags that match a certain type 401 * 402 * @param type {@link AccessTags#WATER_TRANSPORT_TYPE}, 403 * {@link AccessTags#LAND_TRANSPORT_TYPE}, 404 * {@link AccessTags#RAIL_TRANSPORT_TYPE}, or 405 * {@link AccessTags#ALL_TRANSPORT_TYPE} 406 * @return A collection of access tags that match the given transport type 407 */ 408 public static Collection<AccessTags> getByTransportType(AccessTags type) { 409 return Arrays.stream(values()).filter(type::parentOf).collect(Collectors.toList()); 410 } 411 } 412 413 /** 414 * The key for children modes for the map, see {@link Access#getAccessMethods} 415 */ 416 public static final String CHILDREN = "children"; 417 418 /** The key for parent modes for the map, see {@link Access#getAccessMethods} */ 419 public static final String PARENT = "parent"; 420 421 /** This set has keys that indicate that access is possible */ 422 private static final Set<String> POSITIVE_ACCESS = new HashSet<>(Arrays.asList(AccessTags.YES, AccessTags.OFFICIAL, 423 AccessTags.DESIGNATED, AccessTags.DESTINATION, AccessTags.DELIVERY, AccessTags.CUSTOMERS, 424 AccessTags.PERMISSIVE, AccessTags.AGRICULTURAL, AccessTags.FORESTRY).stream().map(AccessTags::getKey) 425 .collect(Collectors.toSet())); 426 427 /** This set has all basic restriction values (yes/no/permissive/private/...) */ 428 private static final Set<String> RESTRICTION_VALUES = new HashSet<>( 429 Arrays.asList(AccessTags.PRIVATE, AccessTags.NO).stream().map(AccessTags::getKey) 430 .collect(Collectors.toSet())); 431 432 /** This set has transport modes (access/foot/ski/motor_vehicle/vehicle/...) */ 433 private static final Set<String> TRANSPORT_MODES = new HashSet<>( 434 Arrays.asList(AccessTags.ACCESS_KEY, AccessTags.FOOT, AccessTags.SKI, AccessTags.INLINE_SKATES, 435 AccessTags.ICE_SKATES, AccessTags.HORSE, AccessTags.VEHICLE, AccessTags.BICYCLE, 436 AccessTags.CARRIAGE, AccessTags.TRAILER, AccessTags.CARAVAN, AccessTags.MOTOR_VEHICLE, 437 AccessTags.MOTORCYCLE, AccessTags.MOPED, AccessTags.MOFA, AccessTags.MOTORCAR, AccessTags.MOTORHOME, 438 AccessTags.PSV, AccessTags.BUS, AccessTags.TAXI, AccessTags.TOURIST_BUS, AccessTags.GOODS, 439 AccessTags.HGV, AccessTags.AGRICULTURAL, AccessTags.ATV, AccessTags.SNOWMOBILE, 440 AccessTags.HGV_ARTICULATED, AccessTags.SKI_NORDIC, AccessTags.SKI_ALPINE, AccessTags.SKI_TELEMARK, 441 AccessTags.COACH, AccessTags.GOLF_CART, AccessTags.MINIBUS, AccessTags.SHARE_TAXI, 442 AccessTags.CAR_SHARING, AccessTags.HOV, AccessTags.EMERGENCY, AccessTags.HAZMAT, 443 AccessTags.DISABLED 444 ).stream().map(AccessTags::getKey).collect(Collectors.toSet())); 445 446 /** 447 * Map<Access Method, Map<Parent/Child, List<Access Methods>>> 448 */ 449 private static final Map<String, Map<String, List<String>>> accessMethods = new HashMap<>(); 450 static { 451 RESTRICTION_VALUES.addAll(POSITIVE_ACCESS); 452 defaultInheritance(); 453 } 454 455 private Access() { 456 // Hide the constructor 457 } 458 459 /** 460 * Create the default access inheritance, as defined at <a href= 461 * "https://wiki.openstreetmap.org/wiki/Key:access#Transport_mode_restrictions">Key:access#Transport_mode_restrictions</a> 462 */ 463 private static void defaultInheritance() { 464 addMode(null, AccessTags.ACCESS_KEY); 465 466 // Land 467 addModes(AccessTags.ACCESS_KEY, AccessTags.FOOT, AccessTags.SKI, AccessTags.INLINE_SKATES, 468 AccessTags.ICE_SKATES, AccessTags.HORSE, AccessTags.VEHICLE); 469 addModes(AccessTags.SKI, AccessTags.SKI_NORDIC, AccessTags.SKI_ALPINE, AccessTags.SKI_TELEMARK); 470 addModes(AccessTags.VEHICLE, AccessTags.BICYCLE, AccessTags.CARRIAGE, AccessTags.TRAILER, 471 AccessTags.MOTOR_VEHICLE); 472 addModes(AccessTags.TRAILER, AccessTags.CARAVAN); 473 addModes(AccessTags.MOTOR_VEHICLE, AccessTags.MOTORCYCLE, AccessTags.MOPED, AccessTags.MOFA, 474 AccessTags.MOTORCAR, AccessTags.MOTORHOME, AccessTags.TOURIST_BUS, AccessTags.COACH, AccessTags.GOODS, 475 AccessTags.HGV, AccessTags.AGRICULTURAL, AccessTags.GOLF_CART, AccessTags.ATV, AccessTags.SNOWMOBILE, 476 AccessTags.PSV, AccessTags.HOV, AccessTags.CAR_SHARING, AccessTags.EMERGENCY, AccessTags.HAZMAT, 477 AccessTags.DISABLED); 478 addMode(AccessTags.HGV, AccessTags.HGV_ARTICULATED); 479 addModes(AccessTags.PSV, AccessTags.BUS, AccessTags.MINIBUS, AccessTags.SHARE_TAXI, AccessTags.TAXI); 480 481 // Water 482 addModes(AccessTags.ACCESS_KEY, AccessTags.SWIMMING, AccessTags.BOAT, AccessTags.FISHING_VESSEL, 483 AccessTags.SHIP); 484 addModes(AccessTags.BOAT, AccessTags.MOTORBOAT, AccessTags.SAILBOAT, AccessTags.CANOE); 485 addModes(AccessTags.SHIP, AccessTags.PASSENGER, AccessTags.CARGO, AccessTags.ISPS); 486 addModes(AccessTags.CARGO, AccessTags.BULK, AccessTags.TANKER, AccessTags.CONTAINER, AccessTags.IMDG); 487 addModes(AccessTags.TANKER, AccessTags.TANKER_GAS, AccessTags.TANKER_OIL, AccessTags.TANKER_CHEMICAL, 488 AccessTags.TANKER_SINGLEHULL); 489 490 // Rail 491 addModes(AccessTags.ACCESS_KEY, AccessTags.TRAIN); 492 } 493 494 /** 495 * Add multiple modes with a common parent 496 * 497 * @param parent The parent of all the modes 498 * @param modes The modes to add 499 */ 500 public static void addModes(AccessTags parent, AccessTags... modes) { 501 for (AccessTags mode : modes) { 502 addMode(parent, mode); 503 } 504 } 505 506 /** 507 * Add modes to a list, modifying parents as needed 508 * 509 * @param mode The mode to be added 510 * @param parent The parent of the mode 511 */ 512 public static void addMode(AccessTags parent, AccessTags mode) { 513 Objects.requireNonNull(mode, "Mode must not be null"); 514 if (parent != null) { 515 Map<String, List<String>> parentMap = accessMethods.getOrDefault(parent.getKey(), new HashMap<>()); 516 accessMethods.putIfAbsent(parent.getKey(), parentMap); 517 List<String> parentChildren = parentMap.getOrDefault(CHILDREN, new ArrayList<>()); 518 if (!parentChildren.contains(mode.getKey())) 519 parentChildren.add(mode.getKey()); 520 parentMap.putIfAbsent(CHILDREN, parentChildren); 521 } 522 Map<String, List<String>> modeMap = accessMethods.getOrDefault(mode.getKey(), new HashMap<>()); 523 accessMethods.putIfAbsent(mode.getKey(), modeMap); 524 List<String> modeParent = modeMap.getOrDefault(PARENT, 525 Collections.singletonList(parent == null ? null : parent.getKey())); 526 modeMap.putIfAbsent(PARENT, modeParent); 527 } 528 529 /** 530 * Get the number of parents a mode has 531 * 532 * @param mode The mode with parents 533 * @return The number of parents the mode has 534 */ 535 public static int depth(String mode) { 536 String tempMode = mode; 537 int maxCount = accessMethods.size(); 538 while (tempMode != null && maxCount > 0) { 539 tempMode = accessMethods.getOrDefault(tempMode, Collections.emptyMap()) 540 .getOrDefault(PARENT, Collections.emptyList()).get(0); 541 if (tempMode != null) 542 maxCount--; 543 } 544 return accessMethods.size() - maxCount; 545 } 546 547 /** 548 * Expand access modes to cover the children of that access mode (currently only 549 * supports the default hierarchy) 550 * 551 * @param mode The transport mode 552 * @param access The access value (the children transport modes inherit this 553 * value) 554 * @return A map of the mode and its children (does not include parents) 555 */ 556 public static Map<String, String> expandAccessMode(String mode, String access) { 557 return expandAccessMode(mode, access, AccessTags.ALL_TRANSPORT_TYPE); 558 } 559 560 /** 561 * Expand access modes to cover the children of that access mode (currently only 562 * supports the default hierarchy) 563 * 564 * @param mode The transport mode 565 * @param access The access value (the children transport modes inherit 566 * this value) 567 * @param transportType {@link AccessTags#ALL_TRANSPORT_TYPE}, 568 * {@link AccessTags#LAND_TRANSPORT_TYPE}, 569 * {@link AccessTags#WATER_TRANSPORT_TYPE}, 570 * {@link AccessTags#RAIL_TRANSPORT_TYPE} 571 * @return A map of the mode and its children (does not include parents) 572 */ 573 public static Map<String, String> expandAccessMode(String mode, String access, AccessTags transportType) { 574 Map<String, String> accessModes = new HashMap<>(); 575 accessModes.put(mode, access); 576 if (accessMethods.containsKey(mode)) { 577 for (String childrenMode : accessMethods.getOrDefault(mode, Collections.emptyMap()).getOrDefault(CHILDREN, 578 Collections.emptyList())) { 579 if (transportType.parentOf(AccessTags.get(childrenMode))) 580 accessModes.putAll(expandAccessMode(childrenMode, access, transportType)); 581 } 582 } 583 return accessModes; 584 } 585 586 /** 587 * Merge two access maps (more specific wins) 588 * 589 * @param map1 A map with access values (see {@link Access#expandAccessMode}) 590 * @param map2 A map with access values (see {@link Access#expandAccessMode}) 591 * @return The merged map 592 */ 593 public static Map<String, String> mergeMaps(Map<String, String> map1, Map<String, String> map2) { 594 Map<String, String> merged; 595 if (map1.keySet().containsAll(map2.keySet())) { 596 merged = new HashMap<>(map1); 597 merged.putAll(map2); 598 } else { // if they don't overlap or if map2 contains all of map1 599 merged = new HashMap<>(map2); 600 merged.putAll(map1); 601 } 602 return merged; 603 } 604 605 /** 606 * Get the set of values that can generally be considered to be accessible 607 * 608 * @return A set of values that can be used for routing purposes (unmodifiable) 609 */ 610 public static Set<String> getPositiveAccessValues() { 611 return Collections.unmodifiableSet(POSITIVE_ACCESS); 612 } 613 614 /** 615 * Get the valid restriction values ({@code unknown} is not included). See 616 * 617 * @return Valid values for restrictions (unmodifiable) 618 * @see <a href= 619 * "https://wiki.openstreetmap.org/wiki/Key:access#List_of_possible_values">Key:access#List_of_possible_values</a> 620 */ 621 public static Set<String> getRestrictionValues() { 622 return Collections.unmodifiableSet(RESTRICTION_VALUES); 623 } 624 625 /** 626 * Get the valid transport modes. See 627 * 628 * @return Value transport modes (unmodifiable) 629 * @see <a href= 630 * "https://wiki.openstreetmap.org/wiki/Key:access#Transport_mode_restrictions">Key:access#Transport_mode_restrictions</a> 631 */ 632 public static Set<String> getTransportModes() { 633 return Collections.unmodifiableSet(TRANSPORT_MODES); 634 } 635 636 /** 637 * Get the access method hierarchy. 638 * 639 * @return The hierarchy for access modes (unmodifiable) 640 * @see <a href= 641 * "https://wiki.openstreetmap.org/wiki/Key:access#Transport_mode_restrictions">Key:access#Transport_mode_restrictions</a> 642 */ 643 public static Map<String, Map<String, List<String>>> getAccessMethods() { 644 Map<String, Map<String, List<String>>> map = new HashMap<>(); 645 for (Entry<String, Map<String, List<String>>> entry : map.entrySet()) { 646 Map<String, List<String>> tMap = new HashMap<>(); 647 entry.getValue().forEach((key, list) -> tMap.put(key, Collections.unmodifiableList(list))); 648 map.put(entry.getKey(), Collections.unmodifiableMap(tMap)); 649 } 650 return Collections.unmodifiableMap(map); 651 } 652 653 /** 654 * Get the implied access values for a primitive 655 * 656 * @param primitive A primitive with access values 657 * @param transportType {@link AccessTags#ALL_TRANSPORT_TYPE}, 658 * {@link AccessTags#LAND_TRANSPORT_TYPE}, 659 * {@link AccessTags#WATER_TRANSPORT_TYPE}, 660 * {@link AccessTags#RAIL_TRANSPORT_TYPE} 661 * @return The implied access values (for example, "hgv=designated" adds 662 * "hgv_articulated=designated") 663 */ 664 public static Map<String, String> getAccessValues(OsmPrimitive primitive, AccessTags transportType) { 665 Map<String, String> accessValues = new HashMap<>(); 666 TRANSPORT_MODES.stream().filter(primitive::hasKey) 667 .map(mode -> expandAccessMode(mode, primitive.get(mode), transportType)) 668 .forEach(modeAccess -> { 669 Map<String, String> tMap = mergeMaps(accessValues, modeAccess); 670 accessValues.clear(); 671 accessValues.putAll(tMap); 672 }); 673 return accessValues; 674 } 675 676 /** 677 * Expand a map of access values 678 * 679 * @param accessValues A map of mode, access type values 680 * @return The expanded access values 681 */ 682 public static Map<String, String> expandAccessValues(Map<String, String> accessValues) { 683 Map<String, String> modes = new HashMap<>(); 684 List<Map<String, String>> list = accessValues.entrySet().stream() 685 .map(entry -> expandAccessMode(entry.getKey(), entry.getValue())) 686 .sorted(Comparator.comparingInt(Map::size)) 687 .collect(Collectors.toCollection(ArrayList::new)); 688 Collections.reverse(list); 689 for (Map<String, String> access : list) { 690 modes = mergeMaps(modes, access); 691 } 692 return modes; 693 } 694 } -
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().timeout(30000); 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 assertFalse(test.getErrors().isEmpty()); 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 assertFalse(test.getErrors().isEmpty()); 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 assertFalse(test.getErrors().isEmpty()); 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 } 358 Long id = Math.max(ds.allPrimitives().parallelStream().mapToLong(prim -> prim.getId()).max().orElse(0L), 0L); 359 for (OsmPrimitive osm : ds.allPrimitives().parallelStream().filter(prim -> prim.getUniqueId() < 0) 360 .collect(Collectors.toList())) { 361 id++; 362 osm.setOsmId(id, 1); 363 } 364 } 365 }