// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.layer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import org.apache.commons.jcs.access.CacheAccess; import org.openstreetmap.gui.jmapviewer.OsmMercator; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource; import org.openstreetmap.gui.jmapviewer.tilesources.ScanexTileSource; import org.openstreetmap.gui.jmapviewer.tilesources.TMSTileSource; import org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry; import org.openstreetmap.josm.data.imagery.CachedAttributionBingAerialTileSource; import org.openstreetmap.josm.data.imagery.ImageryInfo; import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader; import org.openstreetmap.josm.data.preferences.BooleanProperty; import org.openstreetmap.josm.data.preferences.IntegerProperty; /** * Class that displays a slippy map layer. * * @author Frederik Ramm * @author LuVar <lubomir.varga@freemap.sk> * @author Dave Hansen <dave@sr71.net> * @author Upliner <upliner@gmail.com> * @since 3715 */ public class TMSLayer extends AbstractCachedTileSourceLayer<TMSTileSource> implements NativeScaleLayer { private static final String CACHE_REGION_NAME = "TMS"; private static final String PREFERENCE_PREFIX = "imagery.tms"; /** minimum zoom level for TMS layer */ public static final IntegerProperty PROP_MIN_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".min_zoom_lvl", AbstractTileSourceLayer.PROP_MIN_ZOOM_LVL.get()); /** maximum zoom level for TMS layer */ public static final IntegerProperty PROP_MAX_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".max_zoom_lvl", AbstractTileSourceLayer.PROP_MAX_ZOOM_LVL.get()); /** shall TMS layers be added to download dialog */ public static final BooleanProperty PROP_ADD_TO_SLIPPYMAP_CHOOSER = new BooleanProperty(PREFERENCE_PREFIX + ".add_to_slippymap_chooser", true); private static final ScaleList nativeScaleList = initNativeScaleList(); /** * Create a layer based on ImageryInfo * @param info description of the layer */ public TMSLayer(ImageryInfo info) { super(info); } /** * Creates and returns a new TileSource instance depending on the {@link ImageryType} * of the {@link ImageryInfo} object specified in the constructor. * * If no appropriate TileSource is found, null is returned. * Currently supported ImageryType are {@link ImageryType#TMS}, * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. * * * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. * @throws IllegalArgumentException if url from imagery info is null or invalid */ @Override protected TMSTileSource getTileSource() { return getTileSourceStatic(info, () -> { Main.debug("Attribution loaded, running loadAllErrorTiles"); this.loadAllErrorTiles(false); }); } @Override public Collection<String> getNativeProjections() { return Collections.singletonList("EPSG:3857"); } /** * Creates and returns a new TileSource instance depending on the {@link ImageryType} * of the passed ImageryInfo object. * * If no appropriate TileSource is found, null is returned. * Currently supported ImageryType are {@link ImageryType#TMS}, * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. * * @param info imagery info * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. * @throws IllegalArgumentException if url from imagery info is null or invalid */ public static AbstractTMSTileSource getTileSourceStatic(ImageryInfo info) { return getTileSourceStatic(info, null); } /** * Creates and returns a new TileSource instance depending on the {@link ImageryType} * of the passed ImageryInfo object. * * If no appropriate TileSource is found, null is returned. * Currently supported ImageryType are {@link ImageryType#TMS}, * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. * * @param info imagery info * @param attributionLoadedTask task to be run once attribution is loaded, might be null, if nothing special shall happen * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. * @throws IllegalArgumentException if url from imagery info is null or invalid */ public static TMSTileSource getTileSourceStatic(ImageryInfo info, Runnable attributionLoadedTask) { if (info.getImageryType() == ImageryType.TMS) { TemplatedTMSTileSource.checkUrl(info.getUrl()); TMSTileSource t = new TemplatedTMSTileSource(info); info.setAttribution(t); return t; } else if (info.getImageryType() == ImageryType.BING) { return new CachedAttributionBingAerialTileSource(info, attributionLoadedTask); } else if (info.getImageryType() == ImageryType.SCANEX) { return new ScanexTileSource(info); } return null; } @Override protected Class<? extends TileLoader> getTileLoaderClass() { return TMSCachedTileLoader.class; } @Override protected String getCacheName() { return CACHE_REGION_NAME; } /** * @return cache for TMS region */ public static CacheAccess<String, BufferedImageCacheEntry> getCache() { return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME); } @Override public ScaleList getNativeScales() { return nativeScaleList; } private static ScaleList initNativeScaleList() { Collection<Double> scales = new ArrayList<>(AbstractTileSourceLayer.MAX_ZOOM); for (int zoom = AbstractTileSourceLayer.MIN_ZOOM; zoom <= AbstractTileSourceLayer.MAX_ZOOM; zoom++) { double scale = OsmMercator.EARTH_RADIUS * Math.PI * 2 / Math.pow(2, zoom) / OsmMercator.DEFAUL_TILE_SIZE; scales.add(scale); } return new ScaleList(scales); } }