// License: GPL. For details, see LICENSE file. package buildings_tools; import static org.openstreetmap.josm.tools.I18n.tr; import static org.openstreetmap.josm.tools.I18n.trn; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.AbstractMap; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import javax.swing.JOptionPane; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.JosmAction; import org.openstreetmap.josm.command.ChangeCommand; import org.openstreetmap.josm.command.ChangePropertyCommand; import org.openstreetmap.josm.command.Command; import org.openstreetmap.josm.command.DeleteCommand; import org.openstreetmap.josm.command.SequenceCommand; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.Notification; import org.openstreetmap.josm.tools.Geometry; import org.openstreetmap.josm.tools.Shortcut; @SuppressWarnings("serial") public class MergeAddrPointsAction extends JosmAction { public MergeAddrPointsAction() { super(tr("Merge address points"), "mergeaddr", tr("Move tags from address nodes inside buildings to building ways"), Shortcut.registerShortcut("edit:mergeaddrpoints", tr("Edit: {0}", tr("Merge address points")), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE), true); } @Override public void actionPerformed(ActionEvent arg0) { if (!isEnabled()) return; Collection<OsmPrimitive> selection = getLayerManager().getEditDataSet().getSelected(); if (selection.isEmpty()) { new Notification(tr("Select both address nodes and building ways to merge")) .setIcon(JOptionPane.INFORMATION_MESSAGE).show(); return; } List<Node> addrNodes = new LinkedList<>(); List<Way> buildings = new LinkedList<>(); scanSelection: for (OsmPrimitive p : selection) { if (p.getType() == OsmPrimitiveType.NODE) { for (OsmPrimitive r : p.getReferrers()) { if (r.getType() == OsmPrimitiveType.WAY) continue scanSelection; // Don't use nodes if they're referenced by ways } for (String key : p.getKeys().keySet()) { if (key.startsWith("addr:")) { addrNodes.add((Node) p); // Found address node break; } } } else if (p.getType() == OsmPrimitiveType.WAY && p.getKeys().containsKey("building")) buildings.add((Way) p); } if (addrNodes.isEmpty()) { new Notification(tr("No address nodes found in the selection")) .setIcon(JOptionPane.ERROR_MESSAGE).show(); return; } if (buildings.isEmpty()) { new Notification(tr("No building ways found in the selection")) .setIcon(JOptionPane.ERROR_MESSAGE).show(); return; } List<Command> cmds = new LinkedList<>(); int multi = 0; int conflicts = 0; buildingsLoop: for (Way w : buildings) { Node mergeNode = null; for (Node n : addrNodes) { if (Geometry.nodeInsidePolygon(n, w.getNodes())) if (mergeNode != null) { multi++; continue buildingsLoop; // Multiple address nodes inside // one building -- skipping } else mergeNode = n; } if (mergeNode != null) { boolean hasConflicts = false; AbstractMap<String, String> tags = new HashMap<>(); for (Entry<String, String> entry : mergeNode.getKeys().entrySet()) { String newValue = entry.getValue(); if (newValue == null) continue; String oldValue = w.getKeys().get(entry.getKey()); if (!newValue.equals(oldValue)) { if (oldValue == null) { tags.put(entry.getKey(), newValue); } else hasConflicts = true; } } if (hasConflicts) conflicts++; if (!tags.isEmpty()) cmds.add(new ChangePropertyCommand(Collections.singleton(w), tags)); if (!hasConflicts) { for (OsmPrimitive p : mergeNode.getReferrers()) { Relation r = (Relation) p; Relation rnew = new Relation(r); for (int i = 0; i < r.getMembersCount(); i++) { RelationMember member = r.getMember(i); if (mergeNode.equals(member.getMember())) { rnew.removeMember(i); rnew.addMember(i, new RelationMember(member.getRole(), w)); } } cmds.add(new ChangeCommand(r, rnew)); } cmds.add(new DeleteCommand(mergeNode)); } } } if (multi != 0) new Notification(trn("There is {0} building with multiple address nodes inside", "There are {0} buildings with multiple address nodes inside", multi, multi)) .setIcon(JOptionPane.WARNING_MESSAGE).show(); if (conflicts != 0) new Notification(trn("There is {0} building with address conflicts", "There are {0} buildings with address conflicts", conflicts, conflicts)) .setIcon(JOptionPane.WARNING_MESSAGE).show(); if (cmds.isEmpty() && multi == 0 && conflicts == 0) new Notification(tr("No address nodes inside buildings found")) .setIcon(JOptionPane.INFORMATION_MESSAGE).show(); if (!cmds.isEmpty()) Main.main.undoRedo.add(new SequenceCommand("Merge addresses", cmds)); } @Override protected void updateEnabledState() { setEnabled(getLayerManager().getEditDataSet() != null); } }