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

Last change on this file since 12203 was 12203, checked in by stoecker, 22 months ago

drop CycleMap from JOSM core

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