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

Merge 0.5.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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}
Note: See TracChangeset for help on using the changeset viewer.