001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import java.util.Collection; 005import java.util.Collections; 006import java.util.stream.Collectors; 007import java.util.stream.IntStream; 008 009import org.apache.commons.jcs3.access.CacheAccess; 010import org.openstreetmap.gui.jmapviewer.JMapViewer; 011import org.openstreetmap.gui.jmapviewer.OsmMercator; 012import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; 013import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource; 014import org.openstreetmap.gui.jmapviewer.tilesources.ScanexTileSource; 015import org.openstreetmap.gui.jmapviewer.tilesources.TMSTileSource; 016import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry; 017import org.openstreetmap.josm.data.imagery.CachedAttributionBingAerialTileSource; 018import org.openstreetmap.josm.data.imagery.ImageryInfo; 019import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 020import org.openstreetmap.josm.data.imagery.JosmTemplatedTMSTileSource; 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.tools.Logging; 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 /** override minimum/maximum zoom level with those supported by JMapViewer, as these might be used in slippymap chooser */ 050 public static final int MAX_ZOOM = JMapViewer.MAX_ZOOM; 051 public static final int MIN_ZOOM = JMapViewer.MIN_ZOOM; 052 053 private static final ScaleList nativeScaleList = initNativeScaleList(); 054 055 /** 056 * Create a layer based on ImageryInfo 057 * @param info description of the layer 058 */ 059 public TMSLayer(ImageryInfo info) { 060 super(info); 061 } 062 063 /** 064 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 065 * of the {@link ImageryInfo} object specified in the constructor. 066 * 067 * If no appropriate TileSource is found, null is returned. 068 * Currently supported ImageryType are {@link ImageryType#TMS}, 069 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 070 * 071 * 072 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 073 * @throws IllegalArgumentException if url from imagery info is null or invalid 074 */ 075 @Override 076 protected TMSTileSource getTileSource() { 077 return getTileSourceStatic(info, () -> { 078 Logging.debug("Attribution loaded, running loadAllErrorTiles"); 079 this.loadAllErrorTiles(false); 080 }); 081 } 082 083 @Override 084 public Collection<String> getNativeProjections() { 085 return Collections.singletonList("EPSG:3857"); 086 } 087 088 /** 089 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 090 * of the passed ImageryInfo object. 091 * 092 * If no appropriate TileSource is found, null is returned. 093 * Currently supported ImageryType are {@link ImageryType#TMS}, 094 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 095 * 096 * @param info imagery info 097 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 098 * @throws IllegalArgumentException if url from imagery info is null or invalid 099 */ 100 public static AbstractTMSTileSource getTileSourceStatic(ImageryInfo info) { 101 return getTileSourceStatic(info, null); 102 } 103 104 /** 105 * Creates and returns a new TileSource instance depending on the {@link ImageryType} 106 * of the passed ImageryInfo object. 107 * 108 * If no appropriate TileSource is found, null is returned. 109 * Currently supported ImageryType are {@link ImageryType#TMS}, 110 * {@link ImageryType#BING}, {@link ImageryType#SCANEX}. 111 * 112 * @param info imagery info 113 * @param attributionLoadedTask task to be run once attribution is loaded, might be null, if nothing special shall happen 114 * @return a new TileSource instance or null if no TileSource for the ImageryInfo/ImageryType could be found. 115 * @throws IllegalArgumentException if url from imagery info is null or invalid 116 */ 117 public static TMSTileSource getTileSourceStatic(ImageryInfo info, Runnable attributionLoadedTask) { 118 if (info.getImageryType() == ImageryType.TMS) { 119 JosmTemplatedTMSTileSource.checkUrl(info.getUrl()); 120 TMSTileSource t = new JosmTemplatedTMSTileSource(info); 121 info.setAttribution(t); 122 return t; 123 } else if (info.getImageryType() == ImageryType.BING) { 124 return new CachedAttributionBingAerialTileSource(info, attributionLoadedTask); 125 } else if (info.getImageryType() == ImageryType.SCANEX) { 126 return new ScanexTileSource(info); 127 } 128 return null; 129 } 130 131 @Override 132 protected Class<? extends TileLoader> getTileLoaderClass() { 133 return TMSCachedTileLoader.class; 134 } 135 136 @Override 137 protected String getCacheName() { 138 return CACHE_REGION_NAME; 139 } 140 141 /** 142 * Returns cache for TMS region. 143 * @return cache for TMS region 144 */ 145 public static CacheAccess<String, BufferedImageCacheEntry> getCache() { 146 return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME); 147 } 148 149 @Override 150 public ScaleList getNativeScales() { 151 return nativeScaleList; 152 } 153 154 private static ScaleList initNativeScaleList() { 155 Collection<Double> scales = IntStream.rangeClosed(AbstractTileSourceLayer.MIN_ZOOM, AbstractTileSourceLayer.MAX_ZOOM) 156 .mapToDouble(zoom -> OsmMercator.EARTH_RADIUS * Math.PI * 2 / Math.pow(2, zoom) / OsmMercator.DEFAUL_TILE_SIZE) 157 .boxed() 158 .collect(Collectors.toList()); 159 return new ScaleList(scales); 160 } 161}