001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.bbox; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.ArrayList; 007import java.util.Collections; 008import java.util.HashMap; 009import java.util.List; 010import java.util.Map; 011import java.util.concurrent.TimeUnit; 012import java.util.stream.Collectors; 013 014import javax.swing.JOptionPane; 015 016import org.openstreetmap.gui.jmapviewer.JMapViewer; 017import org.openstreetmap.gui.jmapviewer.MemoryTileCache; 018import org.openstreetmap.gui.jmapviewer.OsmTileLoader; 019import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; 020import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 021import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource; 022import org.openstreetmap.josm.data.Version; 023import org.openstreetmap.josm.data.imagery.ImageryInfo; 024import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; 025import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader; 026import org.openstreetmap.josm.data.imagery.TileLoaderFactory; 027import org.openstreetmap.josm.data.preferences.StringProperty; 028import org.openstreetmap.josm.gui.MainApplication; 029import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer; 030import org.openstreetmap.josm.gui.layer.ImageryLayer; 031import org.openstreetmap.josm.gui.layer.TMSLayer; 032import org.openstreetmap.josm.tools.Logging; 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.warn(ex); 079 if (ex.getMessage() != null && !ex.getMessage().isEmpty()) { 080 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), 081 ex.getMessage(), tr("Warning"), 082 JOptionPane.WARNING_MESSAGE); 083 } 084 } 085 } 086 return sources; 087 } 088 089 /** 090 * TileSource provider - providing default OSM tile source 091 */ 092 public static class DefaultOsmTileSourceProvider implements TileSourceProvider { 093 094 protected static final StringProperty DEFAULT_OSM_TILE_URL = new StringProperty( 095 "default.osm.tile.source.url", "https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png"); 096 097 @Override 098 public List<TileSource> getTileSources() { 099 List<TileSource> result = imageryInfosToTileSources(ImageryLayerInfo.instance.getLayers().stream() 100 .filter(l -> l.getUrl().equals(DEFAULT_OSM_TILE_URL.get())).collect(Collectors.toList())); 101 if (result.isEmpty()) { 102 result.add(new OsmTileSource.Mapnik()); 103 } 104 return result; 105 } 106 107 /** 108 * Returns the default OSM tile source. 109 * @return the default OSM tile source 110 */ 111 public static TileSource get() { 112 return new DefaultOsmTileSourceProvider().getTileSources().get(0); 113 } 114 } 115 116 /** 117 * TileSource provider - providing sources from imagery sources menu 118 */ 119 public static class TMSTileSourceProvider extends AbstractImageryInfoBasedTileSourceProvider { 120 @Override 121 public List<ImageryInfo> getImageryInfos() { 122 return ImageryLayerInfo.instance.getLayers(); 123 } 124 } 125 126 /** 127 * TileSource provider - providing sources from current layers 128 */ 129 public static class CurrentLayersTileSourceProvider extends AbstractImageryInfoBasedTileSourceProvider { 130 @Override 131 public List<ImageryInfo> getImageryInfos() { 132 return MainApplication.getLayerManager().getLayers().stream().filter( 133 layer -> layer instanceof ImageryLayer 134 ).map( 135 layer -> ((ImageryLayer) layer).getInfo() 136 ).collect(Collectors.toList()); 137 } 138 } 139 140 protected final transient TileLoader cachedLoader; 141 protected final transient OsmTileLoader uncachedLoader; 142 143 /** 144 * Constructs a new {@code JosmMapViewer}. 145 */ 146 public JosmMapViewer() { 147 Map<String, String> headers = new HashMap<>(); 148 headers.put("User-Agent", Version.getInstance().getFullAgentString()); 149 150 TileLoaderFactory cachedLoaderFactory = AbstractCachedTileSourceLayer.getTileLoaderFactory("TMS", TMSCachedTileLoader.class); 151 if (cachedLoaderFactory != null) { 152 cachedLoader = cachedLoaderFactory.makeTileLoader(this, headers, TimeUnit.HOURS.toSeconds(1)); 153 } else { 154 cachedLoader = null; 155 } 156 157 uncachedLoader = new OsmTileLoader(this); 158 uncachedLoader.headers.putAll(headers); 159 setFileCacheEnabled(true); 160 } 161 162 /** 163 * Enables the disk tile cache. 164 * @param enabled true to enable, false to disable 165 */ 166 public final void setFileCacheEnabled(boolean enabled) { 167 if (enabled && cachedLoader != null) { 168 setTileLoader(cachedLoader); 169 } else { 170 setTileLoader(uncachedLoader); 171 } 172 } 173 174 /** 175 * Sets the maximum number of tiles that may be held in memory 176 * @param tiles The maximum number of tiles. 177 */ 178 public final void setMaxTilesInMemory(int tiles) { 179 ((MemoryTileCache) getTileCache()).setCacheSize(tiles); 180 } 181}