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

Last change on this file since 4531 was 4531, checked in by Don-vip, 13 years ago

fix #6982 - broken TMS url leads to exceptions

  • Property svn:eol-style set to native
File size: 14.0 KB
Line 
1// License: GPL. See LICENSE file for details.
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.Graphics2D;
10import java.awt.Image;
11import java.awt.Point;
12import java.awt.Rectangle;
13import java.io.File;
14import java.io.IOException;
15import java.util.ArrayList;
16import java.util.Arrays;
17import java.util.Collections;
18import java.util.HashSet;
19import java.util.List;
20import java.util.Vector;
21import java.util.concurrent.CopyOnWriteArrayList;
22
23import javax.swing.JOptionPane;
24
25import org.openstreetmap.gui.jmapviewer.Coordinate;
26import org.openstreetmap.gui.jmapviewer.JMapViewer;
27import org.openstreetmap.gui.jmapviewer.MapMarkerDot;
28import org.openstreetmap.gui.jmapviewer.MemoryTileCache;
29import org.openstreetmap.gui.jmapviewer.OsmFileCacheTileLoader;
30import org.openstreetmap.gui.jmapviewer.OsmMercator;
31import org.openstreetmap.gui.jmapviewer.OsmTileLoader;
32import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
33import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
34import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
35import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
36import org.openstreetmap.josm.Main;
37import org.openstreetmap.josm.data.Bounds;
38import org.openstreetmap.josm.data.coor.LatLon;
39import org.openstreetmap.josm.data.imagery.ImageryInfo;
40import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
41import org.openstreetmap.josm.data.preferences.StringProperty;
42import org.openstreetmap.josm.gui.layer.TMSLayer;
43
44public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser{
45
46 public interface TileSourceProvider {
47 List<TileSource> getTileSources();
48 }
49
50 public static class RenamedSourceDecorator implements TileSource {
51
52 private final TileSource source;
53 private final String name;
54
55 public RenamedSourceDecorator(TileSource source, String name) {
56 this.source = source;
57 this.name = name;
58 }
59
60 @Override public String getName() {
61 return name;
62 }
63
64 @Override public int getMaxZoom() { return source.getMaxZoom(); }
65
66 @Override public int getMinZoom() { return source.getMinZoom(); }
67
68 @Override public int getTileSize() { return source.getTileSize(); }
69
70 @Override public String getTileType() { return source.getTileType(); }
71
72 @Override public TileUpdate getTileUpdate() { return source.getTileUpdate(); }
73
74 @Override public String getTileUrl(int zoom, int tilex, int tiley) throws IOException { return source.getTileUrl(zoom, tilex, tiley); }
75
76 @Override public boolean requiresAttribution() { return source.requiresAttribution(); }
77
78 @Override public String getAttributionText(int zoom, Coordinate topLeft, Coordinate botRight) { return source.getAttributionText(zoom, topLeft, botRight); }
79
80 @Override public String getAttributionLinkURL() { return source.getAttributionLinkURL(); }
81
82 @Override public Image getAttributionImage() { return source.getAttributionImage(); }
83
84 @Override public String getAttributionImageURL() { return source.getAttributionImageURL(); }
85
86 @Override public String getTermsOfUseText() { return source.getTermsOfUseText(); }
87
88 @Override public String getTermsOfUseURL() { return source.getTermsOfUseURL(); }
89
90 @Override public double latToTileY(double lat, int zoom) { return source.latToTileY(lat,zoom); }
91
92 @Override public double lonToTileX(double lon, int zoom) { return source.lonToTileX(lon,zoom); }
93
94 @Override public double tileYToLat(int y, int zoom) { return tileYToLat(y, zoom); }
95
96 @Override public double tileXToLon(int x, int zoom) { return tileXToLon(x, zoom); }
97 }
98
99 /**
100 * TMS TileSource provider for the slippymap chooser
101 */
102 public static class TMSTileSourceProvider implements TileSourceProvider {
103 static final HashSet<String> existingSlippyMapUrls = new HashSet<String>();
104 static {
105 // Urls that already exist in the slippymap chooser and shouldn't be copied from TMS layer list
106 existingSlippyMapUrls.add("http://tile.openstreetmap.org/");
107 existingSlippyMapUrls.add("http://tah.openstreetmap.org/Tiles/tile/");
108 existingSlippyMapUrls.add("http://tile.opencyclemap.org/cycle/");
109 }
110
111 @Override
112 public List<TileSource> getTileSources() {
113 if (!TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get()) return Collections.<TileSource>emptyList();
114 List<TileSource> sources = new ArrayList<TileSource>();
115 for (ImageryInfo info : ImageryLayerInfo.instance.getLayers()) {
116 if (existingSlippyMapUrls.contains(info.getUrl())) {
117 continue;
118 }
119 try {
120 TileSource source = TMSLayer.getTileSource(info);
121 if (source != null) {
122 sources.add(source);
123 }
124 } catch (IllegalArgumentException ex) {
125 if (ex.getMessage() != null && !ex.getMessage().isEmpty()) {
126 JOptionPane.showMessageDialog(Main.parent,
127 ex.getMessage(), tr("Warning"),
128 JOptionPane.WARNING_MESSAGE);
129 }
130 }
131 }
132 return sources;
133 }
134
135 public static void addExistingSlippyMapUrl(String url) {
136 existingSlippyMapUrls.add(url);
137 }
138 }
139
140
141 /**
142 * Plugins that wish to add custom tile sources to slippy map choose should call this method
143 * @param tileSourceProvider
144 */
145 public static void addTileSourceProvider(TileSourceProvider tileSourceProvider) {
146 providers.addIfAbsent(tileSourceProvider);
147 }
148
149 private static CopyOnWriteArrayList<TileSourceProvider> providers = new CopyOnWriteArrayList<TileSourceProvider>();
150
151 static {
152 addTileSourceProvider(new TileSourceProvider() {
153 @Override
154 public List<TileSource> getTileSources() {
155 return Arrays.<TileSource>asList(
156 new RenamedSourceDecorator(new OsmTileSource.Mapnik(), "Mapnik"),
157 new RenamedSourceDecorator(new OsmTileSource.TilesAtHome(), "Osmarender"),
158 new RenamedSourceDecorator(new OsmTileSource.CycleMap(), "Cyclemap")
159 );
160 }
161 });
162 addTileSourceProvider(new TMSTileSourceProvider());
163 }
164
165 private static final StringProperty PROP_MAPSTYLE = new StringProperty("slippy_map_chooser.mapstyle", "Mapnik");
166 public static final String RESIZE_PROP = SlippyMapBBoxChooser.class.getName() + ".resize";
167
168 private TileLoader cachedLoader;
169 private TileLoader uncachedLoader;
170
171 private final SizeButton iSizeButton = new SizeButton();
172 private final SourceButton iSourceButton;
173 private Bounds bbox;
174
175 // upper left and lower right corners of the selection rectangle (x/y on
176 // ZOOM_MAX)
177 Point iSelectionRectStart;
178 Point iSelectionRectEnd;
179
180 public SlippyMapBBoxChooser() {
181 super();
182 cachedLoader = null;
183 String cachePath = TMSLayer.PROP_TILECACHE_DIR.get();
184 if (cachePath != null && !cachePath.isEmpty()) {
185 try {
186 cachedLoader = new OsmFileCacheTileLoader(this, new File(cachePath));
187 } catch (IOException e) {
188 }
189 }
190
191 uncachedLoader = new OsmTileLoader(this);
192 setZoomContolsVisible(false);
193 setMapMarkerVisible(false);
194 setMinimumSize(new Dimension(350, 350 / 2));
195 // We need to set an initial size - this prevents a wrong zoom selection
196 // for
197 // the area before the component has been displayed the first time
198 setBounds(new Rectangle(getMinimumSize()));
199 if (cachedLoader == null) {
200 setFileCacheEnabled(false);
201 } else {
202 setFileCacheEnabled(Main.pref.getBoolean("slippy_map_chooser.file_cache", true));
203 }
204 setMaxTilesInMemory(Main.pref.getInteger("slippy_map_chooser.max_tiles", 1000));
205
206 List<TileSource> tileSources = new ArrayList<TileSource>();
207 for (TileSourceProvider provider: providers) {
208 tileSources.addAll(provider.getTileSources());
209 }
210
211 iSourceButton = new SourceButton(tileSources);
212
213 String mapStyle = PROP_MAPSTYLE.get();
214 boolean foundSource = false;
215 for (TileSource source: tileSources) {
216 if (source.getName().equals(mapStyle)) {
217 this.setTileSource(source);
218 iSourceButton.setCurrentMap(source);
219 foundSource = true;
220 break;
221 }
222 }
223 if (!foundSource) {
224 setTileSource(tileSources.get(0));
225 iSourceButton.setCurrentMap(tileSources.get(0));
226 }
227
228 new SlippyMapControler(this, this, iSizeButton, iSourceButton);
229 }
230
231 public boolean handleAttribution(Point p, boolean click) {
232 return attribution.handleAttribution(p, click);
233 }
234
235 protected Point getTopLeftCoordinates() {
236 return new Point(center.x - (getWidth() / 2), center.y - (getHeight() / 2));
237 }
238
239 /**
240 * Draw the map.
241 */
242 @Override
243 public void paint(Graphics g) {
244 try {
245 super.paint(g);
246
247 // draw selection rectangle
248 if (iSelectionRectStart != null && iSelectionRectEnd != null) {
249
250 int zoomDiff = MAX_ZOOM - zoom;
251 Point tlc = getTopLeftCoordinates();
252 int x_min = (iSelectionRectStart.x >> zoomDiff) - tlc.x;
253 int y_min = (iSelectionRectStart.y >> zoomDiff) - tlc.y;
254 int x_max = (iSelectionRectEnd.x >> zoomDiff) - tlc.x;
255 int y_max = (iSelectionRectEnd.y >> zoomDiff) - tlc.y;
256
257 int w = x_max - x_min;
258 int h = y_max - y_min;
259 g.setColor(new Color(0.9f, 0.7f, 0.7f, 0.6f));
260 g.fillRect(x_min, y_min, w, h);
261
262 g.setColor(Color.BLACK);
263 g.drawRect(x_min, y_min, w, h);
264 }
265
266 iSizeButton.paint(g);
267 iSourceButton.paint((Graphics2D)g);
268 } catch (Exception e) {
269 e.printStackTrace();
270 }
271 }
272
273 public void setFileCacheEnabled(boolean enabled) {
274 if (enabled) {
275 setTileLoader(cachedLoader);
276 } else {
277 setTileLoader(uncachedLoader);
278 }
279 }
280
281 public void setMaxTilesInMemory(int tiles) {
282 ((MemoryTileCache) getTileCache()).setCacheSize(tiles);
283 }
284
285
286 /**
287 * Callback for the OsmMapControl. (Re-)Sets the start and end point of the
288 * selection rectangle.
289 *
290 * @param aStart
291 * @param aEnd
292 */
293 public void setSelection(Point aStart, Point aEnd) {
294 if (aStart == null || aEnd == null || aStart.x == aEnd.x || aStart.y == aEnd.y)
295 return;
296
297 Point p_max = new Point(Math.max(aEnd.x, aStart.x), Math.max(aEnd.y, aStart.y));
298 Point p_min = new Point(Math.min(aEnd.x, aStart.x), Math.min(aEnd.y, aStart.y));
299
300 Point tlc = getTopLeftCoordinates();
301 int zoomDiff = MAX_ZOOM - zoom;
302 Point pEnd = new Point(p_max.x + tlc.x, p_max.y + tlc.y);
303 Point pStart = new Point(p_min.x + tlc.x, p_min.y + tlc.y);
304
305 pEnd.x <<= zoomDiff;
306 pEnd.y <<= zoomDiff;
307 pStart.x <<= zoomDiff;
308 pStart.y <<= zoomDiff;
309
310 iSelectionRectStart = pStart;
311 iSelectionRectEnd = pEnd;
312
313 Coordinate l1 = getPosition(p_max);
314 Coordinate l2 = getPosition(p_min);
315 Bounds b = new Bounds(
316 new LatLon(
317 Math.min(l2.getLat(), l1.getLat()),
318 Math.min(l1.getLon(), l2.getLon())
319 ),
320 new LatLon(
321 Math.max(l2.getLat(), l1.getLat()),
322 Math.max(l1.getLon(), l2.getLon()))
323 );
324 Bounds oldValue = this.bbox;
325 this.bbox = b;
326 repaint();
327 firePropertyChange(BBOX_PROP, oldValue, this.bbox);
328 }
329
330 /**
331 * Performs resizing of the DownloadDialog in order to enlarge or shrink the
332 * map.
333 */
334 public void resizeSlippyMap() {
335 boolean large = iSizeButton.isEnlarged();
336 firePropertyChange(RESIZE_PROP, !large, large);
337 }
338
339 public void toggleMapSource(TileSource tileSource) {
340 this.tileController.setTileCache(new MemoryTileCache());
341 this.setTileSource(tileSource);
342 PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique?
343 }
344
345 public Bounds getBoundingBox() {
346 return bbox;
347 }
348
349 /**
350 * Sets the current bounding box in this bbox chooser without
351 * emiting a property change event.
352 *
353 * @param bbox the bounding box. null to reset the bounding box
354 */
355 public void setBoundingBox(Bounds bbox) {
356 if (bbox == null || (bbox.getMin().lat() == 0.0 && bbox.getMin().lon() == 0.0
357 && bbox.getMax().lat() == 0.0 && bbox.getMax().lon() == 0.0)) {
358 this.bbox = null;
359 iSelectionRectStart = null;
360 iSelectionRectEnd = null;
361 repaint();
362 return;
363 }
364
365 this.bbox = bbox;
366 int y1 = OsmMercator.LatToY(bbox.getMin().lat(), MAX_ZOOM);
367 int y2 = OsmMercator.LatToY(bbox.getMax().lat(), MAX_ZOOM);
368 int x1 = OsmMercator.LonToX(bbox.getMin().lon(), MAX_ZOOM);
369 int x2 = OsmMercator.LonToX(bbox.getMax().lon(), MAX_ZOOM);
370
371 iSelectionRectStart = new Point(Math.min(x1, x2), Math.min(y1, y2));
372 iSelectionRectEnd = new Point(Math.max(x1, x2), Math.max(y1, y2));
373
374 // calc the screen coordinates for the new selection rectangle
375 MapMarkerDot xmin_ymin = new MapMarkerDot(bbox.getMin().lat(), bbox.getMin().lon());
376 MapMarkerDot xmax_ymax = new MapMarkerDot(bbox.getMax().lat(), bbox.getMax().lon());
377
378 Vector<MapMarker> marker = new Vector<MapMarker>(2);
379 marker.add(xmin_ymin);
380 marker.add(xmax_ymax);
381 setMapMarkerList(marker);
382 setDisplayToFitMapMarkers();
383 zoomOut();
384 }
385}
Note: See TracBrowser for help on using the repository browser.