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}