source: josm/trunk/src/org/openstreetmap/josm/gui/layer/Layer.java@ 9978

Last change on this file since 9978 was 9978, checked in by Don-vip, 8 years ago

sonar - remove useless initializations

  • Property svn:eol-style set to native
File size: 18.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.Graphics2D;
9import java.awt.event.ActionEvent;
10import java.beans.PropertyChangeListener;
11import java.beans.PropertyChangeSupport;
12import java.io.File;
13import java.util.List;
14
15import javax.swing.AbstractAction;
16import javax.swing.Action;
17import javax.swing.Icon;
18import javax.swing.JOptionPane;
19import javax.swing.JSeparator;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.actions.GpxExportAction;
23import org.openstreetmap.josm.actions.SaveAction;
24import org.openstreetmap.josm.actions.SaveActionBase;
25import org.openstreetmap.josm.actions.SaveAsAction;
26import org.openstreetmap.josm.data.Bounds;
27import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
28import org.openstreetmap.josm.data.projection.Projection;
29import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
30import org.openstreetmap.josm.gui.MapView;
31import org.openstreetmap.josm.tools.Destroyable;
32import org.openstreetmap.josm.tools.ImageProvider;
33import org.openstreetmap.josm.tools.Utils;
34
35/**
36 * A layer encapsulates the gui component of one dataset and its representation.
37 *
38 * Some layers may display data directly imported from OSM server. Other only
39 * display background images. Some can be edited, some not. Some are static and
40 * other changes dynamically (auto-updated).
41 *
42 * Layers can be visible or not. Most actions the user can do applies only on
43 * selected layers. The available actions depend on the selected layers too.
44 *
45 * All layers are managed by the MapView. They are displayed in a list to the
46 * right of the screen.
47 *
48 * @author imi
49 */
50public abstract class Layer implements Destroyable, MapViewPaintable, ProjectionChangeListener {
51
52 /**
53 * Action related to a single layer.
54 */
55 public interface LayerAction {
56
57 /**
58 * Determines if this action supports a given list of layers.
59 * @param layers list of layers
60 * @return {@code true} if this action supports the given list of layers, {@code false} otherwise
61 */
62 boolean supportLayers(List<Layer> layers);
63
64 /**
65 * Creates and return the menu component.
66 * @return the menu component
67 */
68 Component createMenuComponent();
69 }
70
71 /**
72 * Action related to several layers.
73 */
74 public interface MultiLayerAction {
75
76 /**
77 * Returns the action for a given list of layers.
78 * @param layers list of layers
79 * @return the action for the given list of layers
80 */
81 Action getMultiLayerAction(List<Layer> layers);
82 }
83
84 /**
85 * Special class that can be returned by getMenuEntries when JSeparator needs to be created
86 */
87 public static class SeparatorLayerAction extends AbstractAction implements LayerAction {
88 /** Unique instance */
89 public static final SeparatorLayerAction INSTANCE = new SeparatorLayerAction();
90
91 @Override
92 public void actionPerformed(ActionEvent e) {
93 throw new UnsupportedOperationException();
94 }
95
96 @Override
97 public Component createMenuComponent() {
98 return new JSeparator();
99 }
100
101 @Override
102 public boolean supportLayers(List<Layer> layers) {
103 return false;
104 }
105 }
106
107 public static final String VISIBLE_PROP = Layer.class.getName() + ".visible";
108 public static final String OPACITY_PROP = Layer.class.getName() + ".opacity";
109 public static final String NAME_PROP = Layer.class.getName() + ".name";
110 public static final String FILTER_STATE_PROP = Layer.class.getName() + ".filterstate";
111
112 /**
113 * keeps track of property change listeners
114 */
115 protected PropertyChangeSupport propertyChangeSupport;
116
117 /**
118 * The visibility state of the layer.
119 */
120 private boolean visible = true;
121
122 /**
123 * The opacity of the layer.
124 */
125 private double opacity = 1;
126
127 /**
128 * The layer should be handled as a background layer in automatic handling
129 */
130 private boolean background;
131
132 /**
133 * The name of this layer.
134 */
135 private String name;
136
137 /**
138 * This is set if user renamed this layer.
139 */
140 private boolean renamed;
141
142 /**
143 * If a file is associated with this layer, this variable should be set to it.
144 */
145 private File associatedFile;
146
147 /**
148 * Create the layer and fill in the necessary components.
149 * @param name Layer name
150 */
151 public Layer(String name) {
152 this.propertyChangeSupport = new PropertyChangeSupport(this);
153 setName(name);
154 }
155
156 /**
157 * Initialization code, that depends on Main.map.mapView.
158 *
159 * It is always called in the event dispatching thread.
160 * Note that Main.map is null as long as no layer has been added, so do
161 * not execute code in the constructor, that assumes Main.map.mapView is
162 * not null. Instead override this method.
163 *
164 * This implementation provides check, if JOSM will be able to use Layer. Layers
165 * using a lot of memory, which do know in advance, how much memory they use, should
166 * override {@link #estimateMemoryUsage() estimateMemoryUsage} method and give a hint.
167 *
168 * This allows for preemptive warning message for user, instead of failing later on
169 *
170 * Remember to call {@code super.hookUpMapView()} when overriding this method
171 */
172 public void hookUpMapView() {
173 // calculate total memory needed for all layers
174 long memoryBytesRequired = 50 * 1024 * 1024; // assumed minimum JOSM memory footprint
175 if (Main.map != null && Main.map.mapView != null) {
176 for (Layer layer: Main.map.mapView.getAllLayers()) {
177 memoryBytesRequired += layer.estimateMemoryUsage();
178 }
179 if (memoryBytesRequired > Runtime.getRuntime().maxMemory()) {
180 throw new IllegalArgumentException(
181 tr("To add another layer you need to allocate at least {0,number,#}MB memory to JOSM using -Xmx{0,number,#}M "
182 + "option (see http://forum.openstreetmap.org/viewtopic.php?id=25677).\n"
183 + "Currently you have {1,number,#}MB memory allocated for JOSM",
184 memoryBytesRequired / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024));
185 }
186 }
187 }
188
189 /**
190 * Paint the dataset using the engine set.
191 * @param mv The object that can translate GeoPoints to screen coordinates.
192 */
193 @Override
194 public abstract void paint(Graphics2D g, MapView mv, Bounds box);
195
196 /**
197 * Return a representative small image for this layer. The image must not
198 * be larger than 64 pixel in any dimension.
199 * @return layer icon
200 */
201 public abstract Icon getIcon();
202
203 /**
204 * Return a Color for this layer. Return null when no color specified.
205 * @param ignoreCustom Custom color should return null, as no default color
206 * is used. When this is true, then even for custom coloring the base
207 * color is returned - mainly for layer internal use.
208 * @return layer color
209 */
210 public Color getColor(boolean ignoreCustom) {
211 return null;
212 }
213
214 /**
215 * @return A small tooltip hint about some statistics for this layer.
216 */
217 public abstract String getToolTipText();
218
219 /**
220 * Merges the given layer into this layer. Throws if the layer types are
221 * incompatible.
222 * @param from The layer that get merged into this one. After the merge,
223 * the other layer is not usable anymore and passing to one others
224 * mergeFrom should be one of the last things to do with a layer.
225 */
226 public abstract void mergeFrom(Layer from);
227
228 /**
229 * @param other The other layer that is tested to be mergable with this.
230 * @return Whether the other layer can be merged into this layer.
231 */
232 public abstract boolean isMergable(Layer other);
233
234 public abstract void visitBoundingBox(BoundingXYVisitor v);
235
236 public abstract Object getInfoComponent();
237
238 /**
239 * Determines if info dialog can be resized (false by default).
240 * @return {@code true} if the info dialog can be resized, {@code false} otherwise
241 * @since 6708
242 */
243 public boolean isInfoResizable() {
244 return false;
245 }
246
247 /**
248 * Returns list of actions. Action can implement LayerAction interface when it needs to be represented by other
249 * menu component than JMenuItem or when it supports multiple layers. Actions that support multiple layers should also
250 * have correct equals implementation.
251 *
252 * Use {@link SeparatorLayerAction#INSTANCE} instead of new JSeparator
253 * @return menu actions for this layer
254 */
255 public abstract Action[] getMenuEntries();
256
257 /**
258 * Called, when the layer is removed from the mapview and is going to be destroyed.
259 *
260 * This is because the Layer constructor can not add itself safely as listener
261 * to the layerlist dialog, because there may be no such dialog yet (loaded
262 * via command line parameter).
263 */
264 @Override
265 public void destroy() {
266 // Override in subclasses if needed
267 }
268
269 public File getAssociatedFile() {
270 return associatedFile;
271 }
272
273 public void setAssociatedFile(File file) {
274 associatedFile = file;
275 }
276
277 /**
278 * Replies the name of the layer
279 *
280 * @return the name of the layer
281 */
282 public String getName() {
283 return name;
284 }
285
286 /**
287 * Sets the name of the layer
288 *
289 * @param name the name. If null, the name is set to the empty string.
290 */
291 public final void setName(String name) {
292 if (name == null) {
293 name = "";
294 }
295 String oldValue = this.name;
296 this.name = name;
297 if (!this.name.equals(oldValue)) {
298 propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name);
299 }
300 }
301
302 /**
303 * Rename layer and set renamed flag to mark it as renamed (has user given name).
304 *
305 * @param name the name. If null, the name is set to the empty string.
306 */
307 public final void rename(String name) {
308 renamed = true;
309 setName(name);
310 }
311
312 /**
313 * Replies true if this layer was renamed by user
314 *
315 * @return true if this layer was renamed by user
316 */
317 public boolean isRenamed() {
318 return renamed;
319 }
320
321 /**
322 * Replies true if this layer is a background layer
323 *
324 * @return true if this layer is a background layer
325 */
326 public boolean isBackgroundLayer() {
327 return background;
328 }
329
330 /**
331 * Sets whether this layer is a background layer
332 *
333 * @param background true, if this layer is a background layer
334 */
335 public void setBackgroundLayer(boolean background) {
336 this.background = background;
337 }
338
339 /**
340 * Sets the visibility of this layer. Emits property change event for
341 * property {@link #VISIBLE_PROP}.
342 *
343 * @param visible true, if the layer is visible; false, otherwise.
344 */
345 public void setVisible(boolean visible) {
346 boolean oldValue = isVisible();
347 this.visible = visible;
348 if (visible && opacity == 0) {
349 setOpacity(1);
350 } else if (oldValue != isVisible()) {
351 fireVisibleChanged(oldValue, isVisible());
352 }
353 }
354
355 /**
356 * Replies true if this layer is visible. False, otherwise.
357 * @return true if this layer is visible. False, otherwise.
358 */
359 public boolean isVisible() {
360 return visible && opacity != 0;
361 }
362
363 public double getOpacity() {
364 return opacity;
365 }
366
367 public void setOpacity(double opacity) {
368 if (!(opacity >= 0 && opacity <= 1))
369 throw new IllegalArgumentException("Opacity value must be between 0 and 1");
370 double oldOpacity = getOpacity();
371 boolean oldVisible = isVisible();
372 this.opacity = opacity;
373 if (!Utils.equalsEpsilon(oldOpacity, getOpacity())) {
374 fireOpacityChanged(oldOpacity, getOpacity());
375 }
376 if (oldVisible != isVisible()) {
377 fireVisibleChanged(oldVisible, isVisible());
378 }
379 }
380
381 /**
382 * Sets new state to the layer after applying {@link ImageProcessor}.
383 */
384 public void setFilterStateChanged() {
385 fireFilterStateChanged();
386 }
387
388 /**
389 * Toggles the visibility state of this layer.
390 */
391 public void toggleVisible() {
392 setVisible(!isVisible());
393 }
394
395 /**
396 * Adds a {@link PropertyChangeListener}
397 *
398 * @param listener the listener
399 */
400 public void addPropertyChangeListener(PropertyChangeListener listener) {
401 propertyChangeSupport.addPropertyChangeListener(listener);
402 }
403
404 /**
405 * Removes a {@link PropertyChangeListener}
406 *
407 * @param listener the listener
408 */
409 public void removePropertyChangeListener(PropertyChangeListener listener) {
410 propertyChangeSupport.removePropertyChangeListener(listener);
411 }
412
413 /**
414 * fires a property change for the property {@link #VISIBLE_PROP}
415 *
416 * @param oldValue the old value
417 * @param newValue the new value
418 */
419 protected void fireVisibleChanged(boolean oldValue, boolean newValue) {
420 propertyChangeSupport.firePropertyChange(VISIBLE_PROP, oldValue, newValue);
421 }
422
423 /**
424 * fires a property change for the property {@link #OPACITY_PROP}
425 *
426 * @param oldValue the old value
427 * @param newValue the new value
428 */
429 protected void fireOpacityChanged(double oldValue, double newValue) {
430 propertyChangeSupport.firePropertyChange(OPACITY_PROP, oldValue, newValue);
431 }
432
433 /**
434 * fires a property change for the property {@link #FILTER_STATE_PROP}.
435 */
436 protected void fireFilterStateChanged() {
437 propertyChangeSupport.firePropertyChange(FILTER_STATE_PROP, null, null);
438 }
439
440 /**
441 * Check changed status of layer
442 *
443 * @return True if layer was changed since last paint
444 */
445 public boolean isChanged() {
446 return true;
447 }
448
449 /**
450 * allows to check whether a projection is supported or not
451 * @param proj projection
452 *
453 * @return True if projection is supported for this layer
454 */
455 public boolean isProjectionSupported(Projection proj) {
456 return proj != null;
457 }
458
459 /**
460 * Specify user information about projections
461 *
462 * @return User readable text telling about supported projections
463 */
464 public String nameSupportedProjections() {
465 return tr("All projections are supported");
466 }
467
468 /**
469 * The action to save a layer
470 */
471 public static class LayerSaveAction extends AbstractAction {
472 private final transient Layer layer;
473
474 public LayerSaveAction(Layer layer) {
475 putValue(SMALL_ICON, ImageProvider.get("save"));
476 putValue(SHORT_DESCRIPTION, tr("Save the current data."));
477 putValue(NAME, tr("Save"));
478 setEnabled(true);
479 this.layer = layer;
480 }
481
482 @Override
483 public void actionPerformed(ActionEvent e) {
484 SaveAction.getInstance().doSave(layer);
485 }
486 }
487
488 public static class LayerSaveAsAction extends AbstractAction {
489 private final transient Layer layer;
490
491 public LayerSaveAsAction(Layer layer) {
492 putValue(SMALL_ICON, ImageProvider.get("save_as"));
493 putValue(SHORT_DESCRIPTION, tr("Save the current data to a new file."));
494 putValue(NAME, tr("Save As..."));
495 setEnabled(true);
496 this.layer = layer;
497 }
498
499 @Override
500 public void actionPerformed(ActionEvent e) {
501 SaveAsAction.getInstance().doSave(layer);
502 }
503 }
504
505 public static class LayerGpxExportAction extends AbstractAction {
506 private final transient Layer layer;
507
508 public LayerGpxExportAction(Layer layer) {
509 putValue(SMALL_ICON, ImageProvider.get("exportgpx"));
510 putValue(SHORT_DESCRIPTION, tr("Export the data to GPX file."));
511 putValue(NAME, tr("Export to GPX..."));
512 setEnabled(true);
513 this.layer = layer;
514 }
515
516 @Override
517 public void actionPerformed(ActionEvent e) {
518 new GpxExportAction().export(layer);
519 }
520 }
521
522 /* --------------------------------------------------------------------------------- */
523 /* interface ProjectionChangeListener */
524 /* --------------------------------------------------------------------------------- */
525 @Override
526 public void projectionChanged(Projection oldValue, Projection newValue) {
527 if (!isProjectionSupported(newValue)) {
528 String message = "<html><body><p>" +
529 tr("The layer {0} does not support the new projection {1}.", getName(), newValue.toCode()) + "</p>" +
530 "<p style='width: 450px;'>" + tr("Supported projections are: {0}", nameSupportedProjections()) + "</p>" +
531 tr("Change the projection again or remove the layer.");
532
533 JOptionPane.showMessageDialog(Main.parent,
534 message,
535 tr("Warning"),
536 JOptionPane.WARNING_MESSAGE);
537 }
538 }
539
540 /**
541 * Initializes the layer after a successful load of data from a file
542 * @since 5459
543 */
544 public void onPostLoadFromFile() {
545 // To be overriden if needed
546 }
547
548 /**
549 * Replies the savable state of this layer (i.e if it can be saved through a "File-&gt;Save" dialog).
550 * @return true if this layer can be saved to a file
551 * @since 5459
552 */
553 public boolean isSavable() {
554 return false;
555 }
556
557 /**
558 * Checks whether it is ok to launch a save (whether we have data, there is no conflict etc.)
559 * @return <code>true</code>, if it is safe to save.
560 * @since 5459
561 */
562 public boolean checkSaveConditions() {
563 return true;
564 }
565
566 /**
567 * Creates a new "Save" dialog for this layer and makes it visible.<br>
568 * When the user has chosen a file, checks the file extension, and confirms overwrite if needed.
569 * @return The output {@code File}
570 * @see SaveActionBase#createAndOpenSaveFileChooser
571 * @since 5459
572 */
573 public File createAndOpenSaveFileChooser() {
574 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save Layer"), "lay");
575 }
576
577 /**
578 * @return bytes that the tile will use. Needed for resource management
579 */
580 protected long estimateMemoryUsage() {
581 return 0;
582 }
583}
Note: See TracBrowser for help on using the repository browser.