001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.bbox; 003 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.HashMap; 007import java.util.List; 008import java.util.Map; 009import java.util.concurrent.TimeUnit; 010import java.util.stream.Collectors; 011 012import javax.swing.JOptionPane; 013 014import org.openstreetmap.gui.jmapviewer.JMapViewer; 015import org.openstreetmap.gui.jmapviewer.MemoryTileCache; 016import org.openstreetmap.gui.jmapviewer.OsmTileLoader; 017import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; 018import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 019import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource; 020import org.openstreetmap.josm.data.Version; 021import org.openstreetmap.josm.data.imagery.ImageryInfo; 022import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; 023import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader; 024import org.openstreetmap.josm.data.imagery.TileLoaderFactory; 025import org.openstreetmap.josm.data.preferences.StringProperty; 026import org.openstreetmap.josm.gui.MainApplication; 027import org.openstreetmap.josm.gui.Notification; 028import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer; 029import org.openstreetmap.josm.gui.layer.ImageryLayer; 030import org.openstreetmap.josm.gui.layer.TMSLayer; 031import org.openstreetmap.josm.tools.Logging; 032import org.openstreetmap.josm.tools.Utils; 033 034/** 035 * An extension of {@link JMapViewer} that implements JOSM-specific tile loading mechanisms. 036 * @since 15145 037 */ 038public class JosmMapViewer extends JMapViewer { 039 040 /** 041 * A list of tile sources that can be used for displaying the map. 042 */ 043 @FunctionalInterface 044 public interface TileSourceProvider { 045 /** 046 * Gets the tile sources that can be displayed 047 * @return The tile sources 048 */ 049 List<TileSource> getTileSources(); 050 } 051 052 /** 053 * TileSource provider. 054 */ 055 public abstract static class AbstractImageryInfoBasedTileSourceProvider implements TileSourceProvider { 056 /** 057 * Returns the list of imagery infos backing tile sources. 058 * @return the list of imagery infos backing tile sources 059 */ 060 public abstract List<ImageryInfo> getImageryInfos(); 061 062 @Override 063 public List<TileSource> getTileSources() { 064 if (!TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get()) return Collections.<TileSource>emptyList(); 065 return imageryInfosToTileSources(getImageryInfos()); 066 } 067 } 068 069 static List<TileSource> imageryInfosToTileSources(List<ImageryInfo> imageryInfos) { 070 List<TileSource> sources = new ArrayList<>(); 071 for (ImageryInfo info : imageryInfos) { 072 try { 073 TileSource source = TMSLayer.getTileSourceStatic(info); 074 if (source != null) { 075 sources.add(source); 076 } 077 } catch (IllegalArgumentException ex) { 078 Logging.trace(ex); 079 Logging.warn(ex.getMessage()); 080 if (!Utils.isEmpty(ex.getMessage())) { 081 new Notification(ex.getMessage()).setIcon(JOptionPane.WARNING_MESSAGE).show(); 082 } 083 } 084 } 085 return sources; 086 } 087 088 /** 089 * TileSource provider - providing default OSM tile source 090 */ 091 public static class DefaultOsmTileSourceProvider implements TileSourceProvider { 092 093 protected static final StringProperty DEFAULT_OSM_TILE_URL = new StringProperty( 094 "default.osm.tile.source.url", "https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png"); 095 096 @Override 097 public List<TileSource> getTileSources() { 098 List<TileSource> result = imageryInfosToTileSources(ImageryLayerInfo.instance.getLayers().stream() 099 .filter(l -> l.getUrl().equals(DEFAULT_OSM_TILE_URL.get())).collect(Collectors.toList())); 100 if (result.isEmpty()) { 101 result.add(new OsmTileSource.Mapnik()); 102 } 103 return result; 104 } 105 106 /** 107 * Returns the default OSM tile source. 108 * @return the default OSM tile source 109 */ 110 public static TileSource get() { 111 return new DefaultOsmTileSourceProvider().getTileSources().get(0); 112 } 113 } 114 115 /** 116 * TileSource provider - providing sources from imagery sources menu 117 */ 118 public static class TMSTileSourceProvider extends AbstractImageryInfoBasedTileSourceProvider { 119 @Override 120 public List<ImageryInfo> getImageryInfos() { 121 return ImageryLayerInfo.instance.getLayers(); 122 } 123 } 124 125 /** 126 * TileSource provider - providing sources from current layers 127 */ 128 public static class CurrentLayersTileSourceProvider extends AbstractImageryInfoBasedTileSourceProvider { 129 @Override 130 public List<ImageryInfo> getImageryInfos() { 131 return MainApplication.getLayerManager().getLayers().stream().filter( 132 layer -> layer instanceof ImageryLayer 133 ).map( 134 layer -> ((ImageryLayer) layer).getInfo() 135 ).collect(Collectors.toList()); 136 } 137 } 138 139 protected final transient TileLoader cachedLoader; 140 protected final transient OsmTileLoader uncachedLoader; 141 142 /** 143 * Constructs a new {@code JosmMapViewer}. 144 */ 145 public JosmMapViewer() { 146 Map<String, String> headers = new HashMap<>(); 147 headers.put("User-Agent", Version.getInstance().getFullAgentString()); 148 149 TileLoaderFactory cachedLoaderFactory = AbstractCachedTileSourceLayer.getTileLoaderFactory("TMS", TMSCachedTileLoader.class); 150 if (cachedLoaderFactory != null) { 151 cachedLoader = cachedLoaderFactory.makeTileLoader(this, headers, TimeUnit.HOURS.toSeconds(1)); 152 } else { 153 cachedLoader = null; 154 } 155 156 uncachedLoader = new OsmTileLoader(this); 157 uncachedLoader.headers.putAll(headers); 158 setFileCacheEnabled(true); 159 } 160 161 /** 162 * Enables the disk tile cache. 163 * @param enabled true to enable, false to disable 164 */ 165 public final void setFileCacheEnabled(boolean enabled) { 166 if (enabled && cachedLoader != null) { 167 setTileLoader(cachedLoader); 168 } else { 169 setTileLoader(uncachedLoader); 170 } 171 } 172 173 /** 174 * Sets the maximum number of tiles that may be held in memory 175 * @param tiles The maximum number of tiles. 176 */ 177 public final void setMaxTilesInMemory(int tiles) { 178 ((MemoryTileCache) getTileCache()).setCacheSize(tiles); 179 } 180}