source: josm/trunk/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java@ 3965

Last change on this file since 3965 was 3704, checked in by bastiK, 13 years ago

added multipoly plugin to josm core. authors: bilbo and Viesturs Zarins (extropy)

  • Property svn:eol-style set to native
File size: 8.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.HashMap;
11import java.util.List;
12import java.util.Map;
13
14import javax.swing.JOptionPane;
15
16import javax.swing.SwingUtilities;
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.command.AddCommand;
19import org.openstreetmap.josm.command.ChangePropertyCommand;
20import org.openstreetmap.josm.command.Command;
21import org.openstreetmap.josm.command.SequenceCommand;
22import org.openstreetmap.josm.data.osm.MultipolygonCreate;
23import org.openstreetmap.josm.data.osm.MultipolygonCreate.JoinedPolygon;
24import org.openstreetmap.josm.data.osm.OsmPrimitive;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.RelationMember;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
29import org.openstreetmap.josm.tools.Shortcut;
30
31/**
32 * Create multipolygon from selected ways automatically.
33 *
34 * New relation with type=multipolygon is created
35 *
36 * If one or more of ways is already in relation with type=multipolygon or the
37 * way is not closed, then error is reported and no relation is created
38 *
39 * The "inner" and "outer" roles are guessed automatically. First, bbox is
40 * calculated for each way. then the largest area is assumed to be outside and
41 * the rest inside. In cases with one "outside" area and several cut-ins, the
42 * guess should be always good ... In more complex (multiple outer areas) or
43 * buggy (inner and outer ways intersect) scenarios the result is likely to be
44 * wrong.
45 */
46public class CreateMultipolygonAction extends JosmAction {
47
48 public CreateMultipolygonAction() {
49 super(tr("Create multipolygon"), "multipoly_create", tr("Create multipolygon."),
50 Shortcut.registerShortcut("tools:multipoly", tr("Tool: {0}", tr("Create multipolygon")),
51 KeyEvent.VK_A, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
52 }
53 /**
54 * The action button has been clicked
55 *
56 * @param e Action Event
57 */
58 public void actionPerformed(ActionEvent e) {
59 if (Main.main.getEditLayer() == null) {
60 JOptionPane.showMessageDialog(Main.parent, tr("No data loaded."));
61 return;
62 }
63
64 Collection<Way> selectedWays = Main.main.getCurrentDataSet().getSelectedWays();
65
66 if (selectedWays.size() < 1) {
67 // Sometimes it make sense creating multipoly of only one way (so it will form outer way)
68 // and then splitting the way later (so there are multiple ways forming outer way)
69 JOptionPane.showMessageDialog(Main.parent, tr("You must select at least one way."));
70 return;
71 }
72
73 MultipolygonCreate polygon = this.analyzeWays(selectedWays);
74
75 if (polygon == null)
76 return; //could not make multipolygon.
77
78 final Relation relation = this.createRelation(polygon);
79
80 if (Main.pref.getBoolean("multipoly.show-relation-editor", false)) {
81 //Open relation edit window, if set up in preferences
82 RelationEditor editor = RelationEditor.getEditor(Main.main.getEditLayer(), relation, null);
83
84 editor.setModal(true);
85 editor.setVisible(true);
86
87 //TODO: cannot get the resulting relation from RelationEditor :(.
88 /*
89 if (relationCountBefore < relationCountAfter) {
90 //relation saved, clean up the tags
91 List<Command> list = this.removeTagsFromInnerWays(relation);
92 if (list.size() > 0)
93 {
94 Main.main.undoRedo.add(new SequenceCommand(tr("Remove tags from multipolygon inner ways"), list));
95 }
96 }
97 */
98
99 } else {
100 //Just add the relation
101 List<Command> list = this.removeTagsFromInnerWays(relation);
102 list.add(new AddCommand(relation));
103 Main.main.undoRedo.add(new SequenceCommand(tr("Create multipolygon"), list));
104 Main.map.relationListDialog.unfurlDialog();
105 // Use 'SwingUtilities.invokeLater' to make sure the relationListDialog
106 // knows about the new relation before we try to select it.
107 // (Yes, we are already in event dispatch thread. But DatasetEventManager
108 // uses 'SwingUtilities.invokeLater' to fire events so we have to do
109 // the same.)
110 SwingUtilities.invokeLater(new Runnable() {
111 public void run() {
112 Main.map.relationListDialog.selectRelation(relation);
113 }
114 });
115 }
116
117
118 }
119
120 /** Enable this action only if something is selected */
121 @Override protected void updateEnabledState() {
122 if (getCurrentDataSet() == null) {
123 setEnabled(false);
124 } else {
125 updateEnabledState(getCurrentDataSet().getSelected());
126 }
127 }
128
129 /** Enable this action only if something is selected */
130 @Override protected void updateEnabledState(Collection < ? extends OsmPrimitive > selection) {
131 setEnabled(selection != null && !selection.isEmpty());
132 }
133
134 /**
135 * This method analyzes ways and creates multipolygon.
136 * @param selectedWays
137 * @return null, if there was a problem with the ways.
138 */
139 private MultipolygonCreate analyzeWays(Collection < Way > selectedWays) {
140
141 MultipolygonCreate pol = new MultipolygonCreate();
142 String error = pol.makeFromWays(selectedWays);
143
144 if (error != null) {
145 JOptionPane.showMessageDialog(Main.parent, error);
146 return null;
147 } else {
148 return pol;
149 }
150 }
151
152 /**
153 * Builds a relation from polygon ways.
154 * @param pol
155 * @return
156 */
157 private Relation createRelation(MultipolygonCreate pol) {
158 // Create new relation
159 Relation rel = new Relation();
160 rel.put("type", "multipolygon");
161 // Add ways to it
162 for (JoinedPolygon jway:pol.outerWays) {
163 for (Way way:jway.ways) {
164 rel.addMember(new RelationMember("outer", way));
165 }
166 }
167
168 for (JoinedPolygon jway:pol.innerWays) {
169 for (Way way:jway.ways) {
170 rel.addMember(new RelationMember("inner", way));
171 }
172 }
173 return rel;
174 }
175
176 /**
177 * This method removes tags/value pairs from inner ways that are present in relation or outer ways.
178 * @param relation
179 */
180 private List<Command> removeTagsFromInnerWays(Relation relation) {
181 Map<String, String> values = new HashMap<String, String>();
182
183 if (relation.hasKeys()){
184 for(String key: relation.keySet()) {
185 values.put(key, relation.get(key));
186 }
187 }
188
189 List<Way> innerWays = new ArrayList<Way>();
190
191 for (RelationMember m: relation.getMembers()) {
192
193 if (m.hasRole() && m.getRole() == "inner" && m.isWay() && m.getWay().hasKeys()) {
194 innerWays.add(m.getWay());
195 }
196
197 if (m.hasRole() && m.getRole() == "outer" && m.isWay() && m.getWay().hasKeys()) {
198 Way way = m.getWay();
199 for (String key: way.keySet()) {
200 if (!values.containsKey(key)) { //relation values take precedence
201 values.put(key, way.get(key));
202 }
203 }
204 }
205 }
206
207 List<Command> commands = new ArrayList<Command>();
208
209 for(String key: values.keySet()) {
210 List<OsmPrimitive> affectedWays = new ArrayList<OsmPrimitive>();
211 String value = values.get(key);
212
213 for (Way way: innerWays) {
214 if (value.equals(way.get(key))) {
215 affectedWays.add(way);
216 }
217 }
218
219 if (affectedWays.size() > 0) {
220 commands.add(new ChangePropertyCommand(affectedWays, key, null));
221 }
222 }
223
224 return commands;
225 }
226}
Note: See TracBrowser for help on using the repository browser.