Changeset 14772 in josm
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/Highways.java
r14654 r14772 9 9 import java.util.HashMap; 10 10 import java.util.HashSet; 11 import java.util.Iterator; 11 12 import java.util.List; 12 13 import java.util.Locale; 13 14 import java.util.Map; 14 15 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; 25 26 import org.openstreetmap.josm.tools.Logging; 26 27 import org.openstreetmap.josm.tools.Utils; … … 42 43 protected static final String SOURCE_MAXSPEED = "source:maxspeed"; 43 44 45 /** threshold value for angles between two highway segments. */ 46 private static final int MIN_ANGLE_NOT_SHARP = 60; 47 48 // CHECKSTYLE.OFF: SingleSpaceSeparator 49 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 44 57 /** 45 58 * Classified highways in order of importance 46 59 */ 47 // CHECKSTYLE.OFF: SingleSpaceSeparator48 60 private static final List<String> CLASSIFIED_HIGHWAYS = Arrays.asList( 49 61 "motorway", "motorway_link", … … 57 69 // CHECKSTYLE.ON: SingleSpaceSeparator 58 70 71 59 72 private static final Set<String> KNOWN_SOURCE_MAXSPEED_CONTEXTS = new HashSet<>(Arrays.asList( 60 73 "urban", "rural", "zone", "zone20", "zone:20", "zone30", "zone:30", "zone40", … … 99 112 if (w.isClosed() && w.hasTag(HIGHWAY, CLASSIFIED_HIGHWAYS) && w.hasTag("junction", "roundabout") 100 113 && IN_DOWNLOADED_AREA_STRICT.test(w)) { 101 // TODO: find out how to handle split tedroundabouts (see #12841)114 // TODO: find out how to handle split roundabouts (see #12841) 102 115 testWrongRoundabout(w); 103 116 } … … 165 178 public static boolean isHighwayLinkOkay(final Way way) { 166 179 final String highway = way.get(HIGHWAY); 167 if (highway == null || !highway.endsWith("_link") 168 || !IN_DOWNLOADED_AREA.test(way.getNode(0)) || !IN_DOWNLOADED_AREA.test(way.getNode(way.getNodesCount()-1))) { 180 if (highway == null || !highway.endsWith("_link")) { 169 181 return true; 170 182 } 171 183 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 } 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 || !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) { 253 if ("roundabout".equals(way.get("junction"))) { 254 oneway = 1; 255 } 256 } 257 258 if (oneway != 1) { 259 Node prev = getPrevNode(way, nodePos); 260 if (prev != null && !onlySharpAngle(n, prev, otherWay)) 261 return false; 262 } 263 if (oneway != -1) { 264 Node next = getNextNode(way, nodePos); 265 if (next != null && !onlySharpAngle(n, next, otherWay)) 266 return false; 267 } 268 return true; 269 } 270 271 private static Node getNextNode(Way way, int nodePos) { 272 if (nodePos + 1 >= way.getNodesCount()) { 273 if (way.isClosed()) 274 return way.getNode(1); 275 return null; 179 276 } else { 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(); 277 return way.getNode(nodePos + 1); 278 } 279 } 280 281 private static Node getPrevNode(Way way, int nodePos) { 282 if (nodePos == 0) { 283 if (way.isClosed()) 284 return way.getNode(way.getNodesCount() - 2); 285 return null; 286 } else { 287 return way.getNode(nodePos - 1); 288 } 289 } 290 291 private static boolean onlySharpAngle(Node common, Node from, Way toWay) { 292 int oneway = toWay.isOneway(); 293 if (oneway == 0) { 294 if ("roundabout".equals(toWay.get("junction"))) { 295 oneway = 1; 296 } 297 } 298 299 for (int i = 0; i < toWay.getNodesCount(); i++) { 300 if (common == toWay.getNode(i)) { 301 302 if (oneway != 1) { 303 Node to = getNextNode(toWay, i); 304 if (to != null && !isSharpAngle(from, common, to)) 305 return false; 306 } 307 if (oneway != -1) { 308 Node to = getPrevNode(toWay, i); 309 if (to != null && !isSharpAngle(from, common, to)) 310 return false; 311 } 312 break; 313 } 314 } 315 return true; 316 } 317 318 /** 319 * Returns true if angle of a corner defined with 3 point coordinates is < MIN_ANGLE_NOT_SHARP 320 * 321 * @param n1 first node 322 * @param n2 Common node 323 * @param n3 third node 324 * @return true if angle is below value given in MIN_ANGLE_NOT_SHARP 325 */ 326 327 private static boolean isSharpAngle(Node n1, Node n2, Node n3) { 328 double angle = Geometry.getNormalizedAngleInDegrees( 329 Geometry.getCornerAngle(n1.getEastNorth(), n2.getEastNorth(), n3.getEastNorth())); 330 return angle < MIN_ANGLE_NOT_SHARP; 200 331 } 201 332 -
trunk/test/unit/org/openstreetmap/josm/data/validation/tests/HighwaysTest.java
r12312 r14772 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());124 119 } 125 120 }
Note:
See TracChangeset
for help on using the changeset viewer.