Ignore:
Timestamp:
2009-09-13T12:33:18+02:00 (15 years ago)
Author:
Gubaer
Message:

fixed #3469: Merge nodes produce bad results

File:
1 edited

Legend:

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

    r2102 r2113  
    1010import java.util.HashSet;
    1111import java.util.LinkedList;
     12import java.util.List;
    1213import java.util.Set;
    1314
     
    150151
    151152    /**
    152      * Merges the nodes in <code>node</code> onto one of the nodes. Uses the dataset
    153      * managed by <code>layer</code> as reference. <code>backreferences</code> is precomputed
    154      * collection of all parent/child references in the dataset.
    155      *
    156      * @param layer layer the reference data layer. Must not be null.
    157      * @param backreferences if null, backreferneces are first computed from layer.data; otherwise
    158      *    backreferences.getSource() == layer.data must hold
    159      * @param nodes the collection of nodes. Ignored if null.
    160      * @param targetNode the target node the collection of nodes is merged to. Must not be null.
    161      * @throw IllegalArgumentException thrown if layer is null
    162      * @throw IllegalArgumentException thrown if  backreferences.getSource() != layer.data
    163      */
    164     public static Command mergeNodes(OsmDataLayer layer, BackreferencedDataSet backreferences, Collection<Node> nodes, Node targetNode) {
    165         if (layer == null)
    166             throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "nodes"));
    167         if (targetNode == null)
    168             throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "targetNode"));
    169         if (nodes == null)
    170             return null;
    171         if (backreferences == null) {
    172             backreferences = new BackreferencedDataSet(layer.data);
    173             backreferences.build();
    174         }
    175 
    176         Set<RelationToChildReference> relationToNodeReferences = backreferences.getRelationToChildReferences(nodes);
    177 
    178         // build the tag collection
    179         //
    180         TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes);
    181         completeTagCollectionWithMissingTags(nodeTags, nodes);
    182         TagCollection nodeTagsToEdit = new TagCollection(nodeTags);
    183         completeTagCollectionForEditing(nodeTagsToEdit);
    184 
    185         // launch a conflict resolution dialog, if necessary
    186         //
    187         CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
    188         dialog.getTagConflictResolverModel().populate(nodeTagsToEdit);
    189         dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences);
    190         dialog.setTargetPrimitive(targetNode);
    191         dialog.prepareDefaultDecisions();
    192         if (! nodeTags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) {
    193             dialog.setVisible(true);
    194             if (dialog.isCancelled())
    195                 return null;
    196         }
    197         LinkedList<Command> cmds = new LinkedList<Command>();
    198 
    199         // the nodes we will have to delete
    200         //
    201         Collection<OsmPrimitive> nodesToDelete = new HashSet<OsmPrimitive>(nodes);
    202         nodesToDelete.remove(targetNode);
    203 
    204         // change the ways referring to at least one of the merge nodes
    205         //
    206         Collection<Way> waysToDelete= new HashSet<Way>();
    207         for (Way w : OsmPrimitive.getFilteredList(backreferences.getParents(nodesToDelete), Way.class)) {
    208             // OK - this way contains one or more nodes to change
     153     * Fixes the parent ways referring to one of the nodes.
     154     *
     155     * Replies null, if the ways could not be fixed, i.e. because a way would have to be delted
     156     * which is referred to by a relation.
     157     *
     158     * @param backreferences the backreference data set
     159     * @param nodesToDelete the collection of nodes to be deleted
     160     * @param targetNode the target node the other nodes are merged to
     161     * @return a list of command; null, the ways could not be fixed
     162     */
     163    protected static List<Command> fixParentWays(BackreferencedDataSet backreferences, Collection<OsmPrimitive> nodesToDelete, Node targetNode) {
     164        List<Command> cmds = new ArrayList<Command>();
     165        Set<Way> waysToDelete = new HashSet<Way>();
     166
     167        for (Way w: OsmPrimitive.getFilteredList(backreferences.getParents(nodesToDelete), Way.class)) {
    209168            ArrayList<Node> newNodes = new ArrayList<Node>(w.getNodesCount());
    210169            for (Node n: w.getNodes()) {
    211                 if (! nodesToDelete.contains(n)) {
     170                if (! nodesToDelete.contains(n) && n != targetNode) {
    212171                    newNodes.add(n);
     172                } else if (newNodes.isEmpty()) {
     173                    newNodes.add(targetNode);
     174                } else if (newNodes.get(newNodes.size()-1) != targetNode) {
     175                    // make sure we collapse a sequence of deleted nodes
     176                    // to exactly one occurrence of the merged target node
     177                    //
     178                    newNodes.add(targetNode);
    213179                } else {
    214                     newNodes.add(targetNode);
     180                    // drop the node
    215181                }
    216182            }
     
    236202            }
    237203        }
     204        return cmds;
     205    }
     206
     207    /**
     208     * Merges the nodes in <code>node</code> onto one of the nodes. Uses the dataset
     209     * managed by <code>layer</code> as reference. <code>backreferences</code> is precomputed
     210     * collection of all parent/child references in the dataset.
     211     *
     212     * @param layer layer the reference data layer. Must not be null.
     213     * @param backreferences if null, backreferneces are first computed from layer.data; otherwise
     214     *    backreferences.getSource() == layer.data must hold
     215     * @param nodes the collection of nodes. Ignored if null.
     216     * @param targetNode the target node the collection of nodes is merged to. Must not be null.
     217     * @throw IllegalArgumentException thrown if layer is null
     218     * @throw IllegalArgumentException thrown if  backreferences.getSource() != layer.data
     219     */
     220    public static Command mergeNodes(OsmDataLayer layer, BackreferencedDataSet backreferences, Collection<Node> nodes, Node targetNode) {
     221        if (layer == null)
     222            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "nodes"));
     223        if (targetNode == null)
     224            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "targetNode"));
     225        if (nodes == null)
     226            return null;
     227        if (backreferences == null) {
     228            backreferences = new BackreferencedDataSet(layer.data);
     229            backreferences.build();
     230        }
     231
     232        Set<RelationToChildReference> relationToNodeReferences = backreferences.getRelationToChildReferences(nodes);
     233
     234        // build the tag collection
     235        //
     236        TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes);
     237        completeTagCollectionWithMissingTags(nodeTags, nodes);
     238        TagCollection nodeTagsToEdit = new TagCollection(nodeTags);
     239        completeTagCollectionForEditing(nodeTagsToEdit);
     240
     241        // launch a conflict resolution dialog, if necessary
     242        //
     243        CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
     244        dialog.getTagConflictResolverModel().populate(nodeTagsToEdit);
     245        dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences);
     246        dialog.setTargetPrimitive(targetNode);
     247        dialog.prepareDefaultDecisions();
     248        if (! nodeTags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) {
     249            dialog.setVisible(true);
     250            if (dialog.isCancelled())
     251                return null;
     252        }
     253        LinkedList<Command> cmds = new LinkedList<Command>();
     254
     255        // the nodes we will have to delete
     256        //
     257        Collection<OsmPrimitive> nodesToDelete = new HashSet<OsmPrimitive>(nodes);
     258        nodesToDelete.remove(targetNode);
     259
     260        // change the ways referring to at least one of the merge nodes
     261        //
     262        Collection<Way> waysToDelete= new HashSet<Way>();
     263        List<Command> wayFixCommands = fixParentWays(
     264                backreferences,
     265                nodesToDelete,
     266                targetNode
     267        );
     268        if (wayFixCommands == null)
     269            return null;
     270        else {
     271            cmds.addAll(wayFixCommands);
     272        }
    238273
    239274        // build the commands
Note: See TracChangeset for help on using the changeset viewer.