001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Collections; 007 008import org.apache.commons.jcs.access.CacheAccess; 009import org.openstreetmap.gui.jmapviewer.JMapViewer; 010import org.openstreetmap.gui.jmapviewer.OsmMercator; 011import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; 012import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource; 013import org.openstreetmap.gui.jmapviewer.tilesources.ScanexTileSource; 014import org.openstreetmap.gui.jmapviewer.tilesources.TMSTileSource; 015import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry; 016import org.openstreetmap.josm.data.imagery.CachedAttributionBingAerialTileSource; 017import org.openstreetmap.josm.data.imagery.ImageryInfo; 018import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 019import org.openstreetmap.josm.data.imagery.JosmTemplatedTMSTileSource; 020import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader; 021import org.openstreetmap.josm.data.preferences.BooleanProperty; 022import org.openstreetmap.josm.data.preferences.IntegerProperty; 023import org.openstreetmap.josm.tools.Logging; 024 025/** 026 * Class that displays a slippy map layer. 027 * 028 * @author Frederik Ramm 029 * @author LuVar <lubomir.varga@freemap.sk> 030 * @author Dave Hansen <dave@sr71.net> 031 * @author Upliner <upliner@gmail.com> 032 * @since 3715 033 */ 034public class TMSLayer extends AbstractCachedTileSourceLayer<TMSTileSource> implements NativeScaleLayer { 035 private static final String CACHE_REGION_NAME = "TMS"; 036 037 private static final String PREFERENCE_PREFIX = "imagery.tms"; 038 039 /** minimum zoom level for TMS layer */ 040 public static final IntegerProperty PROP_MIN_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".min_zoom_lvl", 041 AbstractTileSourceLayer.PROP_MIN_ZOOM_LVL.get()); 042 /** maximum zoom level for TMS layer */ 043 public static final IntegerProperty PROP_MAX_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".max_zoom_lvl", 044 AbstractTileSourceLayer.PROP_MAX_ZOOM_LVL.get()); 045 /** shall TMS layers be added to download dialog */ 046 public static final BooleanProperty PROP_ADD_TO_SLIPPYMAP_CHOOSER = new BooleanProperty(PREFERENCE_PREFIX + ".add_to_slippymap_chooser", 047 true); 048 /** override minimum/maximum zoom level with those supported by JMapViewer, as these might be used in slippymap chooser */ 049 public static final int MAX_ZOOM = JMapViewer.MAX_ZOOM; 050 public static final int MIN_ZOOM = JMapViewer.MIN_ZOOM; 051 052 private static final ScaleList nativeScaleList = initNativeScaleList(); 053 054 /** 055 * Create a layer based on ImageryInfo 056 * @param info description of the layer 057 */ 058 public TMSLayer(ImageryInfo info) { 059 super(info); 060 } 061 062 /** 063 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 064 * of the {@link ImageryInfo} object specified in the constructor. 065 * 066 * If no appropriate TileSource is found, null is returned. 067 * Currently supported ImageryType are {@link ImageryType#TMS}, 068 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 069 * 070 * 071 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 072 * @throws IllegalArgumentException if url from imagery info is null or invalid 073 */ 074 @Override 075 protected TMSTileSource getTileSource() { 076 return getTileSourceStatic(info, () -> { 077 Logging.debug("Attribution loaded, running loadAllErrorTiles"); 078 this.loadAllErrorTiles(false); 079 }); 080 } 081 082 @Override 083 public Collection<String> getNativeProjections() { 084 return Collections.singletonList("EPSG:3857"); 085 } 086 087 /** 088 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 089 * of the passed ImageryInfo object. 090 * 091 * If no appropriate TileSource is found, null is returned. 092 * Currently supported ImageryType are {@link ImageryType#TMS}, 093 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 094 * 095 * @param info imagery info 096 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 097 * @throws IllegalArgumentException if url from imagery info is null or invalid 098 */ 099 public static AbstractTMSTileSource getTileSourceStatic(ImageryInfo info) { 100 return getTileSourceStatic(info, null); 101 } 102 103 /** 104 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 105 * of the passed ImageryInfo object. 106 * 107 * If no appropriate TileSource is found, null is returned. 108 * Currently supported ImageryType are {@link ImageryType#TMS}, 109 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 110 * 111 * @param info imagery info 112 * @param attributionLoadedTask task to be run once attribution is loaded, might be null, if nothing special shall happen 113 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 114 * @throws IllegalArgumentException if url from imagery info is null or invalid 115 */ 116 public static TMSTileSource getTileSourceStatic(ImageryInfo info, Runnable attributionLoadedTask) { 117 if (info.getImageryType() == ImageryType.TMS) { 118 JosmTemplatedTMSTileSource.checkUrl(info.getUrl()); 119 TMSTileSource t = new JosmTemplatedTMSTileSource(info); 120 info.setAttribution(t); 121 return t; 122 } else if (info.getImageryType() == ImageryType.BING) { 123 return new CachedAttributionBingAerialTileSource(info, attributionLoadedTask); 124 } else if (info.getImageryType() == ImageryType.SCANEX) { 125 return new ScanexTileSource(info); 126 } 127 return null; 128 } 129 130 @Override 131 protected Class<? extends TileLoader> getTileLoaderClass() { 132 return TMSCachedTileLoader.class; 133 } 134 135 @Override 136 protected String getCacheName() { 137 return CACHE_REGION_NAME; 138 } 139 140 /** 141 * @return cache for TMS region 142 */ 143 public static CacheAccess<String, BufferedImageCacheEntry> getCache() { 144 return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME); 145 } 146 147 @Override 148 public ScaleList getNativeScales() { 149 return nativeScaleList; 150 } 151 152 private static ScaleList initNativeScaleList() { 153 Collection<Double> scales = new ArrayList<>(AbstractTileSourceLayer.MAX_ZOOM); 154 for (int zoom = AbstractTileSourceLayer.MIN_ZOOM; zoom <= AbstractTileSourceLayer.MAX_ZOOM; zoom++) { 155 double scale = OsmMercator.EARTH_RADIUS * Math.PI * 2 / Math.pow(2, zoom) / OsmMercator.DEFAUL_TILE_SIZE; 156 scales.add(scale); 157 } 158 return new ScaleList(scales); 159 } 160}