Ticket #18127: 18127.12.patch
| File 18127.12.patch, 12.0 KB (added by , 7 years ago) |
|---|
-
src/org/openstreetmap/josm/data/validation/OsmValidator.java
61 61 import org.openstreetmap.josm.data.validation.tests.RelationChecker; 62 62 import org.openstreetmap.josm.data.validation.tests.RightAngleBuildingTest; 63 63 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay; 64 import org.openstreetmap.josm.data.validation.tests.SharpAngles; 64 65 import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays; 65 66 import org.openstreetmap.josm.data.validation.tests.TagChecker; 66 67 import org.openstreetmap.josm.data.validation.tests.TurnrestrictionTest; … … 148 149 LongSegment.class, // 3500 .. 3599 149 150 PublicTransportRouteTest.class, // 3600 .. 3699 150 151 RightAngleBuildingTest.class, // 3700 .. 3799 152 SharpAngles.class, // 3800 .. 3899 151 153 }; 152 154 153 155 /** -
src/org/openstreetmap/josm/data/validation/tests/SharpAngles.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.Arrays; 7 import java.util.Collection; 8 import java.util.LinkedHashMap; 9 import java.util.Map; 10 import java.util.Map.Entry; 11 import java.util.TreeSet; 12 13 import org.openstreetmap.josm.data.coor.EastNorth; 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.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.tools.Geometry; 22 import org.openstreetmap.josm.tools.Logging; 23 24 /** 25 * Find highways that have sharp angles 26 * @author Taylor Smock 27 * @since xxx 28 */ 29 public class SharpAngles extends Test { 30 private static final int SHARPANGLESCODE = 3800; 31 /** The code for a sharp angle */ 32 private static final int SHARP_ANGLES = SHARPANGLESCODE + 0; 33 /** The maximum angle for sharp angles */ 34 private double maxAngle = 45.0; // degrees 35 /** The length that at least one way segment must be shorter than */ 36 private double maxLength = 10.0; // meters 37 /** The stepping points for severity */ 38 private Map<Double, Severity> severityBreakPoints = new LinkedHashMap<>(); 39 /** Specific highway types to ignore */ 40 private Collection<String> ignoreHighways = new TreeSet<>( 41 Arrays.asList("platform", "rest_area", "services", "via_ferrata")); 42 43 /** 44 * Construct a new {@code IntersectionIssues} object 45 */ 46 public SharpAngles() { 47 super(tr("Sharp angles"), tr("Check for sharp angles on roads")); 48 setBreakPoints(); 49 } 50 51 @Override 52 public void visit(Way way) { 53 if (!way.isUsable()) return; 54 if (way.hasKey("highway") && !way.hasTag("area", "yes") && 55 !ignoreHighways.contains(way.get("highway"))) { 56 try { 57 checkWayForSharpAngles(way); 58 } catch (Exception e) { 59 Logging.error("Way https://osm.org/way/{0} caused an error ({1})", way.getUniqueId(), e); 60 throw e; 61 } 62 } 63 } 64 65 /** 66 * Check nodes in a way for sharp angles 67 * @param way A way to check for sharp angles 68 */ 69 public void checkWayForSharpAngles(Way way) { 70 Node node1 = null; 71 Node node2 = null; 72 Node node3 = null; 73 int i = -2; 74 for (Node node : way.getNodes()) { 75 node1 = node2; 76 node2 = node3; 77 node3 = node; 78 checkAngle(node1, node2, node3, i, way, false); 79 i++; 80 } 81 if (way.isClosed() && way.getNodesCount() > 2) { 82 node1 = node2; 83 node2 = node3; 84 // Get the second node, not the first node, since a closed way has first node == last node 85 node3 = way.getNode(1); 86 checkAngle(node1, node2, node3, i, way, true); 87 } 88 } 89 90 private void checkAngle(Node node1, Node node2, Node node3, int i, Way way, boolean last) { 91 if (node1 == null || node2 == null || node3 == null) return; 92 EastNorth n1 = node1.getEastNorth(); 93 EastNorth n2 = node2.getEastNorth(); 94 EastNorth n3 = node3.getEastNorth(); 95 double angle = Math.toDegrees(Math.abs(Geometry.getCornerAngle(n1, n2, n3))); 96 if (angle < maxAngle) { 97 processSharpAngleForErrorCreation(angle, i, way, last, node2); 98 } 99 } 100 101 private void processSharpAngleForErrorCreation(double angle, int i, Way way, boolean last, Node pointNode) { 102 WaySegment ws1 = new WaySegment(way, i); 103 WaySegment ws2 = new WaySegment(way, last ? 0 : i + 1); 104 double shorterLen = Math.min(ws1.toWay().getLength(), ws2.toWay().getLength()); 105 if (shorterLen < maxLength) { 106 createNearlyOverlappingError(angle, way, pointNode); 107 } 108 } 109 110 private void createNearlyOverlappingError(double angle, Way way, OsmPrimitive primitive) { 111 TestError.Builder testError = TestError.builder(this, getSeverity(angle), SHARP_ANGLES) 112 .primitives(way) 113 .highlight(primitive) 114 .message(tr("Sharp angle")); 115 errors.add(testError.build()); 116 } 117 118 private Severity getSeverity(double angle) { 119 Severity rSeverity = Severity.OTHER; 120 for (Entry<Double, Severity> entry : severityBreakPoints.entrySet()) { 121 if (angle < entry.getKey()) { 122 rSeverity = entry.getValue(); 123 } 124 } 125 return rSeverity; 126 } 127 128 /** 129 * Set the maximum length for the shortest segment 130 * @param length The max length in meters 131 */ 132 public void setMaxLength(double length) { 133 maxLength = length; 134 } 135 136 /** 137 * Add a highway to ignore 138 * @param highway The highway type to ignore (e.g., if you want to ignore residential roads, use "residential") 139 */ 140 public void addIgnoredHighway(String highway) { 141 ignoreHighways.add(highway); 142 } 143 144 /** 145 * Set the maximum angle 146 * @param angle The maximum angle in degrees. 147 */ 148 public void setMaxAngle(double angle) { 149 maxAngle = angle; 150 setBreakPoints(); 151 } 152 153 /** 154 * Set the breakpoints for the test 155 */ 156 private void setBreakPoints() { 157 severityBreakPoints.clear(); 158 severityBreakPoints.put(maxAngle, Severity.OTHER); 159 severityBreakPoints.put(maxAngle * 2 / 3, Severity.WARNING); 160 } 161 } 162 -
test/unit/org/openstreetmap/josm/data/validation/tests/SharpAnglesTest.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.validation.tests; 3 4 import org.junit.Assert; 5 import org.junit.Before; 6 import org.junit.Test; 7 import org.openstreetmap.josm.JOSMFixture; 8 import org.openstreetmap.josm.TestUtils; 9 import org.openstreetmap.josm.data.coor.LatLon; 10 import org.openstreetmap.josm.data.osm.Node; 11 import org.openstreetmap.josm.data.osm.Way; 12 13 /** 14 * JUnit Test of the Sharp Angles validation test. 15 */ 16 17 public class SharpAnglesTest { 18 private SharpAngles angles; 19 20 /** 21 * Setup test. 22 * @throws Exception if an error occurs 23 */ 24 @Before 25 public void setUp() throws Exception { 26 JOSMFixture.createUnitTestFixture().init(); 27 angles = new SharpAngles(); 28 angles.initialize(); 29 } 30 31 /** 32 * Check a closed loop with no sharp angles 33 */ 34 @Test 35 public void closedLoopNoSharpAngles() { 36 Way way = TestUtils.newWay("highway=residential", 37 new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)), 38 new Node(new LatLon(0.1, -0.2)), new Node(new LatLon(-0.1, -0.1))); 39 way.addNode(way.firstNode()); 40 angles.visit(way); 41 Assert.assertEquals(0, angles.getErrors().size()); 42 } 43 44 /** 45 * Check a closed loop with a sharp angle 46 */ 47 @Test 48 public void closedLoopSharpAngles() { 49 Way way = TestUtils.newWay("highway=residential", 50 new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)), 51 new Node(new LatLon(0.1, -0.2))); 52 way.addNode(way.firstNode()); 53 angles.setMaxLength(Double.MAX_VALUE); 54 angles.visit(way); 55 Assert.assertEquals(1, angles.getErrors().size()); 56 } 57 58 /** 59 * Check a way for multiple sharp angles 60 */ 61 @Test 62 public void testMultipleSharpAngles() { 63 Way way = TestUtils.newWay("highway=residential", 64 new Node(new LatLon(0.005069377713748322, -0.0014832642674429382)), 65 new Node(new LatLon(0.005021097951663415, 0.0008636686205880686)), 66 new Node(new LatLon(0.005085470967776624, -0.00013411313295197088)), 67 new Node(new LatLon(0.005031826787678042, 0.0020116540789620915))); 68 angles.setMaxLength(Double.MAX_VALUE); 69 angles.visit(way); 70 Assert.assertEquals(2, angles.getErrors().size()); 71 } 72 73 /** 74 * Check for no sharp angles 75 */ 76 @Test 77 public void testNoSharpAngles() { 78 Way way = TestUtils.newWay("highway=residential", 79 new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)), 80 new Node(new LatLon(0.2, 0.3)), new Node(new LatLon(0.3, 0.1))); 81 angles.visit(way); 82 Assert.assertEquals(0, angles.getErrors().size()); 83 } 84 85 /** 86 * Ensure that we aren't accidentally using the same node twice. 87 * This was found during initial testing. See way 10041221 (on 20190914) 88 */ 89 @Test 90 public void testCheckBadAnglesFromSameNodeTwice() { 91 Way way = TestUtils.newWay("highway=service oneway=yes", 92 new Node(new LatLon(52.8903308, 8.4302322)), 93 new Node(new LatLon(52.8902468, 8.4302138)), 94 new Node(new LatLon(52.8902191, 8.4302282)), 95 new Node(new LatLon(52.8901155, 8.4304753)), 96 new Node(new LatLon(52.8900669, 8.430763)), 97 new Node(new LatLon(52.8901138, 8.4308262)), 98 new Node(new LatLon(52.8902482, 8.4307568))); 99 way.addNode(way.firstNode()); 100 angles.visit(way); 101 Assert.assertEquals(0, angles.getErrors().size()); 102 } 103 104 /** 105 * Check that special cases are ignored 106 */ 107 @Test 108 public void testIgnoredCases() { 109 Way way = TestUtils.newWay("highway=residential", 110 new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)), 111 new Node(new LatLon(0, 0.01))); 112 angles.setMaxLength(Double.MAX_VALUE); 113 angles.visit(way); 114 Assert.assertEquals(1, angles.getErrors().size()); 115 angles.getErrors().clear(); 116 117 way.put("highway", "rest_area"); 118 angles.visit(way); 119 Assert.assertEquals(0, angles.getErrors().size()); 120 121 way.put("highway", "residential"); 122 angles.visit(way); 123 Assert.assertEquals(1, angles.getErrors().size()); 124 angles.getErrors().clear(); 125 way.put("area", "yes"); 126 angles.visit(way); 127 Assert.assertEquals(0, angles.getErrors().size()); 128 way.put("area", "no"); 129 angles.visit(way); 130 Assert.assertEquals(1, angles.getErrors().size()); 131 } 132 }
