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

Last change on this file since 11188 was 11188, checked in by wiktorn, 7 years ago

Allow working on readonly JOSM Home

Return null CachedTileLoaderFactory when cache creation fails.
Adapt SlippyMapBBoxChooser so it uses un-cached loader when cached is unavailable.

Closes: #13786

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