source: josm/trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java@ 14120

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

see #15229 - deprecate all Main methods related to projections. New ProjectionRegistry class

  • Property svn:eol-style set to native
File size: 11.5 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;
5import static org.openstreetmap.josm.tools.I18n.trc;
6
7import java.awt.Component;
8import java.awt.GridBagLayout;
9import java.awt.event.ActionEvent;
10import java.awt.image.BufferedImage;
11import java.awt.image.BufferedImageOp;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.List;
15import java.util.Locale;
16
17import javax.swing.AbstractAction;
18import javax.swing.Action;
19import javax.swing.BorderFactory;
20import javax.swing.Icon;
21import javax.swing.JCheckBoxMenuItem;
22import javax.swing.JComponent;
23import javax.swing.JLabel;
24import javax.swing.JMenu;
25import javax.swing.JMenuItem;
26import javax.swing.JPanel;
27import javax.swing.JPopupMenu;
28import javax.swing.JSeparator;
29import javax.swing.JTextField;
30
31import org.openstreetmap.josm.data.ProjectionBounds;
32import org.openstreetmap.josm.data.imagery.ImageryInfo;
33import org.openstreetmap.josm.data.preferences.IntegerProperty;
34import org.openstreetmap.josm.data.projection.ProjectionRegistry;
35import org.openstreetmap.josm.gui.MainApplication;
36import org.openstreetmap.josm.gui.MapView;
37import org.openstreetmap.josm.gui.MenuScroller;
38import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
39import org.openstreetmap.josm.gui.widgets.UrlLabel;
40import org.openstreetmap.josm.tools.GBC;
41import org.openstreetmap.josm.tools.ImageProcessor;
42import org.openstreetmap.josm.tools.ImageProvider;
43import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
44
45/**
46 * Abstract base class for background imagery layers ({@link WMSLayer}, {@link TMSLayer}, {@link WMTSLayer}).
47 *
48 * Handles some common tasks, like image filters, image processors, etc.
49 */
50public abstract class ImageryLayer extends Layer {
51
52 /**
53 * The default value for the sharpen filter for each imagery layer.
54 */
55 public static final IntegerProperty PROP_SHARPEN_LEVEL = new IntegerProperty("imagery.sharpen_level", 0);
56
57 private final List<ImageProcessor> imageProcessors = new ArrayList<>();
58
59 protected final ImageryInfo info;
60
61 protected Icon icon;
62
63 private final ImageryFilterSettings filterSettings = new ImageryFilterSettings();
64
65 /**
66 * Constructs a new {@code ImageryLayer}.
67 * @param info imagery info
68 */
69 public ImageryLayer(ImageryInfo info) {
70 super(info.getName());
71 this.info = info;
72 if (info.getIcon() != null) {
73 icon = new ImageProvider(info.getIcon()).setOptional(true).
74 setMaxSize(ImageSizes.LAYER).get();
75 }
76 if (icon == null) {
77 icon = ImageProvider.get("imagery_small");
78 }
79 for (ImageProcessor processor : filterSettings.getProcessors()) {
80 addImageProcessor(processor);
81 }
82 filterSettings.setSharpenLevel(1 + PROP_SHARPEN_LEVEL.get() / 2f);
83 }
84
85 public double getPPD() {
86 if (!MainApplication.isDisplayingMapView())
87 return ProjectionRegistry.getProjection().getDefaultZoomInPPD();
88 MapView mapView = MainApplication.getMap().mapView;
89 ProjectionBounds bounds = mapView.getProjectionBounds();
90 return mapView.getWidth() / (bounds.maxEast - bounds.minEast);
91 }
92
93 /**
94 * Returns imagery info.
95 * @return imagery info
96 */
97 public ImageryInfo getInfo() {
98 return info;
99 }
100
101 @Override
102 public Icon getIcon() {
103 return icon;
104 }
105
106 @Override
107 public boolean isMergable(Layer other) {
108 return false;
109 }
110
111 @Override
112 public void mergeFrom(Layer from) {
113 }
114
115 @Override
116 public Object getInfoComponent() {
117 JPanel panel = new JPanel(new GridBagLayout());
118 panel.add(new JLabel(getToolTipText()), GBC.eol());
119 if (info != null) {
120 List<List<String>> content = new ArrayList<>();
121 content.add(Arrays.asList(tr("Name"), info.getName()));
122 content.add(Arrays.asList(tr("Type"), info.getImageryType().getTypeString().toUpperCase(Locale.ENGLISH)));
123 content.add(Arrays.asList(tr("URL"), info.getUrl()));
124 content.add(Arrays.asList(tr("Id"), info.getId() == null ? "-" : info.getId()));
125 if (info.getMinZoom() != 0) {
126 content.add(Arrays.asList(tr("Min. zoom"), Integer.toString(info.getMinZoom())));
127 }
128 if (info.getMaxZoom() != 0) {
129 content.add(Arrays.asList(tr("Max. zoom"), Integer.toString(info.getMaxZoom())));
130 }
131 if (info.getDescription() != null) {
132 content.add(Arrays.asList(tr("Description"), info.getDescription()));
133 }
134 for (List<String> entry: content) {
135 panel.add(new JLabel(entry.get(0) + ':'), GBC.std());
136 panel.add(GBC.glue(5, 0), GBC.std());
137 panel.add(createTextField(entry.get(1)), GBC.eol().fill(GBC.HORIZONTAL));
138 }
139 }
140 return panel;
141 }
142
143 protected JComponent createTextField(String text) {
144 if (text != null && text.matches("https?://.*")) {
145 return new UrlLabel(text);
146 }
147 JTextField ret = new JTextField(text);
148 ret.setEditable(false);
149 ret.setBorder(BorderFactory.createEmptyBorder());
150 return ret;
151 }
152
153 /**
154 * Create a new imagery layer
155 * @param info The imagery info to use as base
156 * @return The created layer
157 */
158 public static ImageryLayer create(ImageryInfo info) {
159 switch(info.getImageryType()) {
160 case WMS:
161 case WMS_ENDPOINT:
162 return new WMSLayer(info);
163 case WMTS:
164 return new WMTSLayer(info);
165 case TMS:
166 case BING:
167 case SCANEX:
168 return new TMSLayer(info);
169 default:
170 throw new AssertionError(tr("Unsupported imagery type: {0}", info.getImageryType()));
171 }
172 }
173
174 private static class ApplyOffsetAction extends AbstractAction {
175 private final transient OffsetMenuEntry menuEntry;
176
177 ApplyOffsetAction(OffsetMenuEntry menuEntry) {
178 super(menuEntry.getLabel());
179 this.menuEntry = menuEntry;
180 }
181
182 @Override
183 public void actionPerformed(ActionEvent ev) {
184 menuEntry.actionPerformed();
185 //TODO: Use some form of listeners for this.
186 MainApplication.getMenu().imageryMenu.refreshOffsetMenu();
187 }
188 }
189
190 public class OffsetAction extends AbstractAction implements LayerAction {
191 @Override
192 public void actionPerformed(ActionEvent e) {
193 // Do nothing
194 }
195
196 @Override
197 public Component createMenuComponent() {
198 return getOffsetMenuItem();
199 }
200
201 @Override
202 public boolean supportLayers(List<Layer> layers) {
203 return false;
204 }
205 }
206
207 /**
208 * Create the menu item that should be added to the offset menu.
209 * It may have a sub menu of e.g. bookmarks added to it.
210 * @return The menu item to add to the imagery menu.
211 */
212 public JMenuItem getOffsetMenuItem() {
213 JMenu subMenu = new JMenu(trc("layer", "Offset"));
214 subMenu.setIcon(ImageProvider.get("mapmode", "adjustimg"));
215 return (JMenuItem) getOffsetMenuItem(subMenu);
216 }
217
218 /**
219 * Create the submenu or the menu item to set the offset of the layer.
220 *
221 * If only one menu item for this layer exists, it is returned by this method.
222 *
223 * If there are multiple, this method appends them to the subMenu and then returns the reference to the subMenu.
224 * @param subMenu The subMenu to use
225 * @return A single menu item to adjust the layer or the passed subMenu to which the menu items were appended.
226 */
227 public JComponent getOffsetMenuItem(JComponent subMenu) {
228 JMenuItem adjustMenuItem = new JMenuItem(getAdjustAction());
229 List<OffsetMenuEntry> usableBookmarks = getOffsetMenuEntries();
230 if (usableBookmarks.isEmpty()) {
231 return adjustMenuItem;
232 }
233
234 subMenu.add(adjustMenuItem);
235 subMenu.add(new JSeparator());
236 int menuItemHeight = 0;
237 for (OffsetMenuEntry b : usableBookmarks) {
238 JCheckBoxMenuItem item = new JCheckBoxMenuItem(new ApplyOffsetAction(b));
239 item.setSelected(b.isActive());
240 subMenu.add(item);
241 menuItemHeight = item.getPreferredSize().height;
242 }
243 if (menuItemHeight > 0) {
244 if (subMenu instanceof JMenu) {
245 MenuScroller.setScrollerFor((JMenu) subMenu);
246 } else if (subMenu instanceof JPopupMenu) {
247 MenuScroller.setScrollerFor((JPopupMenu) subMenu);
248 }
249 }
250 return subMenu;
251 }
252
253 protected abstract Action getAdjustAction();
254
255 protected abstract List<OffsetMenuEntry> getOffsetMenuEntries();
256
257 /**
258 * Gets the settings for the filter that is applied to this layer.
259 * @return The filter settings.
260 * @since 10547
261 */
262 public ImageryFilterSettings getFilterSettings() {
263 return filterSettings;
264 }
265
266 /**
267 * This method adds the {@link ImageProcessor} to this Layer if it is not {@code null}.
268 *
269 * @param processor that processes the image
270 *
271 * @return true if processor was added, false otherwise
272 */
273 public boolean addImageProcessor(ImageProcessor processor) {
274 return processor != null && imageProcessors.add(processor);
275 }
276
277 /**
278 * This method removes given {@link ImageProcessor} from this layer
279 *
280 * @param processor which is needed to be removed
281 *
282 * @return true if processor was removed
283 */
284 public boolean removeImageProcessor(ImageProcessor processor) {
285 return imageProcessors.remove(processor);
286 }
287
288 /**
289 * Wraps a {@link BufferedImageOp} to be used as {@link ImageProcessor}.
290 * @param op the {@link BufferedImageOp}
291 * @param inPlace true to apply filter in place, i.e., not create a new {@link BufferedImage} for the result
292 * (the {@code op} needs to support this!)
293 * @return the {@link ImageProcessor} wrapper
294 */
295 public static ImageProcessor createImageProcessor(final BufferedImageOp op, final boolean inPlace) {
296 return image -> op.filter(image, inPlace ? image : null);
297 }
298
299 /**
300 * This method gets all {@link ImageProcessor}s of the layer
301 *
302 * @return list of image processors without removed one
303 */
304 public List<ImageProcessor> getImageProcessors() {
305 return imageProcessors;
306 }
307
308 /**
309 * Applies all the chosen {@link ImageProcessor}s to the image
310 *
311 * @param img - image which should be changed
312 *
313 * @return the new changed image
314 */
315 public BufferedImage applyImageProcessors(BufferedImage img) {
316 for (ImageProcessor processor : imageProcessors) {
317 img = processor.process(img);
318 }
319 return img;
320 }
321
322 /**
323 * An additional menu entry in the imagery offset menu.
324 * @author Michael Zangl
325 * @see ImageryLayer#getOffsetMenuEntries()
326 * @since 13243
327 */
328 public interface OffsetMenuEntry {
329 /**
330 * Get the label to use for this menu item
331 * @return The label to display in the menu.
332 */
333 String getLabel();
334
335 /**
336 * Test whether this bookmark is currently active
337 * @return <code>true</code> if it is active
338 */
339 boolean isActive();
340
341 /**
342 * Load this bookmark
343 */
344 void actionPerformed();
345 }
346
347 @Override
348 public String toString() {
349 return getClass().getSimpleName() + " [info=" + info + ']';
350 }
351}
Note: See TracBrowser for help on using the repository browser.