Index: src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 14934)
+++ src/org/openstreetmap/josm/data/validation/OsmValidator.java	(working copy)
@@ -49,6 +49,7 @@
 import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
 import org.openstreetmap.josm.data.validation.tests.Highways;
 import org.openstreetmap.josm.data.validation.tests.InternetTags;
+import org.openstreetmap.josm.data.validation.tests.IntersectionIssues;
 import org.openstreetmap.josm.data.validation.tests.Lanes;
 import org.openstreetmap.josm.data.validation.tests.LongSegment;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
@@ -148,6 +149,7 @@
         LongSegment.class, // 3500 .. 3599
         PublicTransportRouteTest.class, // 3600 .. 3699
         RightAngleBuildingTest.class, // 3700 .. 3799
+        IntersectionIssues.class, // 3800 .. 3899
     };
 
     /**
Index: src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/validation/tests/IntersectionIssues.java	(working copy)
@@ -0,0 +1,187 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.validation.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.GpxDistance;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.validation.Severity;
+import org.openstreetmap.josm.data.validation.Test;
+import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Finds issues with highway intersections
+ * @author Taylor Smock
+ * @since xxx
+ */
+public class IntersectionIssues extends Test {
+    private static final int INTERSECTIONISSUESCODE = 3800;
+    /** The code for an intersection which briefly interrupts a road */
+    public static final int SHORT_DISCONNECT = INTERSECTIONISSUESCODE + 0;
+    /** The code for a node that is almost on a way */
+    public static final int NEARBY_NODE = INTERSECTIONISSUESCODE + 1;
+    /** The distance to consider for nearby nodes/short disconnects */
+    public static final double maxDistance = 5.0; // meters
+    /** The distance to consider for nearby nodes with tags */
+    public static final double maxDistanceNodeInformation = maxDistance / 5.0; // meters
+
+    private HashMap<String, ArrayList<Way>> ways;
+    ArrayList<Way> allWays;
+
+    /**
+     * Construct a new {@code IntersectionIssues} object
+     */
+    public IntersectionIssues() {
+        super(tr("Intersection Issues"), tr("Check for issues at intersections"));
+    }
+
+    @Override
+    public void startTest(ProgressMonitor monitor) {
+        super.startTest(monitor);
+        ways = new HashMap<>();
+        allWays = new ArrayList<>();
+    }
+
+    @Override
+    public void endTest() {
+        Way pWay = null;
+        try {
+            for (String key : ways.keySet()) {
+                ArrayList<Way> comparison = ways.get(key);
+                pWay = comparison.get(0);
+                checkNearbyEnds(comparison);
+            }
+            for (Way way : allWays) {
+                pWay = way;
+                for (Way way2 : allWays) {
+                    if (way2.equals(way)) continue;
+                    pWay = way2;
+                    if (way.getBBox().intersects(way2.getBBox())) {
+                        checkNearbyNodes(way, way2);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            if (pWay != null) {
+                System.out.printf("Way https://osm.org/way/%d caused an error".concat(System.lineSeparator()), pWay.getOsmId());
+            }
+            e.printStackTrace();
+        }
+        ways = null;
+        allWays = null;
+        super.endTest();
+    }
+
+    @Override
+    public void visit(Way way) {
+        if (!way.isUsable()) return;
+        if (way.hasKey("highway")) {
+            String[] identityTags = new String[] {"name", "ref"};
+            for (String tag : identityTags) {
+                if (way.hasKey(tag)) {
+                    ArrayList<Way> similar = new ArrayList<>();
+                    if (ways.containsKey(way.get(tag))) similar = ways.get(way.get(tag));
+
+                    if (!similar.contains(way)) similar.add(way);
+                    ways.put(way.get(tag), similar);
+                }
+            }
+            if (!allWays.contains(way)) allWays.add(way);
+        }
+    }
+
+    /**
+     * Check for ends that are nearby but not directly connected
+     * @param comparison Ways to look at
+     */
+    public void checkNearbyEnds(ArrayList<Way> comparison) {
+        Logging.setLogLevel(Logging.LEVEL_INFO);
+        ArrayList<Way> errored = new ArrayList<>();
+        for (Way one : comparison) {
+            LatLon oneLast = one.lastNode().getCoor();
+            LatLon oneFirst = one.firstNode().getCoor();
+            for (Way two : comparison) {
+                if (one.isFirstLastNode(two.firstNode()) || one.isFirstLastNode(two.lastNode()) ||
+                        (errored.contains(one) && errored.contains(two))) continue;
+                LatLon twoLast = two.lastNode().getCoor();
+                LatLon twoFirst = two.firstNode().getCoor();
+                Logging.info("Comparing {0} to {1}", one, two);
+                if (twoLast.greatCircleDistance(oneLast) <= maxDistance ||
+                        twoLast.greatCircleDistance(oneFirst) <= maxDistance ||
+                        twoFirst.greatCircleDistance(oneLast) <= maxDistance ||
+                        twoFirst.greatCircleDistance(oneFirst) <= maxDistance) {
+                    List<Way> nearby = new ArrayList<>();
+                    nearby.add(one);
+                    nearby.add(two);
+                    errored.addAll(nearby);
+                    allWays.removeAll(errored);
+                    TestError.Builder testError = TestError.builder(this, Severity.WARNING, SHORT_DISCONNECT)
+                            .primitives(nearby)
+                            .message(tr("Disconnected road"));
+                    errors.add(testError.build());
+                }
+            }
+        }
+    }
+
+    /**
+     * Check nearby nodes to an intersection of two ways
+     * @param way1 A way to check an almost intersection with
+     * @param way2 A way to check an almost intersection with
+     */
+    public void checkNearbyNodes(Way way1, Way way2) {
+        Node intersectingNode = getIntersectingNode(way1, way2);
+        if (intersectingNode == null) return;
+        checkNearbyNodes(way1, way2, intersectingNode);
+        checkNearbyNodes(way2, way1, intersectingNode);
+    }
+
+    private void checkNearbyNodes(Way way1, Way way2, Node nearby) {
+        for (Node node : way1.getNeighbours(nearby)) {
+            Logging.info("Looking at {0}", node.getUniqueId());
+            if (node.equals(nearby)) continue;
+            WayPoint waypoint = new WayPoint(node.getCoor());
+            double distance = GpxDistance.getDistance(way2, waypoint);
+            if (distance < maxDistance && !node.isTagged()
+            		|| distance < maxDistanceNodeInformation && node.isTagged()) {
+                List<Way> primitiveIssues = new ArrayList<>();
+                primitiveIssues.add(way1);
+                primitiveIssues.add(way2);
+                for (TestError error : getErrors()) {
+                    if ((error.getCode() == SHORT_DISCONNECT || error.getCode() == NEARBY_NODE)
+                            && primitiveIssues.containsAll(error.getPrimitives())) {
+                        return;
+                    }
+                }
+                TestError.Builder testError = TestError.builder(this, Severity.WARNING, NEARBY_NODE)
+                        .primitives(primitiveIssues)
+                        .message(tr("Almost overlapping highways"));
+                errors.add(testError.build());
+            }
+        }
+    }
+
+    /**
+     * Get the intersecting node of two ways
+     * @param way1 A way that (hopefully) intersects with way2
+     * @param way2 A way to find an intersection with
+     * @return {@code Node} if there is an intersecting node, {@code null} otherwise
+     */
+    public Node getIntersectingNode(Way way1, Way way2) {
+        for (Node node : way1.getNodes()) {
+            if (way2.containsNode(node)) {
+                return node;
+            }
+        }
+        return null;
+    }
+}
