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