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, 7 years ago

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

  • Property svn:eol-style set to native
File size: 13.0 KB
RevLine 
[8378]1// License: GPL. For details, see LICENSE file.
[3687]2package org.openstreetmap.josm.gui.bbox;
3
[4531]4import static org.openstreetmap.josm.tools.I18n.tr;
5
[3687]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;
[3715]13import java.util.Collections;
[8168]14import java.util.HashMap;
[3715]15import java.util.HashSet;
[3687]16import java.util.List;
[8168]17import java.util.Map;
[6316]18import java.util.Set;
[3687]19import java.util.concurrent.CopyOnWriteArrayList;
20
[4531]21import javax.swing.JOptionPane;
[6539]22import javax.swing.SpringLayout;
[4531]23
[3687]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;
[8526]29import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
[3687]30import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
[8168]31import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
[3687]32import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
[3915]33import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
[3687]34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.data.Bounds;
[5898]36import org.openstreetmap.josm.data.Version;
[3687]37import org.openstreetmap.josm.data.coor.LatLon;
[3715]38import org.openstreetmap.josm.data.imagery.ImageryInfo;
39import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
[8598]40import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
[11188]41import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
[12339]42import org.openstreetmap.josm.data.osm.BBox;
[3687]43import org.openstreetmap.josm.data.preferences.StringProperty;
[8598]44import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer;
[3715]45import org.openstreetmap.josm.gui.layer.TMSLayer;
[12846]46import org.openstreetmap.josm.spi.preferences.Config;
[12620]47import org.openstreetmap.josm.tools.Logging;
[3687]48
[12339]49/**
50 * This panel displays a map and lets the user chose a {@link BBox}.
51 */
[6364]52public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser {
[3687]53
[12339]54 /**
55 * A list of tile sources that can be used for displaying the map.
56 */
[10600]57 @FunctionalInterface
[3687]58 public interface TileSourceProvider {
[12339]59 /**
60 * Gets the tile sources that can be displayed
61 * @return The tile sources
62 */
[3687]63 List<TileSource> getTileSources();
64 }
65
66 /**
[3715]67 * TMS TileSource provider for the slippymap chooser
68 */
69 public static class TMSTileSourceProvider implements TileSourceProvider {
[8285]70 private static final Set<String> existingSlippyMapUrls = new HashSet<>();
[3715]71 static {
72 // Urls that already exist in the slippymap chooser and shouldn't be copied from TMS layer list
[6917]73 existingSlippyMapUrls.add("https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png"); // Mapnik
[3715]74 }
75
76 @Override
77 public List<TileSource> getTileSources() {
78 if (!TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get()) return Collections.<TileSource>emptyList();
[7005]79 List<TileSource> sources = new ArrayList<>();
[3715]80 for (ImageryInfo info : ImageryLayerInfo.instance.getLayers()) {
[3826]81 if (existingSlippyMapUrls.contains(info.getUrl())) {
[3715]82 continue;
83 }
[4531]84 try {
[8526]85 TileSource source = TMSLayer.getTileSourceStatic(info);
[4531]86 if (source != null) {
87 sources.add(source);
88 }
89 } catch (IllegalArgumentException ex) {
[12620]90 Logging.warn(ex);
[4531]91 if (ex.getMessage() != null && !ex.getMessage().isEmpty()) {
92 JOptionPane.showMessageDialog(Main.parent,
93 ex.getMessage(), tr("Warning"),
94 JOptionPane.WARNING_MESSAGE);
95 }
[3715]96 }
97 }
98 return sources;
99 }
100 }
101
102 /**
[3687]103 * Plugins that wish to add custom tile sources to slippy map choose should call this method
[8470]104 * @param tileSourceProvider new tile source provider
[3687]105 */
106 public static void addTileSourceProvider(TileSourceProvider tileSourceProvider) {
107 providers.addIfAbsent(tileSourceProvider);
108 }
109
[7005]110 private static CopyOnWriteArrayList<TileSourceProvider> providers = new CopyOnWriteArrayList<>();
[3687]111 static {
[12203]112 addTileSourceProvider(() -> Arrays.<TileSource>asList(new OsmTileSource.Mapnik()));
[3715]113 addTileSourceProvider(new TMSTileSourceProvider());
[3687]114 }
115
116 private static final StringProperty PROP_MAPSTYLE = new StringProperty("slippy_map_chooser.mapstyle", "Mapnik");
[12339]117 /**
118 * The property name used for the resize button.
119 * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
120 */
[4336]121 public static final String RESIZE_PROP = SlippyMapBBoxChooser.class.getName() + ".resize";
[3687]122
[9078]123 private final transient TileLoader cachedLoader;
124 private final transient OsmTileLoader uncachedLoader;
[3687]125
[6539]126 private final SizeButton iSizeButton;
[3687]127 private final SourceButton iSourceButton;
[8308]128 private transient Bounds bbox;
[3687]129
[6364]130 // upper left and lower right corners of the selection rectangle (x/y on ZOOM_MAX)
[9623]131 private transient ICoordinate iSelectionRectStart;
132 private transient ICoordinate iSelectionRectEnd;
[3687]133
[6364]134 /**
135 * Constructs a new {@code SlippyMapBBoxChooser}.
136 */
[3687]137 public SlippyMapBBoxChooser() {
[12620]138 debug = Logging.isDebugEnabled();
[6539]139 SpringLayout springLayout = new SpringLayout();
140 setLayout(springLayout);
[3777]141
[8168]142 Map<String, String> headers = new HashMap<>();
143 headers.put("User-Agent", Version.getInstance().getFullAgentString());
144
[11188]145 TileLoaderFactory cachedLoaderFactory = AbstractCachedTileSourceLayer.getTileLoaderFactory("TMS", TMSCachedTileLoader.class);
146 if (cachedLoaderFactory != null) {
147 cachedLoader = cachedLoaderFactory.makeTileLoader(this, headers);
148 } else {
149 cachedLoader = null;
150 }
[8168]151
[3687]152 uncachedLoader = new OsmTileLoader(this);
[8168]153 uncachedLoader.headers.putAll(headers);
[12846]154 setZoomContolsVisible(Config.getPref().getBoolean("slippy_map_chooser.zoomcontrols", false));
[3687]155 setMapMarkerVisible(false);
156 setMinimumSize(new Dimension(350, 350 / 2));
157 // We need to set an initial size - this prevents a wrong zoom selection
[6364]158 // for the area before the component has been displayed the first time
[3687]159 setBounds(new Rectangle(getMinimumSize()));
160 if (cachedLoader == null) {
161 setFileCacheEnabled(false);
162 } else {
[12846]163 setFileCacheEnabled(Config.getPref().getBoolean("slippy_map_chooser.file_cache", true));
[3687]164 }
[12846]165 setMaxTilesInMemory(Config.getPref().getInt("slippy_map_chooser.max_tiles", 1000));
[3687]166
[6378]167 List<TileSource> tileSources = getAllTileSources();
[3687]168
[6539]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);
[3687]173
[6539]174 iSizeButton = new SizeButton(this);
175 add(iSizeButton);
176
[3687]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
[6539]192 new SlippyMapControler(this, this);
[3687]193 }
[6539]194
[10755]195 private static List<TileSource> getAllTileSources() {
[7005]196 List<TileSource> tileSources = new ArrayList<>();
[6364]197 for (TileSourceProvider provider: providers) {
198 tileSources.addAll(provider.getTileSources());
199 }
200 return tileSources;
201 }
[3687]202
[12339]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 */
[4195]209 public boolean handleAttribution(Point p, boolean click) {
[4489]210 return attribution.handleAttribution(p, click);
[4195]211 }
212
[3687]213 /**
214 * Draw the map.
215 */
216 @Override
217 public void paint(Graphics g) {
[10212]218 super.paint(g);
[3687]219
[10212]220 // draw selection rectangle
221 if (iSelectionRectStart != null && iSelectionRectEnd != null) {
222 Rectangle box = new Rectangle(getMapPosition(iSelectionRectStart, false));
223 box.add(getMapPosition(iSelectionRectEnd, false));
[3687]224
[10212]225 g.setColor(new Color(0.9f, 0.7f, 0.7f, 0.6f));
226 g.fillRect(box.x, box.y, box.width, box.height);
[3687]227
[10212]228 g.setColor(Color.BLACK);
229 g.drawRect(box.x, box.y, box.width, box.height);
[3687]230 }
231 }
232
[12339]233 /**
234 * Enables the disk tile cache.
235 * @param enabled true to enable, false to disable
236 */
[6890]237 public final void setFileCacheEnabled(boolean enabled) {
[11188]238 if (enabled && cachedLoader != null) {
[3687]239 setTileLoader(cachedLoader);
240 } else {
241 setTileLoader(uncachedLoader);
242 }
243 }
244
[12339]245 /**
246 * Sets the maximum number of tiles that may be held in memory
247 * @param tiles The maximum number of tiles.
248 */
[6890]249 public final void setMaxTilesInMemory(int tiles) {
[3687]250 ((MemoryTileCache) getTileCache()).setCacheSize(tiles);
251 }
252
253 /**
[8470]254 * Callback for the OsmMapControl. (Re-)Sets the start and end point of the selection rectangle.
[3687]255 *
[8470]256 * @param aStart selection start
257 * @param aEnd selection end
[3687]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
[10001]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));
[3687]265
[10001]266 iSelectionRectStart = getPosition(pMin);
[10378]267 iSelectionRectEnd = getPosition(pMax);
[3687]268
269 Bounds b = new Bounds(
270 new LatLon(
[8349]271 Math.min(iSelectionRectStart.getLat(), iSelectionRectEnd.getLat()),
272 LatLon.toIntervalLon(Math.min(iSelectionRectStart.getLon(), iSelectionRectEnd.getLon()))
[4868]273 ),
274 new LatLon(
[8349]275 Math.max(iSelectionRectStart.getLat(), iSelectionRectEnd.getLat()),
276 LatLon.toIntervalLon(Math.max(iSelectionRectStart.getLon(), iSelectionRectEnd.getLon())))
[4868]277 );
[3687]278 Bounds oldValue = this.bbox;
279 this.bbox = b;
[4336]280 repaint();
[3687]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() {
[4336]289 boolean large = iSizeButton.isEnlarged();
290 firePropertyChange(RESIZE_PROP, !large, large);
[3687]291 }
292
[12339]293 /**
294 * Sets the active tile source
295 * @param tileSource The active tile source
296 */
[3687]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
[6084]303 @Override
[3687]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 */
[6084]314 @Override
[3687]315 public void setBoundingBox(Bounds bbox) {
[8393]316 if (bbox == null || (bbox.getMinLat() == 0 && bbox.getMinLon() == 0
317 && bbox.getMaxLat() == 0 && bbox.getMaxLon() == 0)) {
[3687]318 this.bbox = null;
[4336]319 iSelectionRectStart = null;
320 iSelectionRectEnd = null;
321 repaint();
[3687]322 return;
323 }
324
[4336]325 this.bbox = bbox;
[8349]326 iSelectionRectStart = new Coordinate(bbox.getMinLat(), bbox.getMinLon());
327 iSelectionRectEnd = new Coordinate(bbox.getMaxLat(), bbox.getMaxLon());
[4868]328
[3687]329 // calc the screen coordinates for the new selection rectangle
[8387]330 MapMarkerDot min = new MapMarkerDot(bbox.getMinLat(), bbox.getMinLon());
331 MapMarkerDot max = new MapMarkerDot(bbox.getMaxLat(), bbox.getMaxLon());
[3687]332
[7005]333 List<MapMarker> marker = new ArrayList<>(2);
[8387]334 marker.add(min);
335 marker.add(max);
[3687]336 setMapMarkerList(marker);
337 setDisplayToFitMapMarkers();
338 zoomOut();
[4552]339 repaint();
[3687]340 }
[6539]341
[6364]342 /**
[8732]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 /**
[6364]352 * Refreshes the tile sources
353 * @since 6364
354 */
355 public final void refreshTileSources() {
[6378]356 iSourceButton.setSources(getAllTileSources());
[6364]357 }
[3687]358}
Note: See TracBrowser for help on using the repository browser.