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 * 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 passed ImageryInfo object. 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 * @param info imagery info 070 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 071 * @throws IllegalArgumentException if url from imagery info is null or invalid 072 */ 073 @Override 074 protected TMSTileSource getTileSource(ImageryInfo info) throws IllegalArgumentException { 075 return getTileSourceStatic(info, new Runnable() { 076 @Override 077 public void run() { 078 Main.debug("Attribution loaded, running loadAllErrorTiles"); 079 TMSLayer.this.loadAllErrorTiles(false); 080 } 081 }); 082 } 083 084 @Override 085 public final boolean isProjectionSupported(Projection proj) { 086 return "EPSG:3857".equals(proj.toCode()) || "EPSG:4326".equals(proj.toCode()); 087 } 088 089 @Override 090 public final String nameSupportedProjections() { 091 return tr("EPSG:4326 and Mercator projection are supported"); 092 } 093 094 /** 095 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 096 * of the passed ImageryInfo object. 097 * 098 * If no appropriate TileSource is found, null is returned. 099 * Currently supported ImageryType are {@link ImageryType#TMS}, 100 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 101 * 102 * @param info imagery info 103 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 104 * @throws IllegalArgumentException if url from imagery info is null or invalid 105 */ 106 public static AbstractTMSTileSource getTileSourceStatic(ImageryInfo info) throws IllegalArgumentException { 107 return getTileSourceStatic(info, null); 108 } 109 110 /** 111 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 112 * of the passed ImageryInfo object. 113 * 114 * If no appropriate TileSource is found, null is returned. 115 * Currently supported ImageryType are {@link ImageryType#TMS}, 116 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 117 * 118 * @param info imagery info 119 * @param attributionLoadedTask task to be run once attribution is loaded, might be null, if nothing special shall happen 120 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 121 * @throws IllegalArgumentException if url from imagery info is null or invalid 122 */ 123 public static TMSTileSource getTileSourceStatic(ImageryInfo info, Runnable attributionLoadedTask) throws IllegalArgumentException { 124 if (info.getImageryType() == ImageryType.TMS) { 125 TemplatedTMSTileSource.checkUrl(info.getUrl()); 126 TMSTileSource t = new TemplatedTMSTileSource(info); 127 info.setAttribution(t); 128 return t; 129 } else if (info.getImageryType() == ImageryType.BING) { 130 return new CachedAttributionBingAerialTileSource(info, attributionLoadedTask); 131 } else if (info.getImageryType() == ImageryType.SCANEX) { 132 return new ScanexTileSource(info); 133 } 134 return null; 135 } 136 137 @Override 138 protected Class<? extends TileLoader> getTileLoaderClass() { 139 return TMSCachedTileLoader.class; 140 } 141 142 @Override 143 protected String getCacheName() { 144 return CACHE_REGION_NAME; 145 } 146 147 /** 148 * @return cache for TMS region 149 */ 150 public static CacheAccess<String, BufferedImageCacheEntry> getCache() { 151 return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME); 152 } 153 154 @Override 155 public ScaleList getNativeScales() { 156 return nativeScaleList; 157 } 158 159 private static ScaleList initNativeScaleList() { 160 Collection<Double> scales = new ArrayList<>(AbstractTileSourceLayer.MAX_ZOOM); 161 for (int zoom = AbstractTileSourceLayer.MIN_ZOOM; zoom <= AbstractTileSourceLayer.MAX_ZOOM; zoom++) { 162 double scale = OsmMercator.EARTH_RADIUS * Math.PI * 2 / Math.pow(2, zoom) / OsmMercator.DEFAUL_TILE_SIZE; 163 scales.add(scale); 164 } 165 return new ScaleList(scales); 166 } 167 }