Ticket #16803: 16803.patch

File 16803.patch, 4.9 KB (added by GerdP, 7 years ago)
  • src/org/openstreetmap/josm/data/validation/tests/Highways.java

     
    1212import java.util.Locale;
    1313import java.util.Map;
    1414import java.util.Set;
    15 import java.util.stream.Collectors;
    1615
    1716import org.openstreetmap.josm.command.ChangePropertyCommand;
    1817import org.openstreetmap.josm.data.osm.Node;
     
    2221import org.openstreetmap.josm.data.validation.Severity;
    2322import org.openstreetmap.josm.data.validation.Test;
    2423import org.openstreetmap.josm.data.validation.TestError;
     24import org.openstreetmap.josm.tools.Geometry;
    2525import org.openstreetmap.josm.tools.Logging;
    2626import org.openstreetmap.josm.tools.Utils;
    2727
     
    164164     */
    165165    public static boolean isHighwayLinkOkay(final Way way) {
    166166        final String highway = way.get(HIGHWAY);
    167         if (highway == null || !highway.endsWith("_link")
    168                 || !IN_DOWNLOADED_AREA.test(way.getNode(0)) || !IN_DOWNLOADED_AREA.test(way.getNode(way.getNodesCount()-1))) {
     167        if (highway == null || !highway.endsWith("_link")) {
    169168            return true;
    170169        }
    171170
    172         final Set<OsmPrimitive> referrers = new HashSet<>();
    173 
    174         if (way.isClosed()) {
    175             // for closed way we need to check all adjacent ways
    176             for (Node n: way.getNodes()) {
    177                 referrers.addAll(n.getReferrers());
     171        for (int i = 0; i < way.getNodesCount(); i++) {
     172            Node n = way.getNode(i);
     173            if (!IN_DOWNLOADED_AREA.test(n))
     174                return true;
     175            Set<Way> otherWays = new HashSet<>();
     176            otherWays.addAll(Utils.filteredCollection(n.getReferrers(), Way.class));
     177            if (otherWays.size() == 1)
     178                continue;
     179            otherWays.remove(way);
     180            otherWays.removeIf(w -> !highway.startsWith(w.get(HIGHWAY)) || w.getNodesCount() < 2);
     181            if (otherWays.isEmpty())
     182                continue;
     183            //TODO: ignore ways which are not allowed because of turn restrictions, oneway attributes or access rules?
     184            HashSet<Way> sameTag = new HashSet<>();
     185            for (Way ow : otherWays) {
     186                if (highway.equals(ow.get(HIGHWAY)))
     187                    sameTag.add(ow);
     188                else
     189                    return true;
    178190            }
    179         } else {
    180             referrers.addAll(way.firstNode().getReferrers());
    181             referrers.addAll(way.lastNode().getReferrers());
     191            // we have way(s) with the same _link tag, ignore those with a sharp angle
     192            final int pos = i;
     193            sameTag.removeIf(w -> isSharpAngle(way, pos, w));
     194            if (!sameTag.isEmpty())
     195                return true;
    182196        }
     197        return false;
    183198
    184         // Find ways of same class (exact class of class_link)
    185         List<Way> sameClass = Utils.filteredCollection(referrers, Way.class).stream().filter(
    186                 otherWay -> !way.equals(otherWay) && otherWay.hasTag(HIGHWAY, highway, highway.replaceAll("_link$", "")))
    187                 .collect(Collectors.toList());
    188         if (sameClass.size() > 1) {
    189             // It is possible to have a class_link between 2 segments of same class
    190             // in roundabout designs that physically separate a specific turn from the main roundabout
    191             // But if we have more than a single adjacent class, and one of them is a roundabout, that's an error
    192             for (Way w : sameClass) {
    193                 if (w.hasTag("junction", "roundabout")) {
    194                     return false;
    195                 }
     199    }
     200
     201    /**
     202     * Check if the two given connected ways form a sharp angle.
     203     * @param way 1st way
     204     * @param nodePos node position of connecting node in 1st way
     205     * @param otherWay the 2nd way
     206     * @return true if angle is sharp
     207     */
     208    private static boolean isSharpAngle(Way way, int nodePos, Way otherWay) {
     209        Node n = way.getNode(nodePos);
     210        Node n0 = way.getNode(nodePos == 0 ? 1 : nodePos -1);
     211
     212        Node n2 = null;
     213        for (int i = 0; i < otherWay.getNodesCount(); i++) {
     214            if (n == otherWay.getNode(i)) {
     215                n2 = otherWay.getNode(i > 0 ? i-1 : 1);
     216                break;
    196217            }
    197218        }
    198         // Link roads should always at least one adjacent segment of same class
    199         return !sameClass.isEmpty();
     219        assert n != null && n0 != null : "node not found in 1st way";
     220        assert n2 != null : "node not found in 2nd way";
     221        if (n2 != null) {
     222            double angle = Geometry.getNormalizedAngleInDegrees(
     223                    Geometry.getCornerAngle(n0.getEastNorth(), n.getEastNorth(), n2.getEastNorth()));
     224            return angle < 80;
     225        }
     226        return false;
    200227    }
    201228
    202229    private void testHighwayLink(final Way way) {