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

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

squid:S1244 - Floating point numbers should not be tested for equality

  • 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;
51import org.openstreetmap.josm.tools.Utils;
52
53public abstract class ImageryLayer extends Layer {
54
55 public static final ColorProperty PROP_FADE_COLOR = new ColorProperty(marktr("Imagery fade"), Color.white);
56 public static final IntegerProperty PROP_FADE_AMOUNT = new IntegerProperty("imagery.fade_amount", 0);
57 public static final IntegerProperty PROP_SHARPEN_LEVEL = new IntegerProperty("imagery.sharpen_level", 0);
58
59 public static Color getFadeColor() {
60 return PROP_FADE_COLOR.get();
61 }
62
63 public static Color getFadeColorWithAlpha() {
64 Color c = PROP_FADE_COLOR.get();
65 return new Color(c.getRed(),c.getGreen(),c.getBlue(),PROP_FADE_AMOUNT.get()*255/100);
66 }
67
68 protected final ImageryInfo info;
69
70 protected Icon icon;
71
72 protected double dx = 0.0;
73 protected double dy = 0.0;
74
75 protected int sharpenLevel;
76
77 private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this);
78
79 /**
80 * Constructs a new {@code ImageryLayer}.
81 */
82 public ImageryLayer(ImageryInfo info) {
83 super(info.getName());
84 this.info = info;
85 if (info.getIcon() != null) {
86 icon = new ImageProvider(info.getIcon()).setOptional(true).
87 setMaxHeight(ICON_SIZE).setMaxWidth(ICON_SIZE).get();
88 }
89 if (icon == null) {
90 icon = ImageProvider.get("imagery_small");
91 }
92 this.sharpenLevel = PROP_SHARPEN_LEVEL.get();
93 }
94
95 public double getPPD() {
96 if (!Main.isDisplayingMapView()) return Main.getProjection().getDefaultZoomInPPD();
97 ProjectionBounds bounds = Main.map.mapView.getProjectionBounds();
98 return Main.map.mapView.getWidth() / (bounds.maxEast - bounds.minEast);
99 }
100
101 public double getDx() {
102 return dx;
103 }
104
105 public double getDy() {
106 return dy;
107 }
108
109 public void setOffset(double dx, double dy) {
110 this.dx = dx;
111 this.dy = dy;
112 }
113
114 public void displace(double dx, double dy) {
115 setOffset(this.dx += dx, this.dy += dy);
116 }
117
118 public ImageryInfo getInfo() {
119 return info;
120 }
121
122 @Override
123 public Icon getIcon() {
124 return icon;
125 }
126
127 @Override
128 public boolean isMergable(Layer other) {
129 return false;
130 }
131
132 @Override
133 public void mergeFrom(Layer from) {
134 }
135
136 @Override
137 public Object getInfoComponent() {
138 JPanel panel = new JPanel(new GridBagLayout());
139 panel.add(new JLabel(getToolTipText()), GBC.eol());
140 if (info != null) {
141 String url = info.getUrl();
142 if (url != null) {
143 panel.add(new JLabel(tr("URL: ")), GBC.std().insets(0, 5, 2, 0));
144 panel.add(new UrlLabel(url), GBC.eol().insets(2, 5, 10, 0));
145 }
146 if (Double.doubleToRawLongBits(dx) != 0 || Double.doubleToRawLongBits(dy) != 0) {
147 panel.add(new JLabel(tr("Offset: ") + dx + ";" + dy), GBC.eol().insets(0, 5, 10, 0));
148 }
149 }
150 return panel;
151 }
152
153 public static ImageryLayer create(ImageryInfo info) {
154 if (info.getImageryType() == ImageryType.WMS || info.getImageryType() == ImageryType.HTML)
155 return new WMSLayer(info);
156 else if (info.getImageryType() == ImageryType.TMS || info.getImageryType() == ImageryType.BING || info.getImageryType() == ImageryType.SCANEX)
157 return new TMSLayer(info);
158 else throw new AssertionError();
159 }
160
161 class ApplyOffsetAction extends AbstractAction {
162 private transient OffsetBookmark b;
163 ApplyOffsetAction(OffsetBookmark b) {
164 super(b.name);
165 this.b = b;
166 }
167
168 @Override
169 public void actionPerformed(ActionEvent ev) {
170 setOffset(b.dx, b.dy);
171 Main.main.menu.imageryMenu.refreshOffsetMenu();
172 Main.map.repaint();
173 }
174 }
175
176 public class OffsetAction extends AbstractAction implements LayerAction {
177 @Override
178 public void actionPerformed(ActionEvent e) {
179 }
180
181 @Override
182 public Component createMenuComponent() {
183 return getOffsetMenuItem();
184 }
185
186 @Override
187 public boolean supportLayers(List<Layer> layers) {
188 return false;
189 }
190 }
191
192 public JMenuItem getOffsetMenuItem() {
193 JMenu subMenu = new JMenu(trc("layer", "Offset"));
194 subMenu.setIcon(ImageProvider.get("mapmode", "adjustimg"));
195 return (JMenuItem)getOffsetMenuItem(subMenu);
196 }
197
198 public JComponent getOffsetMenuItem(JComponent subMenu) {
199 JMenuItem adjustMenuItem = new JMenuItem(adjustAction);
200 if (OffsetBookmark.allBookmarks.isEmpty()) return adjustMenuItem;
201
202 subMenu.add(adjustMenuItem);
203 subMenu.add(new JSeparator());
204 boolean hasBookmarks = false;
205 int menuItemHeight = 0;
206 for (OffsetBookmark b : OffsetBookmark.allBookmarks) {
207 if (!b.isUsable(this)) {
208 continue;
209 }
210 JCheckBoxMenuItem item = new JCheckBoxMenuItem(new ApplyOffsetAction(b));
211 if (Utils.equalsEpsilon(b.dx, dx) && Utils.equalsEpsilon(b.dy, dy)) {
212 item.setSelected(true);
213 }
214 subMenu.add(item);
215 menuItemHeight = item.getPreferredSize().height;
216 hasBookmarks = true;
217 }
218 if (menuItemHeight > 0) {
219 if (subMenu instanceof JMenu) {
220 MenuScroller.setScrollerFor((JMenu) subMenu);
221 } else if (subMenu instanceof JPopupMenu) {
222 MenuScroller.setScrollerFor((JPopupMenu)subMenu);
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.