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

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

Initial import of slippy_map_chooser source version 1.1

File size: 15.1 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);
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
209 public void boundingBoxChanged(DownloadDialog gui) {
210
211 //calc the screen coordinates for the new selection rectangle
212 int x1 = OsmMercator.LonToX(gui.minlon, iZoomlevel);
213 int x2 = OsmMercator.LonToX(gui.maxlon, iZoomlevel);
214 int y1 = OsmMercator.LatToY(gui.minlat, iZoomlevel);
215 int y2 = OsmMercator.LatToY(gui.maxlat, iZoomlevel);
216
217 iSelectionRectStart = new Point(Math.min(x1, x2)+iOffsetX, Math.min(y1, y2)+iOffsetY);
218 iSelectionRectEnd = new Point(Math.max(x1, x2)+iOffsetX, Math.max(y1, y2)+iOffsetY);
219
220 //calc zoom level
221 double dLat = Math.abs(gui.maxlat - gui.minlat);
222 double dLon = Math.abs(gui.maxlon - gui.minlon);
223 int zoomLat = (int) (Math.log(90 / dLat)/Math.log(2));
224 int zoomLon = (int) (Math.log(90 / dLon)/Math.log(2));
225 iZoomlevel = Math.max(zoomLat, zoomLon);
226
227 //center on the rectangle
228 if(gui.minlat != 0 && gui.maxlat != 0 && gui.minlon != 0 && gui.maxlat != 0){
229 iFirstPaintCenter = new LatLon((gui.minlat + gui.maxlat)/2, (gui.minlon + gui.maxlon)/2);
230 iFirstPaint = true;
231 }
232 repaint();
233 }
234
235 /**
236 * Loads all tiles that are visible
237 */
238 void loadVisibleTiles(){
239 for(int y=iVisibleTilesY-1; y>=0; y--){
240 for(int x=0; x<iVisibleTilesX; x++){
241 if(y > 0 && y < iVisibleTilesX-2){
242 iTileDB.loadTile(iZoomlevel, (-iOffsetX + x*OsmTile.WIDTH)/OsmTile.WIDTH,((-iOffsetY+ y*OsmTile.HEIGHT)/OsmTile.HEIGHT), TileDB.PRIO_HIGH );
243 }else{
244 iTileDB.loadTile(iZoomlevel, (-iOffsetX + x*OsmTile.WIDTH)/OsmTile.WIDTH,((-iOffsetY+ y*OsmTile.HEIGHT)/OsmTile.HEIGHT), TileDB.PRIO_LOW );
245 }
246 }
247 }
248 }
249
250 /**
251 * Callback for the OsmMapControl. (Re-)Sets the start and end point of the selection rectangle.
252 * @param aStart
253 * @param aEnd
254 */
255 void setSelection(Point aStart, Point aEnd){
256 if(aStart == null || aEnd == null)
257 return;
258 iSelectionRectEnd = new Point(Math.max(aEnd.x , aStart.x ) ,Math.max(aEnd.y, aStart.y ));
259 iSelectionRectStart = new Point(Math.min(aEnd.x , aStart.x ) ,Math.min(aEnd.y , aStart.y ));
260
261 LatLon l1 = getLatLonOfScreenPoint(aStart);
262 LatLon l2 = getLatLonOfScreenPoint(aEnd);
263
264 iGui.minlat = Math.min(l1.lat(), l2.lat());
265 iGui.minlon = Math.min(l1.lon(), l2.lon());
266 iGui.maxlat = Math.max(l1.lat(), l2.lat());
267 iGui.maxlon = Math.max(l1.lon(), l2.lon());
268
269 iGui.boundingBoxChanged(this);
270
271 repaint();
272 }
273
274 /**
275 * Callback for OsmMapControll. Moves the map and the selection rectangle.
276 * @param x number of pixels to move along the x-axis (longitude)
277 * @param y number of pixels to move along the y axis (latitude)
278 */
279 void moveMap(int x, int y) {
280 int moveX = x;
281 int moveY = y;
282
283 int tempOffsetX = iOffsetX - x;
284 int tempOffsetY = iOffsetY - y;
285
286
287 int maxPixels = OsmMercator.getMaxPixels(iZoomlevel);
288
289 if(moveX != 0){
290 //deactivate dragging if the map is smaller than the DownloadDialog
291 if(maxPixels < getWidth()){
292 //center map horizontally
293 iOffsetX = (getWidth()-maxPixels)/2;
294 moveX = 0;
295 }else{
296 //don't allow the map to hide outside the JComponent drawing area
297 if(tempOffsetX > 30){
298 if(moveX < 0){
299 moveX = 0;
300 iOffsetX = 30;
301 }
302 }else if(-tempOffsetX > maxPixels + 30 - getWidth()){
303 if(moveX > 0){
304 moveX = 0;
305 iOffsetX = -(maxPixels + 30 - getWidth());
306 }
307 }
308 }
309 }
310
311 if(moveY != 0){
312 //deactivate dragging if the map is smaller than the DownloadDialog
313 if(maxPixels < getHeight()){
314 //center map vertically
315 iOffsetY = (getHeight()-maxPixels)/2;
316 moveY = 0;
317 }else{
318 //don't allow the map to hide outside the JComponent drawing area
319 if(tempOffsetY > 30){
320 if(moveY < 0){
321 moveY = 0;
322 iOffsetY = 30;
323 }
324 }else if(-tempOffsetY > maxPixels + 30 - getHeight()){
325 if(moveY > 0){
326 moveY = 0;
327 iOffsetY = -(maxPixels + 30 - getHeight());
328 }
329 }
330 }
331 }
332
333
334
335
336
337 //execute the movement
338 iOffsetX -= moveX;
339 iOffsetY -= moveY;
340
341 //also move the selection rect
342 if(iSelectionRectEnd != null && iSelectionRectStart != null){
343 iSelectionRectEnd.x -= moveX;
344 iSelectionRectEnd.y -= moveY;
345 iSelectionRectStart.x -= moveX;
346 iSelectionRectStart.y -= moveY;
347 }
348
349 loadVisibleTiles();
350
351 repaint();
352 }
353
354 /**
355 * Zoom in one level
356 * Callback for OsmMapControl. Zoom out one level
357 */
358 void zoomIn(){
359
360 //cache center of screen and the selection rectangle
361 LatLon l = getLatLonOfScreenPoint(new Point(getWidth()/2, getHeight()/2));
362 LatLon selStart = null;
363 LatLon selEnd = null;
364 if(iSelectionRectEnd != null && iSelectionRectStart != null){
365 selStart = getLatLonOfScreenPoint(iSelectionRectStart);
366 selEnd = getLatLonOfScreenPoint(iSelectionRectEnd);
367 }
368
369 //increment zoom level
370 iZoomlevel += 1;
371 if(iZoomlevel > MAX_ZOOMLEVEL){
372 iZoomlevel = MAX_ZOOMLEVEL;
373 return;
374 }
375
376 //center on cached location
377 centerOnLatLon(l);
378
379 //restore selection
380 if(selStart != null && selEnd != null){
381 iSelectionRectStart = getScreenPointForLatLon(selStart);
382 iSelectionRectEnd = getScreenPointForLatLon(selEnd);
383 }
384
385 loadVisibleTiles();
386
387 centerMap();
388
389 repaint();
390 }
391
392 /**
393 * Zoom out one level.
394 * Callback for OsmMapControl.
395 */
396 void zoomOut(){
397 //cache center of screen and the selction rect
398 LatLon l = getLatLonOfScreenPoint(new Point(getWidth()/2, getHeight()/2));
399 LatLon selStart = null;
400 LatLon selEnd = null;
401 if(iSelectionRectEnd != null && iSelectionRectStart != null){
402 selStart = getLatLonOfScreenPoint(iSelectionRectStart);
403 selEnd = getLatLonOfScreenPoint(iSelectionRectEnd);
404 }
405
406 //decrement zoom level
407 iZoomlevel -= 1;
408 if(iZoomlevel < MIN_ZOOMLEVEL){
409 iZoomlevel = MIN_ZOOMLEVEL;
410 return;
411 }
412
413 //center on cached location
414 centerOnLatLon(l);
415
416 //restore selection
417 if(selStart != null && selEnd != null){
418 iSelectionRectStart = getScreenPointForLatLon(selStart);
419 iSelectionRectEnd = getScreenPointForLatLon(selEnd);
420 }
421
422 loadVisibleTiles();
423
424 centerMap();
425
426 repaint();
427 }
428
429 /**
430 * Calculates the latitude and longitude of a Point given in map space
431 * @param aPoint in pixels on the map
432 * @return the LatLon of the given Point
433 */
434 private LatLon getLatLonOfPoint(Point aPoint){
435 return new LatLon(OsmMercator.YToLat(aPoint.y, iZoomlevel),OsmMercator.XToLon(aPoint.x, iZoomlevel));
436 }
437
438 /**
439 * Returns the map coordinates for a LatLon
440 * @param aLatLon
441 * @return
442 */
443 private Point getPointForLatLon(LatLon aLatLon){
444 Point p = new Point();
445 p.y = OsmMercator.LatToY(aLatLon.lat(), iZoomlevel);
446 p.x = OsmMercator.LonToX(aLatLon.lon(), iZoomlevel);
447 return p;
448 }
449
450 /**
451 * Calculates the latitude and longitude of a Point given in screen space (in the JComponent)
452 * @param aPoint in Pixels on the screen (the JComponent)
453 * @return the LatLon of the given Point
454 */
455 LatLon getLatLonOfScreenPoint(Point aPoint){
456 Point mapCoordinates = new Point(aPoint);
457 mapCoordinates.x -= iOffsetX;
458 mapCoordinates.y -= iOffsetY;
459 return getLatLonOfPoint(mapCoordinates);
460 }
461
462 /**
463 * Calculates the screen coordinates of a LatLon.
464 * @param aLatLon
465 * @return the coordinates as Point in the JComponent
466 */
467 Point getScreenPointForLatLon(LatLon aLatLon){
468 Point p = getPointForLatLon(aLatLon);
469 p.x += iOffsetX;
470 p.y += iOffsetY;
471 return p;
472 }
473
474
475 /**
476 * Centers the map on a Point
477 * @param aPoint in screen coordinates (JComponent)
478 */
479 void centerOnScreenPoint(Point aPoint){
480 moveMap(aPoint.x - getWidth()/2, aPoint.y-getHeight()/2);
481 }
482
483 /**
484 * Centers the map on the location given by LatLon
485 * @param aLatLon the location to center on
486 */
487 private void centerOnLatLon(LatLon aLatLon){
488 int x = OsmMercator.LonToX(aLatLon.lon(), iZoomlevel);
489 int y = OsmMercator.LatToY(aLatLon.lat(), iZoomlevel);
490 iOffsetX = -x +getWidth()/2;
491 iOffsetY = -y +getHeight()/2;
492 repaint();
493 }
494
495 /**
496 * Caclulates the visible tiles for each dimension
497 */
498 private void calcVisibleTiles(){
499 if(iGui != null){
500 iVisibleTilesX = iGui.getWidth()/256 + 2;
501 iVisibleTilesY = iGui.getHeight()/256 + 2;
502 }
503 }
504
505 /**
506 * Centers the map after zooming. I.e. when the map is smaller than the
507 * component it is shown in.
508 */
509 private void centerMap(){
510
511 int maxPixels = OsmMercator.getMaxPixels(iZoomlevel);
512
513 if(maxPixels < getWidth()){
514 //center map horizontally
515 iOffsetX = (getWidth()-maxPixels)/2;
516 }
517 if(maxPixels < getHeight()){
518 //center map vertically
519 iOffsetY = (getHeight()-maxPixels)/2;
520 }
521 }
522}
Note: See TracBrowser for help on using the repository browser.