Ticket #17914: 17914.2.patch

File 17914.2.patch, 10.6 KB (added by GerdP, 4 years ago)
  • src/org/openstreetmap/josm/data/validation/tests/UnconnectedWays.java

     
    1313import java.util.HashMap;
    1414import java.util.HashSet;
    1515import java.util.Iterator;
     16import java.util.LinkedHashSet;
    1617import java.util.List;
    1718import java.util.Map;
    1819import java.util.Map.Entry;
    1920import java.util.Objects;
    2021import java.util.Set;
     22import java.util.stream.Collectors;
    2123
    2224import org.openstreetmap.josm.data.coor.EastNorth;
    2325import org.openstreetmap.josm.data.coor.LatLon;
     
    5355    private final int code;
    5456    private final boolean isHighwayTest;
    5557
     58    static final double DETOUR_FACTOR = 4;
     59
    5660    protected abstract boolean isCandidate(OsmPrimitive p);
    5761
    5862    protected boolean isWantedWay(Way w) {
     
    247251                map.clear();
    248252                return map;
    249253            }
     254            if (s.w.hasTag(HIGHWAY, "platform"))
     255                continue;
    250256            for (Node endnode : s.nearbyNodes(mindist)) {
    251257                Way parentWay = getWantedParentWay(endnode);
    252                 if (parentWay != null) {
    253                     if (!Objects.equals(OsmUtils.getLayer(s.w), OsmUtils.getLayer(parentWay))) {
    254                         continue; // ignore ways with different layer tag
    255                     }
    256 
    257                     // to handle intersections of 't' shapes and similar
    258                     if (!s.isConnectedTo(endnode)) {
    259                         if (parentWay.hasTag(HIGHWAY, "platform") || s.w.hasTag(HIGHWAY, "platform")
    260                                 || s.barrierBetween(endnode)) {
    261                             continue;
    262                         }
    263                         addIfNewOrCloser(map, endnode, s);
    264                     }
     258                if (parentWay != null && !parentWay.hasTag(HIGHWAY, "platform")
     259                        && Objects.equals(OsmUtils.getLayer(s.w), OsmUtils.getLayer(parentWay))
     260                        // to handle intersections of 't' shapes and similar
     261                        && !s.isConnectedTo(endnode) && !s.obstacleBetween(endnode)) {
     262                    addIfNewOrCloser(map, endnode, s);
    265263                }
    266264            }
    267265        }
     
    374372        }
    375373        fillSearchNodes(endnodes);
    376374        if (!searchNodes.isEmpty()) {
    377             maxLen = 4 * mindist;
     375            maxLen = DETOUR_FACTOR * mindist;
    378376            if (isHighwayTest) {
    379377                addErrors(Severity.WARNING, getHighwayEndNodesNearOtherHighway(), tr("Way end node near other highway"));
    380378            } else {
     
    385383        /* the following two should use a shorter distance */
    386384        boolean includeOther = isBeforeUpload ? ValidatorPrefHelper.PREF_OTHER_UPLOAD.get() : ValidatorPrefHelper.PREF_OTHER.get();
    387385        if (minmiddledist > 0.0 && includeOther) {
    388             maxLen = 4 * minmiddledist;
     386            maxLen = DETOUR_FACTOR * minmiddledist;
    389387            fillSearchNodes(middlenodes);
    390388            addErrors(Severity.OTHER, getWayNodesNearOtherWay(), tr("Way node near other way"));
    391389            fillSearchNodes(othernodes);
     
    431429         * @return true if a reasonable connection was found
    432430         */
    433431        boolean isConnectedTo(Node startNode) {
    434             return isConnectedTo(startNode, new HashSet<>(), 0);
     432            return isConnectedTo(startNode, new LinkedHashSet<>(), 0, w);
    435433        }
    436434
    437435        /**
     
    439437         * @param node the given node
    440438         * @param visited set of visited nodes
    441439         * @param len length of the travelled route
     440         * @param parent the previous parent way
    442441         * @return true if a reasonable connection was found
    443442         */
    444         private boolean isConnectedTo(Node node, Set<Node> visited, double len) {
    445             if (n1 == node || n2 == node) {
    446                 return true;
    447             }
     443        private boolean isConnectedTo(Node node, LinkedHashSet<Node> visited, double len, Way parent) {
    448444            if (len > maxLen) {
    449445                return false;
    450446            }
     447            if (n1 == node || n2 == node) {
     448                Node uncon = visited.iterator().next();
     449                LatLon cl = ProjectionRegistry.getProjection().eastNorth2latlon(calcClosest(uncon));
     450                // calculate real detour length, closest point might be somewhere between n1 and n2
     451                double detourLen = len + node.getCoor().greatCircleDistance(cl);
     452                if (detourLen > maxLen)
     453                    return false;
     454                // see #17914: flag also nodes which are very close
     455                double directDist = getDist(uncon);
     456                if (directDist <= 0.1)
     457                    return false;
     458                return directDist > 0.5 || (visited.size() == 2 && directDist * 1.5 > detourLen);
     459            }
    451460            if (visited != null) {
    452461                visited.add(node);
    453                 for (final Way way : node.getParentWays()) {
    454                     if (isWantedWay(way)) {
    455                         List<Node> nextNodes = new ArrayList<>();
    456                         int pos = way.getNodes().indexOf(node);
    457                         if (pos > 0) {
    458                             nextNodes.add(way.getNode(pos - 1));
     462                List<Way> wantedParents = node.getParentWays().stream().filter(pw -> isWantedWay(pw))
     463                        .collect(Collectors.toList());
     464                if (wantedParents.size() > 1 && wantedParents.indexOf(parent) != wantedParents.size() - 1) {
     465                    // we want to find a different way. so move known way to the end of the list
     466                    wantedParents.remove(parent);
     467                    wantedParents.add(parent);
     468                }
     469
     470                for (final Way way : wantedParents) {
     471                    List<Node> nextNodes = new ArrayList<>();
     472                    int pos = way.getNodes().indexOf(node);
     473                    if (pos > 0) {
     474                        nextNodes.add(way.getNode(pos - 1));
     475                    }
     476                    if (pos + 1 < way.getNodesCount()) {
     477                        nextNodes.add(way.getNode(pos + 1));
     478                    }
     479                    for (Node next : nextNodes) {
     480                        final boolean containsN = visited.contains(next);
     481                        visited.add(next);
     482                        if (!containsN && isConnectedTo(next, visited,
     483                                len + node.getCoor().greatCircleDistance(next.getCoor()), way)) {
     484                            return true;
    459485                        }
    460                         if (pos + 1 < way.getNodesCount()) {
    461                             nextNodes.add(way.getNode(pos + 1));
    462                         }
    463                         for (Node next : nextNodes) {
    464                             final boolean containsN = visited.contains(next);
    465                             visited.add(next);
    466                             if (!containsN && isConnectedTo(next, visited,
    467                                     len + node.getCoor().greatCircleDistance(next.getCoor()))) {
    468                                 return true;
    469                             }
    470                         }
    471486                    }
    472487                }
    473488            }
     
    474489            return false;
    475490        }
    476491
     492        private EastNorth calcClosest(Node n) {
     493            return Geometry.closestPointToSegment(n1.getEastNorth(), n2.getEastNorth(), n.getEastNorth());
     494        }
     495
    477496        double getDist(Node n) {
    478             EastNorth coord = n.getEastNorth();
    479             if (coord == null)
    480                 return Double.NaN;
    481             EastNorth closest = Geometry.closestPointToSegment(n1.getEastNorth(), n2.getEastNorth(), coord);
     497            EastNorth closest = calcClosest(n);
    482498            return n.getCoor().greatCircleDistance(ProjectionRegistry.getProjection().eastNorth2latlon(closest));
    483499        }
    484500
     
    509525            return new BBox(topLeft, botRight);
    510526        }
    511527
     528        /**
     529         * We know that any point near the line segment must be at
     530         * least as close as the other end of the line, plus
     531         * a little fudge for the distance away (dist)
     532         * @param dist fudge to add
     533         * @return collection of nearby nodes
     534         */
    512535        Collection<Node> nearbyNodes(double dist) {
    513             /*
    514              * We know that any point near the line segment must be at
    515              * least as close as the other end of the line, plus
    516              * a little fudge for the distance away ('dist').
    517              */
    518 
    519536            BBox bounds = this.getBounds(dist * (360.0d / (Ellipsoid.WGS84.a * 2 * Math.PI)));
    520537            List<Node> result = null;
    521538            List<Node> foundNodes = searchNodes.search(bounds);
     
    534551            return result == null ? Collections.emptyList() : result;
    535552        }
    536553
    537         private boolean barrierBetween(Node endnode) {
     554        private boolean obstacleBetween(Node endnode) {
    538555            EastNorth en = endnode.getEastNorth();
    539             EastNorth closest = Geometry.closestPointToSegment(n1.getEastNorth(), n2.getEastNorth(), en);
    540             BBox bbox = new BBox(endnode.getCoor(), ProjectionRegistry.getProjection().eastNorth2latlon(closest));
     556            EastNorth closest = calcClosest(endnode);
     557            LatLon llClosest = ProjectionRegistry.getProjection().eastNorth2latlon(closest);
     558            // find obstacles between end node and way segment
     559            BBox bbox = new BBox(endnode.getCoor(), llClosest);
    541560            for (Way nearbyWay : ds.searchWays(bbox)) {
    542                 if (nearbyWay != w && nearbyWay.isUsable() && nearbyWay.hasTag("barrier")
     561                if (nearbyWay != w && nearbyWay.isUsable() && isObstacle(nearbyWay)
    543562                        && !endnode.getParentWays().contains(nearbyWay)) {
    544                     //make sure that the barrier is really between endnode and the highway segment, not just close to or around them
     563                    //make sure that the obstacle is really between endnode and the highway segment, not just close to or around them
    545564                    Iterator<Node> iter = nearbyWay.getNodes().iterator();
    546565                    EastNorth prev = iter.next().getEastNorth();
    547566                    while (iter.hasNext()) {
     
    555574            }
    556575            return false;
    557576        }
     577
     578        private boolean isObstacle(Way w) {
     579            return w.hasKey("barrier", "waterway") || isBuilding(w);
     580        }
     581
    558582    }
    559583
    560584    List<MyWaySegment> getWaySegments(Way w) {