source: osm/applications/editors/josm/plugins/reltoolbox/src/relcontext/actions/CreateMultipolygonAction.java@ 27852

Last change on this file since 27852 was 27852, checked in by stoecker, 13 years ago

fix shortcut deprecation

File size: 12.8 KB
Line 
1package relcontext.actions;
2
3import java.awt.Dialog.ModalityType;
4import java.awt.GridBagLayout;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import java.awt.event.ActionEvent;
7import java.awt.event.ActionListener;
8import java.awt.event.KeyEvent;
9import java.util.*;
10import javax.swing.*;
11import org.openstreetmap.josm.Main;
12import org.openstreetmap.josm.actions.JosmAction;
13import org.openstreetmap.josm.command.*;
14import org.openstreetmap.josm.data.osm.*;
15import org.openstreetmap.josm.tools.GBC;
16import org.openstreetmap.josm.tools.Shortcut;
17import relcontext.ChosenRelation;
18
19/**
20 * Creates new multipolygon from selected ways.
21 * Choose relation afterwards.
22 *
23 * @author Zverik
24 */
25public class CreateMultipolygonAction extends JosmAction {
26 private static final String ACTION_NAME = "Create relation";
27 private static final String PREF_MULTIPOLY = "reltoolbox.multipolygon.";
28 protected ChosenRelation chRel;
29
30 public CreateMultipolygonAction( ChosenRelation chRel ) {
31 super("Multi", "data/multipolygon", tr("Create a multipolygon from selected objects"),
32 Shortcut.registerShortcut("reltoolbox:multipolygon", tr("Relation Toolbox: {0}", tr("Create multipolygon")),
33 KeyEvent.VK_B, Shortcut.CTRL), true);
34 this.chRel = chRel;
35 updateEnabledState();
36 }
37
38 public CreateMultipolygonAction() {
39 this(null);
40 }
41
42 public static boolean getDefaultPropertyValue( String property ) {
43 if( property.equals("boundary") )
44 return false;
45 else if( property.equals("boundaryways") )
46 return true;
47 else if( property.equals("tags") )
48 return true;
49 else if( property.equals("alltags") )
50 return false;
51 else if( property.equals("single") )
52 return true;
53 else if( property.equals("allowsplit") )
54 return false;
55 throw new IllegalArgumentException(property);
56 }
57
58 private boolean getPref( String property ) {
59 return Main.pref.getBoolean(PREF_MULTIPOLY + property, getDefaultPropertyValue(property));
60 }
61
62 public void actionPerformed( ActionEvent e ) {
63 boolean isBoundary = getPref("boundary");
64 Collection<Way> selectedWays = getCurrentDataSet().getSelectedWays();
65 if( !isBoundary && getPref("tags") ) {
66 List<Relation> rels = null;
67 if( getPref("allowsplit") || selectedWays.size() == 1 ) {
68 if( SplittingMultipolygons.canProcess(selectedWays) )
69 rels = SplittingMultipolygons.process(getCurrentDataSet().getSelectedWays());
70 } else {
71 if( TheRing.areAllOfThoseRings(selectedWays) ) {
72 List<Command> commands = new ArrayList<Command>();
73 rels = TheRing.makeManySimpleMultipolygons(getCurrentDataSet().getSelectedWays(), commands);
74 if( !commands.isEmpty() )
75 Main.main.undoRedo.add(new SequenceCommand(tr("Create multipolygons from rings"), commands));
76 }
77 }
78 if( rels != null && !rels.isEmpty() ) {
79 if( chRel != null )
80 chRel.set(rels.size() == 1 ? rels.get(0) : null);
81 if( rels.size() == 1 )
82 getCurrentDataSet().setSelected(rels);
83 else
84 getCurrentDataSet().clearSelection();
85 return;
86 }
87 }
88
89 // for now, just copying standard action
90 MultipolygonCreate mpc = new MultipolygonCreate();
91 String error = mpc.makeFromWays(getCurrentDataSet().getSelectedWays());
92 if( error != null ) {
93 JOptionPane.showMessageDialog(Main.parent, error);
94 return;
95 }
96 Relation rel = new Relation();
97 if( isBoundary ) {
98 rel.put("type", "boundary");
99 rel.put("boundary", "administrative");
100 } else
101 rel.put("type", "multipolygon");
102 for( MultipolygonCreate.JoinedPolygon poly : mpc.outerWays )
103 for( Way w : poly.ways )
104 rel.addMember(new RelationMember("outer", w));
105 for( MultipolygonCreate.JoinedPolygon poly : mpc.innerWays )
106 for( Way w : poly.ways )
107 rel.addMember(new RelationMember("inner", w));
108 List<Command> list = removeTagsFromInnerWays(rel);
109 if( !list.isEmpty() && isBoundary ) {
110 Main.main.undoRedo.add(new SequenceCommand(tr("Move tags from ways to relation"), list));
111 list = new ArrayList<Command>();
112 }
113 if( isBoundary ) {
114 if( !askForAdminLevelAndName(rel) )
115 return;
116 addBoundaryMembers(rel);
117 if( getPref("boundaryways") )
118 list.addAll(fixWayTagsForBoundary(rel));
119 }
120 list.add(new AddCommand(rel));
121 Main.main.undoRedo.add(new SequenceCommand(tr("Create multipolygon"), list));
122
123 if( chRel != null )
124 chRel.set(rel);
125
126 getCurrentDataSet().setSelected(rel);
127 }
128
129 @Override
130 protected void updateEnabledState() {
131 if( getCurrentDataSet() == null ) {
132 setEnabled(false);
133 } else {
134 updateEnabledState(getCurrentDataSet().getSelected());
135 }
136 }
137
138 @Override
139 protected void updateEnabledState( Collection<? extends OsmPrimitive> selection ) {
140 boolean isEnabled = true;
141 if( selection == null || selection.isEmpty() )
142 isEnabled = false;
143 else {
144 if( !getPref("boundary") ) {
145 for( OsmPrimitive p : selection ) {
146 if( !(p instanceof Way) ) {
147 isEnabled = false;
148 break;
149 }
150 }
151 }
152 }
153 setEnabled(isEnabled);
154 }
155
156 /**
157 * Add selected nodes and relations with corresponding roles.
158 */
159 private void addBoundaryMembers( Relation rel ) {
160 for( OsmPrimitive p : getCurrentDataSet().getSelected() ) {
161 String role = null;
162 if( p.getType().equals(OsmPrimitiveType.RELATION) ) {
163 role = "subarea";
164 } else if( p.getType().equals(OsmPrimitiveType.NODE) ) {
165 Node n = (Node)p;
166 if( !n.isIncomplete() ) {
167 if( n.hasKey("place") )
168 role = "admin_centre";
169 else
170 role = "label";
171 }
172 }
173 if( role != null )
174 rel.addMember(new RelationMember(role, p));
175 }
176 }
177
178 /**
179 * For all untagged ways in relation, add tags boundary and admin_level.
180 */
181 private List<Command> fixWayTagsForBoundary( Relation rel ) {
182 List<Command> commands = new ArrayList<Command>();
183 if( !rel.hasKey("boundary") || !rel.hasKey("admin_level") )
184 return commands;
185 String adminLevelStr = rel.get("admin_level");
186 int adminLevel = 0;
187 try {
188 adminLevel = Integer.parseInt(adminLevelStr);
189 } catch( NumberFormatException e ) {
190 return commands;
191 }
192 Set<OsmPrimitive> waysBoundary = new HashSet<OsmPrimitive>();
193 Set<OsmPrimitive> waysAdminLevel = new HashSet<OsmPrimitive>();
194 for( OsmPrimitive p : rel.getMemberPrimitives() ) {
195 if( p instanceof Way ) {
196 int count = 0;
197 if( p.hasKey("boundary") && p.get("boundary").equals("administrative") )
198 count++;
199 if( p.hasKey("admin_level") )
200 count++;
201 if( p.keySet().size() - count == 0 ) {
202 if( !p.hasKey("boundary") )
203 waysBoundary.add(p);
204 if( !p.hasKey("admin_level") ) {
205 waysAdminLevel.add(p);
206 } else {
207 try {
208 int oldAdminLevel = Integer.parseInt(p.get("admin_level"));
209 if( oldAdminLevel > adminLevel )
210 waysAdminLevel.add(p);
211 } catch( NumberFormatException e ) {
212 waysAdminLevel.add(p); // some garbage, replace it
213 }
214 }
215 }
216 }
217 }
218 if( !waysBoundary.isEmpty() )
219 commands.add(new ChangePropertyCommand(waysBoundary, "boundary", "administrative"));
220 if( !waysAdminLevel.isEmpty() )
221 commands.add(new ChangePropertyCommand(waysAdminLevel, "admin_level", adminLevelStr));
222 return commands;
223 }
224 static public final List<String> DEFAULT_LINEAR_TAGS = Arrays.asList(new String[] {"barrier", "source"});
225 private static final Set<String> REMOVE_FROM_BOUNDARY_TAGS = new TreeSet<String>(Arrays.asList(new String[] {
226 "boundary", "boundary_type", "type", "admin_level"
227 }));
228
229 /**
230 * This method removes tags/value pairs from inner ways that are present in relation or outer ways.
231 * It was copypasted from the standard {@link org.openstreetmap.josm.actions.CreateMultipolygonAction}.
232 * Todo: rewrite it.
233 */
234 private List<Command> removeTagsFromInnerWays( Relation relation ) {
235 Map<String, String> values = new HashMap<String, String>();
236
237 if( relation.hasKeys() ) {
238 for( String key : relation.keySet() ) {
239 values.put(key, relation.get(key));
240 }
241 }
242
243 List<Way> innerWays = new ArrayList<Way>();
244 List<Way> outerWays = new ArrayList<Way>();
245
246 Set<String> conflictingKeys = new TreeSet<String>();
247
248 for( RelationMember m : relation.getMembers() ) {
249
250 if( m.hasRole() && "inner".equals(m.getRole()) && m.isWay() && m.getWay().hasKeys() ) {
251 innerWays.add(m.getWay());
252 }
253
254 if( m.hasRole() && "outer".equals(m.getRole()) && m.isWay() && m.getWay().hasKeys() ) {
255 Way way = m.getWay();
256 outerWays.add(way);
257 for( String key : way.keySet() ) {
258 if( !values.containsKey(key) ) { //relation values take precedence
259 values.put(key, way.get(key));
260 } else if( !relation.hasKey(key) && !values.get(key).equals(way.get(key)) ) {
261 conflictingKeys.add(key);
262 }
263 }
264 }
265 }
266
267 // filter out empty key conflicts - we need second iteration
268 boolean isBoundary = getPref("boundary");
269 if( isBoundary || !getPref("alltags") )
270 for( RelationMember m : relation.getMembers() )
271 if( m.hasRole() && m.getRole().equals("outer") && m.isWay() )
272 for( String key : values.keySet() )
273 if( !m.getWay().hasKey(key) && !relation.hasKey(key) )
274 conflictingKeys.add(key);
275
276 for( String key : conflictingKeys )
277 values.remove(key);
278
279 for( String linearTag : Main.pref.getCollection(PREF_MULTIPOLY + "lineartags", DEFAULT_LINEAR_TAGS) )
280 values.remove(linearTag);
281
282 if( values.containsKey("natural") && values.get("natural").equals("coastline") )
283 values.remove("natural");
284
285 String name = values.get("name");
286 if( isBoundary ) {
287 Set<String> keySet = new TreeSet<String>(values.keySet());
288 for( String key : keySet )
289 if( !REMOVE_FROM_BOUNDARY_TAGS.contains(key) )
290 values.remove(key);
291 }
292
293 values.put("area", "yes");
294
295 List<Command> commands = new ArrayList<Command>();
296 boolean moveTags = getPref("tags");
297
298 for( String key : values.keySet() ) {
299 List<OsmPrimitive> affectedWays = new ArrayList<OsmPrimitive>();
300 String value = values.get(key);
301
302 for( Way way : innerWays ) {
303 if( way.hasKey(key) && (isBoundary || value.equals(way.get(key))) ) {
304 affectedWays.add(way);
305 }
306 }
307
308 if( moveTags ) {
309 // remove duplicated tags from outer ways
310 for( Way way : outerWays ) {
311 if( way.hasKey(key) ) {
312 affectedWays.add(way);
313 }
314 }
315 }
316
317 if( affectedWays.size() > 0 ) {
318 commands.add(new ChangePropertyCommand(affectedWays, key, null));
319 }
320 }
321
322 if( moveTags ) {
323 // add those tag values to the relation
324 if( isBoundary )
325 values.put("name", name);
326 boolean fixed = false;
327 Relation r2 = new Relation(relation);
328 for( String key : values.keySet() ) {
329 if( !r2.hasKey(key) && !key.equals("area")
330 && (!isBoundary || key.equals("admin_level") || key.equals("name")) ) {
331 if( relation.isNew() )
332 relation.put(key, values.get(key));
333 else
334 r2.put(key, values.get(key));
335 fixed = true;
336 }
337 }
338 if( fixed && !relation.isNew() )
339 commands.add(new ChangeCommand(relation, r2));
340 }
341
342 return commands;
343 }
344
345 /**
346 *
347 * @param rel
348 * @return false if user pressed "cancel".
349 */
350 private boolean askForAdminLevelAndName( Relation rel ) {
351 String relAL = rel.get("admin_level");
352 String relName = rel.get("name");
353 if( relAL != null && relName != null )
354 return true;
355
356 JPanel panel = new JPanel(new GridBagLayout());
357 panel.add(new JLabel(tr("Enter admin level and name for the border relation:")), GBC.eol().insets(0, 0, 0, 5));
358
359 final JTextField admin = new JTextField();
360 admin.setText(relAL != null ? relAL : Main.pref.get(PREF_MULTIPOLY + "lastadmin", ""));
361 panel.add(new JLabel(tr("Admin level")), GBC.std());
362 panel.add(Box.createHorizontalStrut(10), GBC.std());
363 panel.add(admin, GBC.eol().fill(GBC.HORIZONTAL).insets(0, 0, 0, 5));
364
365 final JTextField name = new JTextField();
366 if( relName != null )
367 name.setText(relName);
368 panel.add(new JLabel(tr("Name")), GBC.std());
369 panel.add(Box.createHorizontalStrut(10), GBC.std());
370 panel.add(name, GBC.eol().fill(GBC.HORIZONTAL));
371
372 final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
373 @Override
374 public void selectInitialValue() {
375 admin.requestFocusInWindow();
376 admin.selectAll();
377 }
378 };
379 final JDialog dlg = optionPane.createDialog(Main.parent, tr("Create a new relation"));
380 dlg.setModalityType(ModalityType.DOCUMENT_MODAL);
381
382 name.addActionListener(new ActionListener() {
383 public void actionPerformed( ActionEvent e ) {
384 dlg.setVisible(false);
385 optionPane.setValue(JOptionPane.OK_OPTION);
386 }
387 });
388
389 dlg.setVisible(true);
390
391 Object answer = optionPane.getValue();
392 if( answer == null || answer == JOptionPane.UNINITIALIZED_VALUE
393 || (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION) ) {
394 return false;
395 }
396
397 String admin_level = admin.getText().trim();
398 String new_name = name.getText().trim();
399 if( admin_level.equals("10") || (admin_level.length() == 1 && Character.isDigit(admin_level.charAt(0))) ) {
400 rel.put("admin_level", admin_level);
401 Main.pref.put(PREF_MULTIPOLY + "lastadmin", admin_level);
402 }
403 if( new_name.length() > 0 )
404 rel.put("name", new_name);
405 return true;
406 }
407}
Note: See TracBrowser for help on using the repository browser.