source: josm/trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java @ 12846

Last change on this file since 12846 was 12846, checked in by bastiK, 3 months ago

see #15229 - use Config.getPref() wherever possible

  • Property svn:eol-style set to native
File size: 13.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.bbox;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Dimension;
8import java.awt.Graphics;
9import java.awt.Point;
10import java.awt.Rectangle;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19import java.util.concurrent.CopyOnWriteArrayList;
20
21import javax.swing.JOptionPane;
22import javax.swing.SpringLayout;
23
24import org.openstreetmap.gui.jmapviewer.Coordinate;
25import org.openstreetmap.gui.jmapviewer.JMapViewer;
26import org.openstreetmap.gui.jmapviewer.MapMarkerDot;
27import org.openstreetmap.gui.jmapviewer.MemoryTileCache;
28import org.openstreetmap.gui.jmapviewer.OsmTileLoader;
29import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
30import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
31import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
32import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
33import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.data.Bounds;
36import org.openstreetmap.josm.data.Version;
37import org.openstreetmap.josm.data.coor.LatLon;
38import org.openstreetmap.josm.data.imagery.ImageryInfo;
39import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
40import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
41import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
42import org.openstreetmap.josm.data.osm.BBox;
43import org.openstreetmap.josm.data.preferences.StringProperty;
44import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer;
45import org.openstreetmap.josm.gui.layer.TMSLayer;
46import org.openstreetmap.josm.spi.preferences.Config;
47import org.openstreetmap.josm.tools.Logging;
48
49/**
50 * This panel displays a map and lets the user chose a {@link BBox}.
51 */
52public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser {
53
54    /**
55     * A list of tile sources that can be used for displaying the map.
56     */
57    @FunctionalInterface
58    public interface TileSourceProvider {
59        /**
60         * Gets the tile sources that can be displayed
61         * @return The tile sources
62         */
63        List<TileSource> getTileSources();
64    }
65
66    /**
67     * TMS TileSource provider for the slippymap chooser
68     */
69    public static class TMSTileSourceProvider implements TileSourceProvider {
70        private static final Set<String> existingSlippyMapUrls = new HashSet<>();
71        static {
72            // Urls that already exist in the slippymap chooser and shouldn't be copied from TMS layer list
73            existingSlippyMapUrls.add("https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png");      // Mapnik
74        }
75
76        @Override
77        public List<TileSource> getTileSources() {
78            if (!TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get()) return Collections.<TileSource>emptyList();
79            List<TileSource> sources = new ArrayList<>();
80            for (ImageryInfo info : ImageryLayerInfo.instance.getLayers()) {
81                if (existingSlippyMapUrls.contains(info.getUrl())) {
82                    continue;
83                }
84                try {
85                    TileSource source = TMSLayer.getTileSourceStatic(info);
86                    if (source != null) {
87                        sources.add(source);
88                    }
89                } catch (IllegalArgumentException ex) {
90                    Logging.warn(ex);
91                    if (ex.getMessage() != null && !ex.getMessage().isEmpty()) {
92                        JOptionPane.showMessageDialog(Main.parent,
93                                ex.getMessage(), tr("Warning"),
94                                JOptionPane.WARNING_MESSAGE);
95                    }
96                }
97            }
98            return sources;
99        }
100    }
101
102    /**
103     * Plugins that wish to add custom tile sources to slippy map choose should call this method
104     * @param tileSourceProvider new tile source provider
105     */
106    public static void addTileSourceProvider(TileSourceProvider tileSourceProvider) {
107        providers.addIfAbsent(tileSourceProvider);
108    }
109
110    private static CopyOnWriteArrayList<TileSourceProvider> providers = new CopyOnWriteArrayList<>();
111    static {
112        addTileSourceProvider(() -> Arrays.<TileSource>asList(new OsmTileSource.Mapnik()));
113        addTileSourceProvider(new TMSTileSourceProvider());
114    }
115
116    private static final StringProperty PROP_MAPSTYLE = new StringProperty("slippy_map_chooser.mapstyle", "Mapnik");
117    /**
118     * The property name used for the resize button.
119     * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
120     */
121    public static final String RESIZE_PROP = SlippyMapBBoxChooser.class.getName() + ".resize";
122
123    private final transient TileLoader cachedLoader;
124    private final transient OsmTileLoader uncachedLoader;
125
126    private final SizeButton iSizeButton;
127    private final SourceButton iSourceButton;
128    private transient Bounds bbox;
129
130    // upper left and lower right corners of the selection rectangle (x/y on ZOOM_MAX)
131    private transient ICoordinate iSelectionRectStart;
132    private transient ICoordinate iSelectionRectEnd;
133
134    /**
135     * Constructs a new {@code SlippyMapBBoxChooser}.
136     */
137    public SlippyMapBBoxChooser() {
138        debug = Logging.isDebugEnabled();
139        SpringLayout springLayout = new SpringLayout();
140        setLayout(springLayout);
141
142        Map<String, String> headers = new HashMap<>();
143        headers.put("User-Agent", Version.getInstance().getFullAgentString());
144
145        TileLoaderFactory cachedLoaderFactory = AbstractCachedTileSourceLayer.getTileLoaderFactory("TMS", TMSCachedTileLoader.class);
146        if (cachedLoaderFactory != null) {
147            cachedLoader = cachedLoaderFactory.makeTileLoader(this, headers);
148        } else {
149            cachedLoader = null;
150        }
151
152        uncachedLoader = new OsmTileLoader(this);
153        uncachedLoader.headers.putAll(headers);
154        setZoomContolsVisible(Config.getPref().getBoolean("slippy_map_chooser.zoomcontrols", false));
155        setMapMarkerVisible(false);
156        setMinimumSize(new Dimension(350, 350 / 2));
157        // We need to set an initial size - this prevents a wrong zoom selection
158        // for the area before the component has been displayed the first time
159        setBounds(new Rectangle(getMinimumSize()));
160        if (cachedLoader == null) {
161            setFileCacheEnabled(false);
162        } else {
163            setFileCacheEnabled(Config.getPref().getBoolean("slippy_map_chooser.file_cache", true));
164        }
165        setMaxTilesInMemory(Config.getPref().getInt("slippy_map_chooser.max_tiles", 1000));
166
167        List<TileSource> tileSources = getAllTileSources();
168
169        iSourceButton = new SourceButton(this, tileSources);
170        add(iSourceButton);
171        springLayout.putConstraint(SpringLayout.EAST, iSourceButton, 0, SpringLayout.EAST, this);
172        springLayout.putConstraint(SpringLayout.NORTH, iSourceButton, 30, SpringLayout.NORTH, this);
173
174        iSizeButton = new SizeButton(this);
175        add(iSizeButton);
176
177        String mapStyle = PROP_MAPSTYLE.get();
178        boolean foundSource = false;
179        for (TileSource source: tileSources) {
180            if (source.getName().equals(mapStyle)) {
181                this.setTileSource(source);
182                iSourceButton.setCurrentMap(source);
183                foundSource = true;
184                break;
185            }
186        }
187        if (!foundSource) {
188            setTileSource(tileSources.get(0));
189            iSourceButton.setCurrentMap(tileSources.get(0));
190        }
191
192        new SlippyMapControler(this, this);
193    }
194
195    private static List<TileSource> getAllTileSources() {
196        List<TileSource> tileSources = new ArrayList<>();
197        for (TileSourceProvider provider: providers) {
198            tileSources.addAll(provider.getTileSources());
199        }
200        return tileSources;
201    }
202
203    /**
204     * Handles a click/move on the attribution
205     * @param p The point in the view
206     * @param click true if it was a click, false for hover
207     * @return if the attribution handled the event
208     */
209    public boolean handleAttribution(Point p, boolean click) {
210        return attribution.handleAttribution(p, click);
211    }
212
213    /**
214     * Draw the map.
215     */
216    @Override
217    public void paint(Graphics g) {
218        super.paint(g);
219
220        // draw selection rectangle
221        if (iSelectionRectStart != null && iSelectionRectEnd != null) {
222            Rectangle box = new Rectangle(getMapPosition(iSelectionRectStart, false));
223            box.add(getMapPosition(iSelectionRectEnd, false));
224
225            g.setColor(new Color(0.9f, 0.7f, 0.7f, 0.6f));
226            g.fillRect(box.x, box.y, box.width, box.height);
227
228            g.setColor(Color.BLACK);
229            g.drawRect(box.x, box.y, box.width, box.height);
230        }
231    }
232
233    /**
234     * Enables the disk tile cache.
235     * @param enabled true to enable, false to disable
236     */
237    public final void setFileCacheEnabled(boolean enabled) {
238        if (enabled && cachedLoader != null) {
239            setTileLoader(cachedLoader);
240        } else {
241            setTileLoader(uncachedLoader);
242        }
243    }
244
245    /**
246     * Sets the maximum number of tiles that may be held in memory
247     * @param tiles The maximum number of tiles.
248     */
249    public final void setMaxTilesInMemory(int tiles) {
250        ((MemoryTileCache) getTileCache()).setCacheSize(tiles);
251    }
252
253    /**
254     * Callback for the OsmMapControl. (Re-)Sets the start and end point of the selection rectangle.
255     *
256     * @param aStart selection start
257     * @param aEnd selection end
258     */
259    public void setSelection(Point aStart, Point aEnd) {
260        if (aStart == null || aEnd == null || aStart.x == aEnd.x || aStart.y == aEnd.y)
261            return;
262
263        Point pMax = new Point(Math.max(aEnd.x, aStart.x), Math.max(aEnd.y, aStart.y));
264        Point pMin = new Point(Math.min(aEnd.x, aStart.x), Math.min(aEnd.y, aStart.y));
265
266        iSelectionRectStart = getPosition(pMin);
267        iSelectionRectEnd = getPosition(pMax);
268
269        Bounds b = new Bounds(
270                new LatLon(
271                        Math.min(iSelectionRectStart.getLat(), iSelectionRectEnd.getLat()),
272                        LatLon.toIntervalLon(Math.min(iSelectionRectStart.getLon(), iSelectionRectEnd.getLon()))
273                        ),
274                        new LatLon(
275                                Math.max(iSelectionRectStart.getLat(), iSelectionRectEnd.getLat()),
276                                LatLon.toIntervalLon(Math.max(iSelectionRectStart.getLon(), iSelectionRectEnd.getLon())))
277                );
278        Bounds oldValue = this.bbox;
279        this.bbox = b;
280        repaint();
281        firePropertyChange(BBOX_PROP, oldValue, this.bbox);
282    }
283
284    /**
285     * Performs resizing of the DownloadDialog in order to enlarge or shrink the
286     * map.
287     */
288    public void resizeSlippyMap() {
289        boolean large = iSizeButton.isEnlarged();
290        firePropertyChange(RESIZE_PROP, !large, large);
291    }
292
293    /**
294     * Sets the active tile source
295     * @param tileSource The active tile source
296     */
297    public void toggleMapSource(TileSource tileSource) {
298        this.tileController.setTileCache(new MemoryTileCache());
299        this.setTileSource(tileSource);
300        PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique?
301    }
302
303    @Override
304    public Bounds getBoundingBox() {
305        return bbox;
306    }
307
308    /**
309     * Sets the current bounding box in this bbox chooser without
310     * emiting a property change event.
311     *
312     * @param bbox the bounding box. null to reset the bounding box
313     */
314    @Override
315    public void setBoundingBox(Bounds bbox) {
316        if (bbox == null || (bbox.getMinLat() == 0 && bbox.getMinLon() == 0
317                && bbox.getMaxLat() == 0 && bbox.getMaxLon() == 0)) {
318            this.bbox = null;
319            iSelectionRectStart = null;
320            iSelectionRectEnd = null;
321            repaint();
322            return;
323        }
324
325        this.bbox = bbox;
326        iSelectionRectStart = new Coordinate(bbox.getMinLat(), bbox.getMinLon());
327        iSelectionRectEnd = new Coordinate(bbox.getMaxLat(), bbox.getMaxLon());
328
329        // calc the screen coordinates for the new selection rectangle
330        MapMarkerDot min = new MapMarkerDot(bbox.getMinLat(), bbox.getMinLon());
331        MapMarkerDot max = new MapMarkerDot(bbox.getMaxLat(), bbox.getMaxLon());
332
333        List<MapMarker> marker = new ArrayList<>(2);
334        marker.add(min);
335        marker.add(max);
336        setMapMarkerList(marker);
337        setDisplayToFitMapMarkers();
338        zoomOut();
339        repaint();
340    }
341
342    /**
343     * Enables or disables painting of the shrink/enlarge button
344     *
345     * @param visible {@code true} to enable painting of the shrink/enlarge button
346     */
347    public void setSizeButtonVisible(boolean visible) {
348        iSizeButton.setVisible(visible);
349    }
350
351    /**
352     * Refreshes the tile sources
353     * @since 6364
354     */
355    public final void refreshTileSources() {
356        iSourceButton.setSources(getAllTileSources());
357    }
358}
Note: See TracBrowser for help on using the repository browser.