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