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

Last change on this file since 19050 was 19050, checked in by taylor.smock, 4 weeks ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

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