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

Last change on this file since 5673 was 5357, checked in by stoecker, 12 years ago

improve task handling for jmapviewer in JOSM context

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