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

Last change on this file since 8568 was 8568, checked in by wiktorn, 10 years ago

Basic WMTS support.

  • added information about units and to_meter to EPSG projection definitions (needed for WMTS)
  • added WMTSTileSource and WMTSLayer classes
  • a bit of cleanup of AbstractTileSourceLayer and align so it will work properly with WMTS tile definitions
  • added Imagery Preferences panel for WMTS and icon for button
  • added removal of wms: / tms: / wmts: prefix, if user will paste them into the field
  • CachedFile - added possibility to send custom headers with request
  • added support for unit and to_meter in CustomProjection
  • AbstractTMSTileSource cleanups (change of Coordinate to ICoordinate)
  • moved JCSCachedTileLoaderJob.read() to Utils

Addresses: #10623

Tested with Polish WMTS service proivders, Walonnie needs still some debugging, as it is not working right now.

  • 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.OffsetBookmark;
44import org.openstreetmap.josm.data.preferences.ColorProperty;
45import org.openstreetmap.josm.data.preferences.IntegerProperty;
46import org.openstreetmap.josm.gui.MenuScroller;
47import org.openstreetmap.josm.gui.widgets.UrlLabel;
48import org.openstreetmap.josm.tools.GBC;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.openstreetmap.josm.tools.Utils;
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 * @param info imagery info
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 (dx != 0 || 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 switch(info.getImageryType()) {
155 case WMS:
156 case HTML:
157 return new WMSLayer(info);
158 case WMTS:
159 return new WMTSLayer(info);
160 case TMS:
161 case BING:
162 case SCANEX:
163 return new TMSLayer(info);
164 default:
165 throw new AssertionError(tr("Unsupported imagery type: {0}", info.getImageryType()));
166 }
167 }
168
169 class ApplyOffsetAction extends AbstractAction {
170 private transient OffsetBookmark b;
171
172 ApplyOffsetAction(OffsetBookmark b) {
173 super(b.name);
174 this.b = b;
175 }
176
177 @Override
178 public void actionPerformed(ActionEvent ev) {
179 setOffset(b.dx, b.dy);
180 Main.main.menu.imageryMenu.refreshOffsetMenu();
181 Main.map.repaint();
182 }
183 }
184
185 public class OffsetAction extends AbstractAction implements LayerAction {
186 @Override
187 public void actionPerformed(ActionEvent e) {
188 }
189
190 @Override
191 public Component createMenuComponent() {
192 return getOffsetMenuItem();
193 }
194
195 @Override
196 public boolean supportLayers(List<Layer> layers) {
197 return false;
198 }
199 }
200
201 public JMenuItem getOffsetMenuItem() {
202 JMenu subMenu = new JMenu(trc("layer", "Offset"));
203 subMenu.setIcon(ImageProvider.get("mapmode", "adjustimg"));
204 return (JMenuItem) getOffsetMenuItem(subMenu);
205 }
206
207 public JComponent getOffsetMenuItem(JComponent subMenu) {
208 JMenuItem adjustMenuItem = new JMenuItem(adjustAction);
209 if (OffsetBookmark.allBookmarks.isEmpty()) return adjustMenuItem;
210
211 subMenu.add(adjustMenuItem);
212 subMenu.add(new JSeparator());
213 boolean hasBookmarks = false;
214 int menuItemHeight = 0;
215 for (OffsetBookmark b : OffsetBookmark.allBookmarks) {
216 if (!b.isUsable(this)) {
217 continue;
218 }
219 JCheckBoxMenuItem item = new JCheckBoxMenuItem(new ApplyOffsetAction(b));
220 if (Utils.equalsEpsilon(b.dx, dx) && Utils.equalsEpsilon(b.dy, dy)) {
221 item.setSelected(true);
222 }
223 subMenu.add(item);
224 menuItemHeight = item.getPreferredSize().height;
225 hasBookmarks = true;
226 }
227 if (menuItemHeight > 0) {
228 if (subMenu instanceof JMenu) {
229 MenuScroller.setScrollerFor((JMenu) subMenu);
230 } else if (subMenu instanceof JPopupMenu) {
231 MenuScroller.setScrollerFor((JPopupMenu) subMenu);
232 }
233 }
234 return hasBookmarks ? subMenu : adjustMenuItem;
235 }
236
237 public BufferedImage sharpenImage(BufferedImage img) {
238 if (sharpenLevel <= 0) return img;
239 int width = img.getWidth(null);
240 int height = img.getHeight(null);
241 BufferedImage tmp = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
242 tmp.getGraphics().drawImage(img, 0, 0, null);
243 Kernel kernel;
244 if (sharpenLevel == 1) {
245 kernel = new Kernel(3, 3, new float[] {-0.25f, -0.5f, -0.25f, -0.5f, 4, -0.5f, -0.25f, -0.5f, -0.25f});
246 } else {
247 kernel = new Kernel(3, 3, new float[] {-0.5f, -1, -0.5f, -1, 7, -1, -0.5f, -1, -0.5f});
248 }
249 BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
250 return op.filter(tmp, null);
251 }
252
253 /**
254 * Draws a red error tile when imagery tile cannot be fetched.
255 * @param img The buffered image
256 * @param message Additional error message to display
257 */
258 public void drawErrorTile(BufferedImage img, String message) {
259 Graphics2D g = (Graphics2D) img.getGraphics();
260 g.setColor(Color.RED);
261 g.fillRect(0, 0, img.getWidth(), img.getHeight());
262 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(24.0f));
263 g.setColor(Color.BLACK);
264
265 String text = tr("ERROR");
266 g.drawString(text, (img.getWidth() - g.getFontMetrics().stringWidth(text)) / 2, g.getFontMetrics().getHeight()+5);
267 if (message != null) {
268 float drawPosY = 2.5f*g.getFontMetrics().getHeight()+10;
269 if (!message.contains(" ")) {
270 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(18.0f));
271 g.drawString(message, 5, (int) drawPosY);
272 } else {
273 // Draw message on several lines
274 Map<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
275 map.put(TextAttribute.FAMILY, "Serif");
276 map.put(TextAttribute.SIZE, new Float(18.0));
277 AttributedString vanGogh = new AttributedString(message, map);
278 // Create a new LineBreakMeasurer from the text
279 AttributedCharacterIterator paragraph = vanGogh.getIterator();
280 int paragraphStart = paragraph.getBeginIndex();
281 int paragraphEnd = paragraph.getEndIndex();
282 FontRenderContext frc = g.getFontRenderContext();
283 LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
284 // Set break width to width of image with some margin
285 float breakWidth = img.getWidth()-10;
286 // Set position to the index of the first character in the text
287 lineMeasurer.setPosition(paragraphStart);
288 // Get lines until the entire paragraph has been displayed
289 while (lineMeasurer.getPosition() < paragraphEnd) {
290 // Retrieve next layout
291 TextLayout layout = lineMeasurer.nextLayout(breakWidth);
292
293 // Compute pen x position
294 float drawPosX = layout.isLeftToRight() ? 0 : breakWidth - layout.getAdvance();
295
296 // Move y-coordinate by the ascent of the layout
297 drawPosY += layout.getAscent();
298
299 // Draw the TextLayout at (drawPosX, drawPosY)
300 layout.draw(g, drawPosX, drawPosY);
301
302 // Move y-coordinate in preparation for next layout
303 drawPosY += layout.getDescent() + layout.getLeading();
304 }
305 }
306 }
307 }
308
309 @Override
310 public void destroy() {
311 super.destroy();
312 adjustAction.destroy();
313 }
314}
Note: See TracBrowser for help on using the repository browser.