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

Last change on this file since 5391 was 5391, checked in by bastiK, 12 years ago

add session support for imagery layers

  • Property svn:eol-style set to native
File size: 13.3 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.layer;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Color;
8import java.awt.Component;
9import java.awt.Graphics2D;
10import java.awt.event.ActionEvent;
11import java.beans.PropertyChangeListener;
12import java.beans.PropertyChangeSupport;
13import java.io.File;
14import java.util.List;
15
16import javax.swing.AbstractAction;
17import javax.swing.Action;
18import javax.swing.Icon;
19import javax.swing.JOptionPane;
20import javax.swing.JSeparator;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.actions.GpxExportAction;
24import org.openstreetmap.josm.actions.SaveAction;
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;
33
34/**
35 * A layer encapsulates the gui component of one dataset and its representation.
36 *
37 * Some layers may display data directly imported from OSM server. Other only
38 * display background images. Some can be edited, some not. Some are static and
39 * other changes dynamically (auto-updated).
40 *
41 * Layers can be visible or not. Most actions the user can do applies only on
42 * selected layers. The available actions depend on the selected layers too.
43 *
44 * All layers are managed by the MapView. They are displayed in a list to the
45 * right of the screen.
46 *
47 * @author imi
48 */
49abstract public class Layer implements Destroyable, MapViewPaintable, ProjectionChangeListener {
50
51 public interface LayerAction {
52 boolean supportLayers(List<Layer> layers);
53 Component createMenuComponent();
54 }
55
56 public interface MultiLayerAction {
57 Action getMultiLayerAction(List<Layer> layers);
58 }
59
60
61 /**
62 * Special class that can be returned by getMenuEntries when JSeparator needs to be created
63 *
64 */
65 public static class SeparatorLayerAction extends AbstractAction implements LayerAction {
66 public static final SeparatorLayerAction INSTANCE = new SeparatorLayerAction();
67 @Override
68 public void actionPerformed(ActionEvent e) {
69 throw new UnsupportedOperationException();
70 }
71 @Override
72 public Component createMenuComponent() {
73 return new JSeparator();
74 }
75 @Override
76 public boolean supportLayers(List<Layer> layers) {
77 return false;
78 }
79 }
80
81 static public final String VISIBLE_PROP = Layer.class.getName() + ".visible";
82 static public final String OPACITY_PROP = Layer.class.getName() + ".opacity";
83 static public final String NAME_PROP = Layer.class.getName() + ".name";
84
85 static public final int ICON_SIZE = 16;
86
87 /** keeps track of property change listeners */
88 protected PropertyChangeSupport propertyChangeSupport;
89
90 /**
91 * The visibility state of the layer.
92 *
93 */
94 private boolean visible = true;
95
96 /**
97 * The opacity of the layer.
98 *
99 */
100 private double opacity = 1;
101
102 /**
103 * The layer should be handled as a background layer in automatic handling
104 *
105 */
106 private boolean background = false;
107
108 /**
109 * The name of this layer.
110 *
111 */
112 private String name;
113
114 /**
115 * If a file is associated with this layer, this variable should be set to it.
116 */
117 private File associatedFile;
118
119 /**
120 * Create the layer and fill in the necessary components.
121 */
122 public Layer(String name) {
123 this.propertyChangeSupport = new PropertyChangeSupport(this);
124 setName(name);
125 }
126
127 /**
128 * Initialization code, that depends on Main.map.mapView.
129 *
130 * It is always called in the event dispatching thread.
131 * Note that Main.map is null as long as no layer has been added, so do
132 * not execute code in the constructor, that assumes Main.map.mapView is
133 * not null. Instead override this method.
134 */
135 public void hookUpMapView() {
136 }
137
138 /**
139 * Paint the dataset using the engine set.
140 * @param mv The object that can translate GeoPoints to screen coordinates.
141 */
142 @Override
143 abstract public void paint(Graphics2D g, MapView mv, Bounds box);
144 /**
145 * Return a representative small image for this layer. The image must not
146 * be larger than 64 pixel in any dimension.
147 */
148 abstract public Icon getIcon();
149
150 /**
151 * Return a Color for this layer. Return null when no color specified.
152 * @param ignoreCustom Custom color should return null, as no default color
153 * is used. When this is true, then even for custom coloring the base
154 * color is returned - mainly for layer internal use.
155 */
156 public Color getColor(boolean ignoreCustom) {
157 return null;
158 }
159
160 /**
161 * @return A small tooltip hint about some statistics for this layer.
162 */
163 abstract public String getToolTipText();
164
165 /**
166 * Merges the given layer into this layer. Throws if the layer types are
167 * incompatible.
168 * @param from The layer that get merged into this one. After the merge,
169 * the other layer is not usable anymore and passing to one others
170 * mergeFrom should be one of the last things to do with a layer.
171 */
172 abstract public void mergeFrom(Layer from);
173
174 /**
175 * @param other The other layer that is tested to be mergable with this.
176 * @return Whether the other layer can be merged into this layer.
177 */
178 abstract public boolean isMergable(Layer other);
179
180 abstract public void visitBoundingBox(BoundingXYVisitor v);
181
182 abstract public Object getInfoComponent();
183
184 /**
185 * Returns list of actions. Action can implement LayerAction interface when it needs to be represented by other
186 * menu component than JMenuItem or when it supports multiple layers. Actions that support multiple layers should also
187 * have correct equals implementation.
188 *
189 * Use SeparatorLayerAction.INSTANCE instead of new JSeparator
190 *
191 */
192 abstract public Action[] getMenuEntries();
193
194 /**
195 * Called, when the layer is removed from the mapview and is going to be
196 * destroyed.
197 *
198 * This is because the Layer constructor can not add itself safely as listener
199 * to the layerlist dialog, because there may be no such dialog yet (loaded
200 * via command line parameter).
201 */
202 @Override
203 public void destroy() {}
204
205 public File getAssociatedFile() { return associatedFile; }
206 public void setAssociatedFile(File file) { associatedFile = file; }
207
208 /**
209 * Replies the name of the layer
210 *
211 * @return the name of the layer
212 */
213 public String getName() {
214 return name;
215 }
216
217 /**
218 * Sets the name of the layer
219 *
220 *@param name the name. If null, the name is set to the empty string.
221 *
222 */
223 public void setName(String name) {
224 if (name == null) {
225 name = "";
226 }
227 String oldValue = this.name;
228 this.name = name;
229 if (!this.name.equals(oldValue)) {
230 propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name);
231 }
232 }
233
234 /**
235 * Replies true if this layer is a background layer
236 *
237 * @return true if this layer is a background layer
238 */
239 public boolean isBackgroundLayer() {
240 return background;
241 }
242
243 /**
244 * Sets whether this layer is a background layer
245 *
246 * @param background true, if this layer is a background layer
247 */
248 public void setBackgroundLayer(boolean background) {
249 this.background = background;
250 }
251
252 /**
253 * Sets the visibility of this layer. Emits property change event for
254 * property {@link #VISIBLE_PROP}.
255 *
256 * @param visible true, if the layer is visible; false, otherwise.
257 */
258 public void setVisible(boolean visible) {
259 boolean oldValue = isVisible();
260 this.visible = visible;
261 if (visible && opacity == 0) {
262 setOpacity(1);
263 } else if (oldValue != isVisible()) {
264 fireVisibleChanged(oldValue, isVisible());
265 }
266 }
267
268 /**
269 * Replies true if this layer is visible. False, otherwise.
270 * @return true if this layer is visible. False, otherwise.
271 */
272 public boolean isVisible() {
273 return visible && opacity != 0;
274 }
275
276 public double getOpacity() {
277 return opacity;
278 }
279
280 public void setOpacity(double opacity) {
281 if (!(opacity >= 0 && opacity <= 1))
282 throw new IllegalArgumentException("Opacity value must be between 0 and 1");
283 double oldOpacity = getOpacity();
284 boolean oldVisible = isVisible();
285 this.opacity = opacity;
286 if (oldOpacity != getOpacity()) {
287 fireOpacityChanged(oldOpacity, getOpacity());
288 }
289 if (oldVisible != isVisible()) {
290 fireVisibleChanged(oldVisible, isVisible());
291 }
292 }
293
294 /**
295 * Toggles the visibility state of this layer.
296 */
297 public void toggleVisible() {
298 setVisible(!isVisible());
299 }
300
301 /**
302 * Adds a {@link PropertyChangeListener}
303 *
304 * @param listener the listener
305 */
306 public void addPropertyChangeListener(PropertyChangeListener listener) {
307 propertyChangeSupport.addPropertyChangeListener(listener);
308 }
309
310 /**
311 * Removes a {@link PropertyChangeListener}
312 *
313 * @param listener the listener
314 */
315 public void removePropertyChangeListener(PropertyChangeListener listener) {
316 propertyChangeSupport.removePropertyChangeListener(listener);
317 }
318
319 /**
320 * fires a property change for the property {@link #VISIBLE_PROP}
321 *
322 * @param oldValue the old value
323 * @param newValue the new value
324 */
325 protected void fireVisibleChanged(boolean oldValue, boolean newValue) {
326 propertyChangeSupport.firePropertyChange(VISIBLE_PROP, oldValue, newValue);
327 }
328
329 /**
330 * fires a property change for the property {@link #OPACITY_PROP}
331 *
332 * @param oldValue the old value
333 * @param newValue the new value
334 */
335 protected void fireOpacityChanged(double oldValue, double newValue) {
336 propertyChangeSupport.firePropertyChange(OPACITY_PROP, oldValue, newValue);
337 }
338
339 /**
340 * Check changed status of layer
341 *
342 * @return True if layer was changed since last paint
343 */
344 public boolean isChanged() {
345 return true;
346 }
347
348 /**
349 * allows to check whether a projection is supported or not
350 *
351 * @return True if projection is supported for this layer
352 */
353 public boolean isProjectionSupported(Projection proj) {
354 return true;
355 }
356
357 /**
358 * Specify user information about projections
359 *
360 * @return User readable text telling about supported projections
361 */
362 public String nameSupportedProjections() {
363 return tr("All projections are supported");
364 }
365
366 /**
367 * The action to save a layer
368 *
369 */
370 public static class LayerSaveAction extends AbstractAction {
371 private Layer layer;
372 public LayerSaveAction(Layer layer) {
373 putValue(SMALL_ICON, ImageProvider.get("save"));
374 putValue(SHORT_DESCRIPTION, tr("Save the current data."));
375 putValue(NAME, tr("Save"));
376 setEnabled(true);
377 this.layer = layer;
378 }
379
380 public void actionPerformed(ActionEvent e) {
381 SaveAction.getInstance().doSave(layer);
382 }
383 }
384
385 public static class LayerSaveAsAction extends AbstractAction {
386 private Layer layer;
387 public LayerSaveAsAction(Layer layer) {
388 putValue(SMALL_ICON, ImageProvider.get("save_as"));
389 putValue(SHORT_DESCRIPTION, tr("Save the current data to a new file."));
390 putValue(NAME, tr("Save As..."));
391 setEnabled(true);
392 this.layer = layer;
393 }
394
395 public void actionPerformed(ActionEvent e) {
396 SaveAsAction.getInstance().doSave(layer);
397 }
398 }
399
400 public static class LayerGpxExportAction extends AbstractAction {
401 private Layer layer;
402 public LayerGpxExportAction(Layer layer) {
403 putValue(SMALL_ICON, ImageProvider.get("exportgpx"));
404 putValue(SHORT_DESCRIPTION, tr("Export the data to GPX file."));
405 putValue(NAME, tr("Export to GPX..."));
406 setEnabled(true);
407 this.layer = layer;
408 }
409
410 public void actionPerformed(ActionEvent e) {
411 new GpxExportAction().export(layer);
412 }
413 }
414
415 /* --------------------------------------------------------------------------------- */
416 /* interface ProjectionChangeListener */
417 /* --------------------------------------------------------------------------------- */
418 @Override
419 public void projectionChanged(Projection oldValue, Projection newValue) {
420 if(!isProjectionSupported(newValue)) {
421 JOptionPane.showMessageDialog(Main.parent,
422 tr("The layer {0} does not support the new projection {1}.\n{2}\n"
423 + "Change the projection again or remove the layer.",
424 getName(), newValue.toCode(), nameSupportedProjections()),
425 tr("Warning"),
426 JOptionPane.WARNING_MESSAGE);
427 }
428 }
429}
Note: See TracBrowser for help on using the repository browser.