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

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

fix #10392 - rework of MenuScroller to replace static scrollCount approach by dynamic behaviour (fix regressions from #10207)

  • Property svn:eol-style set to native
File size: 10.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trc;
7
8import java.awt.Color;
9import java.awt.Component;
10import java.awt.Font;
11import java.awt.Graphics2D;
12import java.awt.GridBagLayout;
13import java.awt.event.ActionEvent;
14import java.awt.font.FontRenderContext;
15import java.awt.font.LineBreakMeasurer;
16import java.awt.font.TextAttribute;
17import java.awt.font.TextLayout;
18import java.awt.image.BufferedImage;
19import java.awt.image.BufferedImageOp;
20import java.awt.image.ConvolveOp;
21import java.awt.image.Kernel;
22import java.text.AttributedCharacterIterator;
23import java.text.AttributedString;
24import java.util.Hashtable;
25import java.util.List;
26import java.util.Map;
27
28import javax.swing.AbstractAction;
29import javax.swing.Icon;
30import javax.swing.JCheckBoxMenuItem;
31import javax.swing.JComponent;
32import javax.swing.JLabel;
33import javax.swing.JMenu;
34import javax.swing.JMenuItem;
35import javax.swing.JPanel;
36import javax.swing.JPopupMenu;
37import javax.swing.JSeparator;
38
39import org.openstreetmap.josm.Main;
40import org.openstreetmap.josm.actions.ImageryAdjustAction;
41import org.openstreetmap.josm.data.ProjectionBounds;
42import org.openstreetmap.josm.data.imagery.ImageryInfo;
43import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
44import org.openstreetmap.josm.data.imagery.OffsetBookmark;
45import org.openstreetmap.josm.data.preferences.ColorProperty;
46import org.openstreetmap.josm.data.preferences.IntegerProperty;
47import org.openstreetmap.josm.gui.MenuScroller;
48import org.openstreetmap.josm.gui.widgets.UrlLabel;
49import org.openstreetmap.josm.tools.GBC;
50import org.openstreetmap.josm.tools.ImageProvider;
51
52public abstract class ImageryLayer extends Layer {
53
54 public static final ColorProperty PROP_FADE_COLOR = new ColorProperty(marktr("Imagery fade"), Color.white);
55 public static final IntegerProperty PROP_FADE_AMOUNT = new IntegerProperty("imagery.fade_amount", 0);
56 public static final IntegerProperty PROP_SHARPEN_LEVEL = new IntegerProperty("imagery.sharpen_level", 0);
57
58 public static Color getFadeColor() {
59 return PROP_FADE_COLOR.get();
60 }
61
62 public static Color getFadeColorWithAlpha() {
63 Color c = PROP_FADE_COLOR.get();
64 return new Color(c.getRed(),c.getGreen(),c.getBlue(),PROP_FADE_AMOUNT.get()*255/100);
65 }
66
67 protected final ImageryInfo info;
68
69 protected Icon icon;
70
71 protected double dx = 0.0;
72 protected double dy = 0.0;
73
74 protected int sharpenLevel;
75
76 private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this);
77
78 /**
79 * Constructs a new {@code ImageryLayer}.
80 */
81 public ImageryLayer(ImageryInfo info) {
82 super(info.getName());
83 this.info = info;
84 if (info.getIcon() != null) {
85 icon = new ImageProvider(info.getIcon()).setOptional(true).
86 setMaxHeight(ICON_SIZE).setMaxWidth(ICON_SIZE).get();
87 }
88 if (icon == null) {
89 icon = ImageProvider.get("imagery_small");
90 }
91 this.sharpenLevel = PROP_SHARPEN_LEVEL.get();
92 }
93
94 public double getPPD() {
95 if (!Main.isDisplayingMapView()) return Main.getProjection().getDefaultZoomInPPD();
96 ProjectionBounds bounds = Main.map.mapView.getProjectionBounds();
97 return Main.map.mapView.getWidth() / (bounds.maxEast - bounds.minEast);
98 }
99
100 public double getDx() {
101 return dx;
102 }
103
104 public double getDy() {
105 return dy;
106 }
107
108 public void setOffset(double dx, double dy) {
109 this.dx = dx;
110 this.dy = dy;
111 }
112
113 public void displace(double dx, double dy) {
114 setOffset(this.dx += dx, this.dy += dy);
115 }
116
117 public ImageryInfo getInfo() {
118 return info;
119 }
120
121 @Override
122 public Icon getIcon() {
123 return icon;
124 }
125
126 @Override
127 public boolean isMergable(Layer other) {
128 return false;
129 }
130
131 @Override
132 public void mergeFrom(Layer from) {
133 }
134
135 @Override
136 public Object getInfoComponent() {
137 JPanel panel = new JPanel(new GridBagLayout());
138 panel.add(new JLabel(getToolTipText()), GBC.eol());
139 if (info != null) {
140 String url = info.getUrl();
141 if (url != null) {
142 panel.add(new JLabel(tr("URL: ")), GBC.std().insets(0, 5, 2, 0));
143 panel.add(new UrlLabel(url), GBC.eol().insets(2, 5, 10, 0));
144 }
145 if (dx != 0.0 || dy != 0.0) {
146 panel.add(new JLabel(tr("Offset: ") + dx + ";" + dy), GBC.eol().insets(0, 5, 10, 0));
147 }
148 }
149 return panel;
150 }
151
152 public static ImageryLayer create(ImageryInfo info) {
153 if (info.getImageryType() == ImageryType.WMS || info.getImageryType() == ImageryType.HTML)
154 return new WMSLayer(info);
155 else if (info.getImageryType() == ImageryType.TMS || info.getImageryType() == ImageryType.BING || info.getImageryType() == ImageryType.SCANEX)
156 return new TMSLayer(info);
157 else throw new AssertionError();
158 }
159
160 class ApplyOffsetAction extends AbstractAction {
161 private OffsetBookmark b;
162 ApplyOffsetAction(OffsetBookmark b) {
163 super(b.name);
164 this.b = b;
165 }
166
167 @Override
168 public void actionPerformed(ActionEvent ev) {
169 setOffset(b.dx, b.dy);
170 Main.main.menu.imageryMenu.refreshOffsetMenu();
171 Main.map.repaint();
172 }
173 }
174
175 public class OffsetAction extends AbstractAction implements LayerAction {
176 @Override
177 public void actionPerformed(ActionEvent e) {
178 }
179
180 @Override
181 public Component createMenuComponent() {
182 return getOffsetMenuItem();
183 }
184
185 @Override
186 public boolean supportLayers(List<Layer> layers) {
187 return false;
188 }
189 }
190
191 public JMenuItem getOffsetMenuItem() {
192 JMenu subMenu = new JMenu(trc("layer", "Offset"));
193 subMenu.setIcon(ImageProvider.get("mapmode", "adjustimg"));
194 return (JMenuItem)getOffsetMenuItem(subMenu);
195 }
196
197 public JComponent getOffsetMenuItem(JComponent subMenu) {
198 JMenuItem adjustMenuItem = new JMenuItem(adjustAction);
199 if (OffsetBookmark.allBookmarks.isEmpty()) return adjustMenuItem;
200
201 subMenu.add(adjustMenuItem);
202 subMenu.add(new JSeparator());
203 boolean hasBookmarks = false;
204 int menuItemHeight = 0;
205 for (OffsetBookmark b : OffsetBookmark.allBookmarks) {
206 if (!b.isUsable(this)) {
207 continue;
208 }
209 JCheckBoxMenuItem item = new JCheckBoxMenuItem(new ApplyOffsetAction(b));
210 if (b.dx == dx && b.dy == dy) {
211 item.setSelected(true);
212 }
213 subMenu.add(item);
214 menuItemHeight = item.getPreferredSize().height;
215 hasBookmarks = true;
216 }
217 if (menuItemHeight > 0) {
218 if (subMenu instanceof JMenu) {
219 MenuScroller.setScrollerFor((JMenu) subMenu);
220 } else if (subMenu instanceof JPopupMenu) {
221 MenuScroller.setScrollerFor((JPopupMenu)subMenu);
222 }
223 }
224 return hasBookmarks ? subMenu : adjustMenuItem;
225 }
226
227 public BufferedImage sharpenImage(BufferedImage img) {
228 if (sharpenLevel <= 0) return img;
229 int width = img.getWidth(null);
230 int height = img.getHeight(null);
231 BufferedImage tmp = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
232 tmp.getGraphics().drawImage(img, 0, 0, null);
233 Kernel kernel;
234 if (sharpenLevel == 1) {
235 kernel = new Kernel(3, 3, new float[] { -0.25f, -0.5f, -0.25f, -0.5f, 4, -0.5f, -0.25f, -0.5f, -0.25f});
236 } else {
237 kernel = new Kernel(3, 3, new float[] { -0.5f, -1, -0.5f, -1, 7, -1, -0.5f, -1, -0.5f});
238 }
239 BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
240 return op.filter(tmp, null);
241 }
242
243 /**
244 * Draws a red error tile when imagery tile cannot be fetched.
245 * @param img The buffered image
246 * @param message Additional error message to display
247 */
248 public void drawErrorTile(BufferedImage img, String message) {
249 Graphics2D g = (Graphics2D) img.getGraphics();
250 g.setColor(Color.RED);
251 g.fillRect(0, 0, img.getWidth(), img.getHeight());
252 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(24.0f));
253 g.setColor(Color.BLACK);
254
255 String text = tr("ERROR");
256 g.drawString(text, (img.getWidth() - g.getFontMetrics().stringWidth(text)) / 2, g.getFontMetrics().getHeight()+5);
257 if (message != null) {
258 float drawPosY = 2.5f*g.getFontMetrics().getHeight()+10;
259 if (!message.contains(" ")) {
260 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(18.0f));
261 g.drawString(message, 5, (int)drawPosY);
262 } else {
263 // Draw message on several lines
264 Map<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
265 map.put(TextAttribute.FAMILY, "Serif");
266 map.put(TextAttribute.SIZE, new Float(18.0));
267 AttributedString vanGogh = new AttributedString(message, map);
268 // Create a new LineBreakMeasurer from the text
269 AttributedCharacterIterator paragraph = vanGogh.getIterator();
270 int paragraphStart = paragraph.getBeginIndex();
271 int paragraphEnd = paragraph.getEndIndex();
272 FontRenderContext frc = g.getFontRenderContext();
273 LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
274 // Set break width to width of image with some margin
275 float breakWidth = img.getWidth()-10;
276 // Set position to the index of the first character in the text
277 lineMeasurer.setPosition(paragraphStart);
278 // Get lines until the entire paragraph has been displayed
279 while (lineMeasurer.getPosition() < paragraphEnd) {
280 // Retrieve next layout
281 TextLayout layout = lineMeasurer.nextLayout(breakWidth);
282
283 // Compute pen x position
284 float drawPosX = layout.isLeftToRight() ? 0 : breakWidth - layout.getAdvance();
285
286 // Move y-coordinate by the ascent of the layout
287 drawPosY += layout.getAscent();
288
289 // Draw the TextLayout at (drawPosX, drawPosY)
290 layout.draw(g, drawPosX, drawPosY);
291
292 // Move y-coordinate in preparation for next layout
293 drawPosY += layout.getDescent() + layout.getLeading();
294 }
295 }
296 }
297 }
298
299 @Override
300 public void destroy() {
301 super.destroy();
302 adjustAction.destroy();
303 }
304}
Note: See TracBrowser for help on using the repository browser.