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

Last change on this file since 6793 was 6793, checked in by xeen, 10 years ago

[build fix] classes that use JMapViewer shoudn't use OsmMercator directly (fixes #9654, patch by glebius)

  • Property svn:eol-style set to native
File size: 13.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.Image;
10import java.awt.Point;
11import java.awt.Rectangle;
12import java.io.IOException;
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collections;
16import java.util.HashSet;
17import java.util.List;
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.MapMarker;
30import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
31import org.openstreetmap.gui.jmapviewer.tilesources.MapQuestOpenAerialTileSource;
32import org.openstreetmap.gui.jmapviewer.tilesources.MapQuestOsmTileSource;
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.preferences.StringProperty;
41import org.openstreetmap.josm.gui.layer.TMSLayer;
42
43public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser {
44
45 public interface TileSourceProvider {
46 List<TileSource> getTileSources();
47 }
48
49 /**
50 * TMS TileSource provider for the slippymap chooser
51 */
52 public static class TMSTileSourceProvider implements TileSourceProvider {
53 static final Set<String> existingSlippyMapUrls = new HashSet<String>();
54 static {
55 // Urls that already exist in the slippymap chooser and shouldn't be copied from TMS layer list
56 existingSlippyMapUrls.add("http://tile.openstreetmap.org/{zoom}/{x}/{y}.png"); // Mapnik
57 existingSlippyMapUrls.add("http://tile.opencyclemap.org/cycle/{zoom}/{x}/{y}.png"); // Cyclemap
58 existingSlippyMapUrls.add("http://otile{switch:1,2,3,4}.mqcdn.com/tiles/1.0.0/osm/{zoom}/{x}/{y}.png"); // MapQuest-OSM
59 existingSlippyMapUrls.add("http://oatile{switch:1,2,3,4}.mqcdn.com/tiles/1.0.0/sat/{zoom}/{x}/{y}.png"); // MapQuest Open Aerial
60 }
61
62 @Override
63 public List<TileSource> getTileSources() {
64 if (!TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get()) return Collections.<TileSource>emptyList();
65 List<TileSource> sources = new ArrayList<TileSource>();
66 for (ImageryInfo info : ImageryLayerInfo.instance.getLayers()) {
67 if (existingSlippyMapUrls.contains(info.getUrl())) {
68 continue;
69 }
70 try {
71 TileSource source = TMSLayer.getTileSource(info);
72 if (source != null) {
73 sources.add(source);
74 }
75 } catch (IllegalArgumentException ex) {
76 if (ex.getMessage() != null && !ex.getMessage().isEmpty()) {
77 JOptionPane.showMessageDialog(Main.parent,
78 ex.getMessage(), tr("Warning"),
79 JOptionPane.WARNING_MESSAGE);
80 }
81 }
82 }
83 return sources;
84 }
85
86 public static void addExistingSlippyMapUrl(String url) {
87 existingSlippyMapUrls.add(url);
88 }
89 }
90
91 /**
92 * Plugins that wish to add custom tile sources to slippy map choose should call this method
93 * @param tileSourceProvider
94 */
95 public static void addTileSourceProvider(TileSourceProvider tileSourceProvider) {
96 providers.addIfAbsent(tileSourceProvider);
97 }
98
99 private static CopyOnWriteArrayList<TileSourceProvider> providers = new CopyOnWriteArrayList<TileSourceProvider>();
100
101 static {
102 addTileSourceProvider(new TileSourceProvider() {
103 @Override
104 public List<TileSource> getTileSources() {
105 return Arrays.<TileSource>asList(
106 new OsmTileSource.Mapnik(),
107 new OsmTileSource.CycleMap(),
108 new MapQuestOsmTileSource(),
109 new MapQuestOpenAerialTileSource());
110 }
111 });
112 addTileSourceProvider(new TMSTileSourceProvider());
113 }
114
115 private static final StringProperty PROP_MAPSTYLE = new StringProperty("slippy_map_chooser.mapstyle", "Mapnik");
116 public static final String RESIZE_PROP = SlippyMapBBoxChooser.class.getName() + ".resize";
117
118 private OsmTileLoader cachedLoader;
119 private OsmTileLoader uncachedLoader;
120
121 private final SizeButton iSizeButton;
122 private final SourceButton iSourceButton;
123 private Bounds bbox;
124
125 // upper left and lower right corners of the selection rectangle (x/y on ZOOM_MAX)
126 Point iSelectionRectStart;
127 Point iSelectionRectEnd;
128
129 /**
130 * Constructs a new {@code SlippyMapBBoxChooser}.
131 */
132 public SlippyMapBBoxChooser() {
133 SpringLayout springLayout = new SpringLayout();
134 setLayout(springLayout);
135 TMSLayer.setMaxWorkers();
136 cachedLoader = TMSLayer.loaderFactory.makeTileLoader(this);
137
138 uncachedLoader = new OsmTileLoader(this);
139 uncachedLoader.headers.put("User-Agent", Version.getInstance().getFullAgentString());
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 List<TileSource> getAllTileSources() {
182 List<TileSource> tileSources = new ArrayList<TileSource>();
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 protected Point getTopLeftCoordinates() {
194 return new Point(center.x - (getWidth() / 2), center.y - (getHeight() / 2));
195 }
196
197 /**
198 * Draw the map.
199 */
200 @Override
201 public void paint(Graphics g) {
202 try {
203 super.paint(g);
204
205 // draw selection rectangle
206 if (iSelectionRectStart != null && iSelectionRectEnd != null) {
207
208 int zoomDiff = MAX_ZOOM - zoom;
209 Point tlc = getTopLeftCoordinates();
210 int x_min = (iSelectionRectStart.x >> zoomDiff) - tlc.x;
211 int y_min = (iSelectionRectStart.y >> zoomDiff) - tlc.y;
212 int x_max = (iSelectionRectEnd.x >> zoomDiff) - tlc.x;
213 int y_max = (iSelectionRectEnd.y >> zoomDiff) - tlc.y;
214
215 int w = x_max - x_min;
216 int h = y_max - y_min;
217 g.setColor(new Color(0.9f, 0.7f, 0.7f, 0.6f));
218 g.fillRect(x_min, y_min, w, h);
219
220 g.setColor(Color.BLACK);
221 g.drawRect(x_min, y_min, w, h);
222 }
223 } catch (Exception e) {
224 Main.error(e);
225 }
226 }
227
228 public void setFileCacheEnabled(boolean enabled) {
229 if (enabled) {
230 setTileLoader(cachedLoader);
231 } else {
232 setTileLoader(uncachedLoader);
233 }
234 }
235
236 public void setMaxTilesInMemory(int tiles) {
237 ((MemoryTileCache) getTileCache()).setCacheSize(tiles);
238 }
239
240
241 /**
242 * Callback for the OsmMapControl. (Re-)Sets the start and end point of the
243 * selection rectangle.
244 *
245 * @param aStart
246 * @param aEnd
247 */
248 public void setSelection(Point aStart, Point aEnd) {
249 if (aStart == null || aEnd == null || aStart.x == aEnd.x || aStart.y == aEnd.y)
250 return;
251
252 Point p_max = new Point(Math.max(aEnd.x, aStart.x), Math.max(aEnd.y, aStart.y));
253 Point p_min = new Point(Math.min(aEnd.x, aStart.x), Math.min(aEnd.y, aStart.y));
254
255 Point tlc = getTopLeftCoordinates();
256 int zoomDiff = MAX_ZOOM - zoom;
257 Point pEnd = new Point(p_max.x + tlc.x, p_max.y + tlc.y);
258 Point pStart = new Point(p_min.x + tlc.x, p_min.y + tlc.y);
259
260 pEnd.x <<= zoomDiff;
261 pEnd.y <<= zoomDiff;
262 pStart.x <<= zoomDiff;
263 pStart.y <<= zoomDiff;
264
265 iSelectionRectStart = pStart;
266 iSelectionRectEnd = pEnd;
267
268 Coordinate l1 = getPosition(p_max); // lon may be outside [-180,180]
269 Coordinate l2 = getPosition(p_min); // lon may be outside [-180,180]
270 Bounds b = new Bounds(
271 new LatLon(
272 Math.min(l2.getLat(), l1.getLat()),
273 LatLon.toIntervalLon(Math.min(l1.getLon(), l2.getLon()))
274 ),
275 new LatLon(
276 Math.max(l2.getLat(), l1.getLat()),
277 LatLon.toIntervalLon(Math.max(l1.getLon(), l2.getLon())))
278 );
279 Bounds oldValue = this.bbox;
280 this.bbox = b;
281 repaint();
282 firePropertyChange(BBOX_PROP, oldValue, this.bbox);
283 }
284
285 /**
286 * Performs resizing of the DownloadDialog in order to enlarge or shrink the
287 * map.
288 */
289 public void resizeSlippyMap() {
290 boolean large = iSizeButton.isEnlarged();
291 firePropertyChange(RESIZE_PROP, !large, large);
292 }
293
294 public void toggleMapSource(TileSource tileSource) {
295 this.tileController.setTileCache(new MemoryTileCache());
296 this.setTileSource(tileSource);
297 PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique?
298 }
299
300 @Override
301 public Bounds getBoundingBox() {
302 return bbox;
303 }
304
305 /**
306 * Sets the current bounding box in this bbox chooser without
307 * emiting a property change event.
308 *
309 * @param bbox the bounding box. null to reset the bounding box
310 */
311 @Override
312 public void setBoundingBox(Bounds bbox) {
313 if (bbox == null || (bbox.getMinLat() == 0.0 && bbox.getMinLon() == 0.0
314 && bbox.getMaxLat() == 0.0 && bbox.getMaxLon() == 0.0)) {
315 this.bbox = null;
316 iSelectionRectStart = null;
317 iSelectionRectEnd = null;
318 repaint();
319 return;
320 }
321
322 this.bbox = bbox;
323 double minLon = bbox.getMinLon();
324 double maxLon = bbox.getMaxLon();
325
326 if (bbox.crosses180thMeridian()) {
327 minLon -= 360.0;
328 }
329
330 int y1 = tileSource.LatToY(bbox.getMinLat(), MAX_ZOOM);
331 int y2 = tileSource.LatToY(bbox.getMaxLat(), MAX_ZOOM);
332 int x1 = tileSource.LonToX(minLon, MAX_ZOOM);
333 int x2 = tileSource.LonToX(maxLon, MAX_ZOOM);
334
335 iSelectionRectStart = new Point(Math.min(x1, x2), Math.min(y1, y2));
336 iSelectionRectEnd = new Point(Math.max(x1, x2), Math.max(y1, y2));
337
338 // calc the screen coordinates for the new selection rectangle
339 MapMarkerDot xmin_ymin = new MapMarkerDot(bbox.getMinLat(), bbox.getMinLon());
340 MapMarkerDot xmax_ymax = new MapMarkerDot(bbox.getMaxLat(), bbox.getMaxLon());
341
342 List<MapMarker> marker = new ArrayList<MapMarker>(2);
343 marker.add(xmin_ymin);
344 marker.add(xmax_ymax);
345 setMapMarkerList(marker);
346 setDisplayToFitMapMarkers();
347 zoomOut();
348 repaint();
349 }
350
351 /**
352 * Refreshes the tile sources
353 * @since 6364
354 */
355 public final void refreshTileSources() {
356 iSourceButton.setSources(getAllTileSources());
357 }
358}
Note: See TracBrowser for help on using the repository browser.