Ticket #18127: 18127.8.patch

File 18127.8.patch, 13.5 KB (added by taylor.smock, 7 months ago)

FIx ant pmd checkstyle issues

Line 
1Index: src/org/openstreetmap/josm/data/validation/OsmValidator.java
2===================================================================
3--- src/org/openstreetmap/josm/data/validation/OsmValidator.java        (revision 15353)
4+++ src/org/openstreetmap/josm/data/validation/OsmValidator.java        (working copy)
5@@ -61,6 +61,7 @@
6 import org.openstreetmap.josm.data.validation.tests.RelationChecker;
7 import org.openstreetmap.josm.data.validation.tests.RightAngleBuildingTest;
8 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
9+import org.openstreetmap.josm.data.validation.tests.SharpAngles;
10 import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays;
11 import org.openstreetmap.josm.data.validation.tests.TagChecker;
12 import org.openstreetmap.josm.data.validation.tests.TurnrestrictionTest;
13@@ -148,6 +150,8 @@
14         LongSegment.class, // 3500 .. 3599
15         PublicTransportRouteTest.class, // 3600 .. 3699
16         RightAngleBuildingTest.class, // 3700 .. 3799
17+        SharpAngles.class, // 3800 .. 3899
18     };
19
20     /**
21Index: src/org/openstreetmap/josm/data/validation/tests/SharpAngles.java
22===================================================================
23--- src/org/openstreetmap/josm/data/validation/tests/SharpAngles.java   (nonexistent)
24+++ src/org/openstreetmap/josm/data/validation/tests/SharpAngles.java   (working copy)
25@@ -0,0 +1,196 @@
26+// License: GPL. For details, see LICENSE file.
27+package org.openstreetmap.josm.data.validation.tests;
28+
29+import static org.openstreetmap.josm.tools.I18n.tr;
30+
31+import java.util.ArrayList;
32+import java.util.Collection;
33+import java.util.Collections;
34+import java.util.Comparator;
35+import java.util.LinkedHashMap;
36+import java.util.List;
37+import java.util.Map;
38+import java.util.Map.Entry;
39+import java.util.Optional;
40+import java.util.TreeSet;
41+
42+import org.openstreetmap.josm.data.coor.EastNorth;
43+import org.openstreetmap.josm.data.osm.Node;
44+import org.openstreetmap.josm.data.osm.OsmPrimitive;
45+import org.openstreetmap.josm.data.osm.Way;
46+import org.openstreetmap.josm.data.osm.WaySegment;
47+import org.openstreetmap.josm.data.validation.Severity;
48+import org.openstreetmap.josm.data.validation.Test;
49+import org.openstreetmap.josm.data.validation.TestError;
50+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
51+import org.openstreetmap.josm.tools.Geometry;
52+import org.openstreetmap.josm.tools.Logging;
53+
54+/**
55+ * Find highways that have sharp angles
56+ * @author Taylor Smock
57+ * @since xxx
58+ */
59+public class SharpAngles extends Test {
60+    private static final int SHARPANGLESCODE = 3800;
61+    /** The code for a sharp angle */
62+    protected static final int SHARP_ANGLES = SHARPANGLESCODE + 0;
63+    /** The maximum angle for sharp angles */
64+    protected double maxAngle = 45.0; // degrees
65+    /** The length that at least one way segment must be shorter than */
66+    protected double maxLength = 10.0; // meters
67+    /** The stepping points for severity */
68+    protected Map<Double, Severity> severityBreakPoints = new LinkedHashMap<>();
69+    /** Specific highway types to ignore */
70+    protected Collection<String> ignoreHighways = new TreeSet<>();
71+
72+    ArrayList<Way> allWays;
73+    /**
74+     * Construct a new {@code IntersectionIssues} object
75+     */
76+    public SharpAngles() {
77+        super(tr("Sharp angles"), tr("Check for sharp angles on roads"));
78+        setBreakPoints();
79+        addIgnoredHighway("rest_area");
80+        addIgnoredHighway("platform");
81+        addIgnoredHighway("services");
82+        addIgnoredHighway("via_ferrata"); // mountainside paths
83+    }
84+
85+    @Override
86+    public void startTest(ProgressMonitor monitor) {
87+        super.startTest(monitor);
88+        allWays = new ArrayList<>();
89+    }
90+
91+    @Override
92+    public void endTest() {
93+        Way pWay = null;
94+        try {
95+            for (Way way : allWays) {
96+                pWay = way;
97+                checkWayForSharpAngles(way);
98+            }
99+        } catch (Exception e) {
100+            if (pWay != null) {
101+                Logging.debug("Way https://osm.org/way/{0} caused an error ({1})", pWay.getOsmId(), e);
102+            }
103+            Logging.warn(e);
104+        }
105+        allWays = null;
106+        super.endTest();
107+    }
108+
109+    @Override
110+    public void visit(Way way) {
111+        if (!way.isUsable()) return;
112+        if (way.hasKey("highway") && !way.hasTag("area", "yes") &&
113+                    !ignoreHighways.contains(way.get("highway"))) {
114+            allWays.add(way);
115+        }
116+    }
117+
118+    /**
119+     * Check nodes in a way for sharp angles
120+     * @param way A way to check for sharp angles
121+     */
122+    public void checkWayForSharpAngles(Way way) {
123+        Node node1 = null;
124+        Node node2 = null;
125+        Node node3 = null;
126+        int i = -2;
127+        for (Node node : way.getNodes()) {
128+            node1 = node2;
129+            node2 = node3;
130+            node3 = node;
131+            checkAngle(node1, node2, node3, i, way, false);
132+            i++;
133+        }
134+        if (way.isClosed() && way.getNodesCount() > 2) {
135+            node1 = node2;
136+            node2 = node3;
137+            // Get the second node, not the first node, since a closed way has first node == second node
138+            node3 = way.getNode(1);
139+            checkAngle(node1, node2, node3, i, way, true);
140+        }
141+    }
142+
143+    private void checkAngle(Node node1, Node node2, Node node3, int i, Way way, boolean last) {
144+        if (node1 == null || node2 == null || node3 == null) return;
145+        EastNorth n1 = node1.getEastNorth();
146+        EastNorth n2 = node2.getEastNorth();
147+        EastNorth n3 = node3.getEastNorth();
148+        double angle = Math.toDegrees(Math.abs(Geometry.getCornerAngle(n1, n2, n3)));
149+        if (angle < maxAngle) {
150+            processSharpAngleForErrorCreation(angle, i, way, last, node2);
151+        }
152+    }
153+
154+    private void processSharpAngleForErrorCreation(double angle, int i, Way way, boolean last, Node pointNode) {
155+        List<WaySegment> waysegmentList = new ArrayList<>();
156+        waysegmentList.add(new WaySegment(way, i));
157+        if (last) {
158+            waysegmentList.add(new WaySegment(way, 0));
159+        } else {
160+            waysegmentList.add(new WaySegment(way, i+1));
161+        }
162+        Optional<WaySegment> possibleShortSegment = waysegmentList.stream()
163+                .min(Comparator.comparing(segment -> segment.toWay().getLength()));
164+        if (possibleShortSegment.isPresent() && possibleShortSegment.get().toWay().getLength() < maxLength) {
165+            createNearlyOverlappingError(angle, Collections.singleton(way), pointNode);
166+        }
167+    }
168+
169+    private void createNearlyOverlappingError(double angle,
170+            Collection<? extends OsmPrimitive> primitiveIssues, OsmPrimitive primitive) {
171+        TestError.Builder testError = TestError.builder(this, getSeverity(angle), SHARP_ANGLES)
172+                .primitives(primitiveIssues)
173+                .highlight(primitive)
174+                .message(tr("Sharp angle"));
175+        errors.add(testError.build());
176+    }
177+
178+    private Severity getSeverity(double angle) {
179+        Severity rSeverity = Severity.OTHER;
180+        for (Entry<Double, Severity> entry : severityBreakPoints.entrySet()) {
181+            if (angle < entry.getKey()) {
182+                rSeverity = entry.getValue();
183+            }
184+        }
185+        return rSeverity;
186+    }
187+
188+    /**
189+     * Set the maximum length for the shortest segment
190+     * @param length The max length in meters
191+     */
192+    public void setMaxLength(double length) {
193+        maxLength = length;
194+    }
195+
196+    /**
197+     * Add a highway to ignore
198+     * @param highway The highway type to ignore (e.g., if you want to ignore residential roads, use "residential")
199+     */
200+    public void addIgnoredHighway(String highway) {
201+        ignoreHighways.add(highway);
202+    }
203+
204+    /**
205+     * Set the maximum angle
206+     * @param angle The maximum angle in degrees.
207+     */
208+    public void setMaxAngle(double angle) {
209+        maxAngle = angle;
210+        setBreakPoints();
211+    }
212+
213+    /**
214+     * Set the breakpoints for the test
215+     */
216+    private void setBreakPoints() {
217+        severityBreakPoints.put(maxAngle, Severity.OTHER);
218+        severityBreakPoints.put(maxAngle * 2 / 3, Severity.WARNING);
219+        severityBreakPoints.put(maxAngle / 3, Severity.ERROR);
220+    }
221+}
222Index: test/unit/org/openstreetmap/josm/data/validation/tests/SharpAnglesTest.java
223===================================================================
224--- test/unit/org/openstreetmap/josm/data/validation/tests/SharpAnglesTest.java (nonexistent)
225+++ test/unit/org/openstreetmap/josm/data/validation/tests/SharpAnglesTest.java (working copy)
226@@ -0,0 +1,145 @@
227+// License: GPL. For details, see LICENSE file.
228+package org.openstreetmap.josm.data.validation.tests;
229+
230+import org.junit.Assert;
231+import org.junit.Before;
232+import org.junit.Test;
233+import org.openstreetmap.josm.JOSMFixture;
234+import org.openstreetmap.josm.TestUtils;
235+import org.openstreetmap.josm.data.coor.LatLon;
236+import org.openstreetmap.josm.data.osm.Node;
237+import org.openstreetmap.josm.data.osm.Way;
238+
239+/**
240+ * JUnit Test of the Sharp Angles validation test.
241+ */
242+
243+public class SharpAnglesTest {
244+    private SharpAngles angles;
245+
246+    /**
247+     * Setup test.
248+     * @throws Exception if an error occurs
249+     */
250+    @Before
251+    public void setUp() throws Exception {
252+        JOSMFixture.createUnitTestFixture().init();
253+        angles = new SharpAngles();
254+        angles.initialize();
255+        angles.startTest(null);
256+    }
257+
258+    /**
259+     * Check a closed loop with no sharp angles
260+     */
261+    @Test
262+    public void closedLoopNoSharpAngles() {
263+        Way way = TestUtils.newWay("highway=residential",
264+                new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)),
265+                new Node(new LatLon(0.1, -0.2)), new Node(new LatLon(-0.1, -0.1)));
266+        way.addNode(way.firstNode());
267+        angles.visit(way);
268+        angles.endTest();
269+        Assert.assertEquals(0, angles.getErrors().size());
270+    }
271+
272+    /**
273+     * Check a closed loop with a sharp angle
274+     */
275+    @Test
276+    public void closedLoopSharpAngles() {
277+        Way way = TestUtils.newWay("highway=residential",
278+                new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)),
279+                new Node(new LatLon(0.1, -0.2)));
280+        way.addNode(way.firstNode());
281+        angles.setMaxLength(Double.MAX_VALUE);
282+        angles.visit(way);
283+        angles.endTest();
284+        Assert.assertEquals(1, angles.getErrors().size());
285+    }
286+
287+    /**
288+     * Check a way for multiple sharp angles
289+     */
290+    @Test
291+    public void testMultipleSharpAngles() {
292+        Way way = TestUtils.newWay("highway=residential",
293+                new Node(new LatLon(0.005069377713748322, -0.0014832642674429382)),
294+                new Node(new LatLon(0.005021097951663415, 0.0008636686205880686)),
295+                new Node(new LatLon(0.005085470967776624, -0.00013411313295197088)),
296+                new Node(new LatLon(0.005031826787678042, 0.0020116540789620915)));
297+        angles.setMaxLength(Double.MAX_VALUE);
298+        angles.visit(way);
299+        angles.endTest();
300+        Assert.assertEquals(2, angles.getErrors().size());
301+    }
302+
303+    /**
304+     * Check for no sharp angles
305+     */
306+    @Test
307+    public void testNoSharpAngles() {
308+        Way way = TestUtils.newWay("highway=residential",
309+                new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)),
310+                new Node(new LatLon(0.2, 0.3)), new Node(new LatLon(0.3, 0.1)));
311+        angles.visit(way);
312+        angles.endTest();
313+        Assert.assertEquals(0, angles.getErrors().size());
314+    }
315+
316+    /**
317+     * Ensure that we aren't accidentally using the same node twice.
318+     * This was found during initial testing. See way 10041221 (on 20190914)
319+     */
320+    @Test
321+    public void testCheckBadAnglesFromSameNodeTwice() {
322+        Way way = TestUtils.newWay("highway=service oneway=yes",
323+                new Node(new LatLon(52.8903308, 8.4302322)),
324+                new Node(new LatLon(52.8902468, 8.4302138)),
325+                new Node(new LatLon(52.8902191, 8.4302282)),
326+                new Node(new LatLon(52.8901155, 8.4304753)),
327+                new Node(new LatLon(52.8900669, 8.430763)),
328+                new Node(new LatLon(52.8901138, 8.4308262)),
329+                new Node(new LatLon(52.8902482, 8.4307568)));
330+        way.addNode(way.firstNode());
331+        angles.visit(way);
332+        angles.endTest();
333+        Assert.assertEquals(0, angles.getErrors().size());
334+    }
335+
336+    /**
337+     * Check that special cases are ignored
338+     */
339+    @Test
340+    public void testIgnoredCases() {
341+        Way way = TestUtils.newWay("highway=residential",
342+                new Node(new LatLon(0, 0)), new Node(new LatLon(0.1, 0.1)),
343+                new Node(new LatLon(0, 0.01)));
344+        angles.setMaxLength(Double.MAX_VALUE);
345+        angles.visit(way);
346+        angles.endTest();
347+        Assert.assertEquals(1, angles.getErrors().size());
348+
349+        way.put("highway", "rest_area");
350+        angles.startTest(null);
351+        angles.visit(way);
352+        angles.endTest();
353+        Assert.assertEquals(0, angles.getErrors().size());
354+
355+        way.put("highway", "residential");
356+        angles.startTest(null);
357+        angles.visit(way);
358+        angles.endTest();
359+        Assert.assertEquals(1, angles.getErrors().size());
360+        way.put("area", "yes");
361+        angles.startTest(null);
362+        angles.visit(way);
363+        angles.endTest();
364+        Assert.assertEquals(0, angles.getErrors().size());
365+        way.put("area", "no");
366+        angles.startTest(null);
367+        angles.visit(way);
368+        angles.endTest();
369+        Assert.assertEquals(1, angles.getErrors().size());
370+    }
371+}