Ticket #17528: intersectionissues_v8.patch
File intersectionissues_v8.patch, 30.0 KB (added by , 5 years ago) |
---|
-
src/org/openstreetmap/josm/actions/ValidateAction.java
116 116 private boolean canceled; 117 117 private List<TestError> errors; 118 118 119 private List<Class<? extends Test>> runTests; 120 119 121 /** 120 122 * Constructs a new {@code ValidationTask} 121 123 * @param tests the tests to run … … 153 155 @Override 154 156 protected void realRun() throws SAXException, IOException, 155 157 OsmTransferException { 158 runTests = new ArrayList<>(); 156 159 if (tests == null || tests.isEmpty()) 157 160 return; 158 161 errors = new ArrayList<>(200); 159 162 getProgressMonitor().setTicksCount(tests.size() * validatedPrimitives.size()); 160 int testCounter = 0; 163 runTests(tests, 0); 164 tests = null; 165 if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) { 166 getProgressMonitor().setCustomText(""); 167 getProgressMonitor().subTask(tr("Updating ignored errors ...")); 168 for (TestError error : errors) { 169 if (canceled) return; 170 error.updateIgnored(); 171 } 172 } 173 } 174 175 protected int runTests(Collection<Test> tests, int testCounter) { 176 ArrayList<Test> remaining = new ArrayList<>(); 161 177 for (Test test : tests) { 162 178 if (canceled) 163 return; 179 return testCounter; 180 if (test.getAfterClass() != null && !runTests.contains(test.getAfterClass())) { 181 remaining.add(test); 182 continue; 183 } 164 184 testCounter++; 165 getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, t ests.size(), test.getName()));185 getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, this.tests.size(), test.getName())); 166 186 test.setPartialSelection(formerValidatedPrimitives != null); 187 test.setPreviousErrors(errors); 167 188 test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false)); 168 189 test.visit(validatedPrimitives); 169 190 test.endTest(); 170 191 errors.addAll(test.getErrors()); 171 192 test.clear(); 193 runTests.add(test.getClass()); 172 194 } 173 tests = null; 174 if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) { 175 getProgressMonitor().setCustomText(""); 176 getProgressMonitor().subTask(tr("Updating ignored errors ...")); 177 for (TestError error : errors) { 178 if (canceled) return; 179 error.updateIgnored(); 180 } 195 if (!remaining.isEmpty()) { 196 testCounter = runTests(remaining, testCounter); 181 197 } 198 return testCounter; 182 199 } 183 200 } 184 201 } -
src/org/openstreetmap/josm/data/validation/OsmValidator.java
49 49 import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes; 50 50 import org.openstreetmap.josm.data.validation.tests.Highways; 51 51 import org.openstreetmap.josm.data.validation.tests.InternetTags; 52 import org.openstreetmap.josm.data.validation.tests.IntersectionIssues; 52 53 import org.openstreetmap.josm.data.validation.tests.Lanes; 53 54 import org.openstreetmap.josm.data.validation.tests.LongSegment; 54 55 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker; … … 148 149 LongSegment.class, // 3500 .. 3599 149 150 PublicTransportRouteTest.class, // 3600 .. 3699 150 151 RightAngleBuildingTest.class, // 3700 .. 3799 152 IntersectionIssues.class, // 3800 .. 3899 151 153 }; 152 154 153 155 /** -
src/org/openstreetmap/josm/data/validation/Test.java
46 46 /** Name of the test */ 47 47 protected final String name; 48 48 49 /** Test to run after */ 50 protected Class<? extends Test> afterTest; 51 49 52 /** Description of the test */ 50 53 protected final String description; 51 54 … … 67 70 /** The list of errors */ 68 71 protected List<TestError> errors = new ArrayList<>(30); 69 72 73 /** The list of previously found errors */ 74 protected List<TestError> previousErrors; 75 70 76 /** Whether the test is run on a partial selection data */ 71 77 protected boolean partialSelection; 72 78 … … 84 90 * @param description Description of the test 85 91 */ 86 92 public Test(String name, String description) { 93 this(name, description, null); 94 } 95 96 /** 97 * Constructor 98 * @param name Name of the test 99 * @param description Description of the test 100 * @param afterTest Ensure the test is run after a test with this name 101 * 102 * @since xxx 103 */ 104 public Test(String name, String description, Class<? extends Test> afterTest) { 87 105 this.name = name; 88 106 this.description = description; 107 this.afterTest = afterTest; 89 108 } 90 109 91 110 /** … … 178 197 } 179 198 180 199 /** 200 * Set the validation errors accumulated by other tests until this moment 201 * For validation errors accumulated by this test, use {@code getErrors()} 202 * @param errors The errors from previous tests 203 */ 204 public void setPreviousErrors(List<TestError> errors) { 205 previousErrors = errors; 206 } 207 208 /** 181 209 * Notification of the end of the test. The tester may perform additional 182 210 * actions and destroy the used structures. 183 211 * <p> … … 319 347 } 320 348 321 349 /** 350 * Get the class that the test must run after 351 * @return A class that extends {@code Test} 352 * 353 * @since xxx 354 */ 355 public Class<? extends Test> getAfterClass() { 356 return afterTest; 357 } 358 359 /** 322 360 * Determines if the test has been canceled. 323 361 * @return {@code true} if the test has been canceled, {@code false} otherwise 324 362 */ -
src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.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.Collection; 8 import java.util.HashMap; 9 import java.util.HashSet; 10 import java.util.List; 11 import java.util.Set; 12 13 import org.openstreetmap.josm.data.coor.EastNorth; 14 import org.openstreetmap.josm.data.coor.LatLon; 15 import org.openstreetmap.josm.data.osm.Node; 16 import org.openstreetmap.josm.data.osm.Way; 17 import org.openstreetmap.josm.data.osm.WaySegment; 18 import org.openstreetmap.josm.data.validation.Severity; 19 import org.openstreetmap.josm.data.validation.Test; 20 import org.openstreetmap.josm.data.validation.TestError; 21 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 22 import org.openstreetmap.josm.tools.Geometry; 23 import org.openstreetmap.josm.tools.Logging; 24 25 /** 26 * Finds issues with highway intersections 27 * @author Taylor Smock 28 * @since xxx 29 */ 30 public class IntersectionIssues extends Test { 31 private static final int INTERSECTIONISSUESCODE = 3800; 32 /** The code for an intersection which briefly interrupts a road */ 33 public static final int SHORT_DISCONNECT = INTERSECTIONISSUESCODE + 0; 34 /** The code for a node that is almost on a way */ 35 public static final int NEARBY_NODE = INTERSECTIONISSUESCODE + 1; 36 /** The distance to consider for nearby nodes/short disconnects */ 37 public static final double MAX_DISTANCE = 5.0; // meters 38 /** The distance to consider for nearby nodes with tags */ 39 public static final double MAX_DISTANCE_NODE_INFORMATION = MAX_DISTANCE / 5.0; // meters 40 /** The maximum angle for almost overlapping ways */ 41 public static final double MAX_ANGLE = 15.0; // degrees 42 /** The maximum length to consider for almost overlapping ways */ 43 public static final double MAX_LENGTH = 5.0; // meters 44 45 private HashMap<String, ArrayList<Way>> ways; 46 ArrayList<Way> allWays; 47 48 /** 49 * Construct a new {@code IntersectionIssues} object 50 */ 51 public IntersectionIssues() { 52 super(tr("Intersection Issues"), tr("Check for issues at intersections"), OverlappingWays.class); 53 } 54 55 @Override 56 public void startTest(ProgressMonitor monitor) { 57 super.startTest(monitor); 58 ways = new HashMap<>(); 59 allWays = new ArrayList<>(); 60 } 61 62 @Override 63 public void endTest() { 64 Way pWay = null; 65 try { 66 for (ArrayList<Way> comparison : ways.values()) { 67 pWay = comparison.get(0); 68 checkNearbyEnds(comparison); 69 } 70 for (Way way : allWays) { 71 pWay = way; 72 for (Way way2 : allWays) { 73 if (way2.equals(way)) continue; 74 pWay = way2; 75 if (way.getBBox().intersects(way2.getBBox())) { 76 checkNearbyNodes(way, way2); 77 } 78 } 79 } 80 } catch (Exception e) { 81 if (pWay != null) { 82 Logging.debug("Way https://osm.org/way/{0} caused an error", pWay.getOsmId()); 83 } 84 Logging.warn(e); 85 } 86 ways = null; 87 allWays = null; 88 super.endTest(); 89 } 90 91 @Override 92 public void visit(Way way) { 93 if (!way.isUsable()) return; 94 String HIGHWAY = "highway"; 95 if (way.hasKey(HIGHWAY) && !way.get(HIGHWAY).contains("_link") && 96 !way.get(HIGHWAY).contains("proposed")) { 97 String[] identityTags = new String[] {"name", "ref"}; 98 for (String tag : identityTags) { 99 if (way.hasKey(tag)) { 100 ArrayList<Way> similar = ways.get(way.get(tag)) == null ? new ArrayList<>() : ways.get(way.get(tag)); 101 if (!similar.contains(way)) similar.add(way); 102 ways.put(way.get(tag), similar); 103 } 104 } 105 if (!allWays.contains(way)) allWays.add(way); 106 } 107 } 108 109 /** 110 * Check for ends that are nearby but not directly connected 111 * @param comparison Ways to look at 112 */ 113 public void checkNearbyEnds(List<Way> comparison) { 114 ArrayList<Way> errored = new ArrayList<>(); 115 for (Way one : comparison) { 116 LatLon oneLast = one.lastNode().getCoor(); 117 LatLon oneFirst = one.firstNode().getCoor(); 118 for (Way two : comparison) { 119 if (one.equals(two)) continue; 120 if (one.isFirstLastNode(two.firstNode()) || one.isFirstLastNode(two.lastNode()) || 121 (errored.contains(one) && errored.contains(two))) continue; 122 LatLon twoLast = two.lastNode().getCoor(); 123 LatLon twoFirst = two.firstNode().getCoor(); 124 int nearCase = getNearCase(oneFirst, oneLast, twoFirst, twoLast); 125 if (nearCase != 0) { 126 for (Way way : two.lastNode().getParentWays()) { 127 if (way.equals(two)) continue; 128 if (one.hasKey("name") && way.hasKey("name") && way.get("name").equals(one.get("name")) || 129 one.hasKey("ref") && way.hasKey("ref") && way.get("ref").equals(one.get("ref"))) { 130 return; 131 } 132 } 133 for (Way way : two.firstNode().getParentWays()) { 134 if (way.equals(two)) continue; 135 if (one.hasKey("name") && way.hasKey("name") && way.get("name").equals(one.get("name")) || 136 one.hasKey("ref") && way.hasKey("ref") && way.get("ref").equals(one.get("ref"))) { 137 return; 138 } 139 } 140 } 141 if (nearCase > 0) { 142 List<Way> nearby = new ArrayList<>(); 143 nearby.add(one); 144 nearby.add(two); 145 List<WaySegment> segments = new ArrayList<>(); 146 if ((nearCase & 1) != 0) { 147 segments.add(new WaySegment(two, two.getNodesCount() - 2)); 148 segments.add(new WaySegment(one, one.getNodesCount() - 2)); 149 } 150 if ((nearCase & 2) != 0) { 151 segments.add(new WaySegment(two, two.getNodesCount() - 2)); 152 segments.add(new WaySegment(one, 0)); 153 } 154 if ((nearCase & 4) != 0) { 155 segments.add(new WaySegment(two, 0)); 156 segments.add(new WaySegment(one, one.getNodesCount() - 2)); 157 } 158 if ((nearCase & 8) != 0) { 159 segments.add(new WaySegment(two, 0)); 160 segments.add(new WaySegment(one, 0)); 161 } 162 errored.addAll(nearby); 163 allWays.removeAll(errored); 164 TestError.Builder testError = TestError.builder(this, Severity.WARNING, SHORT_DISCONNECT) 165 .primitives(nearby) 166 .highlightWaySegments(segments) 167 .message(tr("Disconnected road")); 168 errors.add(testError.build()); 169 } 170 } 171 } 172 } 173 174 /** 175 * Get nearby cases 176 * @param oneFirst The {@code LatLon} of the the first node of the first way 177 * @param oneLast The {@code LatLon} of the the last node of the first way 178 * @param twoFirst The {@code LatLon} of the the first node of the second way 179 * @param twoLast The {@code LatLon} of the the last node of the second way 180 * @return A bitwise int (8421 -> twoFirst/oneFirst, twoFirst/oneLast, twoLast/oneFirst, twoLast/oneLast) 181 * 182 */ 183 private int getNearCase(LatLon oneFirst, LatLon oneLast, LatLon twoFirst, LatLon twoLast) { 184 int returnInt = 0; 185 if (twoLast.greatCircleDistance(oneLast) <= MAX_DISTANCE) { 186 returnInt = returnInt | 1; 187 } 188 if (twoLast.greatCircleDistance(oneFirst) <= MAX_DISTANCE) { 189 returnInt = returnInt | 2; 190 } 191 if (twoFirst.greatCircleDistance(oneLast) <= MAX_DISTANCE) { 192 returnInt = returnInt | 4; 193 } 194 if (twoFirst.greatCircleDistance(oneFirst) <= MAX_DISTANCE) { 195 returnInt = returnInt | 8; 196 } 197 return returnInt; 198 } 199 200 /** 201 * Check nearby nodes to an intersection of two ways 202 * @param way1 A way to check an almost intersection with 203 * @param way2 A way to check an almost intersection with 204 */ 205 public void checkNearbyNodes(Way way1, Way way2) { 206 Collection<Node> intersectingNodes = getIntersectingNode(way1, way2); 207 if (intersectingNodes.isEmpty() || 208 (way1.isOneway() != 0 && way2.isOneway() != 0 && 209 (way1.hasKey("name") && way1.get("name").equals(way2.get("name")) || 210 way1.hasKey("ref") && way1.get("ref").equals(way2.get("ref"))))) return; 211 for (Node intersectingNode : intersectingNodes) { 212 checkNearbyNodes(way1, way2, intersectingNode); 213 checkNearbyNodes(way2, way1, intersectingNode); 214 } 215 } 216 217 private void checkNearbyNodes(Way way1, Way way2, Node nearby) { 218 for (Node node : way1.getNeighbours(nearby)) { 219 if (node.equals(nearby) || way2.containsNode(node)) continue; 220 double distance = Geometry.getDistance(way2, node); 221 double angle = getSmallestAngle(way2, nearby, node); 222 if (((distance < MAX_DISTANCE && !node.isTagged()) 223 || (distance < MAX_DISTANCE_NODE_INFORMATION && node.isTagged())) 224 && angle < MAX_ANGLE) { 225 List<Way> primitiveIssues = new ArrayList<>(); 226 primitiveIssues.add(way1); 227 primitiveIssues.add(way2); 228 List<TestError> tErrors = new ArrayList<>(); 229 if (previousErrors != null) tErrors.addAll(previousErrors); 230 tErrors.addAll(getErrors()); 231 for (TestError error : tErrors) { 232 int code = error.getCode(); 233 if ((code == SHORT_DISCONNECT || code == NEARBY_NODE 234 || code == OverlappingWays.OVERLAPPING_HIGHWAY 235 || code == OverlappingWays.DUPLICATE_WAY_SEGMENT 236 || code == OverlappingWays.OVERLAPPING_HIGHWAY_AREA 237 || code == OverlappingWays.OVERLAPPING_WAY 238 || code == OverlappingWays.OVERLAPPING_WAY_AREA 239 || code == OverlappingWays.OVERLAPPING_RAILWAY 240 || code == OverlappingWays.OVERLAPPING_RAILWAY_AREA) 241 && primitiveIssues.containsAll(error.getPrimitives())) { 242 return; 243 } 244 } 245 List<WaySegment> waysegmentsOne = new ArrayList<>(); 246 int index = way1.getNodes().indexOf(nearby); 247 if (index >= way1.getNodesCount() - 1) index--; 248 waysegmentsOne.add(new WaySegment(way1, index)); 249 if (index > 0) waysegmentsOne.add(new WaySegment(way1, index - 1)); 250 index = way2.getNodes().indexOf(nearby); 251 List<WaySegment> waysegmentsTwo = new ArrayList<>(); 252 if (index >= way2.getNodesCount() - 1) index--; 253 waysegmentsTwo.add(new WaySegment(way2, index)); 254 if (index > 0) waysegmentsTwo.add(new WaySegment(way2, index - 1)); 255 List<WaySegment> waysegments = new ArrayList<>(); 256 for (WaySegment twoSegment : waysegmentsTwo) { 257 if (angle == getSmallestAngle(twoSegment.toWay(), nearby, node)) { 258 waysegments.add(twoSegment); 259 break; 260 } 261 } 262 for (WaySegment oneSegment: waysegmentsOne) { 263 if (oneSegment.toWay().containsNode(node)) { 264 waysegments.add(oneSegment); 265 break; 266 } 267 } 268 TestError.Builder testError = TestError.builder(this, Severity.WARNING, NEARBY_NODE) 269 .primitives(primitiveIssues) 270 .highlightWaySegments(waysegments) 271 .message(tr("Sharp angle")); 272 errors.add(testError.build()); 273 } 274 } 275 } 276 277 /** 278 * Get the intersecting node of two ways 279 * @param way1 A way that (hopefully) intersects with way2 280 * @param way2 A way to find an intersection with 281 * @return A collection of nodes where the ways intersect 282 */ 283 public Collection<Node> getIntersectingNode(Way way1, Way way2) { 284 HashSet<Node> nodes = new HashSet<>(); 285 for (Node node : way1.getNodes()) { 286 if (way2.containsNode(node)) { 287 nodes.add(node); 288 } 289 } 290 return nodes; 291 } 292 293 /** 294 * Get the corner angle between nodes 295 * @param way The way with additional nodes 296 * @param intersection The node to get angles around 297 * @param comparison The node to get angles from 298 * @return The angle for comparison->intersection->(additional node) (normalized degrees) 299 */ 300 public double getSmallestAngle(Way way, Node intersection, Node comparison) { 301 Set<Node> neighbours = way.getNeighbours(intersection); 302 double angle = Double.MAX_VALUE; 303 EastNorth eastNorthIntersection = intersection.getEastNorth(); 304 EastNorth eastNorthComparison = comparison.getEastNorth(); 305 for (Node node : neighbours) { 306 EastNorth eastNorthNode = node.getEastNorth(); 307 double tAngle = Geometry.getCornerAngle(eastNorthComparison, eastNorthIntersection, eastNorthNode); 308 if (Math.abs(tAngle) < angle) angle = Math.abs(tAngle); 309 } 310 return Geometry.getNormalizedAngleInDegrees(angle); 311 } 312 } -
test/unit/org/openstreetmap/josm/data/validation/tests/IntersectionIssuesTest.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.validation.tests; 3 4 import java.util.Collection; 5 import java.util.HashSet; 6 import java.util.List; 7 8 import org.junit.Assert; 9 import org.junit.Rule; 10 import org.junit.Test; 11 import org.openstreetmap.josm.TestUtils; 12 import org.openstreetmap.josm.data.coor.LatLon; 13 import org.openstreetmap.josm.data.osm.DataSet; 14 import org.openstreetmap.josm.data.osm.Node; 15 import org.openstreetmap.josm.data.osm.OsmPrimitive; 16 import org.openstreetmap.josm.data.osm.Way; 17 import org.openstreetmap.josm.data.validation.TestError; 18 import org.openstreetmap.josm.testutils.JOSMTestRules; 19 import org.openstreetmap.josm.tools.Utils; 20 21 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 22 23 /** 24 * JUnit Test of "Long Segment" validation test. 25 */ 26 public class IntersectionIssuesTest { 27 28 /** 29 * Setup test. 30 */ 31 @Rule 32 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") 33 public JOSMTestRules test = new JOSMTestRules().projection(); 34 35 private static List<TestError> test(DataSet ds) throws Exception { 36 IntersectionIssues test = new IntersectionIssues(); 37 test.initialize(); 38 test.startTest(null); 39 for (Way w : ds.getWays()) { 40 test.visit(w); 41 } 42 test.endTest(); 43 return test.getErrors(); 44 } 45 46 /** 47 * This test area has 3 ways, where two have the same name and one has a 48 * different name where there is a short disconnect between the two with 49 * the same name. 50 * @return The collection of ways to test. 51 */ 52 private static Collection<OsmPrimitive> getTestArea1() { 53 Node node1 = new Node(new LatLon(43.85619540309, 18.36535033094)); 54 Node node2 = new Node(new LatLon(43.85658651031, 18.36534961159)); 55 Node node3 = new Node(new LatLon(43.85662897034, 18.36534953349)); 56 Node node4 = new Node(new LatLon(43.85694640771, 18.36534894963)); 57 Node node5 = new Node(new LatLon(43.85658576291, 18.36456808743)); 58 Node node6 = new Node(new LatLon(43.8566296379, 18.36604757608)); 59 60 Way way1 = TestUtils.newWay("highway=residential name=\"Test Road 1\"", 61 node1, node2); 62 Way way2 = TestUtils.newWay("highway=residential name=\"Test Road 1\"", 63 node3, node4); 64 Way way3 = TestUtils.newWay("highway=residential name=\"Test Road 2\"", 65 node5, node2, node3, node6); 66 67 HashSet<OsmPrimitive> collection = new HashSet<>(); 68 collection.addAll(way1.getNodes()); 69 collection.addAll(way2.getNodes()); 70 collection.addAll(way3.getNodes()); 71 collection.add(way1); 72 collection.add(way2); 73 collection.add(way3); 74 return collection; 75 } 76 77 private static Collection<OsmPrimitive> getTestArea2() { 78 Node node1 = new Node(new LatLon(43.85641709632, 18.36725849681)); 79 Node node2 = new Node(new LatLon(43.85680820208, 18.36725777746)); 80 Node node3 = new Node(new LatLon(43.85685066196, 18.36725769936)); 81 Node node4 = new Node(new LatLon(43.85716809815, 18.3672571155)); 82 Node node5 = new Node(new LatLon(43.85680745469, 18.3664762533)); 83 Node node6 = new Node(new LatLon(43.85685132951, 18.36795574195)); 84 Way way1 = TestUtils.newWay("highway=residential name=\"Test Road 1\"", 85 node1, node2); 86 Way way2 = TestUtils.newWay("highway=residential name=\"Test Road 1\"", 87 node2, node3); 88 Way way3 = TestUtils.newWay("highway=residential name=\"Test Road 1\"", 89 node3, node4); 90 Way way4 = TestUtils.newWay("highway=residential name=\"Test Road 2\"", 91 node5, node2); 92 Way way5 = TestUtils.newWay("highway=residential name=\"Test Road 2\"", 93 node3, node6); 94 95 HashSet<OsmPrimitive> collection = new HashSet<>(); 96 collection.addAll(way1.getNodes()); 97 collection.addAll(way2.getNodes()); 98 collection.addAll(way3.getNodes()); 99 collection.addAll(way4.getNodes()); 100 collection.addAll(way5.getNodes()); 101 collection.add(way1); 102 collection.add(way2); 103 collection.add(way3); 104 collection.add(way4); 105 collection.add(way5); 106 return collection; 107 } 108 109 private static Collection<OsmPrimitive> getTestArea3() { 110 Node node1 = new Node(new LatLon(43.85570051259, 18.36651114378)); 111 Node node2 = new Node(new LatLon(43.85613408344, 18.36651034633)); 112 Node node3 = new Node(new LatLon(43.85645152344, 18.36650976248)); 113 Node node4 = new Node(new LatLon(43.85609087565, 18.36572890027)); 114 Node node5 = new Node(new LatLon(43.85609162303, 18.3665104064)); 115 Node node6 = new Node(new LatLon(43.85613475101, 18.36720838893)); 116 Way way1 = TestUtils.newWay("highway=residential name=\"Test Road 1\"", 117 node1, node2, node3); 118 Way way2 = TestUtils.newWay("highway=residential name=\"Test Road 2\"", 119 node4, node5, node2, node6); 120 121 Collection<OsmPrimitive> collection = new HashSet<>(); 122 collection.addAll(way1.getNodes()); 123 collection.addAll(way2.getNodes()); 124 collection.add(way1); 125 collection.add(way2); 126 return collection; 127 } 128 129 private static Collection<OsmPrimitive> getTestAreaRealWorld1() { 130 // This was at https://www.openstreetmap.org/node/6123937677 131 Node node1 = new Node(new LatLon(16.4151329, -95.0267841)); 132 Node node2 = new Node(new LatLon(16.4150313, -95.0267948)); 133 Node node3 = new Node(new LatLon(16.4149297, -95.0268057)); 134 Way way1 = TestUtils.newWay("highway=residential name=Calle Los Olivos", 135 node1, node3); 136 Way way2 = TestUtils.newWay( 137 "highway=residential name=Calle Camino Carretero (La Amistad)", 138 node1, node2, node3); 139 140 Collection<OsmPrimitive> collection = new HashSet<>(); 141 collection.addAll(way1.getNodes()); 142 collection.addAll(way2.getNodes()); 143 collection.add(way1); 144 collection.add(way2); 145 return collection; 146 } 147 148 private static DataSet createDataSet(Collection<OsmPrimitive> primitives) { 149 DataSet ds = new DataSet(); 150 for (Node node : Utils.filteredCollection(primitives, Node.class)) { 151 if (ds.containsNode(node)) continue; 152 ds.addPrimitive(node); 153 } 154 for (Way way : Utils.filteredCollection(primitives, Way.class)) { 155 for (Node node : way.getNodes()) { 156 if (ds.containsNode(node)) continue; 157 ds.addPrimitive(node); 158 } 159 if (ds.containsWay(way)) continue; 160 ds.addPrimitive(way); 161 } 162 return ds; 163 } 164 165 /** 166 * Unit test for {@link IntersectionIssues#checkNearbyEnds} 167 * @throws Exception if any error occurs 168 */ 169 @Test 170 public void testCheckNearbyEnds() throws Exception { 171 DataSet area1 = createDataSet(getTestArea1()); 172 List<TestError> testResults = test(area1); 173 Assert.assertEquals(1, testResults.size()); 174 Assert.assertEquals(IntersectionIssues.SHORT_DISCONNECT, testResults.get(0).getCode()); 175 176 DataSet area2 = createDataSet(getTestArea2()); 177 testResults = test(area2); 178 Assert.assertEquals(1, testResults.size()); 179 Assert.assertEquals(IntersectionIssues.SHORT_DISCONNECT, testResults.get(0).getCode()); 180 181 area1.mergeFrom(area2); 182 testResults = test(area1); 183 Assert.assertEquals(2, testResults.size()); 184 for (TestError error : testResults) { 185 Assert.assertEquals(IntersectionIssues.SHORT_DISCONNECT, error.getCode()); 186 } 187 } 188 189 /** 190 * Unit test for {@link IntersectionIssues#checkNearbyNodes} 191 * @throws Exception if any error occurs 192 */ 193 @Test 194 public void testCheckAlmostOverlappingWays() throws Exception { 195 Collection<OsmPrimitive> area1 = getTestArea1(); 196 List<TestError> testResults = test(createDataSet(area1)); 197 Assert.assertEquals(1, testResults.size()); 198 Assert.assertNotEquals(IntersectionIssues.NEARBY_NODE, testResults.get(0).getCode()); 199 200 Collection<OsmPrimitive> area3 = getTestArea3(); 201 testResults = test(createDataSet(area3)); 202 Assert.assertEquals(1, testResults.size()); 203 Assert.assertEquals(IntersectionIssues.NEARBY_NODE, testResults.get(0).getCode()); 204 205 Collection<OsmPrimitive> realWorldArea1 = getTestAreaRealWorld1(); 206 testResults = test(createDataSet(realWorldArea1)); 207 Assert.assertEquals(1, testResults.size()); 208 Assert.assertEquals(IntersectionIssues.NEARBY_NODE, testResults.get(0).getCode()); 209 } 210 }