Ticket #18127: 18112.9.patch

File 18112.9.patch, 13.0 KB (added by GerdP, 6 months ago)
  • clear severityBreakPoints before adding new values
  • src/org/openstreetmap/josm/data/validation/OsmValidator.java

     
    6161import org.openstreetmap.josm.data.validation.tests.RelationChecker;
    6262import org.openstreetmap.josm.data.validation.tests.RightAngleBuildingTest;
    6363import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
     64import org.openstreetmap.josm.data.validation.tests.SharpAngles;
    6465import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays;
    6566import org.openstreetmap.josm.data.validation.tests.TagChecker;
    6667import org.openstreetmap.josm.data.validation.tests.TurnrestrictionTest;
     
    148149        LongSegment.class, // 3500 .. 3599
    149150        PublicTransportRouteTest.class, // 3600 .. 3699
    150151        RightAngleBuildingTest.class, // 3700 .. 3799
     152        SharpAngles.class, // 3800 .. 3899
    151153    };
    152154
    153155    /**
  • src/org/openstreetmap/josm/data/validation/tests/SharpAngles.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.validation.tests;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.util.ArrayList;
     7import java.util.Arrays;
     8import java.util.Collection;
     9import java.util.LinkedHashMap;
     10import java.util.Map;
     11import java.util.Map.Entry;
     12import java.util.TreeSet;
     13
     14import org.openstreetmap.josm.data.coor.EastNorth;
     15import org.openstreetmap.josm.data.osm.Node;
     16import org.openstreetmap.josm.data.osm.OsmPrimitive;
     17import org.openstreetmap.josm.data.osm.Way;
     18import org.openstreetmap.josm.data.osm.WaySegment;
     19import org.openstreetmap.josm.data.validation.Severity;
     20import org.openstreetmap.josm.data.validation.Test;
     21import org.openstreetmap.josm.data.validation.TestError;
     22import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     23import org.openstreetmap.josm.tools.Geometry;
     24import org.openstreetmap.josm.tools.Logging;
     25
     26/**
     27 * Find highways that have sharp angles
     28 * @author Taylor Smock
     29 * @since xxx
     30 */
     31public 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        Logging.error("tests" + errors.size());
     76        super.endTest();
     77    }
     78
     79    @Override
     80    public void visit(Way way) {
     81        if (!way.isUsable()) return;
     82        if (way.hasKey("highway") && !way.hasTag("area", "yes") &&
     83                    !ignoreHighways.contains(way.get("highway"))) {
     84            allWays.add(way);
     85        }
     86    }
     87
     88    /**
     89     * Check nodes in a way for sharp angles
     90     * @param way A way to check for sharp angles
     91     */
     92    public void checkWayForSharpAngles(Way way) {
     93        Node node1 = null;
     94        Node node2 = null;
     95        Node node3 = null;
     96        int i = -2;
     97        for (Node node : way.getNodes()) {
     98            node1 = node2;
     99            node2 = node3;
     100            node3 = node;
     101            checkAngle(node1, node2, node3, i, way, false);
     102            i++;
     103        }
     104        if (way.isClosed() && way.getNodesCount() > 2) {
     105            node1 = node2;
     106            node2 = node3;
     107            // Get the second node, not the first node, since a closed way has first node == last node
     108            node3 = way.getNode(1);
     109            checkAngle(node1, node2, node3, i, way, true);
     110        }
     111    }
     112
     113    private void checkAngle(Node node1, Node node2, Node node3, int i, Way way, boolean last) {
     114        if (node1 == null || node2 == null || node3 == null) return;
     115        EastNorth n1 = node1.getEastNorth();
     116        EastNorth n2 = node2.getEastNorth();
     117        EastNorth n3 = node3.getEastNorth();
     118        double angle = Math.toDegrees(Math.abs(Geometry.getCornerAngle(n1, n2, n3)));
     119        if (angle < maxAngle) {
     120            processSharpAngleForErrorCreation(angle, i, way, last, node2);
     121        }
     122    }
     123
     124    private void processSharpAngleForErrorCreation(double angle, int i, Way way, boolean last, Node pointNode) {
     125        WaySegment ws1 = new WaySegment(way, i);
     126        WaySegment ws2 = new WaySegment(way, last ? 0 : i + 1);
     127        double shorterLen = Math.min(ws1.toWay().getLength(), ws2.toWay().getLength());
     128        if (shorterLen < maxLength) {
     129            createNearlyOverlappingError(angle, way, pointNode);
     130        }
     131    }
     132
     133    private void createNearlyOverlappingError(double angle, Way way, OsmPrimitive primitive) {
     134        TestError.Builder testError = TestError.builder(this, getSeverity(angle), SHARP_ANGLES)
     135                .primitives(way)
     136                .highlight(primitive)
     137                .message(tr("Sharp angle"));
     138        errors.add(testError.build());
     139    }
     140
     141    private Severity getSeverity(double angle) {
     142        Severity rSeverity = Severity.OTHER;
     143        for (Entry<Double, Severity> entry : severityBreakPoints.entrySet()) {
     144            if (angle < entry.getKey()) {
     145                rSeverity = entry.getValue();
     146            }
     147        }
     148        return rSeverity;
     149    }
     150
     151    /**
     152     * Set the maximum length for the shortest segment
     153     * @param length The max length in meters
     154     */
     155    public void setMaxLength(double length) {
     156        maxLength = length;
     157    }
     158
     159    /**
     160     * Add a highway to ignore
     161     * @param highway The highway type to ignore (e.g., if you want to ignore residential roads, use "residential")
     162     */
     163    public void addIgnoredHighway(String highway) {
     164        ignoreHighways.add(highway);
     165    }
     166
     167    /**
     168     * Set the maximum angle
     169     * @param angle The maximum angle in degrees.
     170     */
     171    public void setMaxAngle(double angle) {
     172        maxAngle = angle;
     173        setBreakPoints();
     174    }
     175
     176    /**
     177     * Set the breakpoints for the test
     178     */
     179    private void setBreakPoints() {
     180        severityBreakPoints.clear();
     181        severityBreakPoints.put(maxAngle, Severity.OTHER);
     182        severityBreakPoints.put(maxAngle * 2 / 3, Severity.WARNING);
     183        severityBreakPoints.put(maxAngle / 3, Severity.ERROR);
     184    }
     185}
  • test/unit/org/openstreetmap/josm/data/validation/tests/SharpAnglesTest.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.validation.tests;
     3
     4import org.junit.Assert;
     5import org.junit.Before;
     6import org.junit.Test;
     7import org.openstreetmap.josm.JOSMFixture;
     8import org.openstreetmap.josm.TestUtils;
     9import org.openstreetmap.josm.data.coor.LatLon;
     10import org.openstreetmap.josm.data.osm.Node;
     11import org.openstreetmap.josm.data.osm.Way;
     12
     13/**
     14 * JUnit Test of the Sharp Angles validation test.
     15 */
     16
     17public 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}