Ignore:
Timestamp:
2007-09-24T01:36:24+02:00 (18 years ago)
Author:
framm
Message:

This commit is a manual merge of all changes that have been made to
the intermediate "core_0.5" branch on the main OSM repository,
bevore JOSM was moved to openstreetmap.de.

Changes incorporated here:

r4464@svn.openstreetmap.org
r4466@svn.openstreetmap.org
r4468@svn.openstreetmap.org
r4469@svn.openstreetmap.org
r4479@svn.openstreetmap.org

Location:
branch/0.5/src/org/openstreetmap/josm/actions
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • branch/0.5/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r301 r329  
    1010import java.util.HashMap;
    1111import java.util.LinkedList;
     12import java.util.List;
     13import java.util.ListIterator;
     14import java.util.ArrayList;
    1215import java.util.Map;
    1316import java.util.Set;
     
    1518import java.util.TreeSet;
    1619import java.util.Map.Entry;
     20import java.util.HashSet;
    1721
    1822import javax.swing.Box;
     
    3135import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3236import org.openstreetmap.josm.data.osm.Way;
     37import org.openstreetmap.josm.data.osm.Node;
     38import org.openstreetmap.josm.data.osm.NodePair;
    3339import org.openstreetmap.josm.tools.GBC;
    3440
     
    4248        public CombineWayAction() {
    4349                super(tr("Combine Way"), "combineway", tr("Combine several ways into one."), KeyEvent.VK_C, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK, true);
    44                 DataSet.listeners.add(this);
     50                DataSet.selListeners.add(this);
    4551        }
    4652
     
    6874                }
    6975               
    70                 Way oldWay = selectedWays.poll();
    71                 Way newWay = new Way(oldWay);
    72                 LinkedList<Command> cmds = new LinkedList<Command>();
     76                // Battle plan:
     77                //  1. Split the ways into small chunks of 2 nodes and weed out
     78                //         duplicates.
     79                //  2. Take a chunk and see if others could be appended or prepended,
     80                //         if so, do it and remove it from the list of remaining chunks.
     81                //         Rather, rinse, repeat.
     82                //  3. If this algorithm does not produce a single way,
     83                //     complain to the user.
     84                //  4. Profit!
    7385               
    74                 for (Way w : selectedWays)
    75                         newWay.segments.addAll(w.segments);
     86                HashSet<NodePair> chunkSet = new HashSet<NodePair>();
     87                for (Way w : selectedWays) {
     88                        if (w.nodes.size() == 0) continue;
     89                        Node lastN = null;
     90                        for (Node n : w.nodes) {
     91                                if (lastN == null) {
     92                                        lastN = n;
     93                                        continue;
     94                                }
     95                                chunkSet.add(new NodePair(lastN, n));
     96                                lastN = n;
     97                        }
     98                }
     99                LinkedList<NodePair> chunks = new LinkedList<NodePair>(chunkSet);
     100
     101                if (chunks.isEmpty()) {
     102                        JOptionPane.showMessageDialog(Main.parent, tr("All the ways were empty"));
     103                        return;
     104                }
     105
     106                List<Node> nodeList = chunks.poll().toArrayList();
     107                while (!chunks.isEmpty()) {
     108                        ListIterator<NodePair> it = chunks.listIterator();
     109                        boolean foundChunk = false;
     110                        while (it.hasNext()) {
     111                                NodePair curChunk = it.next();
     112                                if (curChunk.a == nodeList.get(nodeList.size() - 1)) { // append
     113                                        nodeList.add(curChunk.b);
     114                                        foundChunk = true;
     115                                } else if (curChunk.b == nodeList.get(0)) { // prepend
     116                                        nodeList.add(0, curChunk.a);
     117                                        foundChunk = true;
     118                                }
     119                                if (foundChunk) {
     120                                        it.remove();
     121                                        break;
     122                                }
     123                        }
     124                        if (!foundChunk) break;
     125                }
     126
     127                if (!chunks.isEmpty()) {
     128                        JOptionPane.showMessageDialog(Main.parent,
     129                                tr("Could not combine ways (Hint: ways have to point into the same direction)"));
     130                        return;
     131                }
     132
     133                Way newWay = new Way(selectedWays.get(0));
     134                newWay.nodes.clear();
     135                newWay.nodes.addAll(nodeList);
    76136               
    77137                // display conflict dialog
     
    97157                }
    98158
    99                 cmds.add(new DeleteCommand(selectedWays));
    100                 cmds.add(new ChangeCommand(oldWay, newWay));
     159                LinkedList<Command> cmds = new LinkedList<Command>();
     160                cmds.add(new DeleteCommand(selectedWays.subList(1, selectedWays.size())));
     161                cmds.add(new ChangeCommand(selectedWays.peek(), newWay));
    101162                Main.main.undoRedo.add(new SequenceCommand(tr("Combine {0} ways", selectedWays.size()), cmds));
    102                 Main.ds.setSelected(oldWay);
     163                Main.ds.setSelected(selectedWays.peek());
    103164        }
    104165
  • branch/0.5/src/org/openstreetmap/josm/actions/DownloadIncompleteAction.java

    r298 r329  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 package org.openstreetmap.josm.actions;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 import static org.openstreetmap.josm.tools.I18n.trn;
    6 
    7 import java.awt.event.ActionEvent;
    8 import java.awt.event.InputEvent;
    9 import java.awt.event.KeyEvent;
    10 import java.io.IOException;
    11 import java.util.Collection;
    12 import java.util.HashSet;
    13 
    14 import javax.swing.JOptionPane;
    15 
    16 import org.openstreetmap.josm.Main;
    17 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    18 import org.openstreetmap.josm.data.osm.Way;
    19 import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
    20 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    21 import org.openstreetmap.josm.io.IncompleteDownloader;
    22 import org.xml.sax.SAXException;
    23 
    24 /**
    25  * Action that opens a connection to the osm server and download map data.
    26  *
    27  * An dialog is displayed asking the user to specify a rectangle to grab.
    28  * The url and account settings from the preferences are used.
    29  *
    30  * @author imi
    31  */
    32 public class DownloadIncompleteAction extends JosmAction {
    33 
    34         /**
    35          * Open the download dialog and download the data.
    36          * Run in the worker thread.
    37          */
    38         private final class DownloadTask extends PleaseWaitRunnable {
    39                 private IncompleteDownloader reader;
    40 
    41                 private DownloadTask(Collection<Way> toDownload) {
    42                         super(trn("Downloading {0} way", "Downloading {0} ways", toDownload.size(), toDownload.size()));
    43                         reader = new IncompleteDownloader(toDownload);
    44                 }
    45 
    46                 @Override public void realRun() throws IOException, SAXException {
    47                         reader.parse();
    48                 }
    49 
    50                 @Override protected void finish() {
    51                         MergeVisitor merger = new MergeVisitor(Main.ds, reader.data);
    52                         for (OsmPrimitive osm : reader.data.allPrimitives())
    53                                 osm.visit(merger);
    54                         Main.parent.repaint();
    55                 }
    56 
    57                 @Override protected void cancel() {
    58                         reader.cancel();
    59                 }
    60         }
    61 
    62         public DownloadIncompleteAction() {
    63                 super(tr("Download incomplete objects"), "downloadincomplete", tr("Download all (selected) incomplete ways from the OSM server."), KeyEvent.VK_D, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK | InputEvent.ALT_DOWN_MASK, true);
    64         }
    65 
    66         public void actionPerformed(ActionEvent e) {
    67                 Collection<Way> ways = new HashSet<Way>();
    68                 for (Way w : Main.ds.ways)
    69                         if (w.isIncomplete() && w.selected)
    70                                 ways.add(w);
    71                 if (ways.isEmpty()) {
    72                         JOptionPane.showMessageDialog(Main.parent, tr("Please select an incomplete way."));
    73                         return;
    74                 }
    75                 if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(Main.parent, tr("Download {0} incomplete ways?", ways.size()), tr("Download?"), JOptionPane.YES_NO_OPTION))
    76                         return;
    77                 PleaseWaitRunnable task = new DownloadTask(ways);
    78                 Main.worker.execute(task);
    79         }
    80 }
  • branch/0.5/src/org/openstreetmap/josm/actions/ReverseSegmentAction.java

    r301 r329  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 /**
    3  *
    4  */
    5 package org.openstreetmap.josm.actions;
    6 
    7 import static org.openstreetmap.josm.tools.I18n.tr;
    8 
    9 import java.awt.event.ActionEvent;
    10 import java.awt.event.KeyEvent;
    11 import java.util.Collection;
    12 import java.util.LinkedList;
    13 
    14 import javax.swing.JOptionPane;
    15 
    16 import org.openstreetmap.josm.Main;
    17 import org.openstreetmap.josm.command.ChangeCommand;
    18 import org.openstreetmap.josm.command.Command;
    19 import org.openstreetmap.josm.command.SequenceCommand;
    20 import org.openstreetmap.josm.data.osm.Node;
    21 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    22 import org.openstreetmap.josm.data.osm.Segment;
    23 import org.openstreetmap.josm.data.osm.Way;
    24 import org.openstreetmap.josm.data.osm.visitor.Visitor;
    25 
    26 public final class ReverseSegmentAction extends JosmAction {
    27 
    28     public ReverseSegmentAction() {
    29         super(tr("Reverse segments"), "segmentflip", tr("Reverse the direction of all selected Segments."), KeyEvent.VK_R, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK, true);
    30     }
    31 
    32         public void actionPerformed(ActionEvent e) {
    33         final Collection<Segment> sel = new LinkedList<Segment>();
    34         new Visitor(){
    35                         public void visit(Node n)    {}
    36                         public void visit(Segment s) {sel.add(s);}
    37                         public void visit(Way w)     {sel.addAll(w.segments);}
    38                         public void visitAll() {
    39                                 for (OsmPrimitive osm : Main.ds.getSelected())
    40                                         osm.visit(this);
    41                         }
    42         }.visitAll();
    43 
    44         if (sel.isEmpty()) {
    45                 JOptionPane.showMessageDialog(Main.parent, tr("Please select at least one segment."));
    46                 return;
    47         }
    48         Collection<Command> c = new LinkedList<Command>();
    49         for (Segment s : sel) {
    50                 Segment snew = new Segment(s);
    51                 Node n = snew.from;
    52                 snew.from = snew.to;
    53                 snew.to = n;
    54                 c.add(new ChangeCommand(s, snew));
    55         }
    56         Main.main.undoRedo.add(new SequenceCommand(tr("Reverse Segments"), c));
    57         Main.map.repaint();
    58     }
    59 }
  • branch/0.5/src/org/openstreetmap/josm/actions/SplitWayAction.java

    r301 r329  
    1616import java.util.LinkedList;
    1717import java.util.List;
     18import java.util.Set;
    1819import java.util.Map.Entry;
    1920
     
    2728import org.openstreetmap.josm.data.SelectionChangedListener;
    2829import org.openstreetmap.josm.data.osm.DataSet;
     30import org.openstreetmap.josm.data.osm.Relation;
    2931import org.openstreetmap.josm.data.osm.Node;
    3032import org.openstreetmap.josm.data.osm.OsmPrimitive;
    31 import org.openstreetmap.josm.data.osm.Segment;
    3233import org.openstreetmap.josm.data.osm.Way;
    3334import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
     
    3536
    3637/**
    37  * Splits a way into multiple ways (all identical except the segments
    38  * belonging to the way).
     38 * Splits a way into multiple ways (all identical except for their node list).
    3939 *
    40  * Various split modes are used depending on what is selected.
    41  *
    42  * 1. One or more NODES (and, optionally, also one way) selected:
    43  *
    44  * (All nodes must be part of the same way. If a way is also selected, that way
    45  * must contain all selected nodes.)
    46  *
    47  * Way is split AT the node(s) into contiguous ways. If the original contained
    48  * one or more parts that were not reachable from any of the nodes, they form an
    49  * extra new way. Examples (numbers are unselected nodes, letters are selected
    50  * nodes)
    51  *
    52  * 1---A---2  becomes  1---A and A---2
    53  *
    54  * 1---A---2---B---3  becomes  1---A and A---2---B and B---3
    55  * 
    56  *     2                   
    57  *     |                   
    58  * 1---A---3  becomes  1---A and 2---A and A---3
    59  *
    60  * 1---A---2  3---4  becomes  1---A and A---2 and 3---4
    61  *
    62  * If the selected node(s) do not clearly define the way that is to be split,
    63  * then the way must be selected for disambiguation (e.g. you have two ways,
    64  * 1---2---3 and 4---2---5, and select node 2, then you must also select the
    65  * way you want to split).
    66  *
    67  * This function will result in at least two ways, unless the selected node is
    68  * at the end of the way AND the way is contiguous, which will lead to an error
    69  * message.
    70  *
    71  * After executing the operation, the selection will be cleared.
    72  *
    73  * 2. One or more SEGMENTS (and, optionally, also one way) selected:
    74  *
    75  * (All segments must be part of the same way)
    76  *
    77  * The way is split in a fashion that makes a new way from the selected segments,
    78  * i.e. the selected segments are removed from the way to form a new one.
    79  *
    80  * This function will result in exactly two ways.
    81  *
    82  * If splitting the segments out of the way makes a non-contiguous part from
    83  * something that was contiguous before, the action is aborted and an error
    84  * message is shown.
    85  *
    86  * 3. Exactly one WAY selected
    87  *
    88  * If the way is contiguous, you will get an error message. If the way is not
    89  * contiguous it is split it into 2...n contiguous ways.
     40 * Ways are just split at the selected nodes.  The nodes remain in their
     41 * original order.  Selected nodes at the end of a way are ignored.
    9042 */
    9143
     
    9446        private Way selectedWay;
    9547        private List<Node> selectedNodes;
    96         private List<Segment> selectedSegments;
    9748
    9849        /**
     
    10152        public SplitWayAction() {
    10253                super(tr("Split Way"), "splitway", tr("Split a way at the selected node."), KeyEvent.VK_P, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK, true);
    103                 DataSet.listeners.add(this);
     54                DataSet.selListeners.add(this);
    10455        }
    10556
     
    12172                selectedWay = null;
    12273                selectedNodes = null;
    123                 selectedSegments = null;
    12474
    12575                Visitor splitVisitor = new Visitor(){
     
    12979                                selectedNodes.add(n);
    13080            }
    131                         public void visit(Segment s) {
    132                                 if (selectedSegments == null)
    133                                         selectedSegments = new LinkedList<Segment>();
    134                                 selectedSegments.add(s);
    135             }
    13681                        public void visit(Way w) {
    13782                                selectedWay = w;
    13883            }
     84                        public void visit(Relation e) {
     85                                // enties are not considered
     86                        }
    13987                };
    14088               
     
    14896                        for (Node n : selectedNodes) {
    14997                                for (Way w : Main.ds.ways) {
    150                                         for (Segment s : w.segments) {
    151                                                 if (n.equals(s.from) || n.equals(s.to)) {
     98                                        for (Node wn : w.nodes) {
     99                                                if (n.equals(wn)) {
    152100                                                        Integer old = wayOccurenceCounter.get(w);
    153101                                                        wayOccurenceCounter.put(w, (old == null) ? 1 : old+1);
     
    183131
    184132                        HashSet<Node> nds = new HashSet<Node>(selectedNodes);
    185                         for (Segment s : selectedWay.segments) {
    186                                 nds.remove(s.from);
    187                                 nds.remove(s.to);
     133                        for (Node n : selectedWay.nodes) {
     134                                nds.remove(n);
    188135                        }
    189136                        if (!nds.isEmpty()) {
     
    193140                                return;
    194141                        }
    195 
    196                         // If only segments are selected, guess which way to use.
    197                 } else if (selectedWay == null && selectedSegments != null) {
    198 
    199                         HashMap<Way, Integer> wayOccurenceCounter = new HashMap<Way, Integer>();
    200                         for (Segment s : selectedSegments) {
    201                                 for (Way w : Main.ds.ways) {
    202                                         if (w.segments.contains(s)) {
    203                                                 Integer old = wayOccurenceCounter.get(w);
    204                                                 wayOccurenceCounter.put(w, (old == null) ? 1 : old+1);
    205                                                 break;
    206                                         }
    207                                 }
    208                         }
    209                         if (wayOccurenceCounter.isEmpty()) {
    210                                 JOptionPane.showMessageDialog(Main.parent,
    211                                                 trn("The selected segment is not part of any way.",
    212                                                                 "The selected segments are not part of any way.", selectedSegments.size()));
    213                                 return;
    214                         }
    215 
    216                         for (Entry<Way, Integer> entry : wayOccurenceCounter.entrySet()) {
    217                                 if (entry.getValue().equals(selectedSegments.size())) {
    218                                         if (selectedWay != null) {
    219                                                 JOptionPane.showMessageDialog(Main.parent,
    220                                                                 trn("There is more than one way using the segment you selected. Please select the way also.",
    221                                                                                 "There is more than one way using the segments you selected. Please select the way also.", selectedSegments.size()));
    222                                                 return;
    223                                         }
    224                                         selectedWay = entry.getKey();
    225                                 }
    226                         }
    227 
    228                         if (selectedWay == null) {
    229                                 JOptionPane.showMessageDialog(Main.parent, tr("The selected segments do not share the same way."));
    230                                 return;
    231                         }
    232 
    233                         // If a way and segments are selected, verify that the segments are part of the way.
    234                 } else if (selectedWay != null && selectedSegments != null) {
    235 
    236                         if (!selectedWay.segments.containsAll(selectedSegments)) {
    237                                 JOptionPane.showMessageDialog(Main.parent,
    238                                                 trn("The selected way does not contain the selected segment.",
    239                                                                 "The selected way does not contain all the selected segments.", selectedSegments.size()));
    240                                 return;
    241                         }
    242                 }
    243 
    244                 // finally check if the selected way is complete.
    245                 if (selectedWay.isIncomplete()) {
    246                         JOptionPane.showMessageDialog(Main.parent, tr("Warning: This way is incomplete. Try to download it before splitting."));
    247                         return;
    248142                }
    249143
     
    261155        private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
    262156                boolean way = false;
    263                 boolean segment = false;
    264157                boolean node = false;
    265158                for (OsmPrimitive p : selection) {
    266                         if (p instanceof Way && !way)
     159                        if (p instanceof Way && !way) {
    267160                                way = true;
    268                         else if (p instanceof Node && !segment)
     161                        } else if (p instanceof Node) {
    269162                                node = true;
    270                         else if (p instanceof Segment && !node)
    271                                 segment = true;
    272                         else
     163                        } else {
    273164                                return false;
    274165                }
    275                 return way || segment || node;
     166                }
     167                return node;
    276168        }
    277169
     
    279171         * Split a way into two or more parts, starting at a selected node.
    280172         *
     173         * FIXME: what do the following "arguments" refer to?
    281174         * @param way the way to split
    282175         * @param nodes the node(s) to split the way at; must be part of the way.
    283176         */
    284177        private void splitWay() {
    285 
    286                 // The basic idea is to first divide all segments forming this way into
    287                 // groups, and later form new ways according to the groups. Initally,
    288                 // all segments are copied into allSegments, and then gradually removed
    289                 // from there as new groups are built.
    290 
    291                 LinkedList<Segment> allSegments = new LinkedList<Segment>();
    292                 allSegments.addAll(selectedWay.segments);
    293                 List<List<Segment>> segmentSets = new ArrayList<List<Segment>>();
    294 
    295                 if (selectedNodes != null) {
    296 
    297                         // This is the "split at node" mode.
    298 
    299                         boolean split = true;
    300                         Segment splitSeg = null;
    301                         while (split) {
    302                                 split = false;
    303 
    304                                 // Look for a "split segment". A split segment is any segment
    305                                 // that touches one of the split nodes and has not yet been
    306                                 // assigned to one of the segment groups.
    307                                 for (Segment s : allSegments) {
    308                                         for (Node node : selectedNodes) {
    309                                                 if (s.from.equals(node) || s.to.equals(node)) {
    310                                                         split = true;
    311                                                         splitSeg = s;
    312                                                         break;
    313                                                 }
    314                                         }
    315                                         if (split)
    316                                                 break;
     178                // We take our way's list of nodes and copy them to a way chunk (a
     179                // list of nodes).  Whenever we stumble upon a selected node, we start
     180                // a new way chunk.
     181
     182                Set<Node> nodeSet = new HashSet<Node>(selectedNodes);
     183                List<List<Node>> wayChunks = new LinkedList<List<Node>>();
     184                List<Node> currentWayChunk = new ArrayList<Node>();
     185                wayChunks.add(currentWayChunk);
     186
     187                Iterator<Node> it = selectedWay.nodes.iterator();
     188                while (it.hasNext()) {
     189                        Node currentNode = it.next();
     190                        boolean atEndOfWay = currentWayChunk.isEmpty() || !it.hasNext();
     191                        currentWayChunk.add(currentNode);
     192                        if (nodeSet.contains(currentNode) && !atEndOfWay) {
     193                                currentWayChunk = new ArrayList<Node>();
     194                                currentWayChunk.add(currentNode);
     195                                wayChunks.add(currentWayChunk);
    317196                                }
    318 
    319                                 // If a split segment was found, move this segment and all segments
    320                                 // connected to it into a new segment group, stopping only if we
    321                                 // reach another split node. Segment moving is done recursively by
    322                                 // the moveSegments method.
    323                                 if (split) {
    324                                         LinkedList<Segment> subSegments = new LinkedList<Segment>();
    325                                         moveSegments(allSegments, subSegments, splitSeg, selectedNodes);
    326                                         segmentSets.add(subSegments);
    327                                 }
    328 
    329                                 // The loop continues until no more split segments were found.
    330                                 // Nb. not all segments touching a split node are split segments;
    331                                 // e.g.
    332                                 //
    333                                 //     2       4
    334                                 //     |       |
    335                                 // 1---A---3---C---5
    336                                 //
    337                                 // This way will be split into 5 ways (1---A,2---A,A---3---C,4---C,
    338                                 // C---5). Depending on which is processed first, either A---3 becomes
    339                                 // a split segment and 3---C is moved as a connecting segment, or vice
    340                                 // versa. The result is, of course, the same but this explains why we
    341                                 // cannot simply start a new way for each segment connecting to a split
    342                                 // node.
    343                         }
    344 
    345                 } else if (selectedSegments != null) {
    346 
    347                         // This is the "split segments" mode. It is quite easy as the segments to
    348                         // remove are already explicitly selected, but some restrictions have to
    349                         // be observed to make sure that no non-contiguous parts are created.
    350 
    351                         // first create a "scratch" copy of the full segment list and move all
    352                         // segments connected to the first selected segment into a temporary list.
    353                         LinkedList<Segment> copyOfAllSegments = new LinkedList<Segment>(allSegments);
    354                         LinkedList<Segment> partThatContainsSegments = new LinkedList<Segment>();
    355                         moveSegments(copyOfAllSegments, partThatContainsSegments, selectedSegments.get(0), null);
    356 
    357                         // this list must now contain ALL selected segments; otherwise, segments
    358                         // from unconnected parts of the way have been selected and this is not allowed
    359                         // as it would create a new non-contiguous way.
    360                         if (!partThatContainsSegments.containsAll(selectedSegments)) {
    361                                 JOptionPane.showMessageDialog(Main.parent, tr("The selected segments are not in the same contiguous part of the way."));                               
    362                                 return;         
    363                         }
    364 
    365                         // if the contiguous part that contains the segments becomes non-contiguous
    366                         // after the removal of the segments, that is also an error.
    367                         partThatContainsSegments.removeAll(selectedSegments);
    368                         if (!partThatContainsSegments.isEmpty()) {
    369                                 LinkedList<Segment> contiguousSubpart = new LinkedList<Segment>();
    370                                 moveSegments(partThatContainsSegments, contiguousSubpart, partThatContainsSegments.get(0), null);
    371                                 // if partThatContainsSegments was contiguous before, it will now be empty as all segments
    372                                 // connected to the first segment therein have been moved
    373                                 if (!partThatContainsSegments.isEmpty()) {
    374                                         JOptionPane.showMessageDialog(Main.parent, tr("Removing the selected segments would make a part of the way non-contiguous."));                         
    375                                         return;                         
    376                                 }
    377                         }
    378 
    379                         ArrayList<Segment> subSegments = new ArrayList<Segment>();
    380                         subSegments.addAll(selectedSegments);
    381                         allSegments.removeAll(selectedSegments);
    382                         segmentSets.add(subSegments);
    383 
    384                 } else {
    385 
    386                         // This is the "split way into contiguous parts" mode.
    387                         // We use a similar mechanism to splitting at nodes, but we do not
    388                         // select split segments. Instead, we randomly grab a segment out
    389                         // of the way and move all connecting segments to a new group. If
    390                         // segments remain in the original way, we repeat the procedure.
    391 
    392                         while (!allSegments.isEmpty()) {
    393                                 LinkedList<Segment> subSegments = new LinkedList<Segment>();
    394                                 moveSegments(allSegments, subSegments, allSegments.get(0), null);
    395                                 segmentSets.add(subSegments);
    396                         }                       
    397                 }
    398 
    399                 // We now have a number of segment groups.
    400 
    401                 // If segments remain in allSegments, this means that they were not reachable
    402                 // from any of the split nodes, and they will be made into an extra way.
    403                 if (!allSegments.isEmpty()) {
    404                         segmentSets.add(allSegments);
    405                 }
    406 
    407                 // If we do not have at least two parts, then the way was circular or the node(s)
    408                 // were at one end of the way. User error ;-)
    409                 if (segmentSets.size() < 2) {
    410                         if (selectedNodes != null) {
    411                                 JOptionPane.showMessageDialog(Main.parent, tr("The way cannot be split at the selected node. (Hint: To split circular ways, select two nodes.)"));
    412                         } else {
    413                                 JOptionPane.showMessageDialog(Main.parent, tr("The way cannot be split because it is contiguous. (Hint: To split at a node, select that node.)"));                             
    414                         }
     197                        }
     198
     199                if (wayChunks.size() < 2) {
     200                        JOptionPane.showMessageDialog(Main.parent, tr("The way cannot be split at the selected nodes. (Hint: Select nodes in the middle of the way.)"));
    415201                        return;
    416202                }
    417203
    418                 // sort the list of segment lists according to their number of elements, so that
    419                 // the biggest part of the way comes first. That way, we will "change" the largest
    420                 // part of the way by removing a few segments, and "add" new, smaller ways; looks
    421                 // nicer.
    422                 Collections.sort(segmentSets, new Comparator<Collection<Segment>>() {
    423                         public int compare(Collection<Segment> a, Collection<Segment> b) {
    424                                 if (a.size() < b.size())
    425                                         return 1;
    426                                 if (b.size() < a.size())
    427                                         return -1;
    428                                 return 0;
    429                         }
    430                 });
    431 
    432                 // build a list of commands, and also a list of ways
    433                 Collection<Command> commandList = new ArrayList<Command>(segmentSets.size());
    434                 Collection<Way> newSelection = new ArrayList<Way>(segmentSets.size());
    435                 Iterator<List<Segment>> segsIt = segmentSets.iterator();
     204                // build a list of commands, and also a new selection list
     205                Collection<Command> commandList = new ArrayList<Command>(wayChunks.size());
     206                Collection<Way> newSelection = new ArrayList<Way>(wayChunks.size());
    436207               
    437                 // the first is always a change to the existing way;
     208                Iterator<List<Node>> chunkIt = wayChunks.iterator();
     209               
     210                // First, change the original way
    438211                Way changedWay = new Way(selectedWay);
    439                 changedWay.segments.clear();
    440                 changedWay.segments.addAll(segsIt.next());
     212                changedWay.nodes.clear();
     213                changedWay.nodes.addAll(chunkIt.next());
    441214                commandList.add(new ChangeCommand(selectedWay, changedWay));
    442215                newSelection.add(selectedWay);
    443216
    444                 // and commands 1...n are additions of new ways.
    445                 while (segsIt.hasNext()) {
     217                // Second, create new ways
     218                while (chunkIt.hasNext()) {
    446219                        Way wayToAdd = new Way();
    447220                        if (selectedWay.keys != null)
    448221                                wayToAdd.keys = new HashMap<String, String>(selectedWay.keys);
    449                         wayToAdd.segments.clear();
    450                         wayToAdd.segments.addAll(segsIt.next());
     222                        wayToAdd.nodes.addAll(chunkIt.next());
    451223                        commandList.add(new AddCommand(wayToAdd));
    452224                        newSelection.add(wayToAdd);
     
    455227                NameVisitor v = new NameVisitor();
    456228                v.visit(selectedWay);
    457                 Main.main.undoRedo.add(new SequenceCommand(tr("Split way {0} into {1} parts",v.name, segmentSets.size()), commandList));
     229                Main.main.undoRedo.add(
     230                        new SequenceCommand(tr("Split way {0} into {1} parts",
     231                                v.name, wayChunks.size()),
     232                        commandList));
    458233                Main.ds.setSelected(newSelection);
    459234        }
     
    465240                setEnabled(checkSelection(newSelection));
    466241        }
    467 
    468         /**
    469          * Move contiguous segments from one collection to another. The given segment is moved first, and
    470          * then the procedure is recursively called for all segments that connect to the first segment at
    471          * either end.
    472          *
    473          * @param source the source collection
    474          * @param destination the destination collection
    475          * @param start the first segment to be moved
    476          * @param stopNodes collection of nodes which should be considered end points for moving (may be null).
    477          */
    478         private void moveSegments(Collection<Segment> source, LinkedList<Segment> destination, Segment start, Collection<Node> stopNodes) {
    479                 source.remove(start);
    480                 if (destination.isEmpty() || destination.iterator().next().from.equals(start.to))
    481                         destination.addFirst(start);
    482                 else
    483                         destination.addLast(start);
    484                 Segment moveSeg = start;
    485                 while(moveSeg != null) {
    486                         moveSeg = null;
    487 
    488                         for (Node node : new Node[] { start.from, start.to }) {
    489                                 if (stopNodes != null && stopNodes.contains(node))
    490                                         continue;
    491                                 for (Segment sourceSeg : source) {
    492                                         if (sourceSeg.from.equals(node) || sourceSeg.to.equals(node)) {
    493                                                 moveSeg = sourceSeg;
    494                                                 break;
    495                                         }
    496                                 }
    497                                 if (moveSeg != null)
    498                                         break;
    499                         }
    500                         if (moveSeg != null) {
    501                                 moveSegments(source, destination, moveSeg, stopNodes);
    502                         }
    503                 }
    504         }
    505242}
  • branch/0.5/src/org/openstreetmap/josm/actions/mapmode/AddNodeAction.java

    r314 r329  
    2424import org.openstreetmap.josm.data.osm.Node;
    2525import org.openstreetmap.josm.data.osm.OsmPrimitive;
    26 import org.openstreetmap.josm.data.osm.Segment;
    2726import org.openstreetmap.josm.data.osm.Way;
     27import org.openstreetmap.josm.data.osm.WaySegment;
    2828import org.openstreetmap.josm.gui.MapFrame;
    2929import org.openstreetmap.josm.tools.ImageProvider;
     
    3333 * and there is it. Nothing more, nothing less.
    3434 *
     35 * FIXME: "nothing more, nothing less" is a bit out-of-date
     36 *
    3537 * Newly created nodes are selected. Shift modifier does not cancel the old
    3638 * selection as usual.
     
    4143public class AddNodeAction extends MapMode {
    4244
    43         enum Mode {node, nodesegment, autonode}
     45        enum Mode {node, nodeway, autonode}
    4446        private final Mode mode;
    4547
     
    4951                        putValue("help", "Action/AddNode");
    5052                        actions.add(new AddNodeAction(mf,tr("Add node"), Mode.node, tr("Add a new node to the map")));
    51                         actions.add(new AddNodeAction(mf, tr("Add node into segment"), Mode.nodesegment,tr( "Add a node into an existing segment")));
    52                         actions.add(new AddNodeAction(mf, tr("Add node and connect"), Mode.autonode,tr( "Add a node and connect it to the selected node (with CTRL: add node into segment; with SHIFT: re-use existing node)")));
     53                        actions.add(new AddNodeAction(mf, tr("Add node into way"), Mode.nodeway,tr( "Add a node into an existing way")));
     54                        actions.add(new AddNodeAction(mf, tr("Add node and connect"), Mode.autonode,tr( "Add a node and connect it to the selected node (with CTRL: add node into way; with SHIFT: re-use existing node)")));
    5355                        setCurrent(0);
    5456                }
     
    8385         * position.
    8486         *
    85          * If in nodesegment mode, add the node to the line segment by splitting the
    86          * segment. The new created segment will be inserted in every way the segment
    87          * was part of.
     87         * If in nodeway mode, insert the node into the way.
    8888         */
    8989        @Override public void mouseClicked(MouseEvent e) {
     
    9898
    9999                Command c = new AddCommand(n);
    100                 if (mode == Mode.nodesegment) {
    101                         Segment s = Main.map.mapView.getNearestSegment(e.getPoint());
    102                         if (s == null)
     100                if (mode == Mode.nodeway) {
     101                        WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint());
     102                        if (ws == null)
    103103                                return;
    104104                       
    105105                        // see if another segment is also near
    106                         Segment other = Main.map.mapView.getNearestSegment(e.getPoint(), Collections.singleton(s));
     106                        WaySegment other = Main.map.mapView.getNearestWaySegment(e.getPoint(),
     107                                Collections.singleton(ws));
     108
     109                        Node n1 = ws.way.nodes.get(ws.lowerIndex),
     110                                n2 = ws.way.nodes.get(ws.lowerIndex + 1);
    107111
    108112                        if (other == null && (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) == 0) {
    109113                                // moving the new point to the perpendicular point
    110                                 // FIXME: when two segments are split, should move the new point to the
     114                                // FIXME: when two way segments are split, should move the new point to the
    111115                                // intersection point!
    112                                 EastNorth A = s.from.eastNorth;
    113                                 EastNorth B = s.to.eastNorth;
     116                                EastNorth A = n1.eastNorth;
     117                                EastNorth B = n2.eastNorth;
    114118                                double ab = A.distance(B);
    115119                                double nb = n.eastNorth.distance(B);
     
    124128                       
    125129                        // split the first segment
    126                         splitSegmentAtNode(s, n, cmds);
     130                        splitWaySegmentAtNode(ws, n, cmds);
    127131                       
    128132                        // if a second segment was found, split that as well
    129                         if (other != null) splitSegmentAtNode(other, n, cmds);
     133                        if (other != null) splitWaySegmentAtNode(other, n, cmds);
    130134
    131135                        c = new SequenceCommand(tr((other == null) ?
    132                                 "Add node into segment" : "Add common node into two segments"), cmds);
     136                                "Add node into way" : "Add common node into two ways"), cmds);
    133137                }
    134138
     
    136140                if (mode == Mode.autonode) {
    137141
    138                         Segment insertInto = null;
     142                        WaySegment insertInto = null;
    139143                        Node reuseNode = null;
    140144                       
    141                         // If CTRL is held, insert the node into a potentially existing segment
     145                        // If CTRL is held, insert the node into a potentially existing way segment
    142146                        if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
    143                                 insertInto = Main.map.mapView.getNearestSegment(e.getPoint());
     147                                insertInto = Main.map.mapView.getNearestWaySegment(e.getPoint());
     148                                if (insertInto == null) System.err.println("Couldn't find nearby way segment");
    144149                                if (insertInto == null)
    145150                                        return;
     
    150155                        // continuation of the "add node and connect" stuff)
    151156                        else if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
    152                                 OsmPrimitive clicked = Main.map.mapView.getNearest(e.getPoint(), false);
     157                                OsmPrimitive clicked = Main.map.mapView.getNearest(e.getPoint());
    153158                                if (clicked == null || !(clicked instanceof Node))
    154159                                        return;
     
    159164                        if (selection.size() == 1 && selection.iterator().next() instanceof Node) {
    160165                                Node n1 = (Node)selection.iterator().next();
     166
    161167                                Collection<Command> cmds = new LinkedList<Command>();
    162168                               
    163169                                if (reuseNode != null) {
    164170                                        // in re-use node mode, n1 must not be identical to clicked node
     171                                        if (n1 == reuseNode) System.err.println("n1 == reuseNode");
    165172                                        if (n1 == reuseNode) return;
    166173                                        // replace newly created node with existing node
     
    171178                                }
    172179                               
    173                                 Segment s = new Segment(n1, n);
    174                                
     180                                /* Keep track of the way we change, it might be the same into
     181                                 * which we insert the node.
     182                                 */
     183                                Way newInsertInto = null;
    175184                                if (insertInto != null)
    176                                         splitSegmentAtNode(insertInto, n, cmds);
    177                                
    178                                 cmds.add(new AddCommand(s));                   
     185                                        newInsertInto = splitWaySegmentAtNode(insertInto, n, cmds);
    179186
    180187                                Way way = getWayForNode(n1);
    181                                 if (way != null) {
    182                                         Way newWay = new Way(way);
    183                                         if (way.segments.get(0).from == n1) {
    184                                                 Node tmp = s.from;
    185                                                 s.from = s.to;
    186                                                 s.to = tmp;
    187                                                 newWay.segments.add(0, s);
    188                                         } else
    189                                                 newWay.segments.add(s);
    190                                         cmds.add(new ChangeCommand(way, newWay));
    191                                 }
    192 
    193                                 c = new SequenceCommand(tr((insertInto == null) ? "Add node and connect" : "Add node into segment and connect"), cmds);
     188                                if (way == null) {
     189                                        way = new Way();
     190                                        way.nodes.add(n1);
     191                                        cmds.add(new AddCommand(way));
     192                                } else {
     193                                        if (insertInto != null && way == insertInto.way) {
     194                                                way = newInsertInto;
     195                                        } else {
     196                                                Way wnew = new Way(way);
     197                                                cmds.add(new ChangeCommand(way, wnew));
     198                                                way = wnew;
     199                                        }
     200                                }
     201
     202                                if (way.nodes.get(way.nodes.size() - 1) == n1) {
     203                                        way.nodes.add(n);
     204                                } else {
     205                                        way.nodes.add(0, n);
     206                                }
     207
     208                                c = new SequenceCommand(tr((insertInto == null) ? "Add node and connect" : "Add node into way and connect"), cmds);
    194209                        }       
    195210                }               
     
    201216       
    202217        /**
    203          * @return If the node is part of exactly one way, return this.
     218         * @return If the node is the end of exactly one way, return this.
    204219         *      <code>null</code> otherwise.
    205220         */
     
    207222                Way way = null;
    208223                for (Way w : Main.ds.ways) {
    209                         for (Segment s : w.segments) {
    210                                 if (s.from == n || s.to == n) {
     224                        int i = w.nodes.indexOf(n);
     225                        if (i == -1) continue;
     226                        if (i == 0 || i == w.nodes.size() - 1) {
    211227                                        if (way != null)
    212228                                                return null;
    213                                         if (s.from == s.to)
    214                                                 return null;
    215229                                        way = w;
    216230                                }
    217231                        }
    218                 }
    219232                return way;
    220233        }
    221234       
    222         private void splitSegmentAtNode(Segment s, Node n, Collection<Command> cmds) {
    223                 Segment s1 = new Segment(s);
    224                 s1.to = n;
    225                 Segment s2 = new Segment(s.from, s.to);
    226                 s2.from = n;
    227                 if (s.keys != null)
    228                         s2.keys = new HashMap<String, String>(s.keys);
    229 
    230                 cmds.add(new ChangeCommand(s, s1));
    231                 cmds.add(new AddCommand(s2));
    232 
    233                 // Add the segment to every way
    234                 for (Way wold : Main.ds.ways) {
    235                         if (wold.segments.contains(s)) {
    236                                 Way wnew = new Way(wold);
    237                                 Collection<Segment> segs = new ArrayList<Segment>(wnew.segments);
    238                                 wnew.segments.clear();
    239                                 for (Segment waySeg : segs) {
    240                                         wnew.segments.add(waySeg);
    241                                         if (waySeg == s)
    242                                                 wnew.segments.add(s2);
    243                                 }
    244                                 cmds.add(new ChangeCommand(wold, wnew));
    245                         }
    246                 }
     235        private Way splitWaySegmentAtNode(WaySegment ws, Node n, Collection<Command> cmds) {
     236                Way wnew = new Way(ws.way);
     237                wnew.nodes.add(ws.lowerIndex + 1, n);
     238                cmds.add(new ChangeCommand(ws.way, wnew));
     239                return wnew;
    247240        }
    248241}
  • branch/0.5/src/org/openstreetmap/josm/actions/mapmode/AddSegmentAction.java

    r301 r329  
    1515import org.openstreetmap.josm.Main;
    1616import org.openstreetmap.josm.command.AddCommand;
     17import org.openstreetmap.josm.command.ChangeCommand;
    1718import org.openstreetmap.josm.data.osm.Node;
    1819import org.openstreetmap.josm.data.osm.OsmPrimitive;
    19 import org.openstreetmap.josm.data.osm.Segment;
     20import org.openstreetmap.josm.data.osm.Way;
    2021import org.openstreetmap.josm.gui.MapFrame;
    2122import org.openstreetmap.josm.tools.ImageProvider;
     
    5152         */
    5253        public AddSegmentAction(MapFrame mapFrame) {
    53                 super(tr("Add segment"),
     54                super(tr("Connect two nodes"),
    5455                                "addsegment",
    55                                 tr("Add a segment between two nodes."),
     56                                tr("Connect two nodes using ways."),
    5657                                KeyEvent.VK_G,
    5758                                mapFrame,
     
    7273        }
    7374
    74        
     75        /**
     76         * Called when user hits space bar while dragging.
     77         */
    7578        @Override public void actionPerformed(ActionEvent e) {
    7679                super.actionPerformed(e);
     
    8588                        return;
    8689
    87                 OsmPrimitive clicked = Main.map.mapView.getNearest(e.getPoint(), true);
    88                 if (clicked == null || !(clicked instanceof Node))
    89                         return;
     90                Node clicked = Main.map.mapView.getNearestNode(e.getPoint());
     91                if (clicked == null) return;
    9092
    9193                drawHint(false);
    92                 first = second = (Node)clicked;
     94                first = second = clicked;
    9395        }
    9496
     
    101103                        return;
    102104
    103                 OsmPrimitive clicked = Main.map.mapView.getNearest(e.getPoint(), (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0);
    104                 if (clicked == null || clicked == second || !(clicked instanceof Node))
    105                         return;
     105                Node hovered = Main.map.mapView.getNearestNode(e.getPoint());
     106                if (hovered == second) return;
    106107
    107108                drawHint(false);
    108 
    109                 second = (Node)clicked;
     109                second = hovered;
    110110                drawHint(true);
    111111        }
     
    116116        @Override public void mouseReleased(MouseEvent e) {
    117117                if (e.getButton() == MouseEvent.BUTTON1) {
     118                        drawHint(false);
    118119                        makeSegment();
    119                         first = null; // release segment drawing
     120                        first = null;
    120121                }
     122        }
     123
     124        /**
     125         * @return If the node is the end of exactly one way, return this.
     126         *      <code>null</code> otherwise.
     127         */
     128        private Way getWayForNode(Node n) {
     129                Way way = null;
     130                for (Way w : Main.ds.ways) {
     131                        int i = w.nodes.indexOf(n);
     132                        if (i == -1) continue;
     133                        if (i == 0 || i == w.nodes.size() - 1) {
     134                                if (way != null)
     135                                        return null;
     136                                way = w;
     137                        }
     138                }
     139                return way;
    121140        }
    122141
     
    126145         */
    127146        private void makeSegment() {
    128                 if (first == null || second == null) {
    129                         first = null;
    130                         second = null;
    131                         return;
    132                 }
    133 
    134                 drawHint(false);
     147                Node n1 = first;
     148                Node n2 = second;
    135149               
    136                 Node start = first;
    137                 Node end = second;
     150                // this is to allow continued segment drawing by hitting the space bar
     151                // at every intermediate node
    138152                first = second;
    139153                second = null;
     154
     155                if (n1 == null || n2 == null || n1 == n2) return;
     156
     157                Way w = getWayForNode(n1);
     158                Way wnew;
     159                Collection<OsmPrimitive> sel = Main.ds.getSelected();
    140160               
    141                 if (start != end) {
    142                         // try to find a segment
    143                         for (Segment ls : Main.ds.segments)
    144                                 if (!ls.deleted && ((start == ls.from && end == ls.to) || (end == ls.from && start == ls.to)))
    145                                         return; // already a segment here - be happy, do nothing.
    146 
    147                         Segment ls = new Segment(start, end);
    148                         Main.main.undoRedo.add(new AddCommand(ls));
    149                         Collection<OsmPrimitive> sel = Main.ds.getSelected();
    150                         sel.add(ls);
     161                if (w == null) {
     162                        // create a new way and add it to the current selection.
     163                        wnew = new Way();
     164                        wnew.nodes.add(n1);
     165                        wnew.nodes.add(n2);
     166                        Main.main.undoRedo.add(new AddCommand(wnew));
     167                        sel.add(wnew);
     168                        Main.ds.setSelected(sel);
     169                } else {
     170                        // extend an existing way; only add to current selection if
     171                        // it is not already in there.
     172                        wnew = new Way(w);
     173                        if (wnew.nodes.get(wnew.nodes.size() - 1) == n1) {
     174                                wnew.nodes.add(n2);
     175                        } else {
     176                                wnew.nodes.add(0, n2);
     177                        }
     178                        Main.main.undoRedo.add(new ChangeCommand(w, wnew));
     179                        // do not use wnew below; ChangeCommand only uses wnew as a
     180                        // message about changes to be done to w but will not replace w!
     181                        if (!sel.contains(w)) {
     182                                sel.add(w);
     183                        }
     184                        // do not move this into the if block above since it also
     185                        // fires the selection change event which is desired.
    151186                        Main.ds.setSelected(sel);
    152187                }
  • branch/0.5/src/org/openstreetmap/josm/actions/mapmode/AddWayAction.java

    r301 r329  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 package org.openstreetmap.josm.actions.mapmode;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 import static org.openstreetmap.josm.tools.I18n.trn;
    6 
    7 import java.awt.event.KeyEvent;
    8 import java.awt.event.MouseEvent;
    9 import java.util.Arrays;
    10 import java.util.Collection;
    11 import java.util.HashSet;
    12 import java.util.LinkedList;
    13 
    14 import javax.swing.JOptionPane;
    15 
    16 import org.openstreetmap.josm.Main;
    17 import org.openstreetmap.josm.actions.ReorderAction;
    18 import org.openstreetmap.josm.command.AddCommand;
    19 import org.openstreetmap.josm.command.ChangeCommand;
    20 import org.openstreetmap.josm.command.DeleteCommand;
    21 import org.openstreetmap.josm.data.SelectionChangedListener;
    22 import org.openstreetmap.josm.data.osm.DataSet;
    23 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    24 import org.openstreetmap.josm.data.osm.Segment;
    25 import org.openstreetmap.josm.data.osm.Way;
    26 import org.openstreetmap.josm.gui.MapFrame;
    27 import org.openstreetmap.josm.tools.ImageProvider;
    28 
    29 /**
    30  * Add a new way. The action is split into the first phase, where a new way get
    31  * created or selected and the second, where this way is modified.
    32  *
    33  * Way creation mode:
    34  * If there is a selection when the mode is entered, all segments in this
    35  * selection form a new way. All non-segment objects are deselected. If there
    36  * were ways selected, the user is asked whether to select all segments of these
    37  * ways or not, except there is exactly one way selected, which enter the
    38  * edit ways mode for this way immediatly.
    39  *
    40  * If there is no selection on entering, and the user clicks on an segment,
    41  * the way editing starts the with a new way and this segment. If the user click
    42  * on a way (not holding Alt down), then this way is edited in the way edit mode.
    43  *
    44  * Way editing mode:
    45  * The user can click on subsequent segments. If the segment belonged to the way
    46  * it get removed from the way. Elsewhere it get added to the way. JOSM try to add
    47  * the segment in the correct position. This is done by searching for connections
    48  * to the segment at its 'to' node which are also in the way. The segemnt is
    49  * inserted in the way as predecessor of the found segment (or at last segment, if
    50  * nothing found).
    51  *
    52  * @author imi
    53  */
    54 public class AddWayAction extends MapMode implements SelectionChangedListener {
    55         private Way way;
    56 
    57         /**
    58          * Create a new AddWayAction.
    59          * @param mapFrame The MapFrame this action belongs to.
    60          * @param followMode The mode to go into when finished creating a way.
    61          */
    62         public AddWayAction(MapFrame mapFrame) {
    63                 super(tr("Add Way"), "addway", tr("Add a new way to the data."), KeyEvent.VK_W, mapFrame, ImageProvider.getCursor("normal", "way"));
    64                 DataSet.listeners.add(this);
    65         }
    66 
    67         @Override public void enterMode() {
    68                 super.enterMode();
    69                 way = makeWay();
    70                 Main.ds.setSelected(way);
    71                 Main.map.mapView.addMouseListener(this);
    72         }
    73 
    74         @Override public void exitMode() {
    75                 super.exitMode();
    76                 way = null;
    77                 Main.map.mapView.removeMouseListener(this);
    78         }
    79 
    80         @Override public void mouseClicked(MouseEvent e) {
    81                 if (e.getButton() != MouseEvent.BUTTON1)
    82                         return;
    83 
    84                 Segment s = Main.map.mapView.getNearestSegment(e.getPoint());
    85                 if (s == null)
    86                         return;
    87 
    88                 // special case for initial selecting one way
    89                 if (way == null && (e.getModifiers() & MouseEvent.ALT_DOWN_MASK) == 0) {
    90                         Way w = Main.map.mapView.getNearestWay(e.getPoint());
    91                         if (w != null) {
    92                                 way = w;
    93                                 Main.ds.setSelected(way);
    94                                 for (Segment seg : way.segments) {
    95                                         if (seg.incomplete) {
    96                                                 JOptionPane.showMessageDialog(Main.parent,tr("Warning: This way is incomplete. Try to download it before adding segments."));
    97                                                 return;
    98                                         }
    99                                 }
    100                                 return;
    101                         }
    102                 }
    103 
    104                 if (way != null && way.segments.contains(s)) {
    105                         Way copy = new Way(way);
    106 
    107                         copy.segments.remove(s);
    108                         if (copy.segments.isEmpty()) {
    109                                 Main.main.undoRedo.add(new DeleteCommand(Arrays.asList(new OsmPrimitive[]{way})));
    110                                 way = null;
    111                         } else
    112                                 Main.main.undoRedo.add(new ChangeCommand(way, copy));
    113                 } else {
    114                         if (way == null) {
    115                                 way = new Way();
    116                                 way.segments.add(s);
    117                                 Main.main.undoRedo.add(new AddCommand(way));
    118                         } else {
    119                                 Way copy = new Way(way);
    120                                 int i;
    121                                 for (i = 0; i < way.segments.size(); ++i)
    122                                         if (way.segments.get(i).from == s.to)
    123                                                 break;
    124                                 copy.segments.add(i, s);
    125                                 Main.main.undoRedo.add(new ChangeCommand(way, copy));
    126                         }
    127                 }
    128                 Main.ds.setSelected(way);
    129         }
    130 
    131         /**
    132          * Form a way, either out of the (one) selected way or by creating a way over the selected
    133          * line segments.
    134          */
    135         private Way makeWay() {
    136                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    137                 if (selection.isEmpty())
    138                         return null;
    139 
    140                 if (selection.size() == 1 && selection.iterator().next() instanceof Way) {
    141                         Way way = (Way)selection.iterator().next();
    142                         for (Segment seg : way.segments) {
    143                                 if (seg.incomplete) {
    144                                         JOptionPane.showMessageDialog(Main.parent, tr("Warning: This way is incomplete. Try to download it before adding segments."));
    145                                         break;
    146                                 }
    147                         }
    148                         return way;
    149                 }
    150 
    151                 HashSet<Segment> segmentSet = new HashSet<Segment>();
    152                 int numberOfSelectedWays = 0;
    153                 for (OsmPrimitive osm : selection) {
    154                         if (osm instanceof Way)
    155                                 numberOfSelectedWays++;
    156                         else if (osm instanceof Segment)
    157                                 segmentSet.add((Segment)osm);
    158                 }
    159 
    160                 Way wayToAdd = null;
    161                 boolean reordered = false;
    162                 if (numberOfSelectedWays > 0) {
    163                         int answer = JOptionPane.showConfirmDialog(Main.parent,trn("{0} way has been selected.\nDo you wish to select all segments belonging to the way instead?","{0} ways have been selected.\nDo you wish to select all segments belonging to the ways instead?",numberOfSelectedWays,numberOfSelectedWays),tr("Add segments from ways"), JOptionPane.YES_NO_OPTION);
    164                         if (answer == JOptionPane.YES_OPTION) {
    165                                 for (OsmPrimitive osm : selection)
    166                                         if (osm instanceof Way)
    167                                                 segmentSet.addAll(((Way)osm).segments);
    168                         } else if (numberOfSelectedWays == 1) {
    169                                 answer = JOptionPane.showConfirmDialog(Main.parent,tr("Do you want to add all other selected segments to the one selected way?"),tr("Add segments to way?"), JOptionPane.YES_NO_OPTION);
    170                                 if (answer == JOptionPane.YES_OPTION) {
    171                                         for (OsmPrimitive osm : selection) {
    172                                                 if (osm instanceof Way) {
    173                                                         wayToAdd = (Way)osm;
    174                                                         answer = JOptionPane.showConfirmDialog(Main.parent,tr("Reorder all line segments?"), tr("Reorder?"), JOptionPane.YES_NO_CANCEL_OPTION);
    175                                                         if (answer == JOptionPane.CANCEL_OPTION)
    176                                                                 return wayToAdd;
    177                                                         if (answer == JOptionPane.YES_OPTION) {
    178                                                                 segmentSet.addAll(wayToAdd.segments);
    179                                                                 reordered = true;
    180                                                         } else
    181                                                                 segmentSet.removeAll(wayToAdd.segments);
    182                                                         break;
    183                                                 }
    184                                         }
    185                                 }
    186                         }
    187                 }
    188 
    189                 if (segmentSet.isEmpty())
    190                         return null;
    191 
    192                 LinkedList<Segment> rawSegments = new LinkedList<Segment>(segmentSet);
    193                 LinkedList<Segment> sortedSegments = ReorderAction.sortSegments(rawSegments, true);
    194 
    195                 if (wayToAdd != null) {
    196                         Way w = new Way(wayToAdd);
    197                         if (reordered)
    198                                 w.segments.clear();
    199                         w.segments.addAll(sortedSegments);
    200                         Main.main.undoRedo.add(new ChangeCommand(wayToAdd, w));
    201                         return wayToAdd;
    202                 }
    203 
    204                 if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(Main.parent,trn("Create a new way out of {0} segment?","Create a new way out of {0} segments?",sortedSegments.size(),sortedSegments.size()), tr("Create new way"), JOptionPane.YES_NO_OPTION))
    205                         return null;
    206 
    207                 Way w = new Way();
    208                 w.segments.addAll(sortedSegments);
    209                 Main.main.undoRedo.add(new AddCommand(w));
    210                 return w;
    211         }
    212 
    213         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    214                 if (newSelection.size() == 1) {
    215                         OsmPrimitive osm = newSelection.iterator().next();
    216                         way = osm instanceof Way ? (Way)osm : null;
    217                 } else
    218                         way = null;
    219     }
    220 }
  • branch/0.5/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java

    r301 r329  
    2424import org.openstreetmap.josm.data.osm.Node;
    2525import org.openstreetmap.josm.data.osm.OsmPrimitive;
    26 import org.openstreetmap.josm.data.osm.Segment;
    2726import org.openstreetmap.josm.data.osm.Way;
    2827import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
     
    3837 * @see #deleteWithReferences(OsmPrimitive)
    3938 *
    40  * Pressing Alt will select the way instead of a segment, as usual.
    41  *
    4239 * If the user did not press Ctrl and the object has any references, the user
    4340 * is informed and nothing is deleted.
     
    5754                super(tr("Delete"),
    5855                                "delete",
    59                                 tr("Delete nodes, streets or segments."),
     56                                tr("Delete nodes or ways."),
    6057                                KeyEvent.VK_D,
    6158                                mapFrame,
     
    9289                        return;
    9390               
    94                 OsmPrimitive sel = Main.map.mapView.getNearest(e.getPoint(), (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0);
     91                OsmPrimitive sel = Main.map.mapView.getNearest(e.getPoint());
    9592                if (sel == null)
    9693                        return;
     
    105102
    106103        /**
    107          * Delete the primitives and everything they references.
     104         * Delete the primitives and everything they reference.
    108105         *
    109          * If a node is deleted, the node and all segments, ways and areas
     106         * If a node is deleted, the node and all ways and relations
    110107         * the node is part of are deleted as well.
    111108         *
    112          * If a segment is deleted, all ways the segment is part of
    113          * are deleted as well. No nodes are deleted.
     109         * If a way is deleted, all relations the way is member of are also deleted.
    114110         *
    115          * If a way is deleted, only the way and no segments or nodes are
    116          * deleted.
    117          *
    118          * If an area is deleted, only the area gets deleted.
     111         * If a way is deleted, only the way and no nodes are deleted.
    119112         *
    120113         * @param selection The list of all object to be deleted.
     
    134127         * inform the user and do not delete.
    135128         *
    136          * If deleting a node which is part of exactly two segments, and both segments
    137          * have no conflicting keys, join them and remove the node.
    138          * If the two segments are part of the same way, remove the deleted segment
    139          * from the way.
     129         * If a node is to be deleted which is in the middle of exactly one way,
     130         * the node is removed from the way's node list and after that removed
     131         * itself.
    140132         *
    141133         * @param selection The objects to delete.
     
    149141                        if (!selection.containsAll(v.data)) {
    150142                                if (osm instanceof Node && joinIfPossible) {
    151                                         String reason = deleteNodeAndJoinSegment((Node)osm);
     143                                        String reason = deleteNodeAndJoinWay((Node)osm);
    152144                                        if (reason != null && msgBox) {
    153145                                                JOptionPane.showMessageDialog(Main.parent,tr("Cannot delete node.")+" "+reason);
     
    167159        }
    168160
    169         private String deleteNodeAndJoinSegment(Node n) {
    170                 ArrayList<Segment> segs = new ArrayList<Segment>(2);
    171                 for (Segment s : Main.ds.segments) {
    172                         if (!s.deleted && (s.from == n || s.to == n)) {
    173                                 if (segs.size() > 1)
    174                                         return tr("Used by more than two segments.");
    175                                 segs.add(s);
    176                         }
    177                 }
    178                 if (segs.size() != 2)
    179                         return tr("Used by only one segment.");
    180                 Segment seg1 = segs.get(0);
    181                 Segment seg2 = segs.get(1);
    182                 if (seg1.from == seg2.to) {
    183                         Segment s = seg1;
    184                         seg1 = seg2;
    185                         seg2 = s;
    186                 }
    187                 if (seg1.from == seg2.from || seg1.to == seg2.to)
    188                         return tr("Wrong direction of segments.");
    189                 for (Entry<String, String> e : seg1.entrySet())
    190                         if (seg2.keySet().contains(e.getKey()) && !seg2.get(e.getKey()).equals(e.getValue()))
    191                                 return tr("Conflicting keys");
    192                 ArrayList<Way> ways = new ArrayList<Way>(2);
     161        private String deleteNodeAndJoinWay(Node n) {
     162                ArrayList<Way> ways = new ArrayList<Way>(1);
    193163                for (Way w : Main.ds.ways) {
    194                         if (w.deleted)
    195                                 continue;
    196                         if ((w.segments.contains(seg1) && !w.segments.contains(seg2)) || (w.segments.contains(seg2) && !w.segments.contains(seg1)))
    197                                 return tr("Segments are part of different ways.");
    198                         if (w.segments.contains(seg1) && w.segments.contains(seg2))
     164                        if (!w.deleted && w.nodes.contains(n)) {
    199165                                ways.add(w);
    200166                }
    201                 Segment s = new Segment(seg1);
    202                 s.to = seg2.to;
    203                 if (s.keys == null)
    204                         s.keys = seg2.keys;
    205                 else if (seg2.keys != null)
    206                         s.keys.putAll(seg2.keys);
    207                 Collection<Command> cmds = new LinkedList<Command>();
    208                 for (Way w : ways) {
    209                         Way copy = new Way(w);
    210                         copy.segments.remove(seg2);
    211                         cmds.add(new ChangeCommand(w, copy));
    212167                }
    213                 cmds.add(new ChangeCommand(seg1, s));
    214                 cmds.add(new DeleteCommand(Arrays.asList(new OsmPrimitive[]{n, seg2})));
    215                 Main.main.undoRedo.add(new SequenceCommand(tr("Delete Node"), cmds));
     168
     169                if (ways.size() > 1)
     170                        return tr("Used by more than one way.");
     171               
     172                if (ways.size() == 1) {
     173                        // node in way
     174                        Way w = ways.get(0);
     175
     176                        int i = w.nodes.indexOf(n);
     177                        if (w.nodes.lastIndexOf(n) != i)
     178                                return tr("Occurs more than once in the same way.");
     179                        if (i == 0 || i == w.nodes.size() - 1)
     180                                return tr("Is at the end of a way");
     181
     182                        Way wnew = new Way(w);
     183                        wnew.nodes.remove(i);
     184
     185                        Collection<Command> cmds = new LinkedList<Command>();
     186                        cmds.add(new ChangeCommand(w, wnew));
     187                        cmds.add(new DeleteCommand(Collections.singleton(n)));
     188                        Main.main.undoRedo.add(new SequenceCommand(tr("Delete Node"), cmds));
     189                } else {
     190                        // unwayed node
     191                        Main.main.undoRedo.add(new DeleteCommand(Collections.singleton(n)));   
     192                }
    216193                return null;
    217194    }
  • branch/0.5/src/org/openstreetmap/josm/actions/mapmode/MapMode.java

    r298 r329  
    1515/**
    1616 * A class implementing MapMode is able to be selected as an mode for map editing.
    17  * As example scrolling the map is a MapMode, connecting Nodes to new Segments
     17 * As example scrolling the map is a MapMode, connecting Nodes to new Ways
    1818 * is another.
    1919 *
  • branch/0.5/src/org/openstreetmap/josm/actions/mapmode/MoveAction.java

    r312 r329  
    162162
    163163                Collection<OsmPrimitive> sel = Main.ds.getSelected();
    164                 OsmPrimitive osm = Main.map.mapView.getNearest(e.getPoint(), (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0);
     164                OsmPrimitive osm = Main.map.mapView.getNearest(e.getPoint());
    165165                if (osm != null) {
    166166                        if (!sel.contains(osm))
  • branch/0.5/src/org/openstreetmap/josm/actions/mapmode/SelectionAction.java

    r298 r329  
    1717import org.openstreetmap.josm.data.osm.Node;
    1818import org.openstreetmap.josm.data.osm.OsmPrimitive;
    19 import org.openstreetmap.josm.data.osm.Segment;
    2019import org.openstreetmap.josm.gui.MapFrame;
    2120import org.openstreetmap.josm.gui.SelectionManager;
     
    5554 * pixel are considered "only click". If that happens, the nearest Node will
    5655 * be selected if there is any within 10 pixel range. If there is no Node within
    57  * 10 pixel, the nearest Segment (or Street, if user hold down the Alt-Key)
    58  * within 10 pixel range is selected. If there is no Segment within 10 pixel
    59  * and the user clicked in or 10 pixel away from an area, this area is selected.
    60  * If there is even no area, nothing is selected. Shift and Ctrl key applies to
    61  * this as usual. For more, @see MapView#getNearest(Point, boolean)
     56 * 10 pixel, the nearest Way within 10 pixel range is selected. If there is no
     57 * Way within 10 pixel and the user clicked in or 10 pixel away from an area,
     58 * this area is selected.  If there is even no area, nothing is selected.
     59 * Shift and Ctrl key applies to this as usual. For more,
     60 * @see MapView#getNearest(Point)
    6261 *
    6362 * @author imi
     
    6564public class SelectionAction extends MapMode implements SelectionEnded {
    6665
    67         enum Mode {select, straight}
    68         private final Mode mode;
    69 
    7066        public static class Group extends GroupAction {
    7167                public Group(MapFrame mf) {
    7268                        super(KeyEvent.VK_S,0);
    7369                        putValue("help", "Action/Selection");
    74                         actions.add(new SelectionAction(mf, tr("Selection"), Mode.select, tr("Select objects by dragging or clicking.")));
    75                         actions.add(new SelectionAction(mf, tr("Straight line"), Mode.straight, tr("Select objects in a straight line.")));
     70                        actions.add(new SelectionAction(mf, tr("Selection"), tr("Select objects by dragging or clicking.")));
    7671                        setCurrent(0);
    7772                }
     
    8479        private SelectionManager selectionManager;
    8580
    86         private Node straightStart = null;
    87         private Node lastEnd = null;
    88         private Collection<OsmPrimitive> oldSelection = null;
    89 
    90         //TODO: Implement reverse references into data objects and remove this
    91         private final Map<Node, Collection<Segment>> reverseSegmentMap = new HashMap<Node, Collection<Segment>>();
    92 
    9381        /**
    9482         * Create a new SelectionAction in the given frame.
    9583         * @param mapFrame The frame this action belongs to
    9684         */
    97         public SelectionAction(MapFrame mapFrame, String name, Mode mode, String desc) {
    98                 super(name, "selection/"+mode, desc, mapFrame, ImageProvider.getCursor("normal", "selection"));
    99                 this.mode = mode;
    100                 putValue("help", "Action/Selection/"+Character.toUpperCase(mode.toString().charAt(0))+mode.toString().substring(1));
     85        public SelectionAction(MapFrame mapFrame, String name, String desc) {
     86                super(name, "selection/select", desc, mapFrame, ImageProvider.getCursor("normal", "selection"));
     87                putValue("help", "Action/Selection");
    10188                this.selectionManager = new SelectionManager(this, false, mapFrame.mapView);
    10289        }
     
    10491        @Override public void enterMode() {
    10592                super.enterMode();
    106                 if (mode == Mode.select)
    10793                        selectionManager.register(Main.map.mapView);
    108                 else {
    109                         Main.map.mapView.addMouseMotionListener(this);
    110                         Main.map.mapView.addMouseListener(this);
    111                         for (Segment s : Main.ds.segments) {
    112                                 addBackReference(s.from, s);
    113                                 addBackReference(s.to, s);
    114                         }
    115                 }
    116         }
    117 
    118         private void addBackReference(Node n, Segment s) {
    119                 Collection<Segment> c = reverseSegmentMap.get(n);
    120                 if (c == null) {
    121                         c = new HashSet<Segment>();
    122                         reverseSegmentMap.put(n, c);
    123                 }
    124                 c.add(s);
    12594        }
    12695
    12796        @Override public void exitMode() {
    12897                super.exitMode();
    129                 if (mode == Mode.select)
    13098                        selectionManager.unregister(Main.map.mapView);
    131                 else {
    132                         Main.map.mapView.removeMouseMotionListener(this);
    133                         Main.map.mapView.removeMouseListener(this);
    134                         reverseSegmentMap.clear();
    135                 }
    13699        }
    137100
     
    163126                Main.map.mapView.repaint();
    164127    }
    165 
    166         @Override public void mouseDragged(MouseEvent e) {
    167                 Node old = lastEnd;
    168                 lastEnd = Main.map.mapView.getNearestNode(e.getPoint());
    169                 if (straightStart == null)
    170                         straightStart = lastEnd;
    171                 if (straightStart != null && lastEnd != null && straightStart != lastEnd && old != lastEnd) {
    172                         Collection<OsmPrimitive> path = new HashSet<OsmPrimitive>();
    173                         Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
    174                         path.add(straightStart);
    175                         calculateShortestPath(path, straightStart, lastEnd);
    176                         if ((e.getModifiers() & MouseEvent.CTRL_MASK) != 0) {
    177                                 sel.addAll(oldSelection);
    178                                 sel.removeAll(path);
    179                         } else if ((e.getModifiers() & MouseEvent.SHIFT_MASK) != 0) {
    180                                 sel = path;
    181                                 sel.addAll(oldSelection);
    182                         } else
    183                                 sel = path;
    184                         Main.ds.setSelected(sel);
    185                 }
    186         }
    187 
    188         @Override public void mousePressed(MouseEvent e) {
    189                 straightStart = Main.map.mapView.getNearestNode(e.getPoint());
    190                 lastEnd = null;
    191                 oldSelection = Main.ds.getSelected();
    192         }
    193 
    194         @Override public void mouseReleased(MouseEvent e) {
    195                 straightStart = null;
    196                 lastEnd = null;
    197                 oldSelection = null;
    198         }
    199 
    200         /**
    201          * Get the shortest path by stepping through the node with a common segment with start
    202          * and nearest to the end (greedy algorithm).
    203          */
    204         private void calculateShortestPath(Collection<OsmPrimitive> path, Node start, Node end) {
    205                 for (Node pivot = start; pivot != null;)
    206                         pivot = addNearest(path, pivot, end);
    207         }
    208 
    209         private Node addNearest(Collection<OsmPrimitive> path, Node start, Node end) {
    210                 Collection<Segment> c = reverseSegmentMap.get(start);
    211                 if (c == null)
    212                         return null; // start may be a waypoint without segments
    213                 double min = Double.MAX_VALUE;
    214                 Node next = null;
    215                 Segment seg = null;
    216                 for (Segment s : c) {
    217                         Node other = s.from == start ? s.to : s.from;
    218                         if (other == end) {
    219                                 next = other;
    220                                 seg = s;
    221                                 min = 0;
    222                                 break;
    223                         }
    224                         double distance = other.eastNorth.distance(end.eastNorth);
    225                         if (distance < min) {
    226                                 min = distance;
    227                                 next = other;
    228                                 seg = s;
    229                         }
    230                 }
    231                 if (min < start.eastNorth.distance(end.eastNorth) && next != null) {
    232                         path.add(next);
    233                         path.add(seg);
    234                         return next;
    235                 }
    236                 return null;
    237         }
    238128}
  • branch/0.5/src/org/openstreetmap/josm/actions/search/SearchCompiler.java

    r319 r329  
    99import org.openstreetmap.josm.data.osm.Node;
    1010import org.openstreetmap.josm.data.osm.OsmPrimitive;
    11 import org.openstreetmap.josm.data.osm.Segment;
    1211import org.openstreetmap.josm.data.osm.Way;
     12import org.openstreetmap.josm.data.osm.Relation;
    1313
    1414/**
     
    119119                        if (osm instanceof Node)
    120120                                return type.equals("node");
    121                         if (osm instanceof Segment)
    122                                 return type.equals("segment");
    123121                        if (osm instanceof Way)
    124122                                return type.equals("way");
     123                        if (osm instanceof Relation)
     124                                return type.equals("relation");
    125125                        throw new IllegalStateException("unknown class "+osm.getClass());
    126126                }
     
    144144        private static class Incomplete extends Match {
    145145                @Override public boolean match(OsmPrimitive osm) {
    146                         return osm instanceof Way && ((Way)osm).isIncomplete();
     146                        // return osm instanceof Way && ((Way)osm).isIncomplete();
     147            return false;
    147148                }
    148149                @Override public String toString() {return "incomplete";}
Note: See TracChangeset for help on using the changeset viewer.