Ticket #5613: JOSM-SplitObjectAction.patch

File JOSM-SplitObjectAction.patch, 11.5 KB (added by anonymous, 15 years ago)
  • src/org/openstreetmap/josm/actions/SplitObjectAction.java

     
     1// License: GPL. Copyright 2007 by Immanuel Scholz and others
     2package org.openstreetmap.josm.actions;
     3
     4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
     5import static org.openstreetmap.josm.tools.I18n.tr;
     6import static org.openstreetmap.josm.tools.I18n.trn;
     7
     8import java.awt.event.ActionEvent;
     9import java.awt.event.KeyEvent;
     10import java.util.Collection;
     11import java.util.Collections;
     12import java.util.HashMap;
     13import java.util.HashSet;
     14import java.util.List;
     15import java.util.Map;
     16import java.util.Map.Entry;
     17
     18import javax.swing.JOptionPane;
     19
     20import org.openstreetmap.josm.Main;
     21import org.openstreetmap.josm.data.osm.Node;
     22import org.openstreetmap.josm.data.osm.OsmPrimitive;
     23import org.openstreetmap.josm.data.osm.Way;
     24import org.openstreetmap.josm.tools.Shortcut;
     25
     26/**
     27 * Splits a closed way (polygon) into two closed ways.
     28 *
     29 * The closed ways are just split at the selected nodes (which must be exactly two).
     30 * The nodes remain in their original order.
     31 *
     32 * This is similar to SplitWayAction with the addition that the split ways are closed
     33 * immediately.
     34 */
     35
     36public class SplitObjectAction extends JosmAction {
     37    /**
     38     * Create a new SplitObjectAction.
     39     */
     40    public SplitObjectAction() {
     41        super(tr("Split Object"), "splitobject", tr("Split an object at the selected nodes."),
     42                Shortcut.registerShortcut("tools:splitobject", tr("Tool: {0}", "Split Object"),
     43                        KeyEvent.VK_P, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
     44        putValue("help", ht("/Action/SplitObject"));
     45    }
     46
     47    /**
     48     * Called when the action is executed.
     49     *
     50     * This method performs an expensive check whether the selection clearly defines one
     51     * of the split actions outlined above, and if yes, calls the splitObject method.
     52     */
     53    public void actionPerformed(ActionEvent e) {
     54        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
     55
     56        List<Node> selectedNodes = OsmPrimitive.getFilteredList(selection, Node.class);
     57        List<Way> selectedWays = OsmPrimitive.getFilteredList(selection, Way.class);
     58
     59        if (!checkSelection(selection)) {
     60            JOptionPane.showMessageDialog(
     61                    Main.parent,
     62                    tr("The current selection cannot be used for splitting."),
     63                    tr("Warning"),
     64                    JOptionPane.WARNING_MESSAGE
     65            );
     66            return;
     67        }
     68
     69
     70        Way selectedWay = null;
     71        if (!selectedWays.isEmpty()){
     72            selectedWay = selectedWays.get(0);
     73        }
     74
     75        // If only nodes are selected, try to guess which way to split. This works if there
     76        // is exactly one way that all nodes are part of.
     77        if (selectedWay == null && !selectedNodes.isEmpty()) {
     78            Map<Way, Integer> wayOccurenceCounter = new HashMap<Way, Integer>();
     79            for (Node n : selectedNodes) {
     80                for (Way w : OsmPrimitive.getFilteredList(n.getReferrers(), Way.class)) {
     81                    if (!w.isUsable()) {
     82                        continue;
     83                    }
     84                    int last = w.getNodesCount() - 1;
     85                    // Only closed ways with at least four nodes
     86                    // (i.e. five members since the the first/last is listed twice)
     87                    // can be split into two objects
     88                    if (last <= 4 || !w.isClosed()) {
     89                        continue;
     90                    }
     91                    for (Node wn : w.getNodes()) {
     92                        if (n.equals(wn)) {
     93                            Integer old = wayOccurenceCounter.get(w);
     94                            wayOccurenceCounter.put(w, (old == null) ? 1 : old + 1);
     95                            break;
     96                        }
     97                    }
     98                }
     99            }
     100            if (wayOccurenceCounter.isEmpty()) {
     101                JOptionPane.showMessageDialog(Main.parent,
     102                        trn("The selected node is not in the middle of any way.",
     103                                "The selected nodes are not in the middle of any way.",
     104                                selectedNodes.size()),
     105                                tr("Warning"),
     106                                JOptionPane.WARNING_MESSAGE);
     107                return;
     108            }
     109
     110            for (Entry<Way, Integer> entry : wayOccurenceCounter.entrySet()) {
     111                if (entry.getValue().equals(selectedNodes.size())) {
     112                    if (selectedWay != null) {
     113                        JOptionPane.showMessageDialog(Main.parent,
     114                                trn("There is more than one way using the node you selected. Please select the way also.",
     115                                        "There is more than one way using the nodes you selected. Please select the way also.",
     116                                        selectedNodes.size()),
     117                                        tr("Warning"),
     118                                        JOptionPane.WARNING_MESSAGE);
     119                        return;
     120                    }
     121                    selectedWay = entry.getKey();
     122                }
     123            }
     124
     125            if (selectedWay == null) {
     126                JOptionPane.showMessageDialog(Main.parent,
     127                        tr("The selected nodes do not share the same way."),
     128                        tr("Warning"),
     129                        JOptionPane.WARNING_MESSAGE);
     130                return;
     131            }
     132
     133            // If a way and nodes are selected, verify that the nodes
     134            // are part of the way and that the way is closed.
     135        } else if (selectedWay != null && !selectedNodes.isEmpty()) {
     136            if (!selectedWay.isClosed()) {
     137                JOptionPane.showMessageDialog(Main.parent,
     138                        tr("The selected way is not closed."),
     139                        tr("Warning"),
     140                        JOptionPane.WARNING_MESSAGE);
     141                return;
     142            }
     143            HashSet<Node> nds = new HashSet<Node>(selectedNodes);
     144            nds.removeAll(selectedWay.getNodes());
     145            if (!nds.isEmpty()) {
     146                JOptionPane.showMessageDialog(Main.parent,
     147                        trn("The selected way does not contain the selected node.",
     148                                "The selected way does not contain all the selected nodes.",
     149                                selectedNodes.size()),
     150                                tr("Warning"),
     151                                JOptionPane.WARNING_MESSAGE);
     152                return;
     153            }
     154        }
     155
     156        // we're guaranteed to have two nodes
     157        Node node1 = selectedNodes.get(0);
     158        int nodeIndex1 = -1;
     159        Node node2 = selectedNodes.get(1);
     160        int nodeIndex2 = -1;
     161        int i = 0;
     162        for (Node wn : selectedWay.getNodes()) {
     163            if (nodeIndex1 == -1 && wn.equals(node1)) {
     164                nodeIndex1 = i;
     165            } else if (nodeIndex2 == -1 && wn.equals(node2)) {
     166                nodeIndex2 = i;
     167            }
     168            i++;
     169        }
     170        // both nodes aren't allowed to be consecutive
     171        if (nodeIndex1 == nodeIndex2 + 1 ||
     172                nodeIndex2 == nodeIndex1 + 1 ||
     173                // minus 2 because we've a circular way where
     174                // the penultimate node is the last unique one
     175                (nodeIndex1 == 0 && nodeIndex2 == selectedWay.getNodesCount() - 2) ||
     176                (nodeIndex2 == 0 && nodeIndex1 == selectedWay.getNodesCount() - 2)) {
     177            JOptionPane.showMessageDialog(Main.parent,
     178                    tr("The selected nodes can not be consecutive nodes in the object."),
     179                    tr("Warning"),
     180                    JOptionPane.WARNING_MESSAGE);
     181            return;
     182        }
     183
     184        List<List<Node>> wayChunks = SplitWayAction.buildSplitChunks(selectedWay, selectedNodes);
     185        //        List<List<Node>> wayChunks = buildSplitChunks(selectedWay, selectedNodes);
     186        if (wayChunks != null) {
     187            // close the chunks
     188            for (List<Node> wayChunk : wayChunks) {
     189                wayChunk.add(wayChunk.get(0));
     190            }
     191            SplitWayAction.SplitWayResult result = SplitWayAction.splitWay(getEditLayer(), selectedWay, wayChunks, Collections.<OsmPrimitive>emptyList());
     192            //            SplitObjectResult result = splitObject(getEditLayer(),selectedWay, wayChunks);
     193            Main.main.undoRedo.add(result.getCommand());
     194            getCurrentDataSet().setSelected(result.getNewSelection());
     195        }
     196    }
     197
     198    /**
     199     * Checks if the selection consists of something we can work with.
     200     * Checks only if the number and type of items selected looks good;
     201     * does not check whether the selected items are really a valid
     202     * input for splitting (this would be too expensive to be carried
     203     * out from the selectionChanged listener).
     204     */
     205    private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
     206        boolean way = false;
     207        int node = 0;
     208        for (OsmPrimitive p : selection) {
     209            if (p instanceof Way && !way) {
     210                way = true;
     211            } else if (p instanceof Node) {
     212                node++;
     213            } else
     214                return false;
     215        }
     216        return node == 2;
     217    }
     218
     219    @Override
     220    protected void updateEnabledState() {
     221        if (getCurrentDataSet() == null) {
     222            setEnabled(false);
     223        } else {
     224            updateEnabledState(getCurrentDataSet().getSelected());
     225        }
     226    }
     227
     228    @Override
     229    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
     230        if (selection == null) {
     231            setEnabled(false);
     232            return;
     233        }
     234        setEnabled(checkSelection(selection));
     235    }
     236}
  • src/org/openstreetmap/josm/gui/MainMenu.java

     
    6262import org.openstreetmap.josm.actions.SelectAllAction;
    6363import org.openstreetmap.josm.actions.ShowStatusReportAction;
    6464import org.openstreetmap.josm.actions.SimplifyWayAction;
     65import org.openstreetmap.josm.actions.SplitObjectAction;
    6566import org.openstreetmap.josm.actions.SplitWayAction;
    6667import org.openstreetmap.josm.actions.ToggleGPXLinesAction;
    6768import org.openstreetmap.josm.actions.UnGlueAction;
     
    137138
    138139    /* Tools menu */
    139140    public final JosmAction splitWay = new SplitWayAction();
     141    public final JosmAction splitObject = new SplitObjectAction();
    140142    public final JosmAction combineWay = new CombineWayAction();
    141143    public final JosmAction reverseWay = new ReverseWayAction();
    142144    public final JosmAction alignInCircle = new AlignInCircleAction();
     
    297299        add(presetsMenu, presetSearchAction);
    298300
    299301        add(toolsMenu, splitWay);
     302        add(toolsMenu, splitObject);
    300303        add(toolsMenu, combineWay);
    301304        toolsMenu.addSeparator();
    302305        add(toolsMenu, reverseWay);