Ignore:
Timestamp:
2013-07-15T20:09:48+02:00 (11 years ago)
Author:
akks
Message:

see #8861: Selecting "duplicate" nodes etc. : if way is selected, prefer its nodes,
if node is selected, prefer its ways, if relation is selected, prefer its members

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java

    r6056 r6065  
    726726     */
    727727    public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate, boolean use_selected) {
    728         Node n = null;
    729 
     728        return getNearestNode(p, predicate, use_selected, null);
     729    }
     730   
     731    /**
     732     * The *result* depends on the current map selection state IF use_selected is true
     733     *
     734     * If more than one node within node.snap-distance pixels is found,
     735     * the nearest node selected is returned IF use_selected is true.
     736     *
     737     * If there are no selected nodes near that point, the node that is related to some of the preferredRefs
     738     *
     739     * Else the nearest new/id=0 node within about the same distance
     740     * as the true nearest node is returned.
     741     *
     742     * If no such node is found either, the true nearest
     743     * node to p is returned.
     744     *
     745     * Finally, if a node is not found at all, null is returned.
     746     * @since 6065
     747     * @return A node within snap-distance to point p,
     748     *      that is chosen by the algorithm described.
     749     *
     750     * @param p the screen point
     751     * @param predicate this parameter imposes a condition on the returned object, e.g.
     752     *        give the nearest node that is tagged.
     753     * @param preferredRefs primitives, whose nodes we prefer
     754     */
     755    public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate,
     756            boolean use_selected, Collection<OsmPrimitive> preferredRefs) {
     757       
    730758        Map<Double, List<Node>> nlists = getNearestNodesImpl(p, predicate);
    731         if (!nlists.isEmpty()) {
    732             Node ntsel = null, ntnew = null;
    733             double minDistSq = nlists.keySet().iterator().next();
    734 
    735             for (Double distSq : nlists.keySet()) {
    736                 for (Node nd : nlists.get(distSq)) {
    737                     // find the nearest selected node
    738                     if (ntsel == null && nd.isSelected()) {
    739                         ntsel = nd;
    740                         // if there are multiple nearest nodes, prefer the one
    741                         // that is selected. This is required in order to drag
    742                         // the selected node if multiple nodes have the same
    743                         // coordinates (e.g. after unglue)
    744                         use_selected |= (distSq == minDistSq);
    745                     }
    746                     // find the nearest newest node that is within about the same
    747                     // distance as the true nearest node
    748                     if (ntnew == null && nd.isNew() && (distSq-minDistSq < 1)) {
    749                         ntnew = nd;
     759        if (nlists.isEmpty()) return null;
     760       
     761        if (preferredRefs != null && preferredRefs.isEmpty()) preferredRefs = null;
     762        Node ntsel = null, ntnew = null, ntref = null;
     763        boolean useNtsel = use_selected;
     764        double minDistSq = nlists.keySet().iterator().next();
     765
     766        for (Double distSq : nlists.keySet()) {
     767            for (Node nd : nlists.get(distSq)) {
     768                // find the nearest selected node
     769                if (ntsel == null && nd.isSelected()) {
     770                    ntsel = nd;
     771                    // if there are multiple nearest nodes, prefer the one
     772                    // that is selected. This is required in order to drag
     773                    // the selected node if multiple nodes have the same
     774                    // coordinates (e.g. after unglue)
     775                    useNtsel |= (distSq == minDistSq);
     776                }
     777                if (ntref == null && preferredRefs != null && distSq == minDistSq) {
     778                    List<OsmPrimitive> ndRefs = nd.getReferrers();
     779                    for (OsmPrimitive ref: preferredRefs) {
     780                        if (ndRefs.contains(ref)) {
     781                            ntref = nd;
     782                            break;
     783                        }
    750784                    }
    751785                }
    752             }
    753 
    754             // take nearest selected, nearest new or true nearest node to p, in that order
    755             n = (ntsel != null && use_selected) ? ntsel
    756                     : (ntnew != null) ? ntnew
    757                             : nlists.values().iterator().next().get(0);
    758         }
    759         return n;
     786                // find the nearest newest node that is within about the same
     787                // distance as the true nearest node
     788                if (ntnew == null && nd.isNew() && (distSq-minDistSq < 1)) {
     789                    ntnew = nd;
     790                }
     791            }
     792        }
     793
     794        // take nearest selected, nearest new or true nearest node to p, in that order
     795        if (ntsel != null && useNtsel)
     796            return ntsel;
     797        if (ntref != null)
     798            return ntref;
     799        if (ntnew != null)
     800            return ntnew;
     801        return nlists.values().iterator().next().get(0);
    760802    }
    761803
     
    914956        return (ntsel != null && use_selected) ? ntsel : wayseg;
    915957    }
     958   
     959     /**
     960     * The *result* depends on the current map selection state IF use_selected is true.
     961     *
     962     * @return The nearest way segment to point p,
     963     *      and, depending on use_selected, prefers a selected way segment, if found.
     964     * Also prefers segments of ways that are related to one of preferredRefs primitives
     965     * @see #getNearestWaySegments(Point, Collection, Predicate)
     966     * @since 6065
     967     * @param p the point for which to search the nearest segment.
     968     * @param predicate the returned object has to fulfill certain properties.
     969     * @param use_selected whether selected way segments should be preferred.
     970     * @param preferredRefs - prefer segments related to these primitives, may be null
     971     */
     972    public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate,
     973            boolean use_selected,  Collection<OsmPrimitive> preferredRefs) {
     974        WaySegment wayseg = null, ntsel = null, ntref = null;
     975        if (preferredRefs != null && preferredRefs.isEmpty()) preferredRefs = null;
     976       
     977        searchLoop: for (List<WaySegment> wslist : getNearestWaySegmentsImpl(p, predicate).values()) {
     978            for (WaySegment ws : wslist) {
     979                if (wayseg == null) {
     980                    wayseg = ws;
     981                }
     982                if (ntsel == null && ws.way.isSelected()) {
     983                    ntsel = ws;
     984                    break searchLoop;
     985                }
     986                if (ntref == null && preferredRefs != null) {
     987                    // prefer ways containing given nodes
     988                    for (Node nd: ws.way.getNodes()) {
     989                        if (preferredRefs.contains(nd)) {
     990                            ntref = ws;
     991                            break searchLoop;
     992                        }
     993                    }
     994                    Collection<OsmPrimitive> wayRefs = ws.way.getReferrers();
     995                    // prefer member of the given relations
     996                    for (OsmPrimitive ref: preferredRefs) {
     997                        if (ref instanceof Relation && wayRefs.contains(ref)) {
     998                            ntref = ws;
     999                            break searchLoop;
     1000                        }
     1001                    }
     1002                }
     1003            }
     1004        }
     1005        if (ntsel != null && use_selected)
     1006            return ntsel;
     1007        if (ntref != null)
     1008            return ntref;
     1009        return wayseg;
     1010    }
    9161011
    9171012    /**
     
    10511146     */
    10521147    private boolean isPrecedenceNode(Node osm, Point p, boolean use_selected) {
    1053         boolean ret = false;
    1054 
    10551148        if (osm != null) {
    1056             ret |= !(p.distanceSq(getPoint2D(osm)) > (4)*(4));
    1057             ret |= osm.isTagged();
    1058             if (use_selected) {
    1059                 ret |= osm.isSelected();
    1060             }
    1061         }
    1062 
    1063         return ret;
     1149            if (!(p.distanceSq(getPoint2D(osm)) > (4)*(4))) return true;
     1150            if (osm.isTagged()) return true;
     1151            if (use_selected && osm.isSelected()) return true;
     1152        }
     1153        return false;
    10641154    }
    10651155
     
    10871177     * @param p The point on screen.
    10881178     * @param predicate the returned object has to fulfill certain properties.
    1089      * @param use_selected whether to prefer primitives that are currently selected.
     1179     * @param use_selected whether to prefer primitives that are currently selected or referred by selected primitives
    10901180     */
    10911181    public final OsmPrimitive getNearestNodeOrWay(Point p, Predicate<OsmPrimitive> predicate, boolean use_selected) {
    1092         OsmPrimitive osm = getNearestNode(p, predicate, use_selected);
    1093         WaySegment ws = null;
    1094 
    1095         if (!isPrecedenceNode((Node)osm, p, use_selected)) {
     1182        Collection<OsmPrimitive> sel =
     1183                use_selected ? getCurrentDataSet().getSelected() : null;
     1184        OsmPrimitive osm = getNearestNode(p, predicate, use_selected, sel);
     1185
     1186        if (isPrecedenceNode((Node)osm, p, use_selected)) return osm;
     1187        WaySegment ws;
     1188        if (use_selected) {
     1189            ws = getNearestWaySegment(p, predicate, use_selected, sel);
     1190        } else {
    10961191            ws = getNearestWaySegment(p, predicate, use_selected);
    1097 
    1098             if (ws != null) {
    1099                 if ((ws.way.isSelected() && use_selected) || osm == null) {
    1100                     // either (no _selected_ nearest node found, if desired) or no nearest node was found
    1101                     osm = ws.way;
    1102                 } else {
    1103                     int maxWaySegLenSq = 3*PROP_SNAP_DISTANCE.get();
    1104                     maxWaySegLenSq *= maxWaySegLenSq;
    1105 
    1106                     Point2D wp1 = getPoint2D(ws.way.getNode(ws.lowerIndex));
    1107                     Point2D wp2 = getPoint2D(ws.way.getNode(ws.lowerIndex+1));
    1108 
    1109                     // is wayseg shorter than maxWaySegLenSq and
    1110                     // is p closer to the middle of wayseg  than  to the nearest node?
    1111                     if (wp1.distanceSq(wp2) < maxWaySegLenSq &&
    1112                             p.distanceSq(project(0.5, wp1, wp2)) < p.distanceSq(getPoint2D((Node)osm))) {
    1113                         osm = ws.way;
    1114                     }
    1115                 }
    1116             }
    1117         }
    1118 
     1192        }
     1193        if (ws == null) return osm;
     1194
     1195        if ((ws.way.isSelected() && use_selected) || osm == null) {
     1196            // either (no _selected_ nearest node found, if desired) or no nearest node was found
     1197            osm = ws.way;
     1198        } else {
     1199            int maxWaySegLenSq = 3*PROP_SNAP_DISTANCE.get();
     1200            maxWaySegLenSq *= maxWaySegLenSq;
     1201
     1202            Point2D wp1 = getPoint2D(ws.way.getNode(ws.lowerIndex));
     1203            Point2D wp2 = getPoint2D(ws.way.getNode(ws.lowerIndex+1));
     1204
     1205            // is wayseg shorter than maxWaySegLenSq and
     1206            // is p closer to the middle of wayseg  than  to the nearest node?
     1207            if (wp1.distanceSq(wp2) < maxWaySegLenSq &&
     1208                    p.distanceSq(project(0.5, wp1, wp2)) < p.distanceSq(getPoint2D((Node)osm))) {
     1209                osm = ws.way;
     1210            }
     1211        }
    11191212        return osm;
    11201213    }
     
    12491342    }
    12501343
     1344    @Override
    12511345    public String helpTopic() {
    12521346        String n = getClass().getName();
Note: See TracChangeset for help on using the changeset viewer.