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

Last change on this file since 13041 was 12782, checked in by Don-vip, 7 years ago

see #15229 - see #15182 - move ImageProcessor from gui.layer to tools

  • Property svn:eol-style set to native
File size: 11.3 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.Main;
32import org.openstreetmap.josm.data.ProjectionBounds;
33import org.openstreetmap.josm.data.coor.EastNorth;
34import org.openstreetmap.josm.data.imagery.ImageryInfo;
35import org.openstreetmap.josm.data.imagery.OffsetBookmark;
36import org.openstreetmap.josm.data.preferences.IntegerProperty;
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.TileSourceDisplaySettings;
42import org.openstreetmap.josm.tools.GBC;
43import org.openstreetmap.josm.tools.ImageProcessor;
44import org.openstreetmap.josm.tools.ImageProvider;
45import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
46import org.openstreetmap.josm.tools.Utils;
47
48/**
49 * Abstract base class for background imagery layers ({@link WMSLayer}, {@link TMSLayer}, {@link WMTSLayer}).
50 *
51 * Handles some common tasks, like image filters, image processors, etc.
52 */
53public abstract class ImageryLayer extends 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 Main.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 * Gets the x displacement of this layer.
95 * To be removed end of 2016
96 * @return The x displacement.
97 * @deprecated Use {@link TileSourceDisplaySettings#getDx()}
98 */
99 @Deprecated
100 public double getDx() {
101 // moved to AbstractTileSourceLayer/TileSourceDisplaySettings. Remains until all actions migrate.
102 return 0;
103 }
104
105 /**
106 * Gets the y displacement of this layer.
107 * To be removed end of 2016
108 * @return The y displacement.
109 * @deprecated Use {@link TileSourceDisplaySettings#getDy()}
110 */
111 @Deprecated
112 public double getDy() {
113 // moved to AbstractTileSourceLayer/TileSourceDisplaySettings. Remains until all actions migrate.
114 return 0;
115 }
116
117 /**
118 * Sets the displacement offset of this layer. The layer is automatically invalidated.
119 * To be removed end of 2016
120 * @param offset the offset bookmark
121 * @deprecated Use {@link TileSourceDisplaySettings}
122 */
123 @Deprecated
124 public void setOffset(OffsetBookmark offset) {
125 // moved to AbstractTileSourceLayer/TileSourceDisplaySettings. Remains until all actions migrate.
126 }
127
128 /**
129 * Returns imagery info.
130 * @return imagery info
131 */
132 public ImageryInfo getInfo() {
133 return info;
134 }
135
136 @Override
137 public Icon getIcon() {
138 return icon;
139 }
140
141 @Override
142 public boolean isMergable(Layer other) {
143 return false;
144 }
145
146 @Override
147 public void mergeFrom(Layer from) {
148 }
149
150 @Override
151 public Object getInfoComponent() {
152 JPanel panel = new JPanel(new GridBagLayout());
153 panel.add(new JLabel(getToolTipText()), GBC.eol());
154 if (info != null) {
155 List<List<String>> content = new ArrayList<>();
156 content.add(Arrays.asList(tr("Name"), info.getName()));
157 content.add(Arrays.asList(tr("Type"), info.getImageryType().getTypeString().toUpperCase(Locale.ENGLISH)));
158 content.add(Arrays.asList(tr("URL"), info.getUrl()));
159 content.add(Arrays.asList(tr("Id"), info.getId() == null ? "-" : info.getId()));
160 if (info.getMinZoom() != 0) {
161 content.add(Arrays.asList(tr("Min. zoom"), Integer.toString(info.getMinZoom())));
162 }
163 if (info.getMaxZoom() != 0) {
164 content.add(Arrays.asList(tr("Max. zoom"), Integer.toString(info.getMaxZoom())));
165 }
166 if (info.getDescription() != null) {
167 content.add(Arrays.asList(tr("Description"), info.getDescription()));
168 }
169 for (List<String> entry: content) {
170 panel.add(new JLabel(entry.get(0) + ':'), GBC.std());
171 panel.add(GBC.glue(5, 0), GBC.std());
172 panel.add(createTextField(entry.get(1)), GBC.eol().fill(GBC.HORIZONTAL));
173 }
174 }
175 return panel;
176 }
177
178 protected JTextField createTextField(String text) {
179 JTextField ret = new JTextField(text);
180 ret.setEditable(false);
181 ret.setBorder(BorderFactory.createEmptyBorder());
182 return ret;
183 }
184
185 public static ImageryLayer create(ImageryInfo info) {
186 switch(info.getImageryType()) {
187 case WMS:
188 return new WMSLayer(info);
189 case WMTS:
190 return new WMTSLayer(info);
191 case TMS:
192 case BING:
193 case SCANEX:
194 return new TMSLayer(info);
195 default:
196 throw new AssertionError(tr("Unsupported imagery type: {0}", info.getImageryType()));
197 }
198 }
199
200 class ApplyOffsetAction extends AbstractAction {
201 private final transient OffsetBookmark b;
202
203 ApplyOffsetAction(OffsetBookmark b) {
204 super(b.getName());
205 this.b = b;
206 }
207
208 @Override
209 public void actionPerformed(ActionEvent ev) {
210 setOffset(b);
211 MainApplication.getMenu().imageryMenu.refreshOffsetMenu();
212 MainApplication.getMap().repaint();
213 }
214 }
215
216 public class OffsetAction extends AbstractAction implements LayerAction {
217 @Override
218 public void actionPerformed(ActionEvent e) {
219 // Do nothing
220 }
221
222 @Override
223 public Component createMenuComponent() {
224 return getOffsetMenuItem();
225 }
226
227 @Override
228 public boolean supportLayers(List<Layer> layers) {
229 return false;
230 }
231 }
232
233 public JMenuItem getOffsetMenuItem() {
234 JMenu subMenu = new JMenu(trc("layer", "Offset"));
235 subMenu.setIcon(ImageProvider.get("mapmode", "adjustimg"));
236 return (JMenuItem) getOffsetMenuItem(subMenu);
237 }
238
239 public JComponent getOffsetMenuItem(JComponent subMenu) {
240 JMenuItem adjustMenuItem = new JMenuItem(getAdjustAction());
241 List<OffsetBookmark> allBookmarks = OffsetBookmark.getBookmarks();
242 if (allBookmarks.isEmpty()) return adjustMenuItem;
243
244 subMenu.add(adjustMenuItem);
245 subMenu.add(new JSeparator());
246 boolean hasBookmarks = false;
247 int menuItemHeight = 0;
248 for (OffsetBookmark b : allBookmarks) {
249 if (!b.isUsable(this)) {
250 continue;
251 }
252 JCheckBoxMenuItem item = new JCheckBoxMenuItem(new ApplyOffsetAction(b));
253 EastNorth offset = b.getDisplacement(Main.getProjection());
254 if (Utils.equalsEpsilon(offset.east(), getDx()) && Utils.equalsEpsilon(offset.north(), getDy())) {
255 item.setSelected(true);
256 }
257 subMenu.add(item);
258 menuItemHeight = item.getPreferredSize().height;
259 hasBookmarks = true;
260 }
261 if (menuItemHeight > 0) {
262 if (subMenu instanceof JMenu) {
263 MenuScroller.setScrollerFor((JMenu) subMenu);
264 } else if (subMenu instanceof JPopupMenu) {
265 MenuScroller.setScrollerFor((JPopupMenu) subMenu);
266 }
267 }
268 return hasBookmarks ? subMenu : adjustMenuItem;
269 }
270
271 protected abstract Action getAdjustAction();
272
273 /**
274 * Gets the settings for the filter that is applied to this layer.
275 * @return The filter settings.
276 * @since 10547
277 */
278 public ImageryFilterSettings getFilterSettings() {
279 return filterSettings;
280 }
281
282 /**
283 * This method adds the {@link ImageProcessor} to this Layer if it is not {@code null}.
284 *
285 * @param processor that processes the image
286 *
287 * @return true if processor was added, false otherwise
288 */
289 public boolean addImageProcessor(ImageProcessor processor) {
290 return processor != null && imageProcessors.add(processor);
291 }
292
293 /**
294 * This method removes given {@link ImageProcessor} from this layer
295 *
296 * @param processor which is needed to be removed
297 *
298 * @return true if processor was removed
299 */
300 public boolean removeImageProcessor(ImageProcessor processor) {
301 return imageProcessors.remove(processor);
302 }
303
304 /**
305 * Wraps a {@link BufferedImageOp} to be used as {@link ImageProcessor}.
306 * @param op the {@link BufferedImageOp}
307 * @param inPlace true to apply filter in place, i.e., not create a new {@link BufferedImage} for the result
308 * (the {@code op} needs to support this!)
309 * @return the {@link ImageProcessor} wrapper
310 */
311 public static ImageProcessor createImageProcessor(final BufferedImageOp op, final boolean inPlace) {
312 return image -> op.filter(image, inPlace ? image : null);
313 }
314
315 /**
316 * This method gets all {@link ImageProcessor}s of the layer
317 *
318 * @return list of image processors without removed one
319 */
320 public List<ImageProcessor> getImageProcessors() {
321 return imageProcessors;
322 }
323
324 /**
325 * Applies all the chosen {@link ImageProcessor}s to the image
326 *
327 * @param img - image which should be changed
328 *
329 * @return the new changed image
330 */
331 public BufferedImage applyImageProcessors(BufferedImage img) {
332 for (ImageProcessor processor : imageProcessors) {
333 img = processor.process(img);
334 }
335 return img;
336 }
337
338 @Override
339 public String toString() {
340 return getClass().getSimpleName() + " [info=" + info + ']';
341 }
342}
Note: See TracBrowser for help on using the repository browser.