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