001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.event.ActionEvent;
007import java.util.ArrayList;
008import java.util.Arrays;
009import java.util.List;
010import java.util.Map;
011import java.util.Set;
012import java.util.TreeSet;
013
014import javax.swing.AbstractAction;
015import javax.swing.Action;
016
017import org.apache.commons.jcs.access.CacheAccess;
018import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
019import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
020import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource;
021import org.openstreetmap.josm.Main;
022import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
023import org.openstreetmap.josm.data.imagery.ImageryInfo;
024import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
025import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
026import org.openstreetmap.josm.data.imagery.TemplatedWMSTileSource;
027import org.openstreetmap.josm.data.imagery.WMSCachedTileLoader;
028import org.openstreetmap.josm.data.preferences.BooleanProperty;
029import org.openstreetmap.josm.data.preferences.IntegerProperty;
030import org.openstreetmap.josm.data.projection.Projection;
031
032/**
033 * This is a layer that grabs the current screen from an WMS server. The data
034 * fetched this way is tiled and managed to the disc to reduce server load.
035 *
036 */
037public class WMSLayer extends AbstractCachedTileSourceLayer {
038    private static final String PREFERENCE_PREFIX   = "imagery.wms.";
039
040    /** default tile size for WMS Layer */
041    public static final IntegerProperty PROP_IMAGE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + "imageSize", 512);
042
043    /** should WMS layer autozoom in default mode */
044    public static final BooleanProperty PROP_DEFAULT_AUTOZOOM = new BooleanProperty(PREFERENCE_PREFIX + "default_autozoom", true);
045
046    /** limit of concurrent connections to WMS tile source (per source) */
047    public static final IntegerProperty THREAD_LIMIT = new IntegerProperty(PREFERENCE_PREFIX + "simultaneousConnections", 3);
048
049    private static final String CACHE_REGION_NAME = "WMS";
050
051    private Set<String> supportedProjections;
052
053    /**
054     * Constructs a new {@code WMSLayer}.
055     * @param info ImageryInfo description of the layer
056     */
057    public WMSLayer(ImageryInfo info) {
058        super(info);
059        this.supportedProjections = new TreeSet<>(info.getServerProjections());
060        this.autoZoom = PROP_DEFAULT_AUTOZOOM.get();
061
062    }
063
064    @Override
065    public Action[] getMenuEntries() {
066        List<Action> ret = new ArrayList<>();
067        ret.addAll(Arrays.asList(super.getMenuEntries()));
068        ret.add(SeparatorLayerAction.INSTANCE);
069        ret.add(new LayerSaveAction(this));
070        ret.add(new LayerSaveAsAction(this));
071        ret.add(new BookmarkWmsAction());
072        return ret.toArray(new Action[]{});
073    }
074
075    @Override
076    protected AbstractTMSTileSource getTileSource(ImageryInfo info) {
077        if (info.getImageryType() == ImageryType.WMS && info.getUrl() != null) {
078            TemplatedWMSTileSource.checkUrl(info.getUrl());
079            TemplatedWMSTileSource tileSource = new TemplatedWMSTileSource(info);
080            info.setAttribution(tileSource);
081            return tileSource;
082        }
083        return null;
084    }
085
086    /**
087     * This action will add a WMS layer menu entry with the current WMS layer
088     * URL and name extended by the current resolution.
089     * When using the menu entry again, the WMS cache will be used properly.
090     */
091    public class BookmarkWmsAction extends AbstractAction {
092        /**
093         * Constructs a new {@code BookmarkWmsAction}.
094         */
095        public BookmarkWmsAction() {
096            super(tr("Set WMS Bookmark"));
097        }
098
099        @Override
100        public void actionPerformed(ActionEvent ev) {
101            ImageryLayerInfo.addLayer(new ImageryInfo(info));
102        }
103    }
104
105    @Override
106    protected Map<String, String> getHeaders(TileSource tileSource) {
107        if (tileSource instanceof TemplatedWMSTileSource) {
108            return ((TemplatedWMSTileSource) tileSource).getHeaders();
109        }
110        return null;
111    }
112
113    @Override
114    public boolean isProjectionSupported(Projection proj) {
115        return supportedProjections == null || supportedProjections.isEmpty() || supportedProjections.contains(proj.toCode());
116    }
117
118    @Override
119    public String nameSupportedProjections() {
120        StringBuilder ret = new StringBuilder();
121        for (String e: supportedProjections) {
122            ret.append(e).append(", ");
123        }
124        String appendix = "";
125
126        if (supportedProjections.contains("EPSG:4326") &&  "EPSG:3857".equals(Main.getProjection().toCode())) {
127            appendix = ". " + tr("JOSM will use EPSG:4326 to query the server, but results may vary "
128                    + "depending on the WMS server");
129        }
130        return ret.substring(0, ret.length()-2) + appendix;
131    }
132
133    @Override
134    public void projectionChanged(Projection oldValue, Projection newValue) {
135        super.projectionChanged(oldValue, newValue);
136
137        if (!newValue.equals(oldValue) && tileSource instanceof TemplatedWMSTileSource) {
138            ((TemplatedWMSTileSource) tileSource).initProjection(newValue);
139        }
140    }
141
142    @Override
143    protected Class<? extends TileLoader> getTileLoaderClass() {
144        return WMSCachedTileLoader.class;
145    }
146
147    @Override
148    protected String getCacheName() {
149        return CACHE_REGION_NAME;
150    }
151
152    /**
153     * @return cache region for WMS layer
154     */
155    public static CacheAccess<String, BufferedImageCacheEntry> getCache() {
156        return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME);
157    }
158}