Changeset 15610 in josm for trunk/src


Ignore:
Timestamp:
2019-12-24T11:01:03+01:00 (5 years ago)
Author:
GerdP
Message:

fix #18420: Move node onto ways not working anymore

  • sort result from NavigatableComponent again using higher precision
  • improve handling of multiple target ways with similar distance so that the node is added to the closest segment and also to segments with almost the same position but NOT to segments further away or with a similar distance but different position.
  • fix possible error when multiple nodes are selected
  • use LinkedHashMap for field data to avoid random results from iterator (hope this fixes the unit test problems)
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java

    r15428 r15610  
    1313import java.util.HashMap;
    1414import java.util.HashSet;
     15import java.util.LinkedHashMap;
    1516import java.util.LinkedList;
    1617import java.util.List;
    1718import java.util.Map;
     19import java.util.Map.Entry;
    1820import java.util.Set;
     21import java.util.TreeMap;
    1922
    2023import javax.swing.JOptionPane;
     
    9194        Collection<Node> selectedNodes = ds.getSelectedNodes();
    9295        Collection<Command> cmds = new LinkedList<>();
    93         Map<Way, MultiMap<Integer, Node>> data = new HashMap<>();
     96        Map<Way, MultiMap<Integer, Node>> data = new LinkedHashMap<>();
    9497
    9598        // If the user has selected some ways, only join the node to these.
     
    100103        for (Node node : selectedNodes) {
    101104            List<WaySegment> wss = mapView.getNearestWaySegments(mapView.getPoint(node), OsmPrimitive::isSelectable);
    102             Set<Way> seenWays = new HashSet<>();
     105            // we cannot trust the order of elements in wss because it was calculated based on rounded position value of node
     106            TreeMap<Double, List<WaySegment>> nearestMap = new TreeMap<>();
     107            EastNorth en = node.getEastNorth();
    103108            for (WaySegment ws : wss) {
    104109                // Maybe cleaner to pass a "isSelected" predicate to getNearestWaySegments, but this is less invasive.
     
    106111                    continue;
    107112                }
    108                 // only use the closest WaySegment of each way and ignore those that already contain the node
    109                 if (!ws.getFirstNode().equals(node) && !ws.getSecondNode().equals(node)
    110                         && !seenWays.contains(ws.way)) {
    111                     MultiMap<Integer, Node> innerMap = data.get(ws.way);
    112                     if (innerMap == null) {
    113                         innerMap = new MultiMap<>();
    114                         data.put(ws.way, innerMap);
     113                /* perpendicular distance squared
     114                 * loose some precision to account for possible deviations in the calculation above
     115                 * e.g. if identical (A and B) come about reversed in another way, values may differ
     116                 * -- zero out least significant 32 dual digits of mantissa..
     117                 */
     118                double distSq = en.distanceSq(Geometry.closestPointToSegment(ws.getFirstNode().getEastNorth(),
     119                        ws.getSecondNode().getEastNorth(), en));
     120                // resolution in numbers with large exponent not needed here..
     121                distSq = Double.longBitsToDouble(Double.doubleToLongBits(distSq) >> 32 << 32);
     122                List<WaySegment> wslist = nearestMap.computeIfAbsent(distSq, k -> new LinkedList<>());
     123                wslist.add(ws);
     124            }
     125            Set<Way> seenWays = new HashSet<>();
     126            Double usedDist = null;
     127            while (!nearestMap.isEmpty()) {
     128                Entry<Double, List<WaySegment>> entry = nearestMap.pollFirstEntry();
     129                if (usedDist != null) {
     130                    double delta = entry.getKey() - usedDist;
     131                    if (delta > 1e-4)
     132                        break;
     133                }
     134                for (WaySegment ws : entry.getValue()) {
     135                    // only use the closest WaySegment of each way and ignore those that already contain the node
     136                    if (!ws.getFirstNode().equals(node) && !ws.getSecondNode().equals(node)
     137                            && !seenWays.contains(ws.way)) {
     138                        if (usedDist == null)
     139                            usedDist = entry.getKey();
     140                        MultiMap<Integer, Node> innerMap = data.get(ws.way);
     141                        if (innerMap == null) {
     142                            innerMap = new MultiMap<>();
     143                            data.put(ws.way, innerMap);
     144                        }
     145                        innerMap.put(ws.lowerIndex, node);
    115146                    }
    116                     innerMap.put(ws.lowerIndex, node);
    117147                    seenWays.add(ws.way);
    118148                }
     
    142172                        if (prevMove != null) {
    143173                            if (!prevMove.equalsEpsilon(newPosition, 1e-4)) {
     174                                // very unlikely: node has same distance to multiple ways which are not nearly overlapping
    144175                                new Notification(tr("Multiple target ways, no common point found. Nothing was changed."))
    145176                                        .setIcon(JOptionPane.INFORMATION_MESSAGE)
Note: See TracChangeset for help on using the changeset viewer.