Ticket #18127: 18127.7.patch

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

Add missing @since xxx tag

  • 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.Collection;
     8import java.util.Collections;
     9import java.util.Comparator;
     10import java.util.LinkedHashMap;
     11import java.util.List;
     12import java.util.Map;
     13import java.util.Map.Entry;
     14import java.util.Optional;
     15import java.util.TreeSet;
     16
     17import org.openstreetmap.josm.data.coor.EastNorth;
     18import org.openstreetmap.josm.data.osm.Node;
     19import org.openstreetmap.josm.data.osm.OsmPrimitive;
     20import org.openstreetmap.josm.data.osm.Way;
     21import org.openstreetmap.josm.data.osm.WaySegment;
     22import org.openstreetmap.josm.data.validation.Severity;
     23import org.openstreetmap.josm.data.validation.Test;
     24import org.openstreetmap.josm.data.validation.TestError;
     25import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     26import org.openstreetmap.josm.tools.Geometry;
     27import org.openstreetmap.josm.tools.Logging;
     28
     29/**
     30 * Find highways that have sharp angles
     31 * @author Taylor Smock
     32 * @since xxx
     33 */
     34public class SharpAngles extends Test {
     35    private static final int SHARPANGLESCODE = 3800;
     36    /** The code for a sharp angle */
     37    protected static final int SHARP_ANGLES = SHARPANGLESCODE + 0;
     38    /** The maximum angle for sharp angles */
     39    protected double maxAngle = 45.0; // degrees
     40    /** The length that at least one way segment must be shorter than */
     41    protected double maxLength = 10.0; // meters
     42    /** The stepping points for severity */
     43    protected Map<Double, Severity> severityBreakPoints = new LinkedHashMap<>();
     44    /** Specific highway types to ignore */
     45    protected Collection<String> ignoreHighways = new TreeSet<>();
     46
     47    ArrayList<Way> allWays;
     48    /**
     49     * Construct a new {@code IntersectionIssues} object
     50     */
     51    public SharpAngles() {
     52        super(tr("Sharp angles"), tr("Check for sharp angles on roads"));
     53        setBreakPoints();
     54        addIgnoredHighway("rest_area");
     55        addIgnoredHighway("platform");
     56        addIgnoredHighway("services");
     57        addIgnoredHighway("via_ferrata"); // mountainside paths
     58    }
     59
     60    @Override
     61    public void startTest(ProgressMonitor monitor) {
     62        super.startTest(monitor);
     63        allWays = new ArrayList<>();
     64    }
     65
     66    @Override
     67    public void endTest() {
     68        Way pWay = null;
     69        try {
     70            for (Way way : allWays) {
     71                pWay = way;
     72                checkWayForSharpAngles(way);
     73            }
     74        } catch (Exception e) {
     75            if (pWay != null) {
     76                Logging.debug("Way https://osm.org/way/{0} caused an error ({1})", pWay.getOsmId(), e);
     77            }
     78            Logging.warn(e);
     79        }
     80        allWays = null;
     81        super.endTest();
     82    }
     83
     84    @Override
     85    public void visit(Way way) {
     86        if (!way.isUsable()) return;
     87        if (way.hasKey("highway") && !way.hasTag("area", "yes") &&
     88                    !ignoreHighways.contains(way.get("highway"))) {
     89            allWays.add(way);
     90        }
     91    }
     92
     93    /**
     94     * Check nodes in a way for sharp angles
     95     * @param way A way to check for sharp angles
     96     */
     97    public void checkWayForSharpAngles(Way way) {
     98        Node node1 = null;
     99        Node node2 = null;
     100        Node node3 = null;
     101        int i = -2;
     102        for (Node node : way.getNodes()) {
     103            node1 = node2;
     104            node2 = node3;
     105            node3 = node;
     106            checkAngle(node1, node2, node3, i, way, false);
     107            i++;
     108        }
     109        if (way.isClosed() && way.getNodesCount() > 2) {
     110            node1 = node2;
     111            node2 = node3;
     112            // Get the second node, not the first node, since a closed way has first node == second node
     113            node3 = way.getNode(1);
     114            checkAngle(node1, node2, node3, i, way, true);
     115        }
     116    }
     117
     118    private void checkAngle(Node node1, Node node2, Node node3, int i, Way way, boolean last) {
     119        if (node1 == null || node2 == null || node3 == null) return;
     120        EastNorth n1 = node1.getEastNorth();
     121        EastNorth n2 = node2.getEastNorth();
     122        EastNorth n3 = node3.getEastNorth();
     123        double angle = Math.toDegrees(Math.abs(Geometry.getCornerAngle(n1, n2, n3)));
     124        if (angle < maxAngle) {
     125            processSharpAngleForErrorCreation(angle, i, way, last, node2);
     126        }
     127    }
     128
     129    private void processSharpAngleForErrorCreation(double angle, int i, Way way, boolean last, Node pointNode) {
     130        List<WaySegment> waysegmentList = new ArrayList<>();
     131        waysegmentList.add(new WaySegment(way, i));
     132        if (last) {
     133            waysegmentList.add(new WaySegment(way, 0));
     134        } else {
     135            waysegmentList.add(new WaySegment(way, i+1));
     136        }
     137        Optional<WaySegment> possibleShortSegment = waysegmentList.stream()
     138                .min(Comparator.comparing(segment -> segment.toWay().getLength()));
     139        if (possibleShortSegment.isPresent() && possibleShortSegment.get().toWay().getLength() < maxLength) {
     140            createNearlyOverlappingError(angle, Collections.singleton(way), pointNode);
     141        }
     142    }
     143
     144    private void createNearlyOverlappingError(double angle,
     145            Collection<? extends OsmPrimitive> primitiveIssues, OsmPrimitive primitive) {
     146        TestError.Builder testError = TestError.builder(this, getSeverity(angle), SHARP_ANGLES)
     147                .primitives(primitiveIssues)
     148                .highlight(primitive)
     149                .message(tr("Sharp angle"));
     150        errors.add(testError.build());
     151    }
     152
     153    private Severity getSeverity(double angle) {
     154        Severity rSeverity = Severity.OTHER;
     155        for (Entry<Double, Severity> entry : severityBreakPoints.entrySet()) {
     156            if (angle < entry.getKey()) {
     157                rSeverity = entry.getValue();
     158            }
     159        }
     160        return rSeverity;
     161    }
     162
     163    /**
     164     * Set the maximum length for the shortest segment
     165     * @param length The max length in meters
     166     */
     167    public void setMaxLength(double length) {
     168        maxLength = length;
     169    }
     170
     171    /**
     172     * Add a highway to ignore
     173     * @param highway
     174     */
     175    public void addIgnoredHighway(String highway) {
     176        ignoreHighways.add(highway);
     177    }
     178
     179    /**
     180     * Set the maximum angle
     181     * @param angle The maximum angle in degrees.
     182     */
     183    public void setMaxAngle(double angle) {
     184        maxAngle = angle;
     185        setBreakPoints();
     186    }
     187
     188    /**
     189     * Set the breakpoints for the test
     190     */
     191    private void setBreakPoints() {
     192        severityBreakPoints.put(maxAngle, Severity.OTHER);
     193        severityBreakPoints.put(maxAngle * 2 / 3, Severity.WARNING);
     194        severityBreakPoints.put(maxAngle / 3, Severity.ERROR);
     195    }
     196}
  • 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}
     146 No newline at end of file