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

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

fix #3916 - WMS: Improve exception handling. Proper message is now displayed on tiles.

  • Property svn:eol-style set to native
File size: 10.9 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 int scrollcount = MenuScroller.computeScrollCount(subMenu, menuItemHeight);
219 if (subMenu instanceof JMenu) {
220 MenuScroller.setScrollerFor((JMenu) subMenu, scrollcount);
221 } else if (subMenu instanceof JPopupMenu) {
222 MenuScroller.setScrollerFor((JPopupMenu)subMenu, scrollcount);
223 }
224 }
225 return hasBookmarks ? subMenu : adjustMenuItem;
226 }
227
228 public BufferedImage sharpenImage(BufferedImage img) {
229 if (sharpenLevel <= 0) return img;
230 int width = img.getWidth(null);
231 int height = img.getHeight(null);
232 BufferedImage tmp = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
233 tmp.getGraphics().drawImage(img, 0, 0, null);
234 Kernel kernel;
235 if (sharpenLevel == 1) {
236 kernel = new Kernel(3, 3, new float[] { -0.25f, -0.5f, -0.25f, -0.5f, 4, -0.5f, -0.25f, -0.5f, -0.25f});
237 } else {
238 kernel = new Kernel(3, 3, new float[] { -0.5f, -1, -0.5f, -1, 7, -1, -0.5f, -1, -0.5f});
239 }
240 BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
241 return op.filter(tmp, null);
242 }
243
244 /**
245 * Draws a red error tile when imagery tile cannot be fetched.
246 * @param img The buffered image
247 * @param message Additional error message to display
248 */
249 public void drawErrorTile(BufferedImage img, String message) {
250 Graphics2D g = (Graphics2D) img.getGraphics();
251 g.setColor(Color.RED);
252 g.fillRect(0, 0, img.getWidth(), img.getHeight());
253 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(24.0f));
254 g.setColor(Color.BLACK);
255
256 String text = tr("ERROR");
257 g.drawString(text, (img.getWidth() - g.getFontMetrics().stringWidth(text)) / 2, g.getFontMetrics().getHeight()+5);
258 if (message != null) {
259 float drawPosY = 2.5f*g.getFontMetrics().getHeight()+10;
260 if (!message.contains(" ")) {
261 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(18.0f));
262 g.drawString(message, 5, (int)drawPosY);
263 } else {
264 // Draw message on several lines
265 Map<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
266 map.put(TextAttribute.FAMILY, "Serif");
267 map.put(TextAttribute.SIZE, new Float(18.0));
268 AttributedString vanGogh = new AttributedString(message, map);
269 // Create a new LineBreakMeasurer from the text
270 AttributedCharacterIterator paragraph = vanGogh.getIterator();
271 int paragraphStart = paragraph.getBeginIndex();
272 int paragraphEnd = paragraph.getEndIndex();
273 FontRenderContext frc = g.getFontRenderContext();
274 LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
275 // Set break width to width of image with some margin
276 float breakWidth = img.getWidth()-10;
277 // Set position to the index of the first character in the text
278 lineMeasurer.setPosition(paragraphStart);
279 // Get lines until the entire paragraph has been displayed
280 while (lineMeasurer.getPosition() < paragraphEnd) {
281 // Retrieve next layout
282 TextLayout layout = lineMeasurer.nextLayout(breakWidth);
283
284 // Compute pen x position
285 float drawPosX = layout.isLeftToRight() ? 0 : breakWidth - layout.getAdvance();
286
287 // Move y-coordinate by the ascent of the layout
288 drawPosY += layout.getAscent();
289
290 // Draw the TextLayout at (drawPosX, drawPosY)
291 layout.draw(g, drawPosX, drawPosY);
292
293 // Move y-coordinate in preparation for next layout
294 drawPosY += layout.getDescent() + layout.getLeading();
295 }
296 }
297 }
298 }
299
300 @Override
301 public void destroy() {
302 super.destroy();
303 adjustAction.destroy();
304 }
305}
Note: See TracBrowser for help on using the repository browser.