Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/Highways.java
r14777 r14779 9 9 import java.util.HashMap; 10 10 import java.util.HashSet; 11 import java.util.Iterator;12 11 import java.util.List; 13 12 import java.util.Locale; 14 13 import java.util.Map; 15 14 import java.util.Set; 15 import java.util.stream.Collectors; 16 16 17 17 import org.openstreetmap.josm.command.ChangePropertyCommand; … … 23 23 import org.openstreetmap.josm.data.validation.Test; 24 24 import org.openstreetmap.josm.data.validation.TestError; 25 import org.openstreetmap.josm.tools.Geometry;26 25 import org.openstreetmap.josm.tools.Logging; 27 26 import org.openstreetmap.josm.tools.Utils; … … 43 42 protected static final String SOURCE_MAXSPEED = "source:maxspeed"; 44 43 45 /** threshold value for angles between two highway segments. */46 private static final int MIN_ANGLE_NOT_SHARP = 60;47 48 // CHECKSTYLE.OFF: SingleSpaceSeparator49 private static final Set<String> LINK_TO_HIGHWAYS = new HashSet<>(Arrays.asList(50 "motorway", "motorway_link",51 "trunk", "trunk_link",52 "primary", "primary_link",53 "secondary", "secondary_link",54 "tertiary", "tertiary_link"55 ));56 57 44 /** 58 45 * Classified highways in order of importance 59 46 */ 47 // CHECKSTYLE.OFF: SingleSpaceSeparator 60 48 private static final List<String> CLASSIFIED_HIGHWAYS = Arrays.asList( 61 49 "motorway", "motorway_link", … … 69 57 // CHECKSTYLE.ON: SingleSpaceSeparator 70 58 71 72 59 private static final Set<String> KNOWN_SOURCE_MAXSPEED_CONTEXTS = new HashSet<>(Arrays.asList( 73 60 "urban", "rural", "zone", "zone20", "zone:20", "zone30", "zone:30", "zone40", … … 112 99 if (w.isClosed() && w.hasTag(HIGHWAY, CLASSIFIED_HIGHWAYS) && w.hasTag("junction", "roundabout") 113 100 && IN_DOWNLOADED_AREA_STRICT.test(w)) { 114 // TODO: find out how to handle split roundabouts (see #12841)101 // TODO: find out how to handle splitted roundabouts (see #12841) 115 102 testWrongRoundabout(w); 116 103 } … … 178 165 public static boolean isHighwayLinkOkay(final Way way) { 179 166 final String highway = way.get(HIGHWAY); 180 if (highway == null || !highway.endsWith("_link")) { 167 if (highway == null || !highway.endsWith("_link") 168 || !IN_DOWNLOADED_AREA.test(way.getNode(0)) || !IN_DOWNLOADED_AREA.test(way.getNode(way.getNodesCount()-1))) { 181 169 return true; 182 170 } 183 171 184 // check if connected to a high class road where the link must match the higher class 185 String highClass = null; 186 for (int i = 0; i < way.getNodesCount(); i++) { 187 Node n = way.getNode(i); 188 if (!IN_DOWNLOADED_AREA.test(n)) 189 return true; 190 Set<Way> otherWays = new HashSet<>(); 191 otherWays.addAll(Utils.filteredCollection(n.getReferrers(), Way.class)); 192 if (otherWays.size() == 1) 193 continue; 194 Iterator<Way> iter = otherWays.iterator(); 195 while (iter.hasNext()) { 196 Way w = iter.next(); 197 final String hw2 = w.get(HIGHWAY); 198 if (way == w || w.getNodesCount() < 2 || !w.isUsable() || hw2 == null) 199 iter.remove(); 200 else { 201 if ("motorway".equals(hw2)) { 202 highClass = "motorway"; 203 break; 204 } else if ("trunk".equals(hw2)) 205 highClass = "trunk"; 206 } 207 } 208 } 209 210 if (highClass != null && !highway.equals(highClass + "_link")) { 211 return false; 212 } 213 214 for (int i = 0; i < way.getNodesCount(); i++) { 215 Node n = way.getNode(i); 216 Set<Way> otherWays = new HashSet<>(); 217 otherWays.addAll(Utils.filteredCollection(n.getReferrers(), Way.class)); 218 if (otherWays.size() == 1) 219 continue; 220 otherWays.removeIf(w -> w == way || !w.hasTag("highway") || !highway.startsWith(w.get(HIGHWAY)) || !LINK_TO_HIGHWAYS.contains(w.get(HIGHWAY))); 221 if (otherWays.isEmpty()) 222 continue; 223 224 //TODO: ignore ways which are not allowed because of turn restrictions, oneway attributes or access rules? 225 HashSet<Way> sameTag = new HashSet<>(); 226 for (Way ow : otherWays) { 227 if (highway.equals(ow.get(HIGHWAY))) 228 sameTag.add(ow); 229 else 230 return true; 231 } 232 // we have way(s) with the same _link tag, ignore those with a sharp angle 233 final int pos = i; 234 sameTag.removeIf(w -> isSharpAngle(way, pos, w)); 235 if (!sameTag.isEmpty()) 236 return true; 237 } 238 return false; 239 240 } 241 242 /** 243 * Check if the two given connected ways form a sharp angle. 244 * @param way 1st way 245 * @param nodePos node position of connecting node in 1st way 246 * @param otherWay the 2nd way 247 * @return true if angle is sharp or way cannot be travelled because of oneway attributes 248 */ 249 private static boolean isSharpAngle(Way way, int nodePos, Way otherWay) { 250 Node n = way.getNode(nodePos); 251 int oneway = way.isOneway(); 252 if (oneway == 0 && "roundabout".equals(way.get("junction"))) { 253 oneway = 1; 254 } 255 256 if (oneway != 1) { 257 Node prev = getPrevNode(way, nodePos); 258 if (prev != null && !onlySharpAngle(n, prev, otherWay)) 259 return false; 260 } 261 if (oneway != -1) { 262 Node next = getNextNode(way, nodePos); 263 if (next != null && !onlySharpAngle(n, next, otherWay)) 264 return false; 265 } 266 return true; 267 } 268 269 private static Node getNextNode(Way way, int nodePos) { 270 if (nodePos + 1 >= way.getNodesCount()) { 271 if (way.isClosed()) 272 return way.getNode(1); 273 return null; 172 final Set<OsmPrimitive> referrers = new HashSet<>(); 173 174 if (way.isClosed()) { 175 // for closed way we need to check all adjacent ways 176 for (Node n: way.getNodes()) { 177 referrers.addAll(n.getReferrers()); 178 } 274 179 } else { 275 return way.getNode(nodePos + 1); 276 } 277 } 278 279 private static Node getPrevNode(Way way, int nodePos) { 280 if (nodePos == 0) { 281 if (way.isClosed()) 282 return way.getNode(way.getNodesCount() - 2); 283 return null; 284 } else { 285 return way.getNode(nodePos - 1); 286 } 287 } 288 289 private static boolean onlySharpAngle(Node common, Node from, Way toWay) { 290 int oneway = toWay.isOneway(); 291 if (oneway == 0 && "roundabout".equals(toWay.get("junction"))) { 292 oneway = 1; 293 } 294 295 for (int i = 0; i < toWay.getNodesCount(); i++) { 296 if (common == toWay.getNode(i)) { 297 298 if (oneway != 1) { 299 Node to = getNextNode(toWay, i); 300 if (to != null && !isSharpAngle(from, common, to)) 301 return false; 302 } 303 if (oneway != -1) { 304 Node to = getPrevNode(toWay, i); 305 if (to != null && !isSharpAngle(from, common, to)) 306 return false; 307 } 308 break; 309 } 310 } 311 return true; 312 } 313 314 /** 315 * Returns true if angle of a corner defined with 3 point coordinates is < MIN_ANGLE_NOT_SHARP 316 * 317 * @param n1 first node 318 * @param n2 Common node 319 * @param n3 third node 320 * @return true if angle is below value given in MIN_ANGLE_NOT_SHARP 321 */ 322 323 private static boolean isSharpAngle(Node n1, Node n2, Node n3) { 324 double angle = Geometry.getNormalizedAngleInDegrees( 325 Geometry.getCornerAngle(n1.getEastNorth(), n2.getEastNorth(), n3.getEastNorth())); 326 return angle < MIN_ANGLE_NOT_SHARP; 180 referrers.addAll(way.firstNode().getReferrers()); 181 referrers.addAll(way.lastNode().getReferrers()); 182 } 183 184 // Find ways of same class (exact class of class_link) 185 List<Way> sameClass = Utils.filteredCollection(referrers, Way.class).stream().filter( 186 otherWay -> !way.equals(otherWay) && otherWay.hasTag(HIGHWAY, highway, highway.replaceAll("_link$", ""))) 187 .collect(Collectors.toList()); 188 if (sameClass.size() > 1) { 189 // It is possible to have a class_link between 2 segments of same class 190 // in roundabout designs that physically separate a specific turn from the main roundabout 191 // But if we have more than a single adjacent class, and one of them is a roundabout, that's an error 192 for (Way w : sameClass) { 193 if (w.hasTag("junction", "roundabout")) { 194 return false; 195 } 196 } 197 } 198 // Link roads should always at least one adjacent segment of same class 199 return !sameClass.isEmpty(); 327 200 } 328 201 -
trunk/test/unit/org/openstreetmap/josm/data/validation/tests/HighwaysTest.java
r14772 r14779 117 117 fail(test.getErrors().get(0).getMessage()); 118 118 } 119 Way w1 = ways.stream().filter(w -> 28508494 == w.getId()).findFirst().get(); 120 Way w2 = ways.stream().filter(w -> 28508493 == w.getId()).findFirst().get(); 121 test.visit(w1); 122 test.visit(w2); 123 assertEquals(2, test.getErrors().size()); 119 124 } 120 125 }
Note:
See TracChangeset
for help on using the changeset viewer.