source: subversion/applications/editors/josm/plugins/buildings_tools/src/org/openstreetmap/josm/plugins/buildings_tools/MergeAddrPointsAction.java

Last change on this file was 34982, checked in by gerdp, 27 hours ago

fix #17625: IAE: "Node is already deleted" at buildings_tools.MergeAddrPointsAction?.actionPerformed

File size: 7.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.plugins.buildings_tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.LinkedList;
14import java.util.List;
15import java.util.Map;
16import java.util.Map.Entry;
17import java.util.Set;
18
19import javax.swing.JOptionPane;
20
21import org.openstreetmap.josm.actions.JosmAction;
22import org.openstreetmap.josm.command.ChangeCommand;
23import org.openstreetmap.josm.command.ChangePropertyCommand;
24import org.openstreetmap.josm.command.Command;
25import org.openstreetmap.josm.command.DeleteCommand;
26import org.openstreetmap.josm.command.SequenceCommand;
27import org.openstreetmap.josm.data.UndoRedoHandler;
28import org.openstreetmap.josm.data.osm.Node;
29import org.openstreetmap.josm.data.osm.OsmPrimitive;
30import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
31import org.openstreetmap.josm.data.osm.Relation;
32import org.openstreetmap.josm.data.osm.RelationMember;
33import org.openstreetmap.josm.data.osm.Way;
34import org.openstreetmap.josm.gui.Notification;
35import org.openstreetmap.josm.tools.Geometry;
36import org.openstreetmap.josm.tools.Shortcut;
37
38public class MergeAddrPointsAction extends JosmAction {
39
40    public MergeAddrPointsAction() {
41        super(tr("Merge address points"), "mergeaddr",
42                tr("Move tags from address nodes inside buildings to building ways"),
43                Shortcut.registerShortcut("edit:mergeaddrpoints", tr("Edit: {0}", tr("Merge address points")),
44                        KeyEvent.CHAR_UNDEFINED, Shortcut.NONE),
45                true);
46    }
47
48    @Override
49    public void actionPerformed(ActionEvent arg0) {
50        if (!isEnabled())
51            return;
52        Collection<OsmPrimitive> selection = getLayerManager().getEditDataSet().getSelected();
53        if (selection.isEmpty()) {
54            new Notification(tr("Select both address nodes and building ways to merge"))
55                    .setIcon(JOptionPane.INFORMATION_MESSAGE).show();
56            return;
57        }
58        List<Node> addrNodes = new LinkedList<>();
59        List<Way> buildings = new LinkedList<>();
60        for (OsmPrimitive p : selection) {
61            if (p.getType() == OsmPrimitiveType.NODE) {
62                boolean refsOK = true;
63                for (OsmPrimitive r : p.getReferrers()) {
64                    if (r.getType() == OsmPrimitiveType.WAY) {
65                        // Don't use nodes if they're referenced by ways
66                        refsOK = false;
67                        break;
68                    }
69                }
70                if (!refsOK)
71                    continue;
72                for (String key : p.getKeys().keySet()) {
73                    if (key.startsWith("addr:")) {
74                        addrNodes.add((Node) p); // Found address node
75                        break;
76                    }
77                }
78            } else if (p.getType() == OsmPrimitiveType.WAY && p.getKeys().containsKey("building"))
79                buildings.add((Way) p);
80        }
81        if (addrNodes.isEmpty()) {
82            new Notification(tr("No address nodes found in the selection"))
83                    .setIcon(JOptionPane.ERROR_MESSAGE).show();
84            return;
85        }
86        if (buildings.isEmpty()) {
87            new Notification(tr("No building ways found in the selection"))
88                    .setIcon(JOptionPane.ERROR_MESSAGE).show();
89            return;
90        }
91
92        // find nodes covered by more than one building, see #17625
93        Map<Node, Way> nodeToWayMap = new HashMap<>();
94        Set<Way> overlappingWays = new HashSet<>();
95        for (Way w : buildings) {
96            for (Node n : addrNodes) {
97                if (Geometry.nodeInsidePolygon(n, w.getNodes())) {
98                    Way old = nodeToWayMap.put(n, w);
99                    if (old != null) {
100                        overlappingWays.add(w);
101                        overlappingWays.add(old);
102                    }
103                }
104            }
105        }
106        buildings.removeAll(overlappingWays);
107
108        List<Command> cmds = new LinkedList<>();
109        int multi = 0;
110        int conflicts = 0;
111
112        for (Way w : buildings) {
113            Node mergeNode = null;
114            int oldMulti = multi;
115            for (Node n : addrNodes) {
116                if (Geometry.nodeInsidePolygon(n, w.getNodes()))
117                    if (mergeNode != null) {
118                        multi++;
119                        // Multiple address nodes inside one building --
120                        // skipping
121                        break;
122                    } else
123                        mergeNode = n;
124            }
125            if (oldMulti != multi)
126                continue;
127            if (mergeNode != null) {
128                boolean hasConflicts = false;
129                Map<String, String> tags = new HashMap<>();
130                for (Entry<String, String> entry : mergeNode.getKeys().entrySet()) {
131                    String newValue = entry.getValue();
132                    if (newValue == null)
133                        continue;
134                    String oldValue = w.getKeys().get(entry.getKey());
135                    if (!newValue.equals(oldValue)) {
136                        if (oldValue == null) {
137                            tags.put(entry.getKey(), newValue);
138                        } else
139                            hasConflicts = true;
140                    }
141                }
142                if (hasConflicts)
143                    conflicts++;
144                if (!tags.isEmpty())
145                    cmds.add(new ChangePropertyCommand(Collections.singleton(w), tags));
146                if (!hasConflicts) {
147                    for (OsmPrimitive p : mergeNode.getReferrers()) {
148                        Relation r = (Relation) p;
149                        Relation rnew = new Relation(r);
150                        for (int i = 0; i < r.getMembersCount(); i++) {
151                            RelationMember member = r.getMember(i);
152                            if (mergeNode.equals(member.getMember())) {
153                                rnew.removeMember(i);
154                                rnew.addMember(i, new RelationMember(member.getRole(), w));
155                            }
156                        }
157                        cmds.add(new ChangeCommand(r, rnew));
158                    }
159                    cmds.add(new DeleteCommand(mergeNode));
160                }
161            }
162        }
163        if (multi != 0)
164            new Notification(trn("There is {0} building with multiple address nodes inside",
165                    "There are {0} buildings with multiple address nodes inside", multi, multi))
166                    .setIcon(JOptionPane.WARNING_MESSAGE).show();
167        if (conflicts != 0)
168            new Notification(trn("There is {0} building with address conflicts",
169                            "There are {0} buildings with address conflicts", conflicts, conflicts))
170                    .setIcon(JOptionPane.WARNING_MESSAGE).show();
171        if (!overlappingWays.isEmpty())
172            new Notification(tr("There are {0} buildings covering the same address node", overlappingWays.size()))
173                    .setIcon(JOptionPane.WARNING_MESSAGE).show();
174        if (cmds.isEmpty() && multi == 0 && conflicts == 0 && overlappingWays.isEmpty())
175            new Notification(tr("No address nodes inside buildings found"))
176                    .setIcon(JOptionPane.INFORMATION_MESSAGE).show();
177        if (!cmds.isEmpty())
178            UndoRedoHandler.getInstance().add(new SequenceCommand("Merge addresses", cmds));
179    }
180
181    @Override
182    protected void updateEnabledState() {
183        setEnabled(getLayerManager().getEditDataSet() != null);
184    }
185}
Note: See TracBrowser for help on using the repository browser.