001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer;
003
004import java.io.IOException;
005import java.util.Set;
006
007import org.apache.commons.jcs.access.CacheAccess;
008import org.openstreetmap.gui.jmapviewer.TileXY;
009import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
010import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
011import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource;
012import org.openstreetmap.josm.Main;
013import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
014import org.openstreetmap.josm.data.coor.LatLon;
015import org.openstreetmap.josm.data.imagery.ImageryInfo;
016import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
017import org.openstreetmap.josm.data.imagery.WMSCachedTileLoader;
018import org.openstreetmap.josm.data.imagery.WMTSTileSource;
019import org.openstreetmap.josm.data.preferences.BooleanProperty;
020import org.openstreetmap.josm.data.projection.Projection;
021import org.openstreetmap.josm.gui.MapView;
022
023/**
024 * WMTS layer based on AbstractTileSourceLayer. Overrides few methods to align WMTS to Tile based computations
025 * but most magic is done within WMTSTileSource class.
026 *
027 * Full specification of the protocol available at:
028 * http://www.opengeospatial.org/standards/wmts
029 *
030 * @author Wiktor Niesiobędzki
031 *
032 */
033public class WMTSLayer extends AbstractCachedTileSourceLayer {
034    /**
035     * default setting of autozoom per layer
036     */
037    public static final BooleanProperty PROP_DEFAULT_AUTOZOOM = new BooleanProperty("imagery.wmts.default_autozoom", true);
038    private static final String CACHE_REGION_NAME = "WMTS";
039
040
041    /**
042     * Creates WMTS layer from ImageryInfo
043     * @param info Imagery Info describing the layer
044     */
045    public WMTSLayer(ImageryInfo info) {
046        super(info);
047        autoZoom = PROP_DEFAULT_AUTOZOOM.get();
048    }
049
050    @Override
051    protected AbstractTMSTileSource getTileSource(ImageryInfo info) {
052        try {
053            if (info.getImageryType() == ImageryType.WMTS && info.getUrl() != null) {
054                WMTSTileSource.checkUrl(info.getUrl());
055                WMTSTileSource tileSource = new WMTSTileSource(info);
056                info.setAttribution(tileSource);
057                return tileSource;
058            }
059            return null;
060        } catch (IOException e) {
061            Main.warn(e);
062            throw new IllegalArgumentException(e);
063        }
064    }
065
066    /**
067     * @param zoom level of the tile
068     * @return how many pixels of the screen occupies one pixel of the tile
069     */
070    private double getTileToScreenRatio(int zoom) {
071         MapView mv = Main.map.mapView;
072         LatLon topLeft = mv.getLatLon(0, 0);
073         LatLon botLeft = mv.getLatLon(0, tileSource.getTileSize());
074
075         TileXY topLeftTile = tileSource.latLonToTileXY(topLeft.toCoordinate(), zoom);
076
077         ICoordinate north = tileSource.tileXYToLatLon(topLeftTile.getXIndex(), topLeftTile.getYIndex(), zoom);
078         ICoordinate south = tileSource.tileXYToLatLon(topLeftTile.getXIndex(), topLeftTile.getYIndex() + 1, zoom);
079
080         return Math.abs((north.getLat() - south.getLat()) / (topLeft.lat() - botLeft.lat()));
081    }
082
083    @Override
084    protected int getBestZoom() {
085        if (!Main.isDisplayingMapView()) return 1;
086
087        for (int i = getMinZoomLvl() + 1; i <= getMaxZoomLvl(); i++) {
088            double ret = getTileToScreenRatio(i);
089            if (ret < 1) {
090                return i - 1;
091            }
092        }
093        return getMaxZoomLvl();
094    }
095
096    @Override
097    public boolean isProjectionSupported(Projection proj) {
098        Set<String> supportedProjections = ((WMTSTileSource) tileSource).getSupportedProjections();
099        return supportedProjections.contains(proj.toCode());
100    }
101
102    @Override
103    public String nameSupportedProjections() {
104        StringBuilder ret = new StringBuilder();
105        for (String e: ((WMTSTileSource) tileSource).getSupportedProjections()) {
106            ret.append(e).append(", ");
107        }
108        return ret.length() > 2 ? ret.substring(0, ret.length()-2) : ret.toString();
109    }
110
111    @Override
112    public void projectionChanged(Projection oldValue, Projection newValue) {
113        super.projectionChanged(oldValue, newValue);
114        ((WMTSTileSource) tileSource).initProjection(newValue);
115    }
116
117    @Override
118    protected Class<? extends TileLoader> getTileLoaderClass() {
119        return WMSCachedTileLoader.class;
120    }
121
122    @Override
123    protected String getCacheName() {
124        return CACHE_REGION_NAME;
125    }
126
127    /**
128     * @return cache region for WMTS layer
129     */
130    public static CacheAccess<String, BufferedImageCacheEntry> getCache() {
131        return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME);
132    }
133}