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

Last change on this file since 12339 was 12339, checked in by michael2402, 21 months ago

Add javadoc to SlippyMapBBoxChooser

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