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