Changeset 343 in josm for trunk


Ignore:
Timestamp:
2007-10-07T13:20:27+02:00 (17 years ago)
Author:
gebner
Message:

Merge 0.5.

Location:
trunk
Files:
14 added
2 deleted
64 edited

Legend:

Unmodified
Added
Removed
  • trunk/.classpath

    r321 r343  
    22<classpath>
    33        <classpathentry kind="src" path="src"/>
    4         <classpathentry kind="src" path="test/functional"/>
    5         <classpathentry kind="src" path="test/unit"/>
    64        <classpathentry excluding="build/|dist/|src/|test/" including="images/" kind="src" path=""/>
    75        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
  • trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r301 r343  
    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
     
    6773                        }
    6874                }
    69                
    70                 Way oldWay = selectedWays.poll();
    71                 Way newWay = new Way(oldWay);
    72                 LinkedList<Command> cmds = new LinkedList<Command>();
    73                
    74                 for (Way w : selectedWays)
    75                         newWay.segments.addAll(w.segments);
     75
     76                List<Node> nodeList = null;
     77                Object firstTry = actuallyCombineWays(selectedWays, false);
     78                if (firstTry instanceof List) {
     79                        nodeList = (List<Node>) firstTry;
     80                } else {
     81                        Object secondTry = actuallyCombineWays(selectedWays, true);
     82                        if (secondTry instanceof List) {
     83                                int option = JOptionPane.showConfirmDialog(Main.parent,
     84                                        tr("The ways can not be combined in their current directions.  "
     85                                        + "Do you want to reverse some of them?"), tr("Change directions?"),
     86                                        JOptionPane.YES_NO_OPTION);
     87                                if (option != JOptionPane.YES_OPTION) {
     88                                        return;
     89                                }
     90                                nodeList = (List<Node>) secondTry;
     91                        } else {
     92                                JOptionPane.showMessageDialog(Main.parent, (String) secondTry);
     93                                return;
     94                        }
     95                }
     96
     97                Way newWay = new Way(selectedWays.get(0));
     98                newWay.nodes.clear();
     99                newWay.nodes.addAll(nodeList);
    76100               
    77101                // display conflict dialog
     
    97121                }
    98122
    99                 cmds.add(new DeleteCommand(selectedWays));
    100                 cmds.add(new ChangeCommand(oldWay, newWay));
     123                LinkedList<Command> cmds = new LinkedList<Command>();
     124                cmds.add(new DeleteCommand(selectedWays.subList(1, selectedWays.size())));
     125                cmds.add(new ChangeCommand(selectedWays.peek(), newWay));
    101126                Main.main.undoRedo.add(new SequenceCommand(tr("Combine {0} ways", selectedWays.size()), cmds));
    102                 Main.ds.setSelected(oldWay);
     127                Main.ds.setSelected(selectedWays.peek());
     128        }
     129
     130        /**
     131         * @return a message if combining failed, else a list of nodes.
     132         */
     133        private Object actuallyCombineWays(List<Way> ways, boolean ignoreDirection) {
     134                // Battle plan:
     135                //  1. Split the ways into small chunks of 2 nodes and weed out
     136                //         duplicates.
     137                //  2. Take a chunk and see if others could be appended or prepended,
     138                //         if so, do it and remove it from the list of remaining chunks.
     139                //         Rather, rinse, repeat.
     140                //  3. If this algorithm does not produce a single way,
     141                //     complain to the user.
     142                //  4. Profit!
     143               
     144                HashSet<NodePair> chunkSet = new HashSet<NodePair>();
     145                for (Way w : ways) {
     146                        if (w.nodes.size() == 0) continue;
     147                        Node lastN = null;
     148                        for (Node n : w.nodes) {
     149                                if (lastN == null) {
     150                                        lastN = n;
     151                                        continue;
     152                                }
     153
     154                                NodePair np = new NodePair(lastN, n);
     155                                if (ignoreDirection) {
     156                                        np.sort();
     157                                }
     158                                chunkSet.add(np);
     159
     160                                lastN = n;
     161                        }
     162                }
     163                LinkedList<NodePair> chunks = new LinkedList<NodePair>(chunkSet);
     164
     165                if (chunks.isEmpty()) {
     166                        return tr("All the ways were empty");
     167                }
     168
     169                List<Node> nodeList = chunks.poll().toArrayList();
     170                while (!chunks.isEmpty()) {
     171                        ListIterator<NodePair> it = chunks.listIterator();
     172                        boolean foundChunk = false;
     173                        while (it.hasNext()) {
     174                                NodePair curChunk = it.next();
     175                                if (curChunk.a == nodeList.get(nodeList.size() - 1)) { // append
     176                                        nodeList.add(curChunk.b);
     177                                } else if (curChunk.b == nodeList.get(0)) { // prepend
     178                                        nodeList.add(0, curChunk.a);
     179                                } else if (ignoreDirection && curChunk.b == nodeList.get(nodeList.size() - 1)) { // append
     180                                        nodeList.add(curChunk.a);
     181                                } else if (ignoreDirection && curChunk.a == nodeList.get(0)) { // prepend
     182                                        nodeList.add(0, curChunk.b);
     183                                } else {
     184                                        continue;
     185                                }
     186
     187                                foundChunk = true;
     188                                it.remove();
     189                                break;
     190                        }
     191                        if (!foundChunk) break;
     192                }
     193
     194                if (!chunks.isEmpty()) {
     195                        return tr("Could not combine ways "
     196                                + "(They could not be merged into a single string of nodes)");
     197                } else {
     198                        return nodeList;
     199                }
    103200        }
    104201
  • trunk/src/org/openstreetmap/josm/actions/DownloadIncompleteAction.java

    r298 r343  
    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 }
  • trunk/src/org/openstreetmap/josm/actions/ReverseSegmentAction.java

    r301 r343  
    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 }
  • trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java

    r327 r343  
    126126                                }
    127127                                OsmWriter.output(new FileOutputStream(file), new OsmWriter.All(layer.data, false));
    128                                 if (tmpFile != null && !Main.pref.getBoolean("save.keepbackup"))
     128                                if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null))
    129129                                        tmpFile.delete();
    130130                        } else if (ExtensionFileFilter.filters[ExtensionFileFilter.CSV].acceptName(file.getPath())) {
  • trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java

    r301 r343  
    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}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/AddNodeAction.java

    r314 r343  
    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}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/AddSegmentAction.java

    r301 r343  
    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                }
  • trunk/src/org/openstreetmap/josm/actions/mapmode/AddWayAction.java

    r301 r343  
    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 }
  • trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java

    r301 r343  
    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    }
  • trunk/src/org/openstreetmap/josm/actions/mapmode/MapMode.java

    r298 r343  
    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 *
  • trunk/src/org/openstreetmap/josm/actions/mapmode/MoveAction.java

    r312 r343  
    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))
  • trunk/src/org/openstreetmap/josm/actions/mapmode/SelectionAction.java

    r298 r343  
    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}
  • trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java

    r319 r343  
    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";}
  • trunk/src/org/openstreetmap/josm/command/AddCommand.java

    r304 r343  
    1212import org.openstreetmap.josm.Main;
    1313import org.openstreetmap.josm.data.osm.DataSet;
     14import org.openstreetmap.josm.data.osm.Relation;
    1415import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1516import org.openstreetmap.josm.data.osm.visitor.AddVisitor;
     
    2223 * A command that adds an osm primitive to a dataset. Keys cannot be added this
    2324 * way. Use ChangeKeyValueCommand instead.
     25 *
     26 * See ChangeCommand for comments on relation back references.
    2427 *
    2528 * @author imi
  • trunk/src/org/openstreetmap/josm/command/ChangeCommand.java

    r298 r343  
    1313import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
    1414
     15/**
     16 * Command that basically replaces one OSM primitive by another of the
     17 * same type.
     18 * 
     19 * @author Imi
     20 */
    1521public class ChangeCommand extends Command {
    1622
  • trunk/src/org/openstreetmap/josm/command/Command.java

    r304 r343  
    1010import javax.swing.tree.MutableTreeNode;
    1111
     12import org.openstreetmap.josm.data.osm.Relation;
    1213import org.openstreetmap.josm.data.osm.Node;
    1314import org.openstreetmap.josm.data.osm.OsmPrimitive;
    14 import org.openstreetmap.josm.data.osm.Segment;
    1515import org.openstreetmap.josm.data.osm.Way;
    1616import org.openstreetmap.josm.data.osm.visitor.Visitor;
     
    3737                        orig.put(n, new Node(n));
    3838                }
    39                 public void visit(Segment s) {
    40                         orig.put(s, new Segment(s));
    41                 }
    4239                public void visit(Way w) {
    4340                        orig.put(w, new Way(w));
     41                }
     42                public void visit(Relation e) {
     43                        orig.put(e, new Relation(e));
    4444                }
    4545        }
  • trunk/src/org/openstreetmap/josm/command/DeleteCommand.java

    r298 r343  
    1111import javax.swing.tree.MutableTreeNode;
    1212
     13import org.openstreetmap.josm.data.osm.Relation;
    1314import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1415import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
     
    3233        @Override public void executeCommand() {
    3334                super.executeCommand();
    34                 for (OsmPrimitive osm : data)
     35                for (OsmPrimitive osm : data) {
    3536                        osm.delete(true);
     37                }
    3638        }
    37 
     39       
    3840        @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    3941                deleted.addAll(data);
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r298 r343  
    5454
    5555        /**
     56         * Override some values on read. This is intended to be used for technology previews
     57         * where we want to temporarily modify things without changing the user's preferences
     58         * file.
     59         */
     60        protected static final SortedMap<String, String> override = new TreeMap<String, String>();
     61        static {
     62                override.put("osm-server.version", "0.5");
     63                override.put("osm-server.additional-versions", "");
     64                override.put("osm-server.url", "http://openstreetmap.gryph.de/api");
     65                override.put("osm-server.username", "fred@remote.org");
     66                override.put("osm-server.password", "fredfred");
     67                override.put("plugins", null);
     68        }
     69
     70        /**
    5671         * Return the location of the user defined preferences file
    5772         */
     
    6378
    6479        /**
    65          * @return A list of all existing directories, where ressources could be stored.
     80         * @return A list of all existing directories, where resources could be stored.
    6681         */
    6782        public Collection<String> getAllPossiblePreferenceDirs() {
     
    93108
    94109        synchronized public boolean hasKey(final String key) {
    95                 return properties.containsKey(key);
     110                return override.containsKey(key) ? override.get(key) != null : properties.containsKey(key);
    96111        }
    97112        synchronized public String get(final String key) {
     113                if (override.containsKey(key))
     114                        return override.get(key);
    98115                if (!properties.containsKey(key))
    99116                        return "";
     
    101118        }
    102119        synchronized public String get(final String key, final String def) {
     120                if (override.containsKey(key))
     121                        return override.get(key);
    103122                final String prop = properties.get(key);
    104123                if (prop == null || prop.equals(""))
     
    111130                        if (e.getKey().startsWith(prefix))
    112131                                all.put(e.getKey(), e.getValue());
     132                for (final Entry<String,String> e : override.entrySet())
     133                        if (e.getKey().startsWith(prefix))
     134                                if (e.getValue() == null)
     135                                        all.remove(e.getKey());
     136                                else
     137                                        all.put(e.getKey(), e.getValue());
    113138                return all;
    114139        }
     
    117142        }
    118143        synchronized public boolean getBoolean(final String key, final boolean def) {
     144                if (override.containsKey(key))
     145                        return override.get(key) == null ? def : Boolean.parseBoolean(override.get(key));
    119146                return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
    120147        }
     
    148175                try {
    149176                        final PrintWriter out = new PrintWriter(new FileWriter(getPreferencesDir() + "preferences"), false);
    150                         for (final Entry<String, String> e : properties.entrySet())
     177                        for (final Entry<String, String> e : properties.entrySet()) {
    151178                                if (!e.getValue().equals(""))
    152179                                        out.println(e.getKey() + "=" + e.getValue());
     180                        }
    153181                        out.close();
    154182                } catch (final IOException e) {
  • trunk/src/org/openstreetmap/josm/data/ServerSidePreferences.java

    r298 r343  
    4545                public String download() {
    4646                        try {
    47                                 System.out.println("reading preferenced from "+serverUrl);
     47                                System.out.println("reading preferences from "+serverUrl);
    4848                                HttpURLConnection con = (HttpURLConnection)serverUrl.openConnection();
    4949                                addAuth(con);
  • trunk/src/org/openstreetmap/josm/data/conflict/FromConflict.java

    r298 r343  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 package org.openstreetmap.josm.data.conflict;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    6 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    7 import org.openstreetmap.josm.data.osm.Segment;
    8 
    9 public class FromConflict extends ConflictItem {
    10 
    11         @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
    12                 return key instanceof Segment && !((Segment)key).from.equals(((Segment)value).from);
    13         }
    14        
    15         @Override protected String str(OsmPrimitive osm) {
    16                 return osm instanceof Segment ? String.valueOf(((Segment)osm).from.id) : null;
    17         }
    18        
    19         @Override public String key() {
    20                 return "segment|"+tr("from");
    21         }
    22        
    23         @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
    24                 if (target instanceof Segment)
    25                         ((Segment)target).from = ((Segment)other).from;
    26     }
    27 }
  • trunk/src/org/openstreetmap/josm/data/conflict/SegmentConflict.java

    r298 r343  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 package org.openstreetmap.josm.data.conflict;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    6 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    7 import org.openstreetmap.josm.data.osm.Segment;
    8 import org.openstreetmap.josm.data.osm.Way;
    9 
    10 public class SegmentConflict extends ConflictItem {
    11        
    12         @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
    13                 return key instanceof Way && !((Way)key).segments.equals(((Way)value).segments);
    14         }
    15        
    16         @Override protected String str(OsmPrimitive osm) {
    17                 if (!(osm instanceof Way))
    18                         return null;
    19                 String s = "";
    20                 for (Segment ls : ((Way)osm).segments)
    21                         s += ls.id + ",";
    22                 return s.equals("") ? "<html><i>&lt;"+tr("none")+"&gt;</i></html>" : s.substring(0, s.length()-1);
    23         }
    24        
    25         @Override public String key() {
    26                 return "way|"+tr("segments");
    27         }
    28        
    29         @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
    30                 if (!(target instanceof Way))
    31                         return;
    32                 ((Way)target).segments.clear();
    33                 ((Way)target).segments.addAll(((Way)other).segments);
    34     }
    35 }
  • trunk/src/org/openstreetmap/josm/data/conflict/ToConflict.java

    r298 r343  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 package org.openstreetmap.josm.data.conflict;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    6 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    7 import org.openstreetmap.josm.data.osm.Segment;
    8 
    9 public class ToConflict extends ConflictItem {
    10        
    11         @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
    12                 return key instanceof Segment && !((Segment)key).to.equals(((Segment)value).to);
    13         }
    14 
    15         @Override protected String str(OsmPrimitive osm) {
    16                 return osm instanceof Segment ? String.valueOf(((Segment)osm).to.id) : null;
    17         }
    18 
    19         @Override public String key() {
    20                 return "segment|"+tr("to");
    21         }
    22        
    23         @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
    24                 if (target instanceof Segment)
    25                         ((Segment)target).to = ((Segment)other).to;
    26     }
    27 }
  • trunk/src/org/openstreetmap/josm/data/osm/DataSet.java

    r298 r343  
    3131
    3232        /**
    33          * All segments goes here, even when they are in a way.
     33         * All ways (Streets etc.) in the DataSet.
     34         *
     35         * The way nodes are stored only in the way list.
    3436         */
    35         public Collection<Segment> segments = new LinkedList<Segment>();
     37        public Collection<Way> ways = new LinkedList<Way>();
    3638
    3739        /**
    38          * All ways (Streets etc.) in the DataSet.
    39          *
    40          * The nodes of the way segments of this way must be objects from
    41          * the nodes list, however the way segments are stored only in the
    42          * way list.
     40         * All relations/relationships
    4341         */
    44         public Collection<Way> ways = new LinkedList<Way>();
     42        public Collection<Relation> relations = new LinkedList<Relation>();
    4543
    4644        /**
     
    5553         * selection does only change in the active layer)
    5654         */
    57         public static Collection<SelectionChangedListener> listeners = new LinkedList<SelectionChangedListener>();
     55        public static Collection<SelectionChangedListener> selListeners = new LinkedList<SelectionChangedListener>();
    5856
    5957        /**
    6058         * @return A collection containing all primitives of the dataset. The
    61          * data is ordered after: first comes nodes, then segments, then ways.
     59         * data is ordered after: first come nodes, then ways, then relations.
    6260         * Ordering in between the categories is not guaranteed.
    6361         */
     
    6563                List<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
    6664                o.addAll(nodes);
    67                 o.addAll(segments);
    6865                o.addAll(ways);
     66                o.addAll(relations);
    6967                return o;
    7068        }
     
    8886        public void clearSelection() {
    8987                clearSelection(nodes);
    90                 clearSelection(segments);
    9188                clearSelection(ways);
     89                clearSelection(relations);
    9290                Collection<OsmPrimitive> sel = Collections.emptyList();
    9391                fireSelectionChanged(sel);
     
    10098        public Collection<OsmPrimitive> getSelected() {
    10199                Collection<OsmPrimitive> sel = getSelected(nodes);
    102                 sel.addAll(getSelected(segments));
    103100                sel.addAll(getSelected(ways));
     101                sel.addAll(getSelected(relations));
    104102                return sel;
    105103        }
     
    107105        public void setSelected(Collection<? extends OsmPrimitive> selection) {
    108106                clearSelection(nodes);
    109                 clearSelection(segments);
    110107                clearSelection(ways);
     108                clearSelection(relations);
    111109                for (OsmPrimitive osm : selection)
    112110                        osm.selected = true;
     
    120118                }
    121119                clearSelection(nodes);
    122                 clearSelection(segments);
    123120                clearSelection(ways);
     121                clearSelection(relations);
    124122                for (OsmPrimitive o : osm)
    125123                        if (o != null)
     
    158156         */
    159157        public static void fireSelectionChanged(Collection<? extends OsmPrimitive> sel) {
    160                 for (SelectionChangedListener l : listeners)
     158                for (SelectionChangedListener l : selListeners)
    161159                        l.selectionChanged(sel);
    162160        }
    163 
     161       
    164162        @Override public DataSet clone() {
    165163                DataSet ds = new DataSet();
    166164                for (Node n : nodes)
    167165                        ds.nodes.add(new Node(n));
    168                 for (Segment s : segments)
    169                         ds.segments.add(new Segment(s));
    170166                for (Way w : ways)
    171167                        ds.ways.add(new Way(w));
     168                for (Relation e : relations)
     169                        ds.relations.add(new Relation(e));
    172170                for (DataSource source : dataSources)
    173171                        ds.dataSources.add(new DataSource(source.bounds, source.origin));
  • trunk/src/org/openstreetmap/josm/data/osm/Node.java

    r298 r343  
    1818        public volatile EastNorth eastNorth;
    1919
     20        /**
     21         * Create an incomplete Node object
     22         */
     23        public Node(long id) {
     24                this.id = id;
     25                incomplete = true;
     26        }
     27       
    2028        /**
    2129         * Create an identical clone of the argument (including the id)
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r298 r343  
    33
    44import java.text.SimpleDateFormat;
     5import java.util.ArrayList;
    56import java.util.Collection;
    67import java.util.Collections;
     
    9091
    9192        /**
     93         * If set to true, this object is incomplete, which means only the id
     94         * and type is known (type is the objects instance class)
     95         */
     96        public boolean incomplete = false;
     97
     98        /**
    9299         * Implementation of the visitor scheme. Subclases have to call the correct
    93100         * visitor function.
     
    125132                Visitor v = new Visitor(){
    126133                        public void visit(Node n) { ret[0] = 1; }
    127                         public void visit(Segment s) { ret[0] = 2; }
    128                         public void visit(Way w) { ret[0] = 3; }
     134                        public void visit(Way w) { ret[0] = 2; }
     135                        public void visit(Relation e) { ret[0] = 3; }
    129136                };
    130137                visit(v);
    131                 return id == 0 ? super.hashCode() : (int)(id<<3)+ret[0];
     138                return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0];
    132139        }
    133140
     
    205212                return timestamp == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp);
    206213        }
     214       
     215       
    207216}
  • trunk/src/org/openstreetmap/josm/data/osm/Segment.java

    r298 r343  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 package org.openstreetmap.josm.data.osm;
    3 
    4 import org.openstreetmap.josm.data.osm.visitor.Visitor;
    5 
    6 
    7 /**
    8  * One way segment consisting of a pair of nodes (from/to)
    9  *
    10  * @author imi
    11  */
    12 public final class Segment extends OsmPrimitive {
    13 
    14         /**
    15          * The starting node of the segment
    16          */
    17         public Node from;
    18 
    19         /**
    20          * The ending node of the segment
    21          */
    22         public Node to;
    23 
    24         /**
    25          * If set to true, this object is incomplete, which means only the id
    26          * and type is known (type is the objects instance class)
    27          */
    28         public boolean incomplete;
    29 
    30         /**
    31          * Create an identical clone of the argument (including the id)
    32          */
    33         public Segment(Segment clone) {
    34                 cloneFrom(clone);
    35         }
    36 
    37         /**
    38          * Create an segment from the given starting and ending node
    39          * @param from  Starting node of the segment.
    40          * @param to    Ending node of the segment.
    41          */
    42         public Segment(Node from, Node to) {
    43                 this.from = from;
    44                 this.to = to;
    45                 incomplete = false;
    46         }
    47 
    48         public Segment(long id) {
    49                 this.id = id;
    50                 incomplete = true;
    51         }
    52 
    53         @Override public void visit(Visitor visitor) {
    54                 visitor.visit(this);
    55         }
    56 
    57         /**
    58          * @return <code>true</code>, if the <code>ls</code> occupy
    59          * exactly the same place as <code>this</code>.
    60          */
    61         public boolean equalPlace(Segment ls) {
    62                 if (equals(ls))
    63                         return true;
    64                 if (incomplete || ls.incomplete)
    65                         return incomplete == ls.incomplete;
    66                 return ((from.coor.equals(ls.from.coor) && to.coor.equals(ls.to.coor)) ||
    67                                 (from.coor.equals(ls.to.coor) && to.coor.equals(ls.from.coor)));
    68         }
    69 
    70         @Override public void cloneFrom(OsmPrimitive osm) {
    71                 super.cloneFrom(osm);
    72                 Segment ls = ((Segment)osm);
    73                 from = ls.from;
    74                 to = ls.to;
    75                 incomplete = ls.incomplete;
    76         }
    77 
    78         @Override public String toString() {
    79                 return "{Segment id="+id+" from="+from+" to="+to+"}";
    80         }
    81 
    82         @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    83                 if (!(osm instanceof Segment))
    84                         return super.realEqual(osm, semanticOnly);
    85                 if (incomplete)
    86                         return super.realEqual(osm, semanticOnly) && ((Segment)osm).incomplete;
    87                 return super.realEqual(osm, semanticOnly) && from.equals(((Segment)osm).from) && to.equals(((Segment)osm).to);
    88         }
    89 
    90         public int compareTo(OsmPrimitive o) {
    91                 return o instanceof Segment ? Long.valueOf(id).compareTo(o.id) : (o instanceof Node ? -1 : 1);
    92         }
    93 }
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r298 r343  
    99
    1010/**
    11  * One full way, consisting of several way segments chained together.
     11 * One full way, consisting of a list of way nodes.
    1212 *
    1313 * @author imi
     
    1616
    1717        /**
    18          * All way segments in this way
     18         * All way nodes in this way
    1919         */
    20         public final List<Segment> segments = new ArrayList<Segment>();
     20        public final List<Node> nodes = new ArrayList<Node>();
    2121
    2222        @Override public void visit(Visitor visitor) {
     
    3131        }
    3232       
     33        /**
     34         * Create an empty way without id. Use this only if you set meaningful
     35         * values yourself.
     36         */
    3337        public Way() {
     38        }
     39       
     40        /**
     41         * Create an incomplete Way.
     42         */
     43        public Way(long id) {
     44                this.id = id;
     45                incomplete = true;
    3446        }
    3547       
    3648        @Override public void cloneFrom(OsmPrimitive osm) {
    3749                super.cloneFrom(osm);
    38                 segments.clear();
    39                 segments.addAll(((Way)osm).segments);
     50                nodes.clear();
     51                nodes.addAll(((Way)osm).nodes);
    4052        }
    4153
    4254    @Override public String toString() {
    43         return "{Way id="+id+" segments="+Arrays.toString(segments.toArray())+"}";
     55        return "{Way id="+id+" nodes="+Arrays.toString(nodes.toArray())+"}";
    4456    }
    4557
    4658        @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    47                 return osm instanceof Way ? super.realEqual(osm, semanticOnly) && segments.equals(((Way)osm).segments) : false;
     59                return osm instanceof Way ? super.realEqual(osm, semanticOnly) && nodes.equals(((Way)osm).nodes) : false;
    4860    }
    4961
     
    5264    }
    5365       
     66        @Deprecated
    5467        public boolean isIncomplete() {
    55                 for (Segment s : segments)
    56                         if (s.incomplete)
    57                                 return true;
    5868                return false;
    5969        }
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/AddVisitor.java

    r298 r343  
    11// License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 /**
    3  */
    42package org.openstreetmap.josm.data.osm.visitor;
    53
    64import org.openstreetmap.josm.data.osm.DataSet;
    7 import org.openstreetmap.josm.data.osm.Segment;
     5import org.openstreetmap.josm.data.osm.Relation;
    86import org.openstreetmap.josm.data.osm.Node;
    97import org.openstreetmap.josm.data.osm.Way;
     
    2725                ds.nodes.add(n);
    2826        }
    29         public void visit(Segment s) {
    30                 ds.segments.add(s);
    31         }
    3227        public void visit(Way w) {
    3328                ds.ways.add(w);
    3429        }
     30        public void visit(Relation e) {
     31                ds.relations.add(e);
     32        }
    3533}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java

    r298 r343  
    55import java.util.HashSet;
    66
    7 import org.openstreetmap.josm.data.osm.Segment;
     7import org.openstreetmap.josm.data.osm.Relation;
     8import org.openstreetmap.josm.data.osm.RelationMember;
    89import org.openstreetmap.josm.data.osm.Node;
    910import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    3031
    3132        /**
    32          * Line segments have exactly two nodes: from and to.
     33         * Ways have their way nodes.
    3334         */
    34         public void visit(Segment ls) {
    35                 if (!ls.incomplete) {
    36                         visit(ls.from);
    37                         visit(ls.to);
    38                 }
     35        public void visit(Way w) {
     36                for (Node n : w.nodes)
     37                        visit(n);
    3938        }
    4039
    4140        /**
    42          * Ways have all nodes from their segments.
     41         * Relations may have any number of nodes.
     42         * FIXME: do we want to collect nodes from segs/ways that are relation members?
     43         * if so, use AutomatchVisitor!
    4344         */
    44         public void visit(Way w) {
    45                 for (Segment ls : w.segments)
    46                         visit(ls);
     45        public void visit(Relation e) {
     46                for (RelationMember m : e.members)
     47                        if (m.member instanceof Node) visit((Node)m.member);
    4748        }
    48 
    4949        /**
    5050         * @return All nodes the given primitive has.
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java

    r298 r343  
    55import org.openstreetmap.josm.data.Bounds;
    66import org.openstreetmap.josm.data.coor.EastNorth;
    7 import org.openstreetmap.josm.data.osm.Segment;
     7import org.openstreetmap.josm.data.osm.Relation;
    88import org.openstreetmap.josm.data.osm.Node;
    99import org.openstreetmap.josm.data.osm.Way;
     
    2222        }
    2323
    24         public void visit(Segment ls) {
    25                 if (!ls.incomplete) {
    26                         visit(ls.from);
    27                         visit(ls.to);
    28                 }
     24        public void visit(Way w) {
     25                for (Node n : w.nodes)
     26                        visit(n);
    2927        }
    3028
    31         public void visit(Way w) {
    32                 for (Segment ls : w.segments)
    33                         visit(ls);
     29        public void visit(Relation e) {
     30                // relations have no bounding box.
    3431        }
    3532
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java

    r298 r343  
    66
    77import org.openstreetmap.josm.data.osm.DataSet;
    8 import org.openstreetmap.josm.data.osm.Segment;
     8import org.openstreetmap.josm.data.osm.Relation;
     9import org.openstreetmap.josm.data.osm.RelationMember;
    910import org.openstreetmap.josm.data.osm.Node;
    1011import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    1213
    1314/**
    14  * Helper that collect all segments a node is part of, all ways
    15  * a node or segment is part of and all areas a node is part of.
     15 * Helper that collect all ways a node is part of.
    1616 *
    1717 * Deleted objects are not collected.
     
    4141                        if (w.deleted)
    4242                                continue;
    43                         for (Segment ls : w.segments) {
    44                                 if (ls.incomplete)
    45                                         continue;
    46                                 if (ls.from == n || ls.to == n) {
     43                        for (Node n2 : w.nodes) {
     44                                if (n == n2) {
    4745                                        data.add(w);
     46                                }
     47                        }
     48                }
     49                checkRelationMembership(n);
     50        }
     51       
     52        public void visit(Way w) {
     53                checkRelationMembership(w);
     54        }
     55       
     56        public void visit(Relation r) {
     57                checkRelationMembership(r);
     58        }
     59       
     60        private void checkRelationMembership(OsmPrimitive p) {
     61                // FIXME - this might be a candidate for optimisation
     62                // if OSM primitives are made to hold a list of back
     63                // references.
     64                for (Relation r : ds.relations) {
     65                        for (RelationMember m : r.members) {
     66                                if (m.member == p) {
     67                                        data.add(r);
     68                                        // move up the tree (there might be relations
     69                                        // referring to this relation)
     70                                        checkRelationMembership(r);
    4871                                        break;
    4972                                }
    5073                        }
    5174                }
    52                 for (Segment ls : ds.segments) {
    53                         if (ls.deleted || ls.incomplete)
    54                                 continue;
    55                         if (ls.from == n || ls.to == n)
    56                                 data.add(ls);
    57                 }
    5875        }
    59         public void visit(Segment ls) {
    60                 for (Way w : ds.ways) {
    61                         if (w.deleted)
    62                                 continue;
    63                         if (w.segments.contains(ls))
    64                                 data.add(w);
    65                 }
    66         }
    67         public void visit(Way w) {}
    6876}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/DeleteVisitor.java

    r298 r343  
    11// License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 /**
    3  */
    42package org.openstreetmap.josm.data.osm.visitor;
    53
    64import org.openstreetmap.josm.data.osm.DataSet;
    7 import org.openstreetmap.josm.data.osm.Segment;
     5import org.openstreetmap.josm.data.osm.Relation;
    86import org.openstreetmap.josm.data.osm.Node;
    97import org.openstreetmap.josm.data.osm.Way;
     
    2725                ds.nodes.remove(n);
    2826        }
    29         public void visit(Segment ls) {
    30                 ds.segments.remove(ls);
    31         }
    3227        public void visit(Way w) {
    3328                ds.ways.remove(w);
    3429        }
     30        public void visit(Relation e) {
     31                ds.relations.remove(e);
     32        }
    3533}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java

    r298 r343  
    1010
    1111import org.openstreetmap.josm.data.osm.DataSet;
     12import org.openstreetmap.josm.data.osm.Relation;
     13import org.openstreetmap.josm.data.osm.RelationMember;
    1214import org.openstreetmap.josm.data.osm.Node;
    1315import org.openstreetmap.josm.data.osm.OsmPrimitive;
    14 import org.openstreetmap.josm.data.osm.Segment;
    1516import org.openstreetmap.josm.data.osm.Way;
    1617
     
    2526        /**
    2627         * Map from primitives in the database to visited primitives. (Attention: The other way
    27          * round than mergedNodes and mergedSegments)
     28         * round than mergedNodes)
    2829         */
    2930        public Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
     
    3839         */
    3940        private final Map<Node, Node> mergedNodes = new HashMap<Node, Node>();
    40         /**
    41          * A list of all segments that got replaced with others.
    42          * Key is the segment in the other's dataset and the value is the one that is now
    43          * in ds.segments.
    44          */
    45         private final Map<Segment, Segment> mergedSegments = new HashMap<Segment, Segment>();
    4641
    4742        public MergeVisitor(DataSet ds, DataSet mergeds) {
     
    8176
    8277        /**
    83          * Merge the segment if id matches or if both nodes are the same (and the
    84          * id is zero of either segment). Nodes are the "same" when they @see match
    85          */
    86         public void visit(Segment other) {
    87                 if (mergeAfterId(mergedSegments, ds.segments, other))
    88                         return;
    89 
    90                 Segment my = null;
    91                 for (Segment ls : ds.segments) {
    92                         if (match(other, ls) && ((mergeds == null) || (!mergeds.segments.contains(ls)))) {
    93                                 my = ls;
    94                                 break;
    95                         }
    96                 }
    97                
    98                 if (my == null)
    99                         ds.segments.add(other);
    100                 else if (my.incomplete && !other.incomplete) {
    101                         mergedSegments.put(other, my);
    102                         my.cloneFrom(other);
    103                 } else if (!other.incomplete) {
    104                         mergedSegments.put(other, my);
    105                         mergeCommon(my, other);
    106                         if (my.modified && !other.modified)
    107                                 return;
    108                         if (!match(my.from, other.from)) {
    109                                 my.from = other.from;
    110                                 my.modified = other.modified;
    111                         }
    112                         if (!match(my.to, other.to)) {
    113                                 my.to = other.to;
    114                                 my.modified = other.modified;
    115                         }
    116                 }
    117         }
    118 
     78         * Simply calls cloneFrom() for now.
     79         * Might be useful to keep around to facilitate merge with the relations
     80         * branch.
     81         */
    11982        private <T extends OsmPrimitive> void cloneFromExceptIncomplete(T myOsm, T otherOsm) {
    120                 if (!(myOsm instanceof Way))
    121                         myOsm.cloneFrom(otherOsm);
    122                 else {
    123                         Way my = (Way)myOsm;
    124                         Way other = (Way)otherOsm;
    125                         HashMap<Long, Segment> copy = new HashMap<Long, Segment>();
    126                         for (Segment s : my.segments)
    127                                 copy.put(s.id, s);
    128                         my.cloneFrom(other);
    129                         my.segments.clear();
    130                         for (Segment s : other.segments) {
    131                                 Segment myS = copy.get(s.id);
    132                                 if (s.incomplete && myS != null && !myS.incomplete) {
    133                                         mergedSegments.put(s, myS);
    134                                         my.segments.add(myS);
    135                                 } else
    136                                         my.segments.add(s);
    137                         }
    138                 }
     83                myOsm.cloneFrom(otherOsm);
    13984    }
    14085
    14186        /**
    142          * Merge the way if id matches or if all segments matches and the
     87         * Merge the way if id matches or if all nodes match and the
    14388         * id is zero of either way.
    14489         */
     
    155100                }
    156101                if (my == null) {
    157                         // Add the way and replace any incomplete segments that we already have
    158102                        ds.ways.add(other);
    159                         for (Segment s : other.segments) {
    160                                 if (s.incomplete) {
     103                } else {
     104                        mergeCommon(my, other);
     105                        if (my.modified && !other.modified)
     106                                return;
     107                        boolean same = true;
     108                        Iterator<Node> it = other.nodes.iterator();
     109                        for (Node n : my.nodes) {
     110                                if (!match(n, it.next()))
     111                                        same = false;
     112                        }
     113                        if (!same) {
     114                                my.nodes.clear();
     115                                my.nodes.addAll(other.nodes);
     116                                my.modified = other.modified;
     117                        }
     118                }
     119        }
     120
     121        /**
     122         * Merge the relation if id matches or if all members match and the
     123         * id of either relation is zero.
     124         */
     125        public void visit(Relation other) {
     126                if (mergeAfterId(null, ds.relations, other))
     127                        return;
     128
     129                Relation my = null;
     130                for (Relation e : ds.relations) {
     131                        if (match(other, e) && ((mergeds == null) || (!mergeds.relations.contains(e)))) {
     132                                my = e;
     133                                break;
     134                        }
     135                }
     136               
     137                if (my == null) {
     138                        // Add the relation and replace any incomplete segments that we already have
     139                        ds.relations.add(other);
     140                        // FIXME unclear!
     141                        /*
     142                        for (RelationMember em : other.getMembers()) {
     143                                if (em.member.incomplete) {
    161144                                        for (Segment ourSegment : ds.segments) {
    162145                                                if (ourSegment.id == s.id) {
     
    166149                                        }
    167150                                }
    168                         }
     151                        }*/
    169152                } else {
    170153                        mergeCommon(my, other);
     
    172155                                return;
    173156                        boolean same = true;
    174                         Iterator<Segment> it = other.segments.iterator();
    175                         for (Segment ls : my.segments) {
    176                                 if (!match(ls, it.next()))
     157                        if (other.members.size() != my.members.size()) {
    177158                                        same = false;
    178                         }
     159                        } else {
     160                                for (RelationMember em : my.members) {
     161                                        if (!other.members.contains(em)) {
     162                                                same = false;
     163                                                break;
     164                                        }
     165                                }
     166                        }
     167                        // FIXME Unclear
     168                        /*
    179169                        if (!same) {
    180170                                HashMap<Long, Segment> copy = new HashMap<Long, Segment>();
     
    192182                                my.modified = other.modified;
    193183                        }
     184                        */
    194185                }
    195186        }
     
    200191         */
    201192        public void fixReferences() {
    202                 for (Segment s : ds.segments)
    203                         fixSegment(s);
    204                 for (OsmPrimitive osm : conflicts.values())
    205                         if (osm instanceof Segment)
    206                                 fixSegment((Segment)osm);
    207193                for (Way w : ds.ways)
    208194                        fixWay(w);
     
    214200        private void fixWay(Way w) {
    215201            boolean replacedSomething = false;
    216             LinkedList<Segment> newSegments = new LinkedList<Segment>();
    217             for (Segment ls : w.segments) {
    218                 Segment otherLs = mergedSegments.get(ls);
    219                 newSegments.add(otherLs == null ? ls : otherLs);
    220                 if (otherLs != null)
     202            LinkedList<Node> newNodes = new LinkedList<Node>();
     203            for (Node n : w.nodes) {
     204                Node otherN = mergedNodes.get(n);
     205                newNodes.add(otherN == null ? n : otherN);
     206                if (otherN != null)
    221207                        replacedSomething = true;
    222208            }
    223209            if (replacedSomething) {
    224                 w.segments.clear();
    225                 w.segments.addAll(newSegments);
    226             }
    227             for (Segment ls : w.segments)
    228                 fixSegment(ls);
     210                w.nodes.clear();
     211                w.nodes.addAll(newNodes);
    229212    }
    230 
    231         private void fixSegment(Segment ls) {
    232                
    233             if (mergedNodes.containsKey(ls.from))
    234                 ls.from = mergedNodes.get(ls.from);
    235            
    236             if (mergedNodes.containsKey(ls.to)) 
    237                 ls.to = mergedNodes.get(ls.to);
    238            
    239213    }
    240214
    241215        /**
    242          * @return Whether the nodes matches (in sense of "be mergable").
     216         * @return Whether the nodes match (in sense of "be mergable").
    243217         */
    244218        private boolean match(Node n1, Node n2) {
     
    249223
    250224        /**
    251          * @return Whether the segments matches (in sense of "be mergable").
    252          */
    253         private boolean match(Segment ls1, Segment ls2) {
    254                 if (ls1.id == ls2.id && ls1.id != 0)
    255                         return true;
    256                 //if (ls1.id != 0 && ls2.id != 0)
    257                 //      return false;
    258                 if (ls1.incomplete || ls2.incomplete)
    259                         return false;
    260                 return match(ls1.from, ls2.from) && match(ls1.to, ls2.to);
    261         }
    262 
    263         /**
    264          * @return Whether the ways matches (in sense of "be mergable").
     225         * @return Whether the ways match (in sense of "be mergable").
    265226         */
    266227        private boolean match(Way w1, Way w2) {
    267228                if (w1.id == 0 || w2.id == 0) {
    268                         if (w1.segments.size() != w2.segments.size())
    269                                 return false;
    270                         Iterator<Segment> it = w1.segments.iterator();
    271                         for (Segment ls : w2.segments)
    272                                 if (!match(ls, it.next()))
     229                        if (w1.nodes.size() != w2.nodes.size())
     230                        return false;
     231                        Iterator<Node> it = w1.nodes.iterator();
     232                        for (Node n : w2.nodes)
     233                                if (!match(n, it.next()))
    273234                                        return false;
    274235                        return true;
     
    276237                return w1.id == w2.id;
    277238        }
     239        /**
     240         * @return Whether the relations match (in sense of "be mergable").
     241         */
     242        private boolean match(Relation w1, Relation w2) {
     243                // FIXME this is not perfect yet...
     244                if (w1.id == 0 || w2.id == 0) {
     245                        if (w1.members.size() != w2.members.size())
     246                                return false;
     247                        for (RelationMember em : w1.members) {
     248                                if (!w2.members.contains(em)) {
     249                                        return false;
     250                                }
     251                        }
     252                        return true;
     253                }
     254                return w1.id == w2.id;
     255        }
     256
    278257
    279258        /**
     
    327306                        }
    328307                        if (my.id == other.id && my.id != 0) {
    329                                 if (my instanceof Segment && ((Segment)my).incomplete)
    330                                         return false; // merge always over an incomplete
    331308                                if (my.modified && other.modified) {
    332309                                        conflicts.put(my, other);
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java

    r298 r343  
    11// License: GPL. Copyright 2007 by Immanuel Scholz and others
    2 
    32package org.openstreetmap.josm.data.osm.visitor;
    43
     
    1312
    1413import org.openstreetmap.josm.Main;
     14import org.openstreetmap.josm.data.osm.Relation;
    1515import org.openstreetmap.josm.data.osm.Node;
    1616import org.openstreetmap.josm.data.osm.OsmPrimitive;
    17 import org.openstreetmap.josm.data.osm.Segment;
    1817import org.openstreetmap.josm.data.osm.Way;
    1918import org.openstreetmap.josm.tools.ImageProvider;
     
    4140       
    4241        /**
    43          * If the segment has a key named "name", its value is displayed.
    44          * Otherwise, if it has "id", this is used. If none of these available,
    45          * "(x1,y1) -> (x2,y2)" is displayed with the nodes coordinates.
    46          */
    47         public void visit(Segment ls) {
    48                 name = ls.get("name");
    49                 if (name == null) {
    50                         if (ls.incomplete)
    51                                 name = ls.id == 0 ? tr("new") : ls.id+" ("+tr("unknown")+")";
    52                         else
    53                                 name = (ls.id==0?"":ls.id+" ")+"("+ls.from.coor.lat()+","+ls.from.coor.lon()+") -> ("+ls.to.coor.lat()+","+ls.to.coor.lon()+")";
    54                 }
    55                 addId(ls);
    56                 icon = ImageProvider.get("data", "segment");
    57                 trn("segment", "segments", 0); // no marktrn available
    58                 className = "segment";
    59         }
    60 
    61         /**
    6242         * If the node has a name-key or id-key, this is displayed. If not, (lat,lon)
    6343         * is displayed.
     
    8161                if (name == null) name = w.get("ref");
    8262                if (name == null) {
    83                         AllNodesVisitor.getAllNodes(w.segments);
    84                         Set<Node> nodes = new HashSet<Node>();
    85                         for (Segment ls : w.segments) {
    86                                 if (!ls.incomplete) {
    87                                         nodes.add(ls.from);
    88                                         nodes.add(ls.to);
    89                                 }
    90                         }
    9163                        String what = (w.get("highway") != null) ? "highway " : (w.get("railway") != null) ? "railway " : (w.get("waterway") != null) ? "waterway " : "";
    92                         name = what + trn("{0} node", "{0} nodes", nodes.size(), nodes.size());
     64                        name = what + trn("{0} node", "{0} nodes", w.nodes.size(), w.nodes.size());
    9365                }
    94                 if (w.isIncomplete())
    95                         name += " ("+tr("incomplete")+")";
    9666                addId(w);
    9767                icon = ImageProvider.get("data", "way");
    9868                trn("way", "ways", 0); // no marktrn available
    9969                className = "way";
     70        }
     71       
     72        /**
     73         */
     74        public void visit(Relation e) {
     75                name = e.get("type");
     76                // FIXME add names of members
     77                if (name == null)
     78                        name = "relation";
     79                addId(e);
     80                icon = ImageProvider.get("data", "relation");
     81                trn("relation", "relations", 0); // no marktrn available
     82                className = "relation";
    10083        }
    10184       
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java

    r319 r343  
    1212import org.openstreetmap.josm.Main;
    1313import org.openstreetmap.josm.data.osm.DataSet;
     14import org.openstreetmap.josm.data.osm.Relation;
    1415import org.openstreetmap.josm.data.osm.Node;
    1516import org.openstreetmap.josm.data.osm.OsmPrimitive;
    16 import org.openstreetmap.josm.data.osm.Segment;
    1717import org.openstreetmap.josm.data.osm.Way;
    1818import org.openstreetmap.josm.gui.NavigatableComponent;
     
    3030        public final static Color darkblue = new Color(0,0,128);
    3131        public final static Color darkgreen = new Color(0,128,0);
    32        
     32
    3333        /**
    3434         * The environment to paint to.
     
    4141       
    4242        public boolean inactive;
    43        
     43
    4444        protected static final double PHI = Math.toRadians(20);
    45 
     45       
    4646        /**
    4747         * Preferences
    48          */
     48        */
    4949        protected Color inactiveColor;
    5050        protected Color selectedColor;
    5151        protected Color nodeColor;
    52         protected Color segmentColor;
    5352        protected Color dfltWayColor;
    5453        protected Color incompleteColor;
     
    6160         */
    6261        protected Color currentColor = null;
    63         protected GeneralPath currrentPath = new GeneralPath();
    64        
     62        protected GeneralPath currentPath = new GeneralPath();
     63
    6564        public void visitAll(DataSet data) {
     65               
    6666                inactiveColor = getPreferencesColor("inactive", Color.DARK_GRAY);
    6767                selectedColor = getPreferencesColor("selected", Color.WHITE);
    6868                nodeColor = getPreferencesColor("node", Color.RED);
    69                 segmentColor = getPreferencesColor("segment", darkgreen);
    7069                dfltWayColor = getPreferencesColor("way", darkblue);
    7170                incompleteColor = getPreferencesColor("incomplete way", darkerblue);
     
    7473                showOrderNumber = Main.pref.getBoolean("draw.segment.order_number");
    7574               
    76                 for (final OsmPrimitive osm : data.segments)
    77                         if (!osm.deleted && !osm.selected)
    78                                 osm.visit(this);
    7975                for (final OsmPrimitive osm : data.ways)
    8076                        if (!osm.deleted && !osm.selected)
    8177                                osm.visit(this);
    82                 displaySegments(null);  // Flush segment cache before nodes
     78                displaySegments(null);
    8379                for (final OsmPrimitive osm : data.nodes)
    8480                        if (!osm.deleted && !osm.selected)
     
    108104
    109105        /**
    110          * Draw just a line between the points.
    111          * White if selected (as always) or green otherwise.
    112          */
    113         public void visit(Segment ls) {
    114                 Color color;
    115                 if (inactive)
    116                         color = inactiveColor;
    117                 else if (ls.selected)
    118                         color = selectedColor;
    119                 else
    120                         color = segmentColor;
    121                 drawSegment(ls, color, showDirectionArrow);
    122         }
    123 
    124         /**
    125106         * Draw a darkblue line for all segments.
    126107         * @param w The way to draw.
     
    132113                else {
    133114                        wayColor = dfltWayColor;
    134                         for (Segment ls : w.segments) {
    135                                 if (ls.incomplete) {
    136                                         wayColor = incompleteColor;
    137                                         break;
    138                                 }
     115                }
     116
     117                int orderNumber = 0;
     118                Node lastN = null;
     119                for (Node n : w.nodes) {
     120                        if (lastN == null) {
     121                                lastN = n;
     122                                continue;
    139123                        }
    140                 }
    141 
    142                 int orderNumber = 0;
    143                 for (Segment ls : w.segments) {
    144124                        orderNumber++;
    145                         if (!ls.selected) // selected already in good color
    146                                 drawSegment(ls, w.selected && !inactive ? selectedColor : wayColor, showDirectionArrow);
    147                         if (!ls.incomplete && showOrderNumber)
    148                                 drawOrderNumber(ls, orderNumber);
    149                 }
    150         }
    151 
    152         /**
    153          * Draw an number of the order of the segment within the parents way
    154          */
    155         protected void drawOrderNumber(Segment ls, int orderNumber) {
     125                        drawSegment(lastN, n, w.selected && !inactive ? selectedColor : wayColor, showDirectionArrow);
     126                        if (showOrderNumber)
     127                                drawOrderNumber(lastN, n, orderNumber);
     128                        lastN = n;
     129                }
     130        }
     131
     132        public void visit(Relation e) {
     133                // relations are not (yet?) drawn.
     134        }
     135       
     136        /**
     137         * Draw an number of the order of the two consecutive nodes within the
     138         * parents way
     139         */
     140        protected void drawOrderNumber(Node n1, Node n2, int orderNumber) {
    156141                int strlen = (""+orderNumber).length();
    157                 Point p1 = nc.getPoint(ls.from.eastNorth);
    158                 Point p2 = nc.getPoint(ls.to.eastNorth);
     142                Point p1 = nc.getPoint(n1.eastNorth);
     143                Point p2 = nc.getPoint(n2.eastNorth);
    159144                int x = (p1.x+p2.x)/2 - 4*strlen;
    160145                int y = (p1.y+p2.y)/2 + 4;
     
    181166                Rectangle screen = g.getClipBounds();
    182167
    183                 if ( screen.contains(p.x, p.y) )
     168                if (screen.contains(p.x, p.y))
    184169                        g.drawRect(p.x-1, p.y-1, 2, 2);
    185170        }
     
    188173         * Draw a line with the given color.
    189174         */
    190         protected void drawSegment(Segment ls, Color col, boolean showDirection) {
    191                 if (ls.incomplete)
    192                         return;
     175        protected void drawSegment(Node n1, Node n2, Color col, boolean showDirection) {
     176
    193177                if (col != currentColor) {
    194178                        displaySegments(col);
    195179                }
    196180               
    197                 Point p1 = nc.getPoint(ls.from.eastNorth);
    198                 Point p2 = nc.getPoint(ls.to.eastNorth);
    199                
    200                 Rectangle screen = g.getClipBounds();           
     181                Point p1 = nc.getPoint(n1.eastNorth);
     182                Point p2 = nc.getPoint(n2.eastNorth);
     183               
     184                Rectangle screen = g.getClipBounds();
    201185                Line2D line = new Line2D.Double(p1.x, p1.y, p2.x, p2.y);
    202186                if (screen.contains(p1.x, p1.y, p2.x, p2.y) || screen.intersectsLine(line))
    203187                {
    204                         currrentPath.moveTo(p1.x, p1.y);
    205                         currrentPath.lineTo(p2.x, p2.y);
    206        
     188                        currentPath.moveTo(p1.x, p1.y);
     189                        currentPath.lineTo(p2.x, p2.y);
     190                       
    207191                        if (showDirection) {
    208192                                double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
    209                                 currrentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
    210                                 currrentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
    211                                 currrentPath.lineTo(p2.x, p2.y);                        }
    212                 }
    213         }
    214        
    215         protected void displaySegments(Color newColor) {
    216                 if (currrentPath != null) {
    217                         g.setColor(currentColor);
    218                         ((Graphics2D) g).draw(currrentPath);
    219                         currrentPath = new GeneralPath();
    220                         currentColor = newColor;
     193                                currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
     194                                currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
     195                                currentPath.lineTo(p2.x, p2.y); 
     196                        }
    221197                }
    222198        }
     
    230206                return ColorHelper.html2color(colStr);
    231207        }
    232 
    233 
     208       
    234209        public void setGraphics(Graphics g) {
    235210        this.g = g;
     
    239214        this.nc = nc;
    240215    }
     216
     217        protected void displaySegments(Color newColor) {
     218                if (currentPath != null) {
     219                        g.setColor(currentColor);
     220                        ((Graphics2D) g).draw(currentPath);
     221                        currentPath = new GeneralPath();
     222                        currentColor = newColor;
     223                }
     224        }
    241225}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/Visitor.java

    r298 r343  
    22package org.openstreetmap.josm.data.osm.visitor;
    33
     4import org.openstreetmap.josm.data.osm.Relation;
    45import org.openstreetmap.josm.data.osm.Node;
    5 import org.openstreetmap.josm.data.osm.Segment;
    66import org.openstreetmap.josm.data.osm.Way;
    77
     
    1414public interface Visitor {
    1515        void visit(Node n);
    16         void visit(Segment s);
    1716        void visit(Way w);
     17        void visit(Relation e);
    1818}
  • trunk/src/org/openstreetmap/josm/gui/ConflictResolver.java

    r339 r343  
    3838import org.openstreetmap.josm.data.conflict.ConflictItem;
    3939import org.openstreetmap.josm.data.conflict.DeleteConflict;
    40 import org.openstreetmap.josm.data.conflict.FromConflict;
    4140import org.openstreetmap.josm.data.conflict.PositionConflict;
    4241import org.openstreetmap.josm.data.conflict.PropertyConflict;
    43 import org.openstreetmap.josm.data.conflict.SegmentConflict;
    44 import org.openstreetmap.josm.data.conflict.ToConflict;
    4542import org.openstreetmap.josm.data.osm.OsmPrimitive;
    4643import org.openstreetmap.josm.tools.GBC;
     
    140137                possibleConflicts.add(new DeleteConflict());
    141138                possibleConflicts.add(new PositionConflict());
    142                 possibleConflicts.add(new FromConflict());
    143                 possibleConflicts.add(new ToConflict());
    144                 possibleConflicts.add(new SegmentConflict());
    145139                TreeSet<String> allkeys = new TreeSet<String>();
    146140                for (Entry<OsmPrimitive, OsmPrimitive> e : conflicts.entrySet()) {
  • trunk/src/org/openstreetmap/josm/gui/GettingStarted.java

    r312 r343  
    4848                panel = new JPanel(new GridBagLayout());
    4949               
     50                panel.add(new JLabel("<html><h2>You are running a technology preview with support for <i>API 0.5</i>.</h2>" +
     51                                "<h3>API 0.5 supports object relationships, and segments have been removed.</h3>" +
     52                                "<h3>This version is hard-coded to use the API 0.5 running on <i>openstreetmap.gryph.de</i> which has data from a recent planet file."+
     53                                "<br>Please be gentle with that machine and request only moderate bounding boxes.<br>" +
     54                                "<br>Username and password are also hardcoded, so your real username and password are not transmitted.<br>" +
     55                "</h3>"), GBC.eol());
     56               
     57                addLine("wiki", "Read the [Wiki page on API 0.5]");
    5058                addGettingStarted();
    5159                addGettingHelp();
     
    8694                else if (e.getActionCommand().equals("help"))
    8795                        Main.main.menu.help.actionPerformed(e);
     96                else if (e.getActionCommand().equals("wiki"))
     97                        OpenBrowser.displayUrl("http://wiki.openstreetmap.org/index.php?title=OSM_Protocol_Version_0.5");
    8898                else if (e.getActionCommand().equals("tutorial"))
    8999                        OpenBrowser.displayUrl("http://josm.openstreetmap.de/wiki/TutorialVideos");
  • trunk/src/org/openstreetmap/josm/gui/MainMenu.java

    r298 r343  
    1818import org.openstreetmap.josm.actions.CombineWayAction;
    1919import org.openstreetmap.josm.actions.DownloadAction;
    20 import org.openstreetmap.josm.actions.DownloadIncompleteAction;
    2120import org.openstreetmap.josm.actions.ExitAction;
    2221import org.openstreetmap.josm.actions.GpxExportAction;
     
    2625import org.openstreetmap.josm.actions.PreferencesAction;
    2726import org.openstreetmap.josm.actions.RedoAction;
    28 import org.openstreetmap.josm.actions.ReorderAction;
    29 import org.openstreetmap.josm.actions.ReverseSegmentAction;
     27import org.openstreetmap.josm.actions.ReverseWayAction;
    3028import org.openstreetmap.josm.actions.SaveAction;
    3129import org.openstreetmap.josm.actions.SaveAsAction;
     
    5654        public final OpenAction open = new OpenAction();
    5755        public final DownloadAction download = new DownloadAction();
    58         public final Action reverseSegment = new ReverseSegmentAction();
     56        public final Action reverseWay = new ReverseWayAction();
    5957        public final Action splitWay = new SplitWayAction();
    6058        public final Action combineWay = new CombineWayAction();
    6159        public final Action alignInCircle = new AlignInCircleAction();
    6260        public final Action alignInLine = new AlignInLineAction();
    63         public final Action reorder = new ReorderAction();
    6461        public final Action upload = new UploadAction();
    6562        public final Action save = new SaveAction(null);
     
    7067        public final HelpAction help = new HelpAction();
    7168        public final Action about = new AboutAction();
    72         public final DownloadIncompleteAction downloadIncomplete = new DownloadIncompleteAction();
    7369       
    7470        public final JMenu layerMenu = new JMenu(tr("Layer"));
     
    117113                toolsMenu.add(alignInLine);
    118114                toolsMenu.addSeparator();
    119                 toolsMenu.add(reverseSegment);
    120                 toolsMenu.add(reorder);
     115                toolsMenu.add(reverseWay);
    121116                toolsMenu.addSeparator();
    122117                toolsMenu.add(splitWay);
     
    126121                connectionMenu.setMnemonic('C');
    127122                connectionMenu.add(download);
    128                 connectionMenu.add(downloadIncomplete);
    129123                connectionMenu.add(upload);
    130124                add(connectionMenu);
  • trunk/src/org/openstreetmap/josm/gui/MapFrame.java

    r312 r343  
    1515import org.openstreetmap.josm.Main;
    1616import org.openstreetmap.josm.actions.mapmode.AddSegmentAction;
    17 import org.openstreetmap.josm.actions.mapmode.AddWayAction;
    1817import org.openstreetmap.josm.actions.mapmode.DeleteAction;
    1918import org.openstreetmap.josm.actions.mapmode.MapMode;
     
    2423import org.openstreetmap.josm.gui.dialogs.CommandStackDialog;
    2524import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
     25import org.openstreetmap.josm.gui.dialogs.RelationListDialog;
    2626import org.openstreetmap.josm.gui.dialogs.HistoryDialog;
    2727import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
     
    8585                toolBarActions.add(new IconToggleButton(new AddNodeGroup(this)));
    8686                toolBarActions.add(new IconToggleButton(new AddSegmentAction(this)));
    87                 toolBarActions.add(new IconToggleButton(new AddWayAction(this)));
    8887                toolBarActions.add(new IconToggleButton(new DeleteAction(this)));
    8988
     
    104103                addToggleDialog(conflictDialog = new ConflictDialog());
    105104                addToggleDialog(new CommandStackDialog(this));
     105                addToggleDialog(new RelationListDialog());
    106106
    107107                // status line below the map
  • trunk/src/org/openstreetmap/josm/gui/MapStatus.java

    r298 r343  
    126126                                        OsmPrimitive osmNearest = null;
    127127                                        // Set the text label in the bottom status bar
    128                                         osmNearest = mv.getNearest(ms.mousePos, (ms.modifiers & MouseEvent.ALT_DOWN_MASK) != 0);
     128                                        osmNearest = mv.getNearest(ms.mousePos);
    129129                                        if (osmNearest != null) {
    130130                                                NameVisitor visitor = new NameVisitor();
  • trunk/src/org/openstreetmap/josm/gui/MapView.java

    r304 r343  
    8585
    8686                // listend to selection changes to redraw the map
    87                 DataSet.listeners.add(new SelectionChangedListener(){
     87                DataSet.selListeners.add(new SelectionChangedListener(){
    8888                        public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    8989                                repaint();
  • trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java

    r306 r343  
    44import java.awt.Point;
    55import java.util.Collection;
    6 import java.util.Collections;
    76import java.util.HashSet;
    8 import java.util.List;
    97
    108import javax.swing.JComponent;
     
    1614import org.openstreetmap.josm.data.osm.Node;
    1715import org.openstreetmap.josm.data.osm.OsmPrimitive;
    18 import org.openstreetmap.josm.data.osm.Segment;
    1916import org.openstreetmap.josm.data.osm.Way;
     17import org.openstreetmap.josm.data.osm.WaySegment;
    2018import org.openstreetmap.josm.data.projection.Projection;
    2119
     
    147145
    148146        /**
    149          * @return the nearest way to the screen point given.
    150          */
    151         public final Way getNearestWay(Point p) {
     147         * @return the nearest way segment to the screen point given that is not
     148         * in ignore.
     149         *
     150         * @param p the point for which to search the nearest segment.
     151         * @param ignore a collection of segments which are not to be returned.
     152         * May be null.
     153         */
     154        public final WaySegment getNearestWaySegment(Point p, Collection<WaySegment> ignore) {
    152155                Way minPrimitive = null;
     156                int minI = 0;
    153157                double minDistanceSq = Double.MAX_VALUE;
    154158                for (Way w : Main.ds.ways) {
    155159                        if (w.deleted)
    156160                                continue;
    157                         for (Segment ls : w.segments) {
    158                                 if (ls.deleted || ls.incomplete)
     161                        Node lastN = null;
     162                        int i = -2;
     163                        for (Node n : w.nodes) {
     164                                i++;
     165                                if (n.deleted) continue;
     166                                if (lastN == null) {
     167                                        lastN = n;
    159168                                        continue;
    160                                 Point A = getPoint(ls.from.eastNorth);
    161                                 Point B = getPoint(ls.to.eastNorth);
     169                                }
     170                                if (ignore == null || !ignore.contains(new WaySegment(w, i))) {
     171                                        Point A = getPoint(lastN.eastNorth);
     172                                        Point B = getPoint(n.eastNorth);
     173                                        double c = A.distanceSq(B);
     174                                        double a = p.distanceSq(B);
     175                                        double b = p.distanceSq(A);
     176                                        double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
     177                                        if (perDist < 100 && minDistanceSq > perDist && a < c+100 && b < c+100) {
     178                                                minDistanceSq = perDist;
     179                                                minPrimitive = w;
     180                                                minI = i;
     181                                        }
     182                                }
     183                                lastN = n;
     184                        }
     185                }
     186                return minPrimitive == null ? null : new WaySegment(minPrimitive, minI);
     187        }
     188
     189        /**
     190         * @return the nearest way segment to the screen point given.
     191         */
     192        public final WaySegment getNearestWaySegment(Point p) {
     193                return getNearestWaySegment(p, null);
     194        }
     195       
     196        /**
     197         * @return the nearest way to the screen point given.
     198         */
     199        public final Way getNearestWay(Point p) {
     200                WaySegment nearestWaySeg = getNearestWaySegment(p);
     201                return nearestWaySeg == null ? null : nearestWaySeg.way;
     202    }
     203
     204        /**
     205         * Return the object, that is nearest to the given screen point.
     206         *
     207         * First, a node will be searched. If a node within 10 pixel is found, the
     208         * nearest node is returned.
     209         *
     210         * If no node is found, search for near ways.
     211         *
     212         * If nothing is found, return <code>null</code>.
     213         *
     214         * @param p                              The point on screen.
     215         * @return      The primitive, that is nearest to the point p.
     216         */
     217        public OsmPrimitive getNearest(Point p) {
     218                OsmPrimitive osm = getNearestNode(p);
     219                if (osm == null)
     220                        osm = getNearestWay(p);
     221                return osm;
     222        }
     223
     224        @Deprecated
     225        public OsmPrimitive getNearest(Point p, boolean segmentInsteadWay) {
     226                return getNearest(p);
     227        }
     228
     229        /**
     230         * @return A list of all objects that are nearest to
     231         * the mouse.  Does a simple sequential scan on all the data.
     232         *
     233         * @return A collection of all items or <code>null</code>
     234         *              if no item under or near the point. The returned
     235         *              list is never empty.
     236         */
     237        public Collection<OsmPrimitive> getAllNearest(Point p) {
     238                Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>();
     239                        for (Way w : Main.ds.ways) {
     240                        if (w.deleted) continue;
     241                        Node lastN = null;
     242                        for (Node n : w.nodes) {
     243                                if (n.deleted) continue;
     244                                if (lastN == null) {
     245                                        lastN = n;
     246                                        continue;
     247                                }
     248                                Point A = getPoint(lastN.eastNorth);
     249                                Point B = getPoint(n.eastNorth);
    162250                                double c = A.distanceSq(B);
    163251                                double a = p.distanceSq(B);
    164252                                double b = p.distanceSq(A);
    165253                                double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
    166                                 if (perDist < 100 && minDistanceSq > perDist && a < c+100 && b < c+100) {
    167                                         minDistanceSq = perDist;
    168                                         minPrimitive = w;
    169                                 }
    170                         }
    171                 }
    172                 return minPrimitive;
    173         }
    174 
    175         /**
    176          * @return the nearest segment to the screen point given
    177          *
    178          * @param p the point for which to search the nearest segment.
    179          */
    180         public final Segment getNearestSegment(Point p) {
    181                 List<Segment> e = Collections.emptyList();
    182                 return getNearestSegment(p, e);
    183         }
    184        
    185         /**
    186          * @return the nearest segment to the screen point given that is not
    187          * in ignoreThis.
    188          *
    189          * @param p the point for which to search the nearest segment.
    190          * @param ignore a collection of segments which are not to be returned. Must not be null.
    191          */
    192         public final Segment getNearestSegment(Point p, Collection<Segment> ignore) {
    193                 Segment minPrimitive = null;
    194                 double minDistanceSq = Double.MAX_VALUE;
    195                 // segments
    196                 for (Segment ls : Main.ds.segments) {
    197                         if (ls.deleted || ls.incomplete || ignore.contains(ls))
    198                                 continue;
    199                         Point A = getPoint(ls.from.eastNorth);
    200                         Point B = getPoint(ls.to.eastNorth);
    201                         double c = A.distanceSq(B);
    202                         double a = p.distanceSq(B);
    203                         double b = p.distanceSq(A);
    204                         double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
    205                         if (perDist < 100 && minDistanceSq > perDist && a < c+100 && b < c+100) {
    206                                 minDistanceSq = perDist;
    207                                 minPrimitive = ls;
    208                         }
    209                 }
    210                 return minPrimitive;
    211     }
    212 
    213         /**
    214          * Return the object, that is nearest to the given screen point.
    215          *
    216          * First, a node will be searched. If a node within 10 pixel is found, the
    217          * nearest node is returned.
    218          *
    219          * If no node is found, search for pending segments.
    220          *
    221          * If no such segment is found, and a non-pending segment is
    222          * within 10 pixel to p, this segment is returned, except when
    223          * <code>wholeWay</code> is <code>true</code>, in which case the
    224          * corresponding Way is returned.
    225          *
    226          * If no segment is found and the point is within an area, return that
    227          * area.
    228          *
    229          * If no area is found, return <code>null</code>.
    230          *
    231          * @param p                              The point on screen.
    232          * @param segmentInsteadWay Whether the segment (true) or only the whole
    233          *                                               way should be returned.
    234          * @return      The primitive, that is nearest to the point p.
    235          */
    236         public OsmPrimitive getNearest(Point p, boolean segmentInsteadWay) {
    237                 OsmPrimitive osm = getNearestNode(p);
    238                 if (osm == null && !segmentInsteadWay)
    239                         osm = getNearestWay(p);
    240                 if (osm == null)
    241                         osm = getNearestSegment(p);
    242                 return osm;
    243         }
    244 
    245         /**
    246          * @return A list of all objects that are nearest to
    247          * the mouse. To do this, first the nearest object is
    248          * determined.
    249          *
    250          * If its a node, return all segments and
    251          * streets the node is part of, as well as all nodes
    252          * (with their segments and ways) with the same
    253          * location.
    254          *
    255          * If its a segment, return all ways this segment
    256          * belongs to as well as all segments that are between
    257          * the same nodes (in both direction) with all their ways.
    258          *
    259          * @return A collection of all items or <code>null</code>
    260          *              if no item under or near the point. The returned
    261          *              list is never empty.
    262          */
    263         public Collection<OsmPrimitive> getAllNearest(Point p) {
    264                 OsmPrimitive osm = getNearest(p, true);
    265                 if (osm == null)
    266                         return null;
    267                 Collection<OsmPrimitive> c = new HashSet<OsmPrimitive>();
    268                 c.add(osm);
    269                 if (osm instanceof Node) {
    270                         Node node = (Node)osm;
    271                         for (Node n : Main.ds.nodes)
    272                                 if (!n.deleted && n.coor.equals(node.coor))
    273                                         c.add(n);
    274                         for (Segment ls : Main.ds.segments)
    275                                 // segments never match nodes, so they are skipped by contains
    276                                 if (!ls.deleted && !ls.incomplete && (c.contains(ls.from) || c.contains(ls.to)))
    277                                         c.add(ls);
    278                 }
    279                 if (osm instanceof Segment) {
    280                         Segment line = (Segment)osm;
    281                         for (Segment ls : Main.ds.segments)
    282                                 if (!ls.deleted && ls.equalPlace(line))
    283                                         c.add(ls);
    284                 }
    285                 if (osm instanceof Node || osm instanceof Segment) {
    286                         for (Way w : Main.ds.ways) {
    287                                 if (w.deleted)
    288                                         continue;
    289                                 for (Segment ls : w.segments) {
    290                                         if (!ls.deleted && !ls.incomplete && c.contains(ls)) {
    291                                                 c.add(w);
     254                                if (perDist < 100 && a < c+100 && b < c+100) {
     255                                        nearest.add(w);
    292256                                                break;
    293257                                        }
    294                                 }
     258                                lastN = n;
     259                                }
     260                        }
     261                for (Node n : Main.ds.nodes) {
     262                        if (!n.deleted && getPoint(n.eastNorth).distanceSq(p) < 100) {
     263                                nearest.add(n);
    295264                        }
    296265                }
    297                 return c;
     266                return nearest.isEmpty() ? null : nearest;
    298267        }
    299268
  • trunk/src/org/openstreetmap/josm/gui/OsmPrimitivRenderer.java

    r298 r343  
    55
    66import javax.swing.DefaultListCellRenderer;
     7import javax.swing.JLabel;
    78import javax.swing.JList;
     9import javax.swing.JTable;
     10import javax.swing.ListCellRenderer;
     11import javax.swing.table.DefaultTableCellRenderer;
     12import javax.swing.table.TableCellRenderer;
    813
    914import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    1217/**
    1318 * Renderer that renders the objects from an OsmPrimitive as data.
     19 *
     20 * Can be used in lists and tables.
     21 *
    1422 * @author imi
     23 * @author Frederik Ramm <frederik@remote.org>
    1524 */
    16 public class OsmPrimitivRenderer extends DefaultListCellRenderer {
     25public class OsmPrimitivRenderer implements ListCellRenderer, TableCellRenderer {
    1726
     27        /**
     28         * NameVisitor provides proper names and icons for OsmPrimitives
     29         */
    1830        private NameVisitor visitor = new NameVisitor();
    1931
    20         @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    21                 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    22                 if (value != null) {
     32        /**
     33         * Default list cell renderer - delegate for ListCellRenderer operation
     34         */
     35        private DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer();
     36       
     37        /**
     38         * Default table cell renderer - delegate for TableCellRenderer operation
     39         */
     40        private DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
     41
     42        /**
     43         * Adapter method supporting the ListCellRenderer interface.
     44         */
     45        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
     46                Component def = defaultListCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     47                return renderer(def, (OsmPrimitive) value);
     48        }
     49
     50        /**
     51         * Adapter method supporting the TableCellRenderer interface.
     52         */
     53        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     54                Component def = defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     55                return renderer(def, (OsmPrimitive) value);
     56        }
     57       
     58        /**
     59         * Internal method that stuffs information into the rendering component
     60         * provided that it's a kind of JLabel.
     61         * @param def the rendering component
     62         * @param value the OsmPrimtive to render
     63         * @return the modified rendering component
     64         */
     65        private Component renderer(Component def, OsmPrimitive value) {
     66                if (def != null && value != null && def instanceof JLabel) {
    2367                        ((OsmPrimitive)value).visit(visitor);
    24                         setText(visitor.name);
    25                         setIcon(visitor.icon);
     68                        ((JLabel)def).setText(visitor.name);
     69                        ((JLabel)def).setIcon(visitor.icon);
    2670                }
    27                 return this;
     71                return def;
    2872        }
     73       
    2974}
  • trunk/src/org/openstreetmap/josm/gui/SelectionManager.java

    r298 r343  
    2525import org.openstreetmap.josm.data.osm.Node;
    2626import org.openstreetmap.josm.data.osm.OsmPrimitive;
    27 import org.openstreetmap.josm.data.osm.Segment;
    2827import org.openstreetmap.josm.data.osm.Way;
    2928
     
    282281         * modifier.
    283282         * @param alt Whether the alt key was pressed, which means select all objects
    284          *              that are touched, instead those which are completly covered. Also
    285          *              select whole ways instead of segments.
     283         *              that are touched, instead those which are completly covered.
    286284         */
    287285        public Collection<OsmPrimitive> getObjectsInRectangle(Rectangle r, boolean alt) {
     
    293291
    294292                if (clicked) {
    295                         OsmPrimitive osm = nc.getNearest(center, alt);
     293                        OsmPrimitive osm = nc.getNearest(center);
    296294                        if (osm != null)
    297295                                selection.add(osm);
     
    303301                        }
    304302                       
    305                         // pending segments
    306                         for (Segment s : Main.ds.segments)
    307                                 if (!s.deleted && rectangleContainSegment(r, alt, s))
    308                                         selection.add(s);
    309 
    310303                        // ways
    311304                        for (Way w : Main.ds.ways) {
    312                                 if (w.deleted)
    313                                         continue;
    314                                 boolean someSelectableSegment = false;
    315                                 boolean wholeWaySelected = true;
    316                                 for (Segment s : w.segments) {
    317                                         if (s.incomplete)
     305                                if (w.deleted || w.nodes.isEmpty())
    318306                                                continue;
    319                                         someSelectableSegment = true;
    320                                         if (!rectangleContainSegment(r, alt, s)) {
    321                                                 wholeWaySelected = false;
     307                                if (alt) {
     308                                        for (Node n : w.nodes) {
     309                                                if (r.contains(nc.getPoint(n.eastNorth))) {
     310                                                        selection.add(w);
    322311                                                break;
    323312                                        }
    324313                                }
    325                                 if (someSelectableSegment && wholeWaySelected)
    326                                         selection.add(w);
     314                                } else {
     315                                        boolean allIn = true;
     316                                        for (Node n : w.nodes) {
     317                                                if (!r.contains(nc.getPoint(n.eastNorth))) {
     318                                                        allIn = false;
     319                                                        break;
    327320                        }
    328321                }
     322                                        if (allIn) selection.add(w);
     323                                }
     324        }
     325                }
    329326                return selection;
    330         }
    331 
    332         /**
    333          * Decide whether the segment is in the rectangle Return
    334          * <code>true</code>, if it is in or false if not.
    335          *
    336          * @param r                     The rectangle, in which the segment has to be.
    337          * @param alt           Whether user pressed the Alt key
    338          * @param ls            The segment.
    339          * @return <code>true</code>, if the Segment was added to the selection.
    340          */
    341         private boolean rectangleContainSegment(Rectangle r, boolean alt, Segment ls) {
    342                 if (ls.incomplete)
    343                         return false;
    344                 if (alt) {
    345                         Point p1 = nc.getPoint(ls.from.eastNorth);
    346                         Point p2 = nc.getPoint(ls.to.eastNorth);
    347                         if (r.intersectsLine(p1.x, p1.y, p2.x, p2.y))
    348                                 return true;
    349                 } else {
    350                         if (r.contains(nc.getPoint(ls.from.eastNorth))
    351                                         && r.contains(nc.getPoint(ls.to.eastNorth)))
    352                                 return true;
    353                 }
    354                 return false;
    355327        }
    356328       
  • trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java

    r301 r343  
    3333import org.openstreetmap.josm.data.SelectionChangedListener;
    3434import org.openstreetmap.josm.data.osm.DataSet;
     35import org.openstreetmap.josm.data.osm.Relation;
     36import org.openstreetmap.josm.data.osm.RelationMember;
    3537import org.openstreetmap.josm.data.osm.Node;
    3638import org.openstreetmap.josm.data.osm.OsmPrimitive;
    37 import org.openstreetmap.josm.data.osm.Segment;
    3839import org.openstreetmap.josm.data.osm.Way;
    3940import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
     
    8889                add(buttonPanel, BorderLayout.SOUTH);
    8990
    90                 DataSet.listeners.add(new SelectionChangedListener(){
     91                DataSet.selListeners.add(new SelectionChangedListener(){
    9192                        public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    9293                                displaylist.clearSelection();
     
    130131                                model.addElement(osm);
    131132                for (OsmPrimitive osm : this.conflicts.keySet())
    132                         if (osm instanceof Segment)
    133                                 model.addElement(osm);
    134                 for (OsmPrimitive osm : this.conflicts.keySet())
    135133                        if (osm instanceof Way)
    136134                                model.addElement(osm);
     
    155153                                g.drawRect(p.x-1, p.y-1, 2, 2);
    156154                        }
    157                         public void visit(Segment ls) {
    158                                 if (ls.incomplete)
    159                                         return;
    160                                 Point p1 = nc.getPoint(ls.from.eastNorth);
    161                                 Point p2 = nc.getPoint(ls.to.eastNorth);
     155                        public void visit(Node n1, Node n2) {
     156                                Point p1 = nc.getPoint(n1.eastNorth);
     157                                Point p2 = nc.getPoint(n2.eastNorth);
    162158                                g.drawLine(p1.x, p1.y, p2.x, p2.y);
    163159                        }
    164160                        public void visit(Way w) {
    165                                 for (Segment ls : w.segments)
    166                                         visit(ls);
     161                                Node lastN = null;
     162                                for (Node n : w.nodes) {
     163                                        if (lastN == null) {
     164                                                lastN = n;
     165                                                continue;
     166                                        }
     167                                        visit(lastN, n);
     168                                        lastN = n;
     169                                }
     170                        }
     171                        public void visit(Relation e) {
     172                                for (RelationMember em : e.members)
     173                                        em.member.visit(this);
    167174                        }
    168175                };
  • trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java

    r298 r343  
    144144                revertButton.putClientProperty("help", "Dialog/History/Revert");
    145145               
    146                 DataSet.listeners.add(this);
     146                DataSet.selListeners.add(this);
    147147        }
    148148
  • trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java

    r301 r343  
    4848import org.openstreetmap.josm.data.osm.DataSet;
    4949import org.openstreetmap.josm.data.osm.OsmPrimitive;
     50import org.openstreetmap.josm.data.osm.Relation;
     51import org.openstreetmap.josm.data.osm.RelationMember;
     52import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
    5053import org.openstreetmap.josm.gui.MapFrame;
    5154import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
     55import org.openstreetmap.josm.gui.tagging.ForwardActionListener;
    5256import org.openstreetmap.josm.gui.tagging.TaggingCellRenderer;
    53 import org.openstreetmap.josm.gui.tagging.ForwardActionListener;
    5457import org.openstreetmap.josm.gui.tagging.TaggingPreset;
    5558import org.openstreetmap.josm.tools.AutoCompleteComboBox;
     
    7780
    7881        /**
     82         * Used to display relation names in the membership table
     83         */
     84        private NameVisitor nameVisitor = new NameVisitor();
     85       
     86        /**
    7987         * Watches for double clicks and from editing or new property, depending on the
    8088         * location, the click was.
     
    8593                        if (e.getClickCount() < 2)
    8694                                return;
    87                         if (e.getSource() instanceof JScrollPane)
    88                                 add();
    89                         else {
     95       
     96                        if (e.getSource() == propertyTable)
     97                        {
    9098                                int row = propertyTable.rowAtPoint(e.getPoint());
    91                                 edit(row);
    92                         }
    93                 }
    94         }
    95 
    96         /**
    97          * Edit the value in the table row
    98          * @param row   The row of the table, from which the value is edited.
    99          */
    100         void edit(int row) {
    101                 String key = data.getValueAt(row, 0).toString();
     99                                if (row > -1) {
     100                                        propertyEdit(row);
     101                                        return;
     102                        }
     103                        } else if (e.getSource() == membershipTable) {
     104                                int row = membershipTable.rowAtPoint(e.getPoint());
     105                                if (row > -1) {
     106                                        membershipEdit(row);
     107                                        return;
     108                                }
     109                        }
     110                        add();
     111                }
     112        }
     113
     114        /**
     115         * Edit the value in the properties table row
     116         * @param row The row of the table from which the value is edited.
     117         */
     118        void propertyEdit(int row) {
     119                String key = propertyData.getValueAt(row, 0).toString();
    102120                Collection<OsmPrimitive> sel = Main.ds.getSelected();
    103121                if (sel.isEmpty()) {
     
    118136                p.add(keyField, GBC.eol().fill(GBC.HORIZONTAL));
    119137                               
    120                 final JComboBox combo = (JComboBox)data.getValueAt(row, 1);
     138                final JTextField valueField = new JTextField((String)propertyData.getValueAt(row, 1));
    121139                p.add(new JLabel(tr("Value")), GBC.std());
    122140                p.add(Box.createHorizontalStrut(10), GBC.std());
    123                 p.add(combo, GBC.eol().fill(GBC.HORIZONTAL));
     141                p.add(valueField, GBC.eol().fill(GBC.HORIZONTAL));
    124142
    125143                final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
    126144                        @Override public void selectInitialValue() {
    127                                 combo.requestFocusInWindow();
    128                                 combo.getEditor().selectAll();
     145                                valueField.requestFocusInWindow();
     146                                valueField.selectAll();
    129147                        }
    130148                };
    131149                final JDialog dlg = optionPane.createDialog(Main.parent, tr("Change values?"));
    132                 combo.getEditor().addActionListener(new ActionListener(){
     150                valueField.addActionListener(new ActionListener(){
    133151                        public void actionPerformed(ActionEvent e) {
    134152                                optionPane.setValue(JOptionPane.OK_OPTION);
     
    136154                        }
    137155                });
    138                 String oldComboEntry = combo.getEditor().getItem().toString();
    139156                dlg.setVisible(true);
    140157
     
    142159                if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
    143160                                (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
    144                         combo.getEditor().setItem(oldComboEntry);
    145                         return;
    146                 }
    147 
    148                 String value = combo.getEditor().getItem().toString();
     161                        return;
     162                }
     163
     164                String value = valueField.getText();
    149165                if (value.equals(tr("<different>")))
    150166                        return;
     
    164180                }
    165181
    166                 if (!key.equals(newkey) || value == null)
    167182                        selectionChanged(sel); // update whole table
    168 
    169183                Main.parent.repaint(); // repaint all - drawing could have been changed
     184        }
     185
     186        /**
     187         * This simply fires up an relation editor for the relation shown; everything else
     188         * is the editor's business.
     189         *
     190         * @param row
     191         */
     192        void membershipEdit(int row) { 
     193                final RelationEditor editor = new RelationEditor((Relation)membershipData.getValueAt(row, 0));
     194                editor.setVisible(true);
    170195        }
    171196
     
    197222                        }
    198223                }
    199                 for (int i = 0; i < data.getRowCount(); ++i)
    200                         allData.remove(data.getValueAt(i, 0));
     224                for (int i = 0; i < propertyData.getRowCount(); ++i)
     225                        allData.remove(propertyData.getValueAt(i, 0));
    201226                final AutoCompleteComboBox keys = new AutoCompleteComboBox();
    202227                keys.setPossibleItems(allData.keySet());
     
    249274         */
    250275        private void delete(int row) {
    251                 String key = data.getValueAt(row, 0).toString();
     276                String key = propertyData.getValueAt(row, 0).toString();
    252277                Collection<OsmPrimitive> sel = Main.ds.getSelected();
    253278                Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, null));
     
    258283         * The property data.
    259284         */
    260         private final DefaultTableModel data = new DefaultTableModel(){
     285        private final DefaultTableModel propertyData = new DefaultTableModel() {
    261286                @Override public boolean isCellEditable(int row, int column) {
    262287                        return false;
    263288                }
    264289                @Override public Class<?> getColumnClass(int columnIndex) {
    265                         return columnIndex == 1 ? JComboBox.class : String.class;
     290                        return String.class;
    266291                }
    267292        };
     293
     294        /**
     295         * The membership data.
     296         */
     297        private final DefaultTableModel membershipData = new DefaultTableModel() {
     298                @Override public boolean isCellEditable(int row, int column) {
     299                        return false;
     300                }
     301                @Override public Class<?> getColumnClass(int columnIndex) {
     302                        return columnIndex == 1 ? Relation.class : String.class;
     303                }
     304        };
     305       
    268306        /**
    269307         * The properties list.
    270308         */
    271         private final JTable propertyTable = new JTable(data);
     309        private final JTable propertyTable = new JTable(propertyData);
     310        private final JTable membershipTable = new JTable(membershipData);
     311
    272312        public JComboBox taggingPresets = new JComboBox();
    273313
     
    277317         */
    278318        public PropertiesDialog(MapFrame mapFrame) {
    279                 super(tr("Properties"), "propertiesdialog", tr("Properties for selected objects."), KeyEvent.VK_P, 150);
     319                super(tr("Properties/Memberships"), "propertiesdialog", tr("Properties for selected objects."), KeyEvent.VK_P, 150);
    280320
    281321                if (TaggingPresetPreference.taggingPresets.size() > 0) {
     
    300340                taggingPresets.setRenderer(new TaggingCellRenderer());
    301341
    302                 data.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
     342                // setting up the properties table
     343               
     344                propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
    303345                propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    304                 propertyTable.setDefaultRenderer(JComboBox.class, new DefaultTableCellRenderer(){
     346       
     347                propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer(){
    305348                        @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    306349                                Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
    307350                                if (c instanceof JLabel) {
    308                                         String str = ((JComboBox)value).getEditor().getItem().toString();
     351                                        String str = (String) value;
    309352                                        ((JLabel)c).setText(str);
    310353                                        if (str.equals(tr("<different>")))
     
    314357                        }
    315358                });
    316                 propertyTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer(){
     359               
     360                // setting up the membership table
     361               
     362                membershipData.setColumnIdentifiers(new String[]{tr("Member Of"),tr("Role")});
     363                membershipTable.setRowSelectionAllowed(false);
     364               
     365                membershipTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
    317366                        @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    318                                 return super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
     367                                Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
     368                                if (c instanceof JLabel) {
     369                                        nameVisitor.visit((Relation)value);
     370                                        ((JLabel)c).setText(nameVisitor.name);
     371                                }
     372                                return c;
    319373                        }
    320374                });
     375               
     376                // combine both tables and wrap them in a scrollPane
     377                JPanel bothTables = new JPanel();
     378                bothTables.setLayout(new GridBagLayout());
     379                bothTables.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
     380                bothTables.add(propertyTable, GBC.eol().fill(GBC.BOTH));
     381                bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
     382                bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH));
     383               
    321384                DblClickWatch dblClickWatch = new DblClickWatch();
    322385                propertyTable.addMouseListener(dblClickWatch);
    323                 JScrollPane scrollPane = new JScrollPane(propertyTable);
     386                membershipTable.addMouseListener(dblClickWatch);
     387                JScrollPane scrollPane = new JScrollPane(bothTables);
    324388                scrollPane.addMouseListener(dblClickWatch);
    325389                add(scrollPane, BorderLayout.CENTER);
     
    335399                                                JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
    336400                                        else
    337                                                 edit(sel);
     401                                                propertyEdit(sel);
    338402                                } else if (e.getActionCommand().equals("Delete")) {
    339403                                        if (sel == -1)
     
    344408                        }
    345409                };
     410               
    346411                buttonPanel.add(createButton(marktr("Add"),tr("Add a new key/value pair to all objects"), KeyEvent.VK_A, buttonAction));
    347412                buttonPanel.add(createButton(marktr("Edit"),tr( "Edit the value of the selected key for all objects"), KeyEvent.VK_E, buttonAction));
     
    349414                add(buttonPanel, BorderLayout.SOUTH);
    350415
    351                 DataSet.listeners.add(this);
     416                DataSet.selListeners.add(this);
    352417        }
    353418
     
    375440                if (propertyTable.getCellEditor() != null)
    376441                        propertyTable.getCellEditor().cancelCellEditing();
    377                 data.setRowCount(0);
     442
     443                // re-load property data
     444               
     445                propertyData.setRowCount(0);
    378446
    379447                Map<String, Integer> valueCount = new HashMap<String, Integer>();
     
    391459                }
    392460                for (Entry<String, Collection<String>> e : props.entrySet()) {
    393                         JComboBox value = new JComboBox(e.getValue().toArray());
    394                         value.setEditable(true);
    395                         value.getEditor().setItem(e.getValue().size() > 1 || valueCount.get(e.getKey()) != newSelection.size() ? tr("<different>") : e.getValue().iterator().next());
    396                         data.addRow(new Object[]{e.getKey(), value});
     461                        String value=(e.getValue().size() > 1 || valueCount.get(e.getKey()) != newSelection.size() ? tr("<different>") : e.getValue().iterator().next());
     462                        propertyData.addRow(new Object[]{e.getKey(), value});
     463                }
     464               
     465                // re-load membership data
     466                // this is rather expensive since we have to walk through all members of all existing relationships.
     467                // could use back references here for speed if necessary.
     468               
     469                membershipData.setRowCount(0);
     470               
     471                Map<Relation, Integer> valueCountM = new HashMap<Relation, Integer>();
     472                TreeMap<Relation, Collection<String>> roles = new TreeMap<Relation, Collection<String>>();
     473                for (Relation r : Main.ds.relations) {
     474                        for (RelationMember m : r.members) {
     475                                if (newSelection.contains(m.member)) {
     476                                        Collection<String> value = roles.get(r);
     477                                        if (value == null) {
     478                                                value = new TreeSet<String>();
     479                                                roles.put(r, value);
     480                                        }
     481                                        value.add(m.role);
     482                                        valueCountM.put(r, valueCount.containsKey(r) ? valueCount.get(r)+1 : 1);
     483                                }
     484                        }
     485                }
     486               
     487                for (Entry<Relation, Collection<String>> e : roles.entrySet()) {
     488                        //JComboBox value = new JComboBox(e.getValue().toArray());
     489                        //value.setEditable(true);
     490                        //value.getEditor().setItem(e.getValue().size() > 1 || valueCount.get(e.getKey()) != newSelection.size() ? tr("<different>") : e.getValue().iterator().next());
     491                        String value = e.getValue().size() > 1 || valueCountM.get(e.getKey()) != newSelection.size() ? tr("<different>") : e.getValue().iterator().next();
     492                        membershipData.addRow(new Object[]{e.getKey(), value});
    397493                }
    398494        }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java

    r298 r343  
    8080                selectionChanged(Main.ds.getSelected());
    8181
    82                 DataSet.listeners.add(this);
     82                DataSet.selListeners.add(this);
    8383        }
    8484
  • trunk/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java

    r298 r343  
    5454                selectionChanged(Main.ds.getSelected());
    5555               
    56                 DataSet.listeners.add(this);
     56                DataSet.selListeners.add(this);
    5757        }
    5858
  • trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    r312 r343  
    3434import org.openstreetmap.josm.data.osm.DataSet;
    3535import org.openstreetmap.josm.data.osm.DataSource;
     36import org.openstreetmap.josm.data.osm.Relation;
    3637import org.openstreetmap.josm.data.osm.Node;
    3738import org.openstreetmap.josm.data.osm.OsmPrimitive;
    38 import org.openstreetmap.josm.data.osm.Segment;
    3939import org.openstreetmap.josm.data.osm.Way;
    4040import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
     
    6060                public final int[] normal = new int[3];         
    6161                public final int[] deleted = new int[3];
    62                 public final String[] names = {"node", "segment", "way"};
     62                public final String[] names = {"node", "way", "relation"};
    6363
    6464                private void inc(final OsmPrimitive osm, final int i) {
     
    7272                }
    7373
    74                 public void visit(final Segment ls) {
    75                         inc(ls, 1);
    76                 }
    77 
    7874                public void visit(final Way w) {
     75                        inc(w, 1);
     76                }
     77                public void visit(final Relation w) {
    7978                        inc(w, 2);
    8079                }
     
    133132         * Draw all primitives in this layer but do not draw modified ones (they
    134133         * are drawn by the edit layer).
    135          * Draw nodes last to overlap the segments they belong to.
     134         * Draw nodes last to overlap the ways they belong to.
    136135         */
    137136        @Override public void paint(final Graphics g, final MapView mv) {
     
    163162                String tool = "";
    164163                tool += undeletedSize(data.nodes)+" "+trn("node", "nodes", undeletedSize(data.nodes))+", ";
    165                 tool += undeletedSize(data.segments)+" "+trn("segment", "segments", undeletedSize(data.segments))+", ";
    166164                tool += undeletedSize(data.ways)+" "+trn("way", "ways", undeletedSize(data.ways));
    167165                if (associatedFile != null)
     
    222220                        for (final Iterator<Node> it = data.nodes.iterator(); it.hasNext();)
    223221                                cleanIterator(it, processedSet);
    224                         for (final Iterator<Segment> it = data.segments.iterator(); it.hasNext();)
    225                                 cleanIterator(it, processedSet);
    226222                        for (final Iterator<Way> it = data.ways.iterator(); it.hasNext();)
    227223                                cleanIterator(it, processedSet);
  • trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java

    r298 r343  
    3838import org.openstreetmap.josm.data.osm.DataSet;
    3939import org.openstreetmap.josm.data.osm.Node;
    40 import org.openstreetmap.josm.data.osm.Segment;
    4140import org.openstreetmap.josm.data.osm.Way;
    4241import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
     
    7170                        for (Collection<GpsPoint> c : data) {
    7271                                Way w = new Way();
    73                                 Node start = null;
    7472                                for (GpsPoint p : c) {
    75                                         Node end = new Node(p.latlon);
    76                                         ds.nodes.add(end);
    77                                         if (start != null) {
    78                                                 Segment segment = new Segment(start,end);
    79                                                 w.segments.add(segment);
    80                                                 ds.segments.add(segment);
    81                                         }
    82                                         start = end;
     73                                        Node n = new Node(p.latlon);
     74                                        ds.nodes.add(n);
     75                                        w.nodes.add(n);
    8376                                }
    8477                                ds.ways.add(w);
  • trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java

    r302 r343  
    1313import java.util.Arrays;
    1414import java.util.Collection;
     15import java.util.Collections;
    1516import java.util.Comparator;
    1617import java.util.HashMap;
     
    194195                pluginMap = new HashMap<PluginDescription, Boolean>();
    195196                pluginPanel.removeAll();
    196                 Collection<String> enabledPlugins = Arrays.asList(Main.pref.get("plugins").split(","));
     197
     198                // the following could probably be done more elegantly?
     199                Collection<String> enabledPlugins = null;
     200                String enabledProp = Main.pref.get("plugins");
     201                if ((enabledProp == null) || ("".equals(enabledProp))) {
     202                        enabledPlugins = Collections.EMPTY_SET;
     203                }
     204                else
     205                {
     206                        enabledPlugins = Arrays.asList(enabledProp.split(","));
     207                }
     208               
    197209                for (final PluginDescription plugin : availablePlugins) {
    198210                        boolean enabled = enabledPlugins.contains(plugin.name);
  • trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java

    r298 r343  
    103103                Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data..."));
    104104                final DataSet data = OsmReader.parseDataSet(in, null, Main.pleaseWaitDlg);
    105                 String origin = Main.pref.get("osm-server.url")+"/"+Main.pref.get("osm-server.version", "0.4");
     105                String origin = Main.pref.get("osm-server.url")+"/"+Main.pref.get("osm-server.version", "0.5");
    106106                Bounds bounds = new Bounds(new LatLon(lat1, lon1), new LatLon(lat2, lon2));
    107107                        DataSource src = new DataSource(bounds, origin);
  • trunk/src/org/openstreetmap/josm/io/GpxWriter.java

    r298 r343  
    1111import org.openstreetmap.josm.data.osm.Node;
    1212import org.openstreetmap.josm.data.osm.OsmPrimitive;
    13 import org.openstreetmap.josm.data.osm.Segment;
    1413import org.openstreetmap.josm.data.osm.Way;
    1514import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
     
    3231
    3332        /**
    34          * Export the dataset to gpx. Only the physical segment structure is
    35          * exported. To do this, the list of ways is processed. If a way span a
    36          * sequence of segments, this is added as one trkseg.
    37          * Then, all remaining segments are added in one extra trk. Finally,
    38          * all remaining nodes are added as wpt.
     33         * Export the dataset to gpx.  The ways are converted to trksegs, each in
     34         * a seperate trk.  Finally, all remaining nodes are added as wpt.
    3935         */
    4036        public static final class All implements XmlWriter.OsmWriterInterface {
     
    106102                                        continue;
    107103                                out.println("  <trk>");
    108                                 Segment oldLs = null;
    109                                 for (Segment ls : w.segments) {
    110                                         if (ls.incomplete)
    111                                                 continue;
    112                                         // end old segemnt, if no longer match a chain
    113                                         if (oldLs != null && !oldLs.to.coor.equals(ls.from.coor)) {
    114                                                 out.println("    </trkseg>");
    115                                                 writer.outputNode(oldLs.to, false);
    116                                                 all.remove(oldLs.to);
    117                                                 oldLs = null;
    118                                         }
    119                                         // start new segment if necessary
    120                                         if (oldLs == null)
    121104                                                out.println("    <trkseg>");
    122                                         writer.outputNode(ls.from, false);
    123                                         all.remove(ls.from);
    124                                         oldLs = ls;
    125                                         all.remove(ls);
    126                                 }
    127                                 // write last node if there
    128                                 if (oldLs != null) {
    129                                         writer.outputNode(oldLs.to, false);
    130                                         all.remove(oldLs.to);
     105                                for (Node n : w.nodes) {
     106                                        writer.outputNode(n, false);
     107                                        all.remove(n);
     108                        }
    131109                                        out.println("    </trkseg>");
    132                                 }
    133110                                out.println("  </trk>");
    134111                                all.remove(w);
    135                         }
    136 
    137                         // add remaining segments
    138                         Collection<Segment> segments = new LinkedList<Segment>();
    139                         for (OsmPrimitive osm : all)
    140                                 if (osm instanceof Segment && !((Segment)osm).incomplete)
    141                                         segments.add((Segment)osm);
    142                         if (!segments.isEmpty()) {
    143                                 out.println("  <trk>");
    144                                 for (Segment ls : segments) {
    145                                         out.println("    <trkseg>");
    146                                         writer.outputNode(ls.from, false);
    147                                         all.remove(ls.from);
    148                                         writer.outputNode(ls.to, false);
    149                                         all.remove(ls.to);
    150                                         out.println("    </trkseg>");
    151                                         all.remove(ls);
    152                                 }
    153                                 out.println("  </trk>");
    154112                        }
    155113
  • trunk/src/org/openstreetmap/josm/io/OsmIdReader.java

    r319 r343  
    3131
    3232        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
    33                 if (qName.equals("node") || qName.equals("segment") || qName.equals("way")) {
     33                if (qName.equals("node") || qName.equals("way")) {
    3434                        try {
    3535                                entries.put(Long.valueOf(atts.getValue("id")), qName);
  • trunk/src/org/openstreetmap/josm/io/OsmReader.java

    r319 r343  
    2424import org.openstreetmap.josm.data.osm.DataSet;
    2525import org.openstreetmap.josm.data.osm.DataSource;
     26import org.openstreetmap.josm.data.osm.Relation;
     27import org.openstreetmap.josm.data.osm.RelationMember;
    2628import org.openstreetmap.josm.data.osm.Node;
    2729import org.openstreetmap.josm.data.osm.OsmPrimitive;
    28 import org.openstreetmap.josm.data.osm.Segment;
    2930import org.openstreetmap.josm.data.osm.User;
    3031import org.openstreetmap.josm.data.osm.Way;
     
    4445 * all nodes are read and stored. Other information than nodes are stored in a raw list
    4546 *
    46  * The second phase reads from the raw list all segments and create Segment objects.
    47  *
    48  * The third phase read all ways out of the remaining objects in the raw list.
     47 * The second phase read all ways out of the remaining objects in the raw list.
    4948 *
    5049 * @author Imi
     
    9190
    9291        /**
    93          * Data structure for the remaining segment objects
    94          * Maps the raw attributes to key/value pairs.
    95          */
    96         private Map<OsmPrimitiveData, long[]> segs = new HashMap<OsmPrimitiveData, long[]>();
     92         * Used as a temporary storage for relation members, before they
     93         * are resolved into pointers to real objects.
     94         */
     95        private static class RelationMemberData {
     96                public String type;
     97                public long id;
     98                public RelationMember relationMember;
     99        }
    97100
    98101        /**
     
    100103         */
    101104        private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>();
     105
     106        /**
     107         * Data structure for relation objects
     108         */
     109        private Map<OsmPrimitiveData, Collection<RelationMemberData>> relations = new HashMap<OsmPrimitiveData, Collection<RelationMemberData>>();
    102110
    103111        /**
     
    132140                                                ds.dataSources.add(src);
    133141                                        }
     142                                       
     143                                // ---- PARSING NODES AND WAYS ----
     144                                       
    134145                                } else if (qName.equals("node")) {
    135146                                        current = new Node(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon")));
    136147                                        readCommon(atts, current);
    137148                                        nodes.put(current.id, (Node)current);
    138                                 } else if (qName.equals("segment")) {
    139                                         current = new OsmPrimitiveData();
    140                                         readCommon(atts, current);
    141                                         segs.put((OsmPrimitiveData)current, new long[]{getLong(atts, "from"), getLong(atts, "to")});
    142149                                } else if (qName.equals("way")) {
    143150                                        current = new OsmPrimitiveData();
    144151                                        readCommon(atts, current);
    145152                                        ways.put((OsmPrimitiveData)current, new LinkedList<Long>());
    146                                 } else if (qName.equals("seg")) {
     153                                } else if (qName.equals("nd")) {
    147154                                        Collection<Long> list = ways.get(current);
    148155                                        if (list == null)
    149                                                 throw new SAXException(tr("Found <seg> tag on non-way."));
    150                                         long id = getLong(atts, "id");
     156                                                throw new SAXException(tr("Found <nd> element in non-way."));
     157                                        long id = getLong(atts, "ref");
    151158                                        if (id == 0)
    152                                                 throw new SAXException(tr("Incomplete segment with id=0"));
     159                                                throw new SAXException(tr("<nd> has zero ref"));
    153160                                        list.add(id);
    154                                 } else if (qName.equals("tag"))
     161
     162                                // ---- PARSING RELATIONS ----                 
     163
     164                                } else if (qName.equals("relation")) {
     165                                        current = new OsmPrimitiveData();
     166                                        readCommon(atts, current);
     167                                        relations.put((OsmPrimitiveData)current, new LinkedList<RelationMemberData>());
     168                                } else if (qName.equals("member")) {
     169                                        Collection<RelationMemberData> list = relations.get(current);
     170                                        if (list == null)
     171                                                throw new SAXException(tr("Found <member> tag on non-relation."));
     172                                        RelationMemberData emd = new RelationMemberData();
     173                                        emd.relationMember = new RelationMember();
     174                                        emd.id = getLong(atts, "ref");
     175                                        emd.type=atts.getValue("type");
     176                                        emd.relationMember.role = atts.getValue("role");
     177                                       
     178                                        if (emd.id == 0)
     179                                                throw new SAXException(tr("Incomplete <member> specification with ref=0"));
     180                                       
     181                                        list.add(emd);
     182                                       
     183                                // ---- PARSING TAGS (applicable to all objects) ----
     184                                       
     185                                } else if (qName.equals("tag")) {
    155186                                        current.put(atts.getValue("k"), atts.getValue("v"));
     187                                }
    156188                        } catch (NumberFormatException x) {
    157189                                x.printStackTrace(); // SAXException does not chain correctly
     
    173205        public OsmReader() {
    174206                // first add the main server version
    175                 allowedVersions.add(Main.pref.get("osm-server.version", "0.4"));
     207                allowedVersions.add(Main.pref.get("osm-server.version", "0.5"));
    176208                // now also add all compatible versions
    177209                String[] additionalVersions =
    178                         Main.pref.get("osm-server.additional-versions", "0.3").split("/,/");
     210                        Main.pref.get("osm-server.additional-versions", "").split("/,/");
     211                if (additionalVersions.length == 1 && additionalVersions[0].length() == 0)
     212                        additionalVersions = new String[] {};
    179213                allowedVersions.addAll(Arrays.asList(additionalVersions));     
    180214        }
     
    224258                        throw new SAXException(tr("Missing required attribute \"{0}\".",value));
    225259                return Long.parseLong(s);
    226         }
    227 
    228         private void createSegments() {
    229                 for (Entry<OsmPrimitiveData, long[]> e : segs.entrySet()) {
    230                         Node from = findNode(e.getValue()[0]);
    231                         Node to = findNode(e.getValue()[1]);
    232                         if (from == null || to == null)
    233                                 continue; //TODO: implement support for incomplete nodes.
    234                         Segment s = new Segment(from, to);
    235                         e.getKey().copyTo(s);
    236                         segments.put(s.id, s);
    237                         adder.visit(s);
    238                 }
    239260        }
    240261
     
    253274    }
    254275
    255         private Segment findSegment(long id) {
    256                 Segment s = segments.get(id);
    257                 if (s != null)
    258                         return s;
    259                 for (Segment seg : references.segments)
    260                         if (seg.id == id)
    261                                 return seg;
    262                 // TODO: This has to be changed to support multiple layers.
    263                 for (Segment seg : Main.ds.segments)
    264                         if (seg.id == id)
    265                                 return new Segment(seg);
    266                 return null;
    267         }
    268 
    269276        private void createWays() {
    270277                for (Entry<OsmPrimitiveData, Collection<Long>> e : ways.entrySet()) {
    271278                        Way w = new Way();
     279                        boolean failed = false;
    272280                        for (long id : e.getValue()) {
    273                                 Segment s = findSegment(id);
    274                                 if (s == null) {
    275                                         s = new Segment(id); // incomplete line segment
    276                                         adder.visit(s);
     281                                Node n = findNode(id);
     282                                if (n == null) {
     283                                        failed = true;
     284                                        break;
    277285                                }
    278                                 w.segments.add(s);
     286                                w.nodes.add(n);
    279287                        }
     288                        if (failed) continue;
    280289                        e.getKey().copyTo(w);
    281290                        adder.visit(w);
     
    284293
    285294        /**
    286          * All read segments after phase 2.
    287          */
    288         private Map<Long, Segment> segments = new HashMap<Long, Segment>();
     295         * Return the Way object with the given id, or null if it doesn't
     296         * exist yet. This method only looks at ways stored in the data set.
     297         *
     298         * @param id
     299         * @return way object or null
     300         */
     301        private Way findWay(long id) {
     302                for (Way wy : ds.ways)
     303                        if (wy.id == id)
     304                                return wy;
     305                for (Way wy : Main.ds.ways)
     306                        if (wy.id == id)
     307                                return wy;
     308                return null;
     309        }
     310
     311        /**
     312         * Return the Relation object with the given id, or null if it doesn't
     313         * exist yet. This method only looks at relations stored in the data set.
     314         *
     315         * @param id
     316         * @return relation object or null
     317         */
     318        private Relation findRelation(long id) {
     319                for (Relation e : ds.relations)
     320                        if (e.id == id)
     321                                return e;
     322                for (Relation e : Main.ds.relations)
     323                        if (e.id == id)
     324                                return e;
     325                return null;
     326        }
     327
     328        /**
     329         * Create relations. This is slightly different than n/s/w because
     330         * unlike other objects, relations may reference other relations; it
     331         * is not guaranteed that a referenced relation will have been created
     332         * before it is referenced. So we have to create all relations first,
     333         * and populate them later.
     334         */
     335        private void createRelations() {
     336               
     337                // pass 1 - create all relations
     338                for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) {
     339                        Relation en = new Relation();
     340                        e.getKey().copyTo(en);
     341                        adder.visit(en);
     342                }
     343
     344                // pass 2 - sort out members
     345                for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) {
     346                        Relation en = findRelation(e.getKey().id);
     347                        if (en == null) throw new Error("Failed to create relation " + e.getKey().id);
     348                       
     349                        for (RelationMemberData emd : e.getValue()) {
     350                                RelationMember em = emd.relationMember;
     351                                if (emd.type.equals("node")) {
     352                                        em.member = findNode(emd.id);
     353                                        if (em.member == null) {
     354                                                em.member = new Node(emd.id);
     355                                                adder.visit((Node)em.member);
     356                                        }
     357                                } else if (emd.type.equals("way")) {
     358                                        em.member = findWay(emd.id);
     359                                        if (em.member == null) {
     360                                                em.member = new Way(emd.id);
     361                                                adder.visit((Way)em.member);
     362                                        }
     363                                } else if (emd.type.equals("relation")) {
     364                                        em.member = findRelation(emd.id);
     365                                        if (em.member == null) {
     366                                                em.member = new Relation(emd.id);
     367                                                adder.visit((Relation)em.member);
     368                                        }
     369                                } else {
     370                                        // this is an error.
     371                                }
     372                                en.members.add(em);
     373                        }
     374                }
     375        }
    289376
    290377        /**
     
    298385                osm.references = ref == null ? new DataSet() : ref;
    299386
    300                 // phase 1: Parse nodes and read in raw segments and ways
     387                // phase 1: Parse nodes and read in raw ways
    301388                InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
    302389                try {
     
    314401
    315402                try {
    316                         osm.createSegments();
    317403                        osm.createWays();
     404                        osm.createRelations();
    318405                } catch (NumberFormatException e) {
    319406                        e.printStackTrace();
  • trunk/src/org/openstreetmap/josm/io/OsmServerReader.java

    r298 r343  
    3030         */
    3131        protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
    32                 String version = Main.pref.get("osm-server.version", "0.4");
     32                String version = Main.pref.get("osm-server.version", "0.5");
    3333                urlStr = Main.pref.get("osm-server.url")+"/"+version+"/" + urlStr;
    3434                System.out.println("download: "+urlStr);
  • trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java

    r298 r343  
    1717
    1818import org.openstreetmap.josm.Main;
     19import org.openstreetmap.josm.data.osm.Relation;
    1920import org.openstreetmap.josm.data.osm.Node;
    2021import org.openstreetmap.josm.data.osm.OsmPrimitive;
    21 import org.openstreetmap.josm.data.osm.Segment;
    2222import org.openstreetmap.josm.data.osm.Way;
    2323import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
     
    9696
    9797        /**
    98          * Upload a segment (without the nodes).
    99          */
    100         public void visit(Segment ls) {
    101                 if (ls.id == 0 && !ls.deleted && ls.get("created_by") == null) {
    102                         ls.put("created_by", "JOSM");
    103                         sendRequest("PUT", "segment", ls, true);
    104                 } else if (ls.deleted) {
    105                         sendRequest("DELETE", "segment", ls, false);
    106                 } else {
    107                         sendRequest("PUT", "segment", ls, true);
    108                 }
    109                 processed.add(ls);
    110         }
    111 
    112         /**
    113          * Upload a whole way with the complete segment id list.
     98         * Upload a whole way with the complete node id list.
    11499         */
    115100        public void visit(Way w) {
     
    125110        }
    126111
     112        /**
     113         * Upload an relation with all members.
     114         */
     115        public void visit(Relation e) {
     116                if (e.id == 0 && !e.deleted && e.get("created_by") == null) {
     117                        e.put("created_by", "JOSM");
     118                        sendRequest("PUT", "relation", e, true);
     119                } else if (e.deleted) {
     120                        sendRequest("DELETE", "relation", e, false);
     121                } else {
     122                        sendRequest("PUT", "relation", e, true);
     123                }
     124                processed.add(e);
     125        }
    127126        /**
    128127         * Read a long from the input stream and return it.
     
    154153                        OsmPrimitive osm, boolean addBody) {
    155154                try {
    156                         String version = Main.pref.get("osm-server.version", "0.4");
     155                        String version = Main.pref.get("osm-server.version", "0.5");
    157156                        URL url = new URL(
    158157                                        Main.pref.get("osm-server.url") +
    159158                                        "/" + version +
    160159                                        "/" + urlSuffix +
    161                                         "/" + ((version.equals("0.4") && osm.id==0) ? "create":osm.id));
     160                                        "/" + (osm.id==0 ? "create" : osm.id));
    162161                        System.out.println("upload to: "+url);
    163162                        activeConnection = (HttpURLConnection)url.openConnection();
  • trunk/src/org/openstreetmap/josm/io/OsmWriter.java

    r298 r343  
    99import org.openstreetmap.josm.data.osm.DataSet;
    1010import org.openstreetmap.josm.data.osm.DataSource;
     11import org.openstreetmap.josm.data.osm.Relation;
     12import org.openstreetmap.josm.data.osm.RelationMember;
    1113import org.openstreetmap.josm.data.osm.Node;
    1214import org.openstreetmap.josm.data.osm.OsmPrimitive;
    13 import org.openstreetmap.josm.data.osm.Segment;
    1415import org.openstreetmap.josm.data.osm.Way;
    1516import org.openstreetmap.josm.data.osm.visitor.Visitor;
     
    3738                public void header(PrintWriter out) {
    3839                        out.print("<osm version='");
    39                         out.print(Main.pref.get("osm-server.version", "0.4"));
     40                        out.print(Main.pref.get("osm-server.version", "0.5"));
    4041                        out.println("' generator='JOSM'>");
    4142                }
     
    4445                }
    4546        }
     47       
     48        // simple helper to write the object's class to the out stream
     49        private Visitor typeWriteVisitor = new Visitor() {
     50                public void visit(Node n) { out.print("node"); }
     51                public void visit(Way w) { out.print("way"); }
     52                public void visit(Relation e) { out.print("relation"); }
     53        };
    4654       
    4755        /**
     
    6977                                if (shouldWrite(n))
    7078                                        writer.visit(n);
    71                         for (Segment ls : ds.segments)
    72                                 if (shouldWrite(ls))
    73                                         writer.visit(ls);
    7479                        for (Way w : ds.ways)
    7580                                if (shouldWrite(w))
    7681                                        writer.visit(w);
     82                        for (Relation e : ds.relations)
     83                                if (shouldWrite(e))
     84                                        writer.visit(e);
    7785        }
    7886
     
    123131        }
    124132
    125         public void visit(Segment ls) {
    126                 if (ls.incomplete)
    127                         return; // Do not write an incomplete segment
    128                 addCommon(ls, "segment");
    129                 out.print(" from='"+getUsedId(ls.from)+"' to='"+getUsedId(ls.to)+"'");
    130                 addTags(ls, "segment", true);
    131         }
    132 
    133133        public void visit(Way w) {
    134134                addCommon(w, "way");
    135135                out.println(">");
    136                 for (Segment ls : w.segments)
    137                         out.println("    <seg id='"+getUsedId(ls)+"' />");
     136                for (Node n : w.nodes)
     137                        out.println("    <nd ref='"+getUsedId(n)+"' />");
    138138                addTags(w, "way", false);
    139139        }
     140
     141        public void visit(Relation e) {
     142                addCommon(e, "relation");
     143                out.println(">");
     144                for (RelationMember em : e.members) {
     145                        out.print("    <member type='");
     146                        em.member.visit(typeWriteVisitor);
     147                        out.println("' ref='"+getUsedId(em.member)+"' role='" +
     148                                XmlWriter.encode(em.role) + "' />");
     149                }
     150                addTags(e, "relation", false);
     151        }
     152       
    140153
    141154        /**
  • trunk/src/org/openstreetmap/josm/io/RawGpsReader.java

    r319 r343  
    9494                                }
    9595                                tags.push(tag);
    96                         }
     96                        } else if (peek.equals("text")) {
     97                            String tag = tags.pop();
     98                            if (tags.empty() || !tags.peek().equals("link")) {
     99                                tags.push(tag);
     100                                return;
     101                            }
     102                            String contents = new String(ch, start, length);
     103                            // we just want the contents of <link><text></text></link> to
     104                            // all be stored under link.
     105                            String oldContents = currentTagValues.get("link");
     106                            if (oldContents == null) {
     107                                currentTagValues.put("link", contents);
     108                            } else {
     109                                currentTagValues.put("link", oldContents + contents);
     110                            }
     111                            tags.push(tag);
     112                        }
    97113                }
    98114
     
    109125                                newTrack();
    110126                                currentTagValues.clear();
    111                         }
     127                        } else if (qName.equals("link")) {
     128                            String contents = currentTagValues.get(qName);
     129                            if (contents != null) {
     130                                // strip off leading and trailing whitespace
     131                                currentTagValues.put(qName,
     132                                                     contents
     133                                                      .replaceFirst("^\\s+", "")
     134                                                      .replaceFirst("\\s+$", ""));
     135                            }
     136                        }
    112137                        tags.pop();
    113138                }
  • trunk/src/org/openstreetmap/josm/io/XmlWriter.java

    r298 r343  
    6767        }
    6868
    69 
    70 
    7169        /**
    7270         * The output writer to save the values to.
Note: See TracChangeset for help on using the changeset viewer.