001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import static org.openstreetmap.josm.tools.I18n.marktr; 005import static org.openstreetmap.josm.tools.I18n.tr; 006import static org.openstreetmap.josm.tools.I18n.trc; 007 008import java.awt.Color; 009import java.awt.Component; 010import java.awt.Font; 011import java.awt.Graphics; 012import java.awt.GridBagLayout; 013import java.awt.Toolkit; 014import java.awt.event.ActionEvent; 015import java.awt.image.BufferedImage; 016import java.awt.image.BufferedImageOp; 017import java.awt.image.ConvolveOp; 018import java.awt.image.Kernel; 019import java.util.List; 020 021import javax.swing.AbstractAction; 022import javax.swing.Icon; 023import javax.swing.JCheckBoxMenuItem; 024import javax.swing.JComponent; 025import javax.swing.JLabel; 026import javax.swing.JMenu; 027import javax.swing.JMenuItem; 028import javax.swing.JPanel; 029import javax.swing.JPopupMenu; 030import javax.swing.JSeparator; 031 032import org.openstreetmap.josm.Main; 033import org.openstreetmap.josm.actions.ImageryAdjustAction; 034import org.openstreetmap.josm.data.ProjectionBounds; 035import org.openstreetmap.josm.data.imagery.ImageryInfo; 036import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 037import org.openstreetmap.josm.data.imagery.OffsetBookmark; 038import org.openstreetmap.josm.data.preferences.ColorProperty; 039import org.openstreetmap.josm.data.preferences.IntegerProperty; 040import org.openstreetmap.josm.gui.MenuScroller; 041import org.openstreetmap.josm.gui.widgets.UrlLabel; 042import org.openstreetmap.josm.tools.GBC; 043import org.openstreetmap.josm.tools.ImageProvider; 044 045public abstract class ImageryLayer extends Layer { 046 047 public static final ColorProperty PROP_FADE_COLOR = new ColorProperty(marktr("Imagery fade"), Color.white); 048 public static final IntegerProperty PROP_FADE_AMOUNT = new IntegerProperty("imagery.fade_amount", 0); 049 public static final IntegerProperty PROP_SHARPEN_LEVEL = new IntegerProperty("imagery.sharpen_level", 0); 050 051 public static Color getFadeColor() { 052 return PROP_FADE_COLOR.get(); 053 } 054 055 public static Color getFadeColorWithAlpha() { 056 Color c = PROP_FADE_COLOR.get(); 057 return new Color(c.getRed(),c.getGreen(),c.getBlue(),PROP_FADE_AMOUNT.get()*255/100); 058 } 059 060 protected final ImageryInfo info; 061 062 protected Icon icon; 063 064 protected double dx = 0.0; 065 protected double dy = 0.0; 066 067 protected int sharpenLevel; 068 069 private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this); 070 071 public ImageryLayer(ImageryInfo info) { 072 super(info.getName()); 073 this.info = info; 074 if (info.getIcon() != null) { 075 icon = new ImageProvider(info.getIcon()).setOptional(true). 076 setMaxHeight(ICON_SIZE).setMaxWidth(ICON_SIZE).get(); 077 } 078 if (icon == null) { 079 icon = ImageProvider.get("imagery_small"); 080 } 081 this.sharpenLevel = PROP_SHARPEN_LEVEL.get(); 082 } 083 084 public double getPPD(){ 085 if (!Main.isDisplayingMapView()) return Main.getProjection().getDefaultZoomInPPD(); 086 ProjectionBounds bounds = Main.map.mapView.getProjectionBounds(); 087 return Main.map.mapView.getWidth() / (bounds.maxEast - bounds.minEast); 088 } 089 090 public double getDx() { 091 return dx; 092 } 093 094 public double getDy() { 095 return dy; 096 } 097 098 public void setOffset(double dx, double dy) { 099 this.dx = dx; 100 this.dy = dy; 101 } 102 103 public void displace(double dx, double dy) { 104 setOffset(this.dx += dx, this.dy += dy); 105 } 106 107 public ImageryInfo getInfo() { 108 return info; 109 } 110 111 @Override 112 public Icon getIcon() { 113 return icon; 114 } 115 116 @Override 117 public boolean isMergable(Layer other) { 118 return false; 119 } 120 121 @Override 122 public void mergeFrom(Layer from) { 123 } 124 125 @Override 126 public Object getInfoComponent() { 127 JPanel panel = new JPanel(new GridBagLayout()); 128 panel.add(new JLabel(getToolTipText()), GBC.eol()); 129 if (info != null) { 130 String url = info.getUrl(); 131 if (url != null) { 132 panel.add(new JLabel(tr("URL: ")), GBC.std().insets(0, 5, 2, 0)); 133 panel.add(new UrlLabel(url), GBC.eol().insets(2, 5, 10, 0)); 134 } 135 if (dx != 0.0 || dy != 0.0) { 136 panel.add(new JLabel(tr("Offset: ") + dx + ";" + dy), GBC.eol().insets(0, 5, 10, 0)); 137 } 138 } 139 return panel; 140 } 141 142 public static ImageryLayer create(ImageryInfo info) { 143 if (info.getImageryType() == ImageryType.WMS || info.getImageryType() == ImageryType.HTML) 144 return new WMSLayer(info); 145 else if (info.getImageryType() == ImageryType.TMS || info.getImageryType() == ImageryType.BING || info.getImageryType() == ImageryType.SCANEX) 146 return new TMSLayer(info); 147 else throw new AssertionError(); 148 } 149 150 class ApplyOffsetAction extends AbstractAction { 151 private OffsetBookmark b; 152 ApplyOffsetAction(OffsetBookmark b) { 153 super(b.name); 154 this.b = b; 155 } 156 157 @Override 158 public void actionPerformed(ActionEvent ev) { 159 setOffset(b.dx, b.dy); 160 Main.main.menu.imageryMenu.refreshOffsetMenu(); 161 Main.map.repaint(); 162 } 163 } 164 165 public class OffsetAction extends AbstractAction implements LayerAction { 166 @Override 167 public void actionPerformed(ActionEvent e) { 168 } 169 170 @Override 171 public Component createMenuComponent() { 172 return getOffsetMenuItem(); 173 } 174 175 @Override 176 public boolean supportLayers(List<Layer> layers) { 177 return false; 178 } 179 } 180 181 public JMenuItem getOffsetMenuItem() { 182 JMenu subMenu = new JMenu(trc("layer", "Offset")); 183 subMenu.setIcon(ImageProvider.get("mapmode", "adjustimg")); 184 return (JMenuItem)getOffsetMenuItem(subMenu); 185 } 186 187 public JComponent getOffsetMenuItem(JComponent subMenu) { 188 JMenuItem adjustMenuItem = new JMenuItem(adjustAction); 189 if (OffsetBookmark.allBookmarks.isEmpty()) return adjustMenuItem; 190 191 subMenu.add(adjustMenuItem); 192 subMenu.add(new JSeparator()); 193 boolean hasBookmarks = false; 194 int menuItemHeight = 0; 195 for (OffsetBookmark b : OffsetBookmark.allBookmarks) { 196 if (!b.isUsable(this)) { 197 continue; 198 } 199 JCheckBoxMenuItem item = new JCheckBoxMenuItem(new ApplyOffsetAction(b)); 200 if (b.dx == dx && b.dy == dy) { 201 item.setSelected(true); 202 } 203 subMenu.add(item); 204 menuItemHeight = item.getPreferredSize().height; 205 hasBookmarks = true; 206 } 207 if (menuItemHeight > 0) { 208 int scrollcount = (Toolkit.getDefaultToolkit().getScreenSize().height / menuItemHeight) - 1; 209 if (subMenu instanceof JMenu) { 210 MenuScroller.setScrollerFor((JMenu) subMenu, scrollcount); 211 } else if (subMenu instanceof JPopupMenu) { 212 MenuScroller.setScrollerFor((JPopupMenu)subMenu, scrollcount); 213 } 214 } 215 return hasBookmarks ? subMenu : adjustMenuItem; 216 } 217 218 public BufferedImage sharpenImage(BufferedImage img) { 219 if (sharpenLevel <= 0) return img; 220 int width = img.getWidth(null); 221 int height = img.getHeight(null); 222 BufferedImage tmp = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 223 tmp.getGraphics().drawImage(img, 0, 0, null); 224 Kernel kernel; 225 if (sharpenLevel == 1) { 226 kernel = new Kernel(3, 3, new float[] { -0.25f, -0.5f, -0.25f, -0.5f, 4, -0.5f, -0.25f, -0.5f, -0.25f}); 227 } else { 228 kernel = new Kernel(3, 3, new float[] { -0.5f, -1, -0.5f, -1, 7, -1, -0.5f, -1, -0.5f}); 229 } 230 BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); 231 return op.filter(tmp, null); 232 } 233 234 public void drawErrorTile(BufferedImage img) { 235 Graphics g = img.getGraphics(); 236 g.setColor(Color.RED); 237 g.fillRect(0, 0, img.getWidth(), img.getHeight()); 238 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(36.0f)); 239 g.setColor(Color.BLACK); 240 241 String text = tr("ERROR"); 242 g.drawString(text, (img.getWidth() + g.getFontMetrics().stringWidth(text)) / 2, img.getHeight()/2); 243 } 244 245 /* (non-Javadoc) 246 * @see org.openstreetmap.josm.gui.layer.Layer#destroy() 247 */ 248 @Override 249 public void destroy() { 250 super.destroy(); 251 adjustAction.destroy(); 252 } 253}