Ticket #17528: intersectionissues.patch

File intersectionissues.patch, 8.9 KB (added by taylor.smock, 5 years ago)

Initial patch for finding some intersection issues

  • src/org/openstreetmap/josm/data/validation/OsmValidator.java

     
    4949import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
    5050import org.openstreetmap.josm.data.validation.tests.Highways;
    5151import org.openstreetmap.josm.data.validation.tests.InternetTags;
     52import org.openstreetmap.josm.data.validation.tests.IntersectionIssues;
    5253import org.openstreetmap.josm.data.validation.tests.Lanes;
    5354import org.openstreetmap.josm.data.validation.tests.LongSegment;
    5455import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
     
    148149        LongSegment.class, // 3500 .. 3599
    149150        PublicTransportRouteTest.class, // 3600 .. 3699
    150151        RightAngleBuildingTest.class, // 3700 .. 3799
     152        IntersectionIssues.class, // 3800 .. 3899
    151153    };
    152154
    153155    /**
  • src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.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.HashMap;
     8import java.util.List;
     9
     10import org.openstreetmap.josm.data.coor.LatLon;
     11import org.openstreetmap.josm.data.gpx.GpxDistance;
     12import org.openstreetmap.josm.data.gpx.WayPoint;
     13import org.openstreetmap.josm.data.osm.Node;
     14import org.openstreetmap.josm.data.osm.Way;
     15import org.openstreetmap.josm.data.validation.Severity;
     16import org.openstreetmap.josm.data.validation.Test;
     17import org.openstreetmap.josm.data.validation.TestError;
     18import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     19import org.openstreetmap.josm.tools.Logging;
     20
     21/**
     22 * Finds issues with highway intersections
     23 * @author Taylor Smock
     24 * @since xxx
     25 */
     26public class IntersectionIssues extends Test {
     27    private static final int INTERSECTIONISSUESCODE = 3800;
     28    /** The code for an intersection which briefly interrupts a road */
     29    public static final int SHORT_DISCONNECT = INTERSECTIONISSUESCODE + 0;
     30    /** The code for a node that is almost on a way */
     31    public static final int NEARBY_NODE = INTERSECTIONISSUESCODE + 1;
     32    /** The distance to consider for nearby nodes/short disconnects */
     33    public static final double maxDistance = 5.0; // meters
     34    /** The distance to consider for nearby nodes with tags */
     35    public static final double maxDistanceNodeInformation = maxDistance / 5.0; // meters
     36
     37    private HashMap<String, ArrayList<Way>> ways;
     38    ArrayList<Way> allWays;
     39
     40    /**
     41     * Construct a new {@code IntersectionIssues} object
     42     */
     43    public IntersectionIssues() {
     44        super(tr("Intersection Issues"), tr("Check for issues at intersections"));
     45    }
     46
     47    @Override
     48    public void startTest(ProgressMonitor monitor) {
     49        super.startTest(monitor);
     50        ways = new HashMap<>();
     51        allWays = new ArrayList<>();
     52    }
     53
     54    @Override
     55    public void endTest() {
     56        Way pWay = null;
     57        try {
     58            for (String key : ways.keySet()) {
     59                ArrayList<Way> comparison = ways.get(key);
     60                pWay = comparison.get(0);
     61                checkNearbyEnds(comparison);
     62            }
     63            for (Way way : allWays) {
     64                pWay = way;
     65                for (Way way2 : allWays) {
     66                    if (way2.equals(way)) continue;
     67                    pWay = way2;
     68                    if (way.getBBox().intersects(way2.getBBox())) {
     69                        checkNearbyNodes(way, way2);
     70                    }
     71                }
     72            }
     73        } catch (Exception e) {
     74            if (pWay != null) {
     75                System.out.printf("Way https://osm.org/way/%d caused an error".concat(System.lineSeparator()), pWay.getOsmId());
     76            }
     77            e.printStackTrace();
     78        }
     79        ways = null;
     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")) {
     88            String[] identityTags = new String[] {"name", "ref"};
     89            for (String tag : identityTags) {
     90                if (way.hasKey(tag)) {
     91                    ArrayList<Way> similar = new ArrayList<>();
     92                    if (ways.containsKey(way.get(tag))) similar = ways.get(way.get(tag));
     93
     94                    if (!similar.contains(way)) similar.add(way);
     95                    ways.put(way.get(tag), similar);
     96                }
     97            }
     98            if (!allWays.contains(way)) allWays.add(way);
     99        }
     100    }
     101
     102    /**
     103     * Check for ends that are nearby but not directly connected
     104     * @param comparison Ways to look at
     105     */
     106    public void checkNearbyEnds(ArrayList<Way> comparison) {
     107        Logging.setLogLevel(Logging.LEVEL_INFO);
     108        ArrayList<Way> errored = new ArrayList<>();
     109        for (Way one : comparison) {
     110            LatLon oneLast = one.lastNode().getCoor();
     111            LatLon oneFirst = one.firstNode().getCoor();
     112            for (Way two : comparison) {
     113                if (one.isFirstLastNode(two.firstNode()) || one.isFirstLastNode(two.lastNode()) ||
     114                        (errored.contains(one) && errored.contains(two))) continue;
     115                LatLon twoLast = two.lastNode().getCoor();
     116                LatLon twoFirst = two.firstNode().getCoor();
     117                Logging.info("Comparing {0} to {1}", one, two);
     118                if (twoLast.greatCircleDistance(oneLast) <= maxDistance ||
     119                        twoLast.greatCircleDistance(oneFirst) <= maxDistance ||
     120                        twoFirst.greatCircleDistance(oneLast) <= maxDistance ||
     121                        twoFirst.greatCircleDistance(oneFirst) <= maxDistance) {
     122                    List<Way> nearby = new ArrayList<>();
     123                    nearby.add(one);
     124                    nearby.add(two);
     125                    errored.addAll(nearby);
     126                    allWays.removeAll(errored);
     127                    TestError.Builder testError = TestError.builder(this, Severity.WARNING, SHORT_DISCONNECT)
     128                            .primitives(nearby)
     129                            .message(tr("Disconnected road"));
     130                    errors.add(testError.build());
     131                }
     132            }
     133        }
     134    }
     135
     136    /**
     137     * Check nearby nodes to an intersection of two ways
     138     * @param way1 A way to check an almost intersection with
     139     * @param way2 A way to check an almost intersection with
     140     */
     141    public void checkNearbyNodes(Way way1, Way way2) {
     142        Node intersectingNode = getIntersectingNode(way1, way2);
     143        if (intersectingNode == null) return;
     144        checkNearbyNodes(way1, way2, intersectingNode);
     145        checkNearbyNodes(way2, way1, intersectingNode);
     146    }
     147
     148    private void checkNearbyNodes(Way way1, Way way2, Node nearby) {
     149        for (Node node : way1.getNeighbours(nearby)) {
     150            Logging.info("Looking at {0}", node.getUniqueId());
     151            if (node.equals(nearby)) continue;
     152            WayPoint waypoint = new WayPoint(node.getCoor());
     153            double distance = GpxDistance.getDistance(way2, waypoint);
     154            if (distance < maxDistance && !node.isTagged()
     155                        || distance < maxDistanceNodeInformation && node.isTagged()) {
     156                List<Way> primitiveIssues = new ArrayList<>();
     157                primitiveIssues.add(way1);
     158                primitiveIssues.add(way2);
     159                for (TestError error : getErrors()) {
     160                    if ((error.getCode() == SHORT_DISCONNECT || error.getCode() == NEARBY_NODE)
     161                            && primitiveIssues.containsAll(error.getPrimitives())) {
     162                        return;
     163                    }
     164                }
     165                TestError.Builder testError = TestError.builder(this, Severity.WARNING, NEARBY_NODE)
     166                        .primitives(primitiveIssues)
     167                        .message(tr("Almost overlapping highways"));
     168                errors.add(testError.build());
     169            }
     170        }
     171    }
     172
     173    /**
     174     * Get the intersecting node of two ways
     175     * @param way1 A way that (hopefully) intersects with way2
     176     * @param way2 A way to find an intersection with
     177     * @return {@code Node} if there is an intersecting node, {@code null} otherwise
     178     */
     179    public Node getIntersectingNode(Way way1, Way way2) {
     180        for (Node node : way1.getNodes()) {
     181            if (way2.containsNode(node)) {
     182                return node;
     183            }
     184        }
     185        return null;
     186    }
     187}