source: osm/applications/editors/josm/plugins/slippy_map_chooser/src/SlippyMapChooser.java@ 7343

Last change on this file since 7343 was 7343, checked in by tim, 17 years ago

CHANGED slippy_map_chooser:

  • Tried to fix ticket #629 and #599 (IllegalArgumentException when zooming on Japan)

ADDED to slippy_map_chooser:

  • Patch (Ticket #678): Zooming doesn't take cursor location into consideration (now it does thanks to Johannes Rudolph)
  • Patch (Ticket #678): When zooming use parent tiles while new tiles are loading (by Johannes Rudolph)
File size: 15.7 KB
Line 
1// This code has been adapted and copied from code that has been written by Immanuel Scholz and others for JOSM.
2// License: GPL. Copyright 2007 by Tim Haussmann
3
4import java.awt.BorderLayout;
5import java.awt.Color;
6import java.awt.Component;
7import java.awt.Dimension;
8import java.awt.Graphics;
9import java.awt.Point;
10import java.awt.Toolkit;
11import java.util.Enumeration;
12
13import javax.swing.JComponent;
14import javax.swing.JLabel;
15import javax.swing.JPanel;
16
17import org.openstreetmap.josm.data.coor.LatLon;
18import org.openstreetmap.josm.gui.download.DownloadDialog;
19import org.openstreetmap.josm.gui.download.DownloadSelection;
20import static org.openstreetmap.josm.tools.I18n.tr;
21
22/**
23 * JComponent that displays the slippy map tiles
24 * @author Tim Haussmann
25 *
26 */
27public class SlippyMapChooser extends JComponent implements DownloadSelection{
28
29 private static final int MAX_ZOOMLEVEL = 20;
30 private static final int MIN_ZOOMLEVEL = 1;
31
32 private TileDB iTileDB;
33
34 private DownloadDialog iGui;
35
36 //the upper left and lower right corners of the selection rectangle
37 Point iSelectionRectStart;
38 Point iSelectionRectEnd;
39
40 //Offsets for x and y (i.e. to center the first tile)
41 int iOffsetX = 0;
42 int iOffsetY = 0;
43
44 //the zoom level of the currently shown tiles
45 static int iZoomlevel = 3;
46
47 private boolean iFirstPaint = true;
48 private LatLon iFirstPaintCenter = new LatLon(51,7);
49
50 private SizeButton iSizeButton = new SizeButton();
51
52 //standard dimension
53 private Dimension iDownloadDialogDimension;
54 //screen size
55 private Dimension iScreenSize;
56
57 private LatLon iScreenCenterBeforeResize;
58 private LatLon iSelectionStartBeforeResize;
59 private LatLon iSelectionEndBeforeResize;
60 private boolean isJustResized = false;
61
62 private int iVisibleTilesX = 2;
63 private int iVisibleTilesY = 3;
64
65 /**
66 * Create the chooser component.
67 */
68 public SlippyMapChooser() {
69
70 //create the tile db
71 iTileDB = new TileDB(this);
72
73
74
75 setMinimumSize(new Dimension(350, 350/2));
76 }
77
78 public void addGui(final DownloadDialog gui) {
79 iGui = gui;
80 JPanel temp = new JPanel();
81 temp.setLayout(new BorderLayout());
82 temp.add(this, BorderLayout.CENTER);
83 temp.add(new JLabel((tr("Zoom: Mousewheel or double click. Move map: Hold right mousebutton and move mouse."))), BorderLayout.SOUTH);
84 iGui.tabpane.add(temp, "Slippy map");
85
86 new OsmMapControl(this,temp, iSizeButton);
87 repaint();
88 }
89
90 /**
91 * Performs resizing of the DownloadDialog in order to enlarge or shrink the map.
92 */
93 public void resizeSlippyMap(){
94 if(iScreenSize == null){
95 Component c = iGui.getParent().getParent().getParent().getParent().getParent().getParent().getParent().getParent().getParent();
96 //remember the initial set screen dimensions
97 iDownloadDialogDimension = c.getSize();
98 //retrive the size of the display
99 iScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
100 }
101
102 //remember the screen center (we want to have the same center after resizing)
103 iScreenCenterBeforeResize = getLatLonOfScreenPoint(new Point(getWidth()/2, getHeight()/2));
104
105 //remember lat/lon of the selection rectangle
106 if(iSelectionRectEnd != null && iSelectionRectStart != null){
107 iSelectionStartBeforeResize = getLatLonOfScreenPoint(iSelectionRectStart);
108 iSelectionEndBeforeResize = getLatLonOfScreenPoint(iSelectionRectEnd);
109 }
110
111 //resize
112 Component co = iGui.getParent().getParent().getParent().getParent().getParent().getParent().getParent().getParent().getParent();
113 Dimension currentDimension = co.getSize();
114
115 //enlarge
116 if(currentDimension.equals(iDownloadDialogDimension)){
117 //make the each dimension 90% of the absolute display size and center the DownloadDialog
118 int w = iScreenSize.width*90/100;
119 int h = iScreenSize.height*90/100;
120 co.setBounds((iScreenSize.width-w)/2, (iScreenSize.height-h)/2, w, h);
121 }
122 //shrink
123 else{
124 //set the size back to the initial dimensions and center the DownloadDialog
125 int w = iDownloadDialogDimension.width;
126 int h = iDownloadDialogDimension.height;
127 co.setBounds((iScreenSize.width-w)/2, (iScreenSize.height-h)/2, w, h);
128 }
129
130 //the new dimension are 'available' after (or while) the next repaint
131 isJustResized = true;
132
133 repaint();
134 }
135
136 /**
137 * Draw the map.
138 */
139 @Override public void paint(Graphics g) {
140
141 if(iFirstPaint){
142 //calculate numbers of visible tiles
143 calcVisibleTiles();
144
145 //save selection
146 LatLon selStart = null;
147 LatLon selEnd = null;
148 if(iSelectionRectEnd != null && iSelectionRectStart != null){
149 selStart = getLatLonOfScreenPoint(iSelectionRectStart);
150 selEnd = getLatLonOfScreenPoint(iSelectionRectEnd);
151 }
152 centerOnLatLon(iFirstPaintCenter);
153 //restore selection
154 if(selStart != null && selEnd != null){
155 iSelectionRectStart = getScreenPointForLatLon(selStart);
156 iSelectionRectEnd = getScreenPointForLatLon(selEnd);
157 }
158
159 loadVisibleTiles();
160 iFirstPaint = false;
161 repaint();
162 }
163
164 if(isJustResized){
165 centerOnLatLon(iScreenCenterBeforeResize);
166 //restore selection
167 if(iSelectionEndBeforeResize != null && iSelectionStartBeforeResize != null){
168 iSelectionRectStart = getScreenPointForLatLon(iSelectionStartBeforeResize);
169 iSelectionRectEnd = getScreenPointForLatLon(iSelectionEndBeforeResize);
170 }
171
172 //calculate numbers of visible tiles
173 calcVisibleTiles();
174
175 loadVisibleTiles();
176 isJustResized = false;
177 }
178
179 //translate origin to draw map tiles in 'map space'
180 g.translate(iOffsetX, iOffsetY);
181
182 //draw tiles of the current zoomlevel
183 for(int y=0; y<iVisibleTilesY; y++){
184 for(int x=0; x<iVisibleTilesX; x++){
185 OsmTile t = iTileDB.get(OsmTile.key(iZoomlevel, (-iOffsetX + x*OsmTile.WIDTH)/OsmTile.WIDTH, ((-iOffsetY+ y*OsmTile.HEIGHT)/OsmTile.HEIGHT)));
186 if(t != null){
187 t.paint(g,iTileDB);
188 }
189 }
190 }
191
192 //translate origin back
193 g.translate(-iOffsetX, -iOffsetY);
194
195 //draw selection rectangle
196 if(iSelectionRectStart != null && iSelectionRectEnd != null){
197
198 g.setColor(Color.black);
199 g.drawRect(iSelectionRectStart.x, iSelectionRectStart.y, iSelectionRectEnd.x -iSelectionRectStart.x, iSelectionRectEnd.y -iSelectionRectStart.y);
200
201 g.setColor(new Color(0.9f,0.7f,0.7f,0.6f));
202 g.fillRect(iSelectionRectStart.x+1, iSelectionRectStart.y+1, iSelectionRectEnd.x -iSelectionRectStart.x-1, iSelectionRectEnd.y -iSelectionRectStart.y-1);
203 }
204
205 iSizeButton.paint(g);
206
207
208 if(SlippyMapChooserPlugin.DEBUG_MODE){
209 g.setColor(Color.black);
210 g.drawString("Free Memory: " + Runtime.getRuntime().freeMemory()/1024 + "/" + Runtime.getRuntime().totalMemory()/1024 + "kB" , 5, 50);
211 g.drawString("Tiles in DB: " + iTileDB.getCachedTilesSize(), 5, 65);
212 g.drawString("Loading Queue Size: " + iTileDB.getLoadingQueueSize(), 5, 80);
213
214 }
215 }
216
217
218 public void boundingBoxChanged(DownloadDialog gui) {
219
220 //calc the screen coordinates for the new selection rectangle
221 int x1 = OsmMercator.LonToX(gui.minlon, iZoomlevel);
222 int x2 = OsmMercator.LonToX(gui.maxlon, iZoomlevel);
223 int y1 = OsmMercator.LatToY(gui.minlat, iZoomlevel);
224 int y2 = OsmMercator.LatToY(gui.maxlat, iZoomlevel);
225
226 iSelectionRectStart = new Point(Math.min(x1, x2)+iOffsetX, Math.min(y1, y2)+iOffsetY);
227 iSelectionRectEnd = new Point(Math.max(x1, x2)+iOffsetX, Math.max(y1, y2)+iOffsetY);
228
229 //calc zoom level
230 double dLat = Math.abs(gui.maxlat - gui.minlat);
231 double dLon = Math.abs(gui.maxlon - gui.minlon);
232 int zoomLat = (int) (Math.log(90 / dLat)/Math.log(2));
233 int zoomLon = (int) (Math.log(90 / dLon)/Math.log(2));
234 iZoomlevel = Math.max(zoomLat, zoomLon);
235
236 //center on the rectangle
237 if(gui.minlat != 0 && gui.maxlat != 0 && gui.minlon != 0 && gui.maxlat != 0){
238 iFirstPaintCenter = new LatLon((gui.minlat + gui.maxlat)/2, (gui.minlon + gui.maxlon)/2);
239 iFirstPaint = true;
240 }
241 repaint();
242 }
243
244 /**
245 * Loads all tiles that are visible
246 */
247 void loadVisibleTiles(){
248 for(int y=iVisibleTilesY-1; y>=0; y--){
249 for(int x=0; x<iVisibleTilesX; x++){
250 if(y > 0 && y < iVisibleTilesX-2){
251 iTileDB.loadTile(iZoomlevel, (-iOffsetX + x*OsmTile.WIDTH)/OsmTile.WIDTH,((-iOffsetY+ y*OsmTile.HEIGHT)/OsmTile.HEIGHT), TileDB.PRIO_HIGH );
252 }else{
253 iTileDB.loadTile(iZoomlevel, (-iOffsetX + x*OsmTile.WIDTH)/OsmTile.WIDTH,((-iOffsetY+ y*OsmTile.HEIGHT)/OsmTile.HEIGHT), TileDB.PRIO_LOW );
254 }
255 }
256 }
257 }
258
259 /**
260 * Callback for the OsmMapControl. (Re-)Sets the start and end point of the selection rectangle.
261 * @param aStart
262 * @param aEnd
263 */
264 void setSelection(Point aStart, Point aEnd){
265 if(aStart == null || aEnd == null)
266 return;
267 iSelectionRectEnd = new Point(Math.max(aEnd.x , aStart.x ) ,Math.max(aEnd.y, aStart.y ));
268 iSelectionRectStart = new Point(Math.min(aEnd.x , aStart.x ) ,Math.min(aEnd.y , aStart.y ));
269
270 LatLon l1 = getLatLonOfScreenPoint(aStart);
271 LatLon l2 = getLatLonOfScreenPoint(aEnd);
272
273 iGui.minlat = Math.min(l1.lat(), l2.lat());
274 iGui.minlon = Math.min(l1.lon(), l2.lon());
275 iGui.maxlat = Math.max(l1.lat(), l2.lat());
276 iGui.maxlon = Math.max(l1.lon(), l2.lon());
277
278 iGui.boundingBoxChanged(this);
279
280 repaint();
281 }
282
283 /**
284 * Callback for OsmMapControll. Moves the map and the selection rectangle.
285 * @param x number of pixels to move along the x-axis (longitude)
286 * @param y number of pixels to move along the y axis (latitude)
287 */
288 void moveMap(int x, int y) {
289 int moveX = x;
290 int moveY = y;
291
292 int tempOffsetX = iOffsetX - x;
293 int tempOffsetY = iOffsetY - y;
294
295
296 int maxPixels = OsmMercator.getMaxPixels(iZoomlevel);
297
298 if(moveX != 0){
299 //deactivate dragging if the map is smaller than the DownloadDialog
300 if(maxPixels < getWidth()){
301 //center map horizontally
302 iOffsetX = (getWidth()-maxPixels)/2;
303 moveX = 0;
304 }else{
305 //don't allow the map to hide outside the JComponent drawing area
306 if(tempOffsetX > 30){
307 if(moveX < 0){
308 moveX = 0;
309 iOffsetX = 30;
310 }
311 }else if(-tempOffsetX > maxPixels + 30 - getWidth()){
312 if(moveX > 0){
313 moveX = 0;
314 iOffsetX = -(maxPixels + 30 - getWidth());
315 }
316 }
317 }
318 }
319
320 if(moveY != 0){
321 //deactivate dragging if the map is smaller than the DownloadDialog
322 if(maxPixels < getHeight()){
323 //center map vertically
324 iOffsetY = (getHeight()-maxPixels)/2;
325 moveY = 0;
326 }else{
327 //don't allow the map to hide outside the JComponent drawing area
328 if(tempOffsetY > 30){
329 if(moveY < 0){
330 moveY = 0;
331 iOffsetY = 30;
332 }
333 }else if(-tempOffsetY > maxPixels + 30 - getHeight()){
334 if(moveY > 0){
335 moveY = 0;
336 iOffsetY = -(maxPixels + 30 - getHeight());
337 }
338 }
339 }
340 }
341
342
343
344
345
346 //execute the movement
347 iOffsetX -= moveX;
348 iOffsetY -= moveY;
349
350 //also move the selection rect
351 if(iSelectionRectEnd != null && iSelectionRectStart != null){
352 iSelectionRectEnd.x -= moveX;
353 iSelectionRectEnd.y -= moveY;
354 iSelectionRectStart.x -= moveX;
355 iSelectionRectStart.y -= moveY;
356 }
357
358 loadVisibleTiles();
359
360 repaint();
361 }
362
363 /**
364 * Zoom in one level
365 * Callback for OsmMapControl. Zoom out one level
366 */
367 void zoomIn(Point curPos){
368
369 //cache center of screen and the selection rectangle
370 LatLon l = getLatLonOfScreenPoint(curPos);
371 LatLon selStart = null;
372 LatLon selEnd = null;
373 if(iSelectionRectEnd != null && iSelectionRectStart != null){
374 selStart = getLatLonOfScreenPoint(iSelectionRectStart);
375 selEnd = getLatLonOfScreenPoint(iSelectionRectEnd);
376 }
377
378 //increment zoom level
379 iZoomlevel += 1;
380 if(iZoomlevel > MAX_ZOOMLEVEL){
381 iZoomlevel = MAX_ZOOMLEVEL;
382 return;
383 }
384
385 setLatLonAtPoint(l, curPos);
386
387 //restore selection
388 if(selStart != null && selEnd != null){
389 iSelectionRectStart = getScreenPointForLatLon(selStart);
390 iSelectionRectEnd = getScreenPointForLatLon(selEnd);
391 }
392
393 loadVisibleTiles();
394
395 centerMap();
396
397 repaint();
398 }
399
400 /**
401 * Zoom out one level.
402 * Callback for OsmMapControl.
403 */
404 void zoomOut(Point curPos){
405 //cache center of screen and the selction rect
406 LatLon l = getLatLonOfScreenPoint(curPos);
407 LatLon selStart = null;
408 LatLon selEnd = null;
409 if(iSelectionRectEnd != null && iSelectionRectStart != null){
410 selStart = getLatLonOfScreenPoint(iSelectionRectStart);
411 selEnd = getLatLonOfScreenPoint(iSelectionRectEnd);
412 }
413
414 //decrement zoom level
415 iZoomlevel -= 1;
416 if(iZoomlevel < MIN_ZOOMLEVEL){
417 iZoomlevel = MIN_ZOOMLEVEL;
418 return;
419 }
420
421 setLatLonAtPoint(l, curPos);
422
423 //restore selection
424 if(selStart != null && selEnd != null){
425 iSelectionRectStart = getScreenPointForLatLon(selStart);
426 iSelectionRectEnd = getScreenPointForLatLon(selEnd);
427 }
428
429 loadVisibleTiles();
430
431 centerMap();
432
433 repaint();
434 }
435
436 /**
437 * Calculates the latitude and longitude of a Point given in map space
438 * @param aPoint in pixels on the map
439 * @return the LatLon of the given Point
440 */
441 private LatLon getLatLonOfPoint(Point aPoint){
442 return new LatLon(OsmMercator.YToLat(aPoint.y, iZoomlevel),OsmMercator.XToLon(aPoint.x, iZoomlevel));
443 }
444
445 /**
446 * Returns the map coordinates for a LatLon
447 * @param aLatLon
448 * @return
449 */
450 private Point getPointForLatLon(LatLon aLatLon){
451 Point p = new Point();
452 p.y = OsmMercator.LatToY(aLatLon.lat(), iZoomlevel);
453 p.x = OsmMercator.LonToX(aLatLon.lon(), iZoomlevel);
454 return p;
455 }
456
457 /**
458 * Calculates the latitude and longitude of a Point given in screen space (in the JComponent)
459 * @param aPoint in Pixels on the screen (the JComponent)
460 * @return the LatLon of the given Point
461 */
462 LatLon getLatLonOfScreenPoint(Point aPoint){
463 Point mapCoordinates = new Point(aPoint);
464 mapCoordinates.x -= iOffsetX;
465 mapCoordinates.y -= iOffsetY;
466 return getLatLonOfPoint(mapCoordinates);
467 }
468
469 /**
470 * Calculates the screen coordinates of a LatLon.
471 * @param aLatLon
472 * @return the coordinates as Point in the JComponent
473 */
474 Point getScreenPointForLatLon(LatLon aLatLon){
475 Point p = getPointForLatLon(aLatLon);
476 p.x += iOffsetX;
477 p.y += iOffsetY;
478 return p;
479 }
480
481
482 /**
483 * Centers the map on a Point
484 * @param aPoint in screen coordinates (JComponent)
485 */
486 void centerOnScreenPoint(Point aPoint){
487 moveMap(aPoint.x - getWidth()/2, aPoint.y-getHeight()/2);
488 }
489
490 /**
491 * Centers the map on the location given by LatLon
492 * @param aLatLon the location to center on
493 */
494 private void centerOnLatLon(LatLon aLatLon){
495 setLatLonAtPoint(aLatLon, new Point(getWidth()/2,getHeight()/2));
496 }
497
498 /**
499 * Moves the map that the specified latLon is shown at the point on screen
500 * given
501 * @param aLatLon a position
502 * @param p a point on the screen
503 */
504 private void setLatLonAtPoint(LatLon aLatLon, Point p){
505 int x = OsmMercator.LonToX(aLatLon.lon(), iZoomlevel);
506 int y = OsmMercator.LatToY(aLatLon.lat(), iZoomlevel);
507 iOffsetX = - x + p.x;
508 iOffsetY = - y + p.y;
509 repaint();
510 }
511
512 /**
513 * Caclulates the visible tiles for each dimension
514 */
515 private void calcVisibleTiles(){
516 if(iGui != null){
517 iVisibleTilesX = iGui.getWidth()/256 + 2;
518 iVisibleTilesY = iGui.getHeight()/256 + 2;
519 }
520 }
521
522 /**
523 * Centers the map after zooming. I.e. when the map is smaller than the
524 * component it is shown in.
525 */
526 private void centerMap(){
527
528 int maxPixels = OsmMercator.getMaxPixels(iZoomlevel);
529
530 if(maxPixels < getWidth()){
531 //center map horizontally
532 iOffsetX = (getWidth()-maxPixels)/2;
533 }
534 if(maxPixels < getHeight()){
535 //center map vertically
536 iOffsetY = (getHeight()-maxPixels)/2;
537 }
538 }
539}
Note: See TracBrowser for help on using the repository browser.