source: josm/branch/0.5/src/org/openstreetmap/josm/actions/CombineWayAction.java@ 329

Last change on this file since 329 was 329, checked in by framm, 17 years ago

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

Changes incorporated here:

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

File size: 5.6 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GridBagLayout;
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.util.Collection;
10import java.util.HashMap;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.ListIterator;
14import java.util.ArrayList;
15import java.util.Map;
16import java.util.Set;
17import java.util.TreeMap;
18import java.util.TreeSet;
19import java.util.Map.Entry;
20import java.util.HashSet;
21
22import javax.swing.Box;
23import javax.swing.JComboBox;
24import javax.swing.JLabel;
25import javax.swing.JOptionPane;
26import javax.swing.JPanel;
27
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.command.ChangeCommand;
30import org.openstreetmap.josm.command.Command;
31import org.openstreetmap.josm.command.DeleteCommand;
32import org.openstreetmap.josm.command.SequenceCommand;
33import org.openstreetmap.josm.data.SelectionChangedListener;
34import org.openstreetmap.josm.data.osm.DataSet;
35import org.openstreetmap.josm.data.osm.OsmPrimitive;
36import org.openstreetmap.josm.data.osm.Way;
37import org.openstreetmap.josm.data.osm.Node;
38import org.openstreetmap.josm.data.osm.NodePair;
39import org.openstreetmap.josm.tools.GBC;
40
41/**
42 * Combines multiple ways into one.
43 *
44 * @author Imi
45 */
46public class CombineWayAction extends JosmAction implements SelectionChangedListener {
47
48 public CombineWayAction() {
49 super(tr("Combine Way"), "combineway", tr("Combine several ways into one."), KeyEvent.VK_C, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK, true);
50 DataSet.selListeners.add(this);
51 }
52
53 public void actionPerformed(ActionEvent event) {
54 Collection<OsmPrimitive> selection = Main.ds.getSelected();
55 LinkedList<Way> selectedWays = new LinkedList<Way>();
56
57 for (OsmPrimitive osm : selection)
58 if (osm instanceof Way)
59 selectedWays.add((Way)osm);
60
61 if (selectedWays.size() < 2) {
62 JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two ways to combine."));
63 return;
64 }
65
66 // collect properties for later conflict resolving
67 Map<String, Set<String>> props = new TreeMap<String, Set<String>>();
68 for (Way w : selectedWays) {
69 for (Entry<String,String> e : w.entrySet()) {
70 if (!props.containsKey(e.getKey()))
71 props.put(e.getKey(), new TreeSet<String>());
72 props.get(e.getKey()).add(e.getValue());
73 }
74 }
75
76 // Battle plan:
77 // 1. Split the ways into small chunks of 2 nodes and weed out
78 // duplicates.
79 // 2. Take a chunk and see if others could be appended or prepended,
80 // if so, do it and remove it from the list of remaining chunks.
81 // Rather, rinse, repeat.
82 // 3. If this algorithm does not produce a single way,
83 // complain to the user.
84 // 4. Profit!
85
86 HashSet<NodePair> chunkSet = new HashSet<NodePair>();
87 for (Way w : selectedWays) {
88 if (w.nodes.size() == 0) continue;
89 Node lastN = null;
90 for (Node n : w.nodes) {
91 if (lastN == null) {
92 lastN = n;
93 continue;
94 }
95 chunkSet.add(new NodePair(lastN, n));
96 lastN = n;
97 }
98 }
99 LinkedList<NodePair> chunks = new LinkedList<NodePair>(chunkSet);
100
101 if (chunks.isEmpty()) {
102 JOptionPane.showMessageDialog(Main.parent, tr("All the ways were empty"));
103 return;
104 }
105
106 List<Node> nodeList = chunks.poll().toArrayList();
107 while (!chunks.isEmpty()) {
108 ListIterator<NodePair> it = chunks.listIterator();
109 boolean foundChunk = false;
110 while (it.hasNext()) {
111 NodePair curChunk = it.next();
112 if (curChunk.a == nodeList.get(nodeList.size() - 1)) { // append
113 nodeList.add(curChunk.b);
114 foundChunk = true;
115 } else if (curChunk.b == nodeList.get(0)) { // prepend
116 nodeList.add(0, curChunk.a);
117 foundChunk = true;
118 }
119 if (foundChunk) {
120 it.remove();
121 break;
122 }
123 }
124 if (!foundChunk) break;
125 }
126
127 if (!chunks.isEmpty()) {
128 JOptionPane.showMessageDialog(Main.parent,
129 tr("Could not combine ways (Hint: ways have to point into the same direction)"));
130 return;
131 }
132
133 Way newWay = new Way(selectedWays.get(0));
134 newWay.nodes.clear();
135 newWay.nodes.addAll(nodeList);
136
137 // display conflict dialog
138 Map<String, JComboBox> components = new HashMap<String, JComboBox>();
139 JPanel p = new JPanel(new GridBagLayout());
140 for (Entry<String, Set<String>> e : props.entrySet()) {
141 if (e.getValue().size() > 1) {
142 JComboBox c = new JComboBox(e.getValue().toArray());
143 c.setEditable(true);
144 p.add(new JLabel(e.getKey()), GBC.std());
145 p.add(Box.createHorizontalStrut(10), GBC.std());
146 p.add(c, GBC.eol());
147 components.put(e.getKey(), c);
148 } else
149 newWay.put(e.getKey(), e.getValue().iterator().next());
150 }
151 if (!components.isEmpty()) {
152 int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter values for all conflicts."), JOptionPane.OK_CANCEL_OPTION);
153 if (answer != JOptionPane.OK_OPTION)
154 return;
155 for (Entry<String, JComboBox> e : components.entrySet())
156 newWay.put(e.getKey(), e.getValue().getEditor().getItem().toString());
157 }
158
159 LinkedList<Command> cmds = new LinkedList<Command>();
160 cmds.add(new DeleteCommand(selectedWays.subList(1, selectedWays.size())));
161 cmds.add(new ChangeCommand(selectedWays.peek(), newWay));
162 Main.main.undoRedo.add(new SequenceCommand(tr("Combine {0} ways", selectedWays.size()), cmds));
163 Main.ds.setSelected(selectedWays.peek());
164 }
165
166 /**
167 * Enable the "Combine way" menu option if more then one way is selected
168 */
169 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
170 boolean first = false;
171 for (OsmPrimitive osm : newSelection) {
172 if (osm instanceof Way) {
173 if (first) {
174 setEnabled(true);
175 return;
176 }
177 first = true;
178 }
179 }
180 setEnabled(false);
181 }
182}
Note: See TracBrowser for help on using the repository browser.