Changeset 15707 in osm for applications/editors/josm/plugins/slippymap/src
- Timestamp:
- 2009-06-06T22:25:31+02:00 (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
r14732 r15707 36 36 /** 37 37 * Class that displays a slippy map layer. 38 * 38 * 39 39 * @author Frederik Ramm <frederik@remote.org> 40 40 * @author LuVar <lubomir.varga@freemap.sk> 41 41 * @author Dave Hansen <dave@sr71.net> 42 * 42 * 43 43 */ 44 44 public class SlippyMapLayer extends Layer implements ImageObserver, 45 PreferenceChangedListener { 46 /** 47 * Actual zoom lvl. Initial zoom lvl is set to 48 * {@link SlippyMapPreferences#getMinZoomLvl()}. 49 */ 50 public int currentZoomLevel = SlippyMapPreferences.getMinZoomLvl(); 51 private HashMap<SlippyMapKey, SlippyMapTile> tileStorage = null; 52 53 Point[][] pixelpos = new Point[21][21]; 54 LatLon lastTopLeft; 55 LatLon lastBotRight; 56 int z12x0, z12x1, z12y0, z12y1; 57 private Image bufferImage; 58 private SlippyMapTile clickedTile; 59 private boolean needRedraw; 60 private JPopupMenu tileOptionMenu; 61 62 @SuppressWarnings("serial") 63 public SlippyMapLayer() { 64 super(tr("Slippy Map")); 65 background = true; 66 67 clearTileStorage(); 68 69 tileOptionMenu = new JPopupMenu(); 70 tileOptionMenu.add(new JMenuItem(new AbstractAction(tr("Load Tile")) { 71 public void actionPerformed(ActionEvent ae) { 72 if (clickedTile != null) { 73 loadSingleTile(clickedTile); 74 needRedraw = true; 75 Main.map.repaint(); 76 } 77 } 78 })); 79 80 tileOptionMenu.add(new JMenuItem(new AbstractAction( 81 tr("Show Tile Status")) { 82 public void actionPerformed(ActionEvent ae) { 83 if (clickedTile != null) { 84 clickedTile.loadMetadata(); 85 needRedraw = true; 86 Main.map.repaint(); 87 } 88 } 89 })); 90 91 tileOptionMenu.add(new JMenuItem(new AbstractAction( 92 tr("Request Update")) { 93 public void actionPerformed(ActionEvent ae) { 94 if (clickedTile != null) { 95 clickedTile.requestUpdate(); 96 needRedraw = true; 97 Main.map.repaint(); 98 } 99 } 100 })); 101 102 tileOptionMenu.add(new JMenuItem(new AbstractAction( 103 tr("Load All Tiles")) { 104 public void actionPerformed(ActionEvent ae) { 105 loadAllTiles(); 106 needRedraw = true; 107 Main.map.repaint(); 108 } 109 })); 110 111 // increase and decrease commands 112 tileOptionMenu.add(new JMenuItem( 113 new AbstractAction(tr("Increase zoom")) { 114 public void actionPerformed(ActionEvent ae) { 115 increaseZoomLevel(); 116 needRedraw = true; 117 Main.map.repaint(); 118 } 119 })); 120 121 tileOptionMenu.add(new JMenuItem( 122 new AbstractAction(tr("Decrease zoom")) { 123 public void actionPerformed(ActionEvent ae) { 124 decreaseZoomLevel(); 125 Main.map.repaint(); 126 } 127 })); 128 // end of adding menu commands 129 130 SwingUtilities.invokeLater(new Runnable() { 131 public void run() { 132 Main.map.mapView.addMouseListener(new MouseAdapter() { 133 @Override 134 public void mouseClicked(MouseEvent e) { 135 if (e.getButton() != MouseEvent.BUTTON3) 136 return; 137 clickedTile = getTileForPixelpos(e.getX(), e.getY()); 138 tileOptionMenu.show(e.getComponent(), e.getX(), e 139 .getY()); 140 } 141 }); 142 143 listeners.add(new LayerChangeListener() { 144 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 145 } 146 147 public void layerAdded(Layer newLayer) { 148 } 149 150 public void layerRemoved(Layer oldLayer) { 151 Main.pref.listener.remove(SlippyMapLayer.this); 152 } 153 }); 154 } 155 }); 156 157 Main.pref.listener.add(this); 158 } 159 160 /** 161 * Zoom in, go closer to map. 162 */ 163 public void increaseZoomLevel() { 164 if (currentZoomLevel < SlippyMapPreferences.getMaxZoomLvl()) { 165 currentZoomLevel++; 166 Main.debug("increasing zoom level to: " + currentZoomLevel); 167 needRedraw = true; 168 } else { 169 System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+ 170 "MaxZoomLvl ("+SlippyMapPreferences.getMaxZoomLvl()+") reached."); 171 } 172 } 173 174 /** 175 * Zoom out from map. 176 */ 177 public void decreaseZoomLevel() { 178 if (currentZoomLevel > SlippyMapPreferences.getMinZoomLvl()) { 179 Main.debug("decreasing zoom level to: " + currentZoomLevel); 180 currentZoomLevel--; 181 needRedraw = true; 182 } else { 183 System.err.println("current zoom lvl couldnt be decreased. MinZoomLvl reached."); 184 } 185 } 186 187 public void clearTileStorage() { 188 // when max zoom lvl is begin saved, this method is called and probably 189 // the setting isnt saved yet. 190 int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl(); 191 tileStorage = new HashMap<SlippyMapKey, SlippyMapTile>(); 192 193 checkTileStorage(); 194 } 195 196 class TileTimeComp implements Comparator<SlippyMapTile> { 197 public int compare(SlippyMapTile s1, SlippyMapTile s2) { 198 long t1 = s1.access_time(); 199 long t2 = s2.access_time(); 200 if (s1 == s2) 201 return 0; 202 if (t1 == t2) { 203 t1 = s1.hashCode(); 204 t2 = s2.hashCode(); 205 } 206 if (t1 < t2) 207 return -1; 208 return 1; 209 } 210 } 211 212 long lastCheck = 0; 213 /** 214 * <p> 215 * Check if tiles.size() is not more than max_nr_tiles. If yes, oldest tiles by timestamp 216 * are fired out from cache. 217 * </p> 218 */ 219 public void checkTileStorage() { 220 int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl(); 221 long now = System.currentTimeMillis(); 222 if (now - lastCheck < 1000) 223 return; 224 lastCheck = now; 225 TreeSet<SlippyMapTile> tiles = new TreeSet<SlippyMapTile>(new TileTimeComp()); 226 tiles.addAll(tileStorage.values()); 227 int max_nr_tiles = 100; 228 if (tiles.size() < max_nr_tiles) { 229 Main.debug("total of " + tiles.size() + " loaded tiles, size OK " + now); 230 return; 231 } 232 int nr_to_drop = tiles.size() - max_nr_tiles;; 233 Main.debug("total of " + tiles.size() + " tiles, need to flush " + nr_to_drop + " tiles"); 234 for (SlippyMapTile t : tiles) { 235 if (nr_to_drop <= 0) 236 break; 237 t.dropImage(); 238 nr_to_drop--; 239 } 240 } 241 242 void loadSingleTile(SlippyMapTile tile) 243 { 244 tile.loadImage(); 245 this.checkTileStorage(); 246 } 247 248 void loadAllTiles() { 249 MapView mv = Main.map.mapView; 250 LatLon topLeft = mv.getLatLon(0, 0); 251 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight()); 252 z12x0 = lonToTileX(topLeft.lon()); 253 z12x1 = lonToTileX(botRight.lon()); 254 z12y0 = latToTileY(topLeft.lat()); 255 z12y1 = latToTileY(botRight.lat()); 256 if (z12x0 > z12x1) { 257 int tmp = z12x0; 258 z12x0 = z12x1; 259 z12x1 = tmp; 260 } 261 if (z12y0 > z12y1) { 262 int tmp = z12y0; 263 z12y0 = z12y1; 264 z12y1 = tmp; 265 } 266 // if there is more than 18 tiles on screen in any direction, do not 267 // load all tiles! 268 if (z12x1 - z12x0 > 18) { 269 System.out 270 .println("Not downloading all tiles because there is more than 18 tiles on X axis!"); 271 return; 272 } 273 if (z12y1 - z12y0 > 18) { 274 System.out 275 .println("Not downloading all tiles because there is more than 18 tiles on Y axis!"); 276 return; 277 } 278 279 for (int x = z12x0 - 1; x <= z12x1; x++) { 280 for (int y = z12y0 - 1; y <= z12y1; y++) { 281 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y); 282 SlippyMapTile tile = tileStorage.get(key); 283 if (!key.valid) { 284 System.out.println("paint-1() made invalid key"); 285 continue; 286 } 287 if (tile == null) 288 tileStorage.put(key, 289 tile = new SlippyMapTile(x, y, currentZoomLevel)); 290 if (tile.getImage() == null) { 291 this.loadSingleTile(tile); 292 } 293 } 294 } 295 } 296 297 /* 298 * Attempt to approximate how much the image is 299 * being scaled. For instance, a 100x100 image 300 * being scaled to 50x50 would return 0.25. 301 */ 302 double getImageScaling(Image img, Point p0, Point p1) 303 { 304 int realWidth = img.getWidth(this); 305 int realHeight = img.getHeight(this); 306 if (realWidth == -1 || realHeight == -1) 307 return 1.0; 308 int drawWidth = p1.x - p0.x; 309 int drawHeight = p1.x - p0.x; 310 311 double drawArea = drawWidth * drawHeight; 312 double realArea = realWidth * realHeight; 313 314 return drawArea / realArea; 315 } 316 317 /** 45 PreferenceChangedListener { 46 /** 47 * Actual zoom lvl. Initial zoom lvl is set to 48 * {@link SlippyMapPreferences#getMinZoomLvl()}. 318 49 */ 319 @Override 320 public void paint(Graphics g, MapView mv) { 321 long start = System.currentTimeMillis(); 322 LatLon topLeft = mv.getLatLon(0, 0); 323 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight()); 324 Graphics oldg = g; 325 326 if (botRight.lon() == 0.0 || botRight.lat() == 0) { 327 // probably still initializing 328 return; 329 } 330 if (lastTopLeft != null && lastBotRight != null 331 && topLeft.equalsEpsilon(lastTopLeft) 332 && botRight.equalsEpsilon(lastBotRight) && bufferImage != null 333 && mv.getWidth() == bufferImage.getWidth(null) 334 && mv.getHeight() == bufferImage.getHeight(null) && !needRedraw) { 335 336 g.drawImage(bufferImage, 0, 0, null); 337 return; 338 } 339 340 needRedraw = false; 341 lastTopLeft = topLeft; 342 lastBotRight = botRight; 343 bufferImage = mv.createImage(mv.getWidth(), mv.getHeight()); 344 g = bufferImage.getGraphics(); 345 346 z12x0 = lonToTileX(topLeft.lon()); 347 z12x1 = lonToTileX(botRight.lon()); 348 z12y0 = latToTileY(topLeft.lat()); 349 z12y1 = latToTileY(botRight.lat()); 350 351 if (z12x0 > z12x1) { 352 int tmp = z12x0; 353 z12x0 = z12x1; 354 z12x1 = tmp; 355 } 356 if (z12y0 > z12y1) { 357 int tmp = z12y0; 358 z12y0 = z12y1; 359 z12y1 = tmp; 360 } 361 362 if (z12x1 - z12x0 > 18) 363 return; 364 if (z12y1 - z12y0 > 18) 365 return; 366 367 for (int x = z12x0 - 1; x <= z12x1 + 1; x++) { 368 double lon = tileXToLon(x); 369 for (int y = z12y0 - 1; y <= z12y1 + 1; y++) { 370 LatLon tmpLL = new LatLon(tileYToLat(y), lon); 371 pixelpos[x - z12x0 + 1][y - z12y0 + 1] = mv.getPoint(Main.proj 372 .latlon2eastNorth(tmpLL)); 373 } 374 } 375 376 int fontHeight = g.getFontMetrics().getHeight(); 377 378 g.setColor(Color.DARK_GRAY); 379 380 float fadeBackground = SlippyMapPreferences.getFadeBackground(); 381 382 Double imageScale = null; 383 int count = 0; 384 for (int x = z12x0 - 1; x <= z12x1; x++) { 385 for (int y = z12y0 - 1; y <= z12y1; y++) { 386 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y); 387 SlippyMapTile tile; 388 tile = tileStorage.get(key); 389 if (!key.valid) { 390 System.out.println("loadAllTiles() made invalid key"); 391 continue; 392 } 393 if (tile == null) { 394 tile = new SlippyMapTile(x, y, currentZoomLevel); 395 tileStorage.put(key, tile); 396 if (SlippyMapPreferences.getAutoloadTiles()) { 397 // TODO probably do on background 398 loadSingleTile(tile); 399 } 400 checkTileStorage(); 401 } 402 Image img = tile.getImage(); 403 404 if (img != null) { 405 Point p = pixelpos[x - z12x0 + 1][y - z12y0 + 1]; 406 Point p2 = pixelpos[x - z12x0 + 2][y - z12y0 + 2]; 407 g.drawImage(img, p.x, p.y, p2.x - p.x, p2.y - p.y, this); 408 if (imageScale == null) 409 imageScale = new Double(getImageScaling(img, p, p2)); 410 count++; 411 if (fadeBackground != 0f) { 412 // dimm by painting opaque rect... 413 g.setColor(new Color(1f, 1f, 1f, fadeBackground)); 414 g.fillRect(p.x, p.y, p2.x - p.x, p2.y - p.y); 415 }// end of if dim != 0 416 }// end of if img != null 417 }// end of for 418 }// end of for 419 g.setColor(Color.red); 420 for (int x = z12x0 - 1; x <= z12x1; x++) { 421 Point p = pixelpos[x - z12x0 + 1][0]; 422 423 if(SlippyMapPreferences.getDrawDebug()) { 424 if (x % 32 == 0) { 425 // level 7 tile boundary 426 g.fillRect(p.x - 1, 0, 3, mv.getHeight()); 427 } else { 428 g.drawLine(p.x, 0, p.x, mv.getHeight()); 429 } 430 }//end of if draw debug 431 432 for (int y = z12y0 - 1; y <= z12y1; y++) { 433 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y); 434 int texty = p.y + 2 + fontHeight; 435 SlippyMapTile tile = tileStorage.get(key); 436 if (tile == null) { 437 continue; 438 } 439 if (!key.valid) { 440 System.out.println("paint-0() made invalid key"); 441 continue; 442 } 443 if (tile.getImage() == null) { 444 loadSingleTile(tile); 445 } 446 p = pixelpos[x - z12x0 + 1][y - z12y0 + 2]; 447 448 if(SlippyMapPreferences.getDrawDebug()) { 449 g.drawString("x=" + x + " y=" + y + " z=" + currentZoomLevel 450 + "", p.x + 2, texty); 451 texty += 1 + fontHeight; 452 if ((x % 32 == 0) && (y % 32 == 0)) { 453 g.drawString("x=" + x / 32 + " y=" + y / 32 + " z=7", 454 p.x + 2, texty); 455 texty += 1 + fontHeight; 456 } 457 }//end of if draw debug 458 459 String md = tile.getMetadata(); 460 if (md != null) { 461 g.drawString(md, p.x + 2, texty); 462 texty += 1 + fontHeight; 463 } 464 465 if (tile.getImage() == null) { 466 g.drawString(tr("image not loaded"), p.x + 2, texty); 467 texty += 1 + fontHeight; 468 } 469 470 if(SlippyMapPreferences.getDrawDebug()) { 471 if (x == z12x0 - 1) { 472 if (y % 32 == 31) { 473 g.fillRect(0, p.y - 1, mv.getWidth(), 3); 474 } else { 475 g.drawLine(0, p.y, mv.getWidth(), p.y); 476 } 477 } 478 }//end of if draw debug 479 } // end of for 480 } 481 482 oldg.drawImage(bufferImage, 0, 0, null); 483 484 if (imageScale != null) { 485 // If each source image pixel is being stretched into > 3 486 // drawn pixels, zoom in... getting too pixelated 487 if (imageScale > 3) { 488 if (SlippyMapPreferences.getAutozoom()) { 489 Main.debug("autozoom increase: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0 490 + topLeft + " " + botRight + " scale: " + imageScale); 491 increaseZoomLevel(); 492 } 493 this.paint(oldg, mv); 494 } 495 496 // If each source image pixel is being squished into > 0.32 497 // of a drawn pixels, zoom out. 498 if (imageScale < 0.32) { 499 if (SlippyMapPreferences.getAutozoom()) { 500 Main.debug("autozoom decrease: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0 501 + topLeft + " " + botRight + " scale: " + imageScale); 502 decreaseZoomLevel(); 503 } 504 this.paint(oldg, mv); 505 } 506 } 507 g.setColor(Color.black); 508 g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120); 509 }// end of paint metod 510 511 SlippyMapTile getTileForPixelpos(int px, int py) { 512 int tilex = z12x1; 513 int tiley = z12y1; 514 for (int x = z12x0; x <= z12x1; x++) { 515 516 if (pixelpos[x - z12x0 + 1][0].x > px) { 517 tilex = x - 1; 518 break; 519 } 520 } 521 if (tilex == -1) 522 return null; 523 for (int y = z12y0; y <= z12y1; y++) { 524 525 if (pixelpos[0][y - z12y0 + 1].y > py) { 526 tiley = y - 1; 527 break; 528 } 529 } 530 if (tiley == -1) { 531 return null; 532 } 533 534 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, tilex, tiley); 535 if (!key.valid) { 536 System.err.println("getTileForPixelpos("+px+","+py+") made invalid key"); 537 return null; 538 } 539 SlippyMapTile tile = tileStorage.get(key); 540 if (tile == null) 541 tileStorage.put(key, tile = new SlippyMapTile(tilex, tiley, currentZoomLevel)); 542 checkTileStorage(); 543 return tile; 544 } 545 546 @Override 547 public Icon getIcon() { 548 return ImageProvider.get("slippymap"); 549 } 550 551 @Override 552 public Object getInfoComponent() { 553 return null; 554 } 555 556 @Override 557 public Component[] getMenuEntries() { 558 return new Component[] { 559 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 560 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 561 new JSeparator(), 562 // color, 563 new JMenuItem(new RenameLayerAction(associatedFile, this)), 564 new JSeparator(), 565 new JMenuItem(new LayerListPopup.InfoAction(this)) }; 566 } 567 568 @Override 569 public String getToolTipText() { 570 return null; 571 } 572 573 @Override 574 public boolean isMergable(Layer other) { 575 return false; 576 } 577 578 @Override 579 public void mergeFrom(Layer from) { 580 } 581 582 @Override 583 public void visitBoundingBox(BoundingXYVisitor v) { 584 } 585 586 private int latToTileY(double lat) { 587 double l = lat / 180 * Math.PI; 588 double pf = Math.log(Math.tan(l) + (1 / Math.cos(l))); 589 return (int) (Math.pow(2.0, currentZoomLevel - 1) * (Math.PI - pf) / Math.PI); 590 } 591 592 private int lonToTileX(double lon) { 593 return (int) (Math.pow(2.0, currentZoomLevel - 3) * (lon + 180.0) / 45.0); 594 } 595 596 private double tileYToLat(int y) { 597 return Math.atan(Math.sinh(Math.PI 598 - (Math.PI * y / Math.pow(2.0, currentZoomLevel - 1)))) 599 * 180 / Math.PI; 600 } 601 602 private double tileXToLon(int x) { 603 return x * 45.0 / Math.pow(2.0, currentZoomLevel - 3) - 180.0; 604 } 605 606 public boolean imageUpdate(Image img, int infoflags, int x, int y, 607 int width, int height) { 608 boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0); 609 if ((infoflags & ERROR) != 0) { 610 String url = "unknown"; 611 for (SlippyMapTile tile : tileStorage.values()) { 612 if (tile.getImage() != img) 613 continue; 614 url = tile.getImageURL().toString(); 615 } 616 System.err.println("imageUpdate(" + img + ") error " + url +")"); 617 } 618 if ((infoflags & SOMEBITS) != 0) { 619 //if (y%100 == 0) 620 // System.out.println("imageUpdate("+img+") SOMEBITS ("+x+","+y+")"); 621 } 622 // Repaint immediately if we are done, otherwise batch up 623 // repaint requests every 100 milliseconds 624 needRedraw = true; 625 Main.map.repaint(done ? 0 : 100); 626 return !done; 627 } 628 629 /* 630 * (non-Javadoc) 631 * 632 * @seeorg.openstreetmap.josm.data.Preferences.PreferenceChangedListener# 633 * preferenceChanged(java.lang.String, java.lang.String) 634 */ 635 public void preferenceChanged(String key, String newValue) { 636 if (key.startsWith(SlippyMapPreferences.PREFERENCE_PREFIX)) { 637 // System.err.println(this + ".preferenceChanged('" + key + "', '" 638 // + newValue + "') called"); 639 // when fade background changed, no need to clear tile storage 640 // TODO move this code to SlippyMapPreferences class. 641 if (!key.equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) { 642 clearTileStorage(); 643 } 644 } 645 } 646 647 @Override 648 public void destroy() { 649 Main.pref.listener.remove(SlippyMapLayer.this); 650 } 50 public int currentZoomLevel = SlippyMapPreferences.getMinZoomLvl(); 51 private HashMap<SlippyMapKey, SlippyMapTile> tileStorage = null; 52 53 Point[][] pixelpos = new Point[21][21]; 54 LatLon lastTopLeft; 55 LatLon lastBotRight; 56 int z12x0, z12x1, z12y0, z12y1; 57 private Image bufferImage; 58 private SlippyMapTile clickedTile; 59 private boolean needRedraw; 60 private JPopupMenu tileOptionMenu; 61 62 @SuppressWarnings("serial") 63 public SlippyMapLayer() { 64 super(tr("Slippy Map")); 65 background = true; 66 67 clearTileStorage(); 68 69 tileOptionMenu = new JPopupMenu(); 70 tileOptionMenu.add(new JMenuItem(new AbstractAction(tr("Load Tile")) { 71 public void actionPerformed(ActionEvent ae) { 72 if (clickedTile != null) { 73 loadSingleTile(clickedTile); 74 needRedraw = true; 75 Main.map.repaint(); 76 } 77 } 78 })); 79 80 tileOptionMenu.add(new JMenuItem(new AbstractAction( 81 tr("Show Tile Status")) { 82 public void actionPerformed(ActionEvent ae) { 83 if (clickedTile != null) { 84 clickedTile.loadMetadata(); 85 needRedraw = true; 86 Main.map.repaint(); 87 } 88 } 89 })); 90 91 tileOptionMenu.add(new JMenuItem(new AbstractAction( 92 tr("Request Update")) { 93 public void actionPerformed(ActionEvent ae) { 94 if (clickedTile != null) { 95 clickedTile.requestUpdate(); 96 needRedraw = true; 97 Main.map.repaint(); 98 } 99 } 100 })); 101 102 tileOptionMenu.add(new JMenuItem(new AbstractAction( 103 tr("Load All Tiles")) { 104 public void actionPerformed(ActionEvent ae) { 105 loadAllTiles(); 106 needRedraw = true; 107 Main.map.repaint(); 108 } 109 })); 110 111 // increase and decrease commands 112 tileOptionMenu.add(new JMenuItem( 113 new AbstractAction(tr("Increase zoom")) { 114 public void actionPerformed(ActionEvent ae) { 115 increaseZoomLevel(); 116 needRedraw = true; 117 Main.map.repaint(); 118 } 119 })); 120 121 tileOptionMenu.add(new JMenuItem( 122 new AbstractAction(tr("Decrease zoom")) { 123 public void actionPerformed(ActionEvent ae) { 124 decreaseZoomLevel(); 125 Main.map.repaint(); 126 } 127 })); 128 // end of adding menu commands 129 130 SwingUtilities.invokeLater(new Runnable() { 131 public void run() { 132 Main.map.mapView.addMouseListener(new MouseAdapter() { 133 @Override 134 public void mouseClicked(MouseEvent e) { 135 if (e.getButton() != MouseEvent.BUTTON3) 136 return; 137 clickedTile = getTileForPixelpos(e.getX(), e.getY()); 138 tileOptionMenu.show(e.getComponent(), e.getX(), e 139 .getY()); 140 } 141 }); 142 143 listeners.add(new LayerChangeListener() { 144 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 145 } 146 147 public void layerAdded(Layer newLayer) { 148 } 149 150 public void layerRemoved(Layer oldLayer) { 151 Main.pref.listener.remove(SlippyMapLayer.this); 152 } 153 }); 154 } 155 }); 156 157 Main.pref.listener.add(this); 158 } 159 160 /** 161 * Zoom in, go closer to map. 162 */ 163 public void increaseZoomLevel() { 164 if (currentZoomLevel < SlippyMapPreferences.getMaxZoomLvl()) { 165 currentZoomLevel++; 166 Main.debug("increasing zoom level to: " + currentZoomLevel); 167 needRedraw = true; 168 } else { 169 System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+ 170 "MaxZoomLvl ("+SlippyMapPreferences.getMaxZoomLvl()+") reached."); 171 } 172 } 173 174 /** 175 * Zoom out from map. 176 */ 177 public void decreaseZoomLevel() { 178 if (currentZoomLevel > SlippyMapPreferences.getMinZoomLvl()) { 179 Main.debug("decreasing zoom level to: " + currentZoomLevel); 180 currentZoomLevel--; 181 needRedraw = true; 182 } else { 183 System.err.println("current zoom lvl couldnt be decreased. MinZoomLvl reached."); 184 } 185 } 186 187 public void clearTileStorage() { 188 // when max zoom lvl is begin saved, this method is called and probably 189 // the setting isnt saved yet. 190 int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl(); 191 tileStorage = new HashMap<SlippyMapKey, SlippyMapTile>(); 192 193 checkTileStorage(); 194 } 195 196 class TileTimeComp implements Comparator<SlippyMapTile> { 197 public int compare(SlippyMapTile s1, SlippyMapTile s2) { 198 long t1 = s1.access_time(); 199 long t2 = s2.access_time(); 200 if (s1 == s2) 201 return 0; 202 if (t1 == t2) { 203 t1 = s1.hashCode(); 204 t2 = s2.hashCode(); 205 } 206 if (t1 < t2) 207 return -1; 208 return 1; 209 } 210 } 211 212 long lastCheck = 0; 213 /** 214 * <p> 215 * Check if tiles.size() is not more than max_nr_tiles. If yes, oldest tiles by timestamp 216 * are fired out from cache. 217 * </p> 218 */ 219 public void checkTileStorage() { 220 int maxZoom = 30; // SlippyMapPreferences.getMaxZoomLvl(); 221 long now = System.currentTimeMillis(); 222 if (now - lastCheck < 1000) 223 return; 224 lastCheck = now; 225 TreeSet<SlippyMapTile> tiles = new TreeSet<SlippyMapTile>(new TileTimeComp()); 226 tiles.addAll(tileStorage.values()); 227 int max_nr_tiles = 100; 228 if (tiles.size() < max_nr_tiles) { 229 Main.debug("total of " + tiles.size() + " loaded tiles, size OK " + now); 230 return; 231 } 232 int nr_to_drop = tiles.size() - max_nr_tiles;; 233 Main.debug("total of " + tiles.size() + " tiles, need to flush " + nr_to_drop + " tiles"); 234 for (SlippyMapTile t : tiles) { 235 if (nr_to_drop <= 0) 236 break; 237 t.dropImage(); 238 nr_to_drop--; 239 } 240 } 241 242 void loadSingleTile(SlippyMapTile tile) 243 { 244 tile.loadImage(); 245 this.checkTileStorage(); 246 } 247 248 void loadAllTiles() { 249 MapView mv = Main.map.mapView; 250 LatLon topLeft = mv.getLatLon(0, 0); 251 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight()); 252 z12x0 = lonToTileX(topLeft.lon()); 253 z12x1 = lonToTileX(botRight.lon()); 254 z12y0 = latToTileY(topLeft.lat()); 255 z12y1 = latToTileY(botRight.lat()); 256 if (z12x0 > z12x1) { 257 int tmp = z12x0; 258 z12x0 = z12x1; 259 z12x1 = tmp; 260 } 261 if (z12y0 > z12y1) { 262 int tmp = z12y0; 263 z12y0 = z12y1; 264 z12y1 = tmp; 265 } 266 // if there is more than 18 tiles on screen in any direction, do not 267 // load all tiles! 268 if (z12x1 - z12x0 > 18) { 269 System.out 270 .println("Not downloading all tiles because there is more than 18 tiles on X axis!"); 271 return; 272 } 273 if (z12y1 - z12y0 > 18) { 274 System.out 275 .println("Not downloading all tiles because there is more than 18 tiles on Y axis!"); 276 return; 277 } 278 279 for (int x = z12x0 - 1; x <= z12x1; x++) { 280 for (int y = z12y0 - 1; y <= z12y1; y++) { 281 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y); 282 SlippyMapTile tile = tileStorage.get(key); 283 if (!key.valid) { 284 System.out.println("paint-1() made invalid key"); 285 continue; 286 } 287 if (tile == null) 288 tileStorage.put(key, 289 tile = new SlippyMapTile(x, y, currentZoomLevel)); 290 if (tile.getImage() == null) { 291 this.loadSingleTile(tile); 292 } 293 } 294 } 295 } 296 297 /* 298 * Attempt to approximate how much the image is 299 * being scaled. For instance, a 100x100 image 300 * being scaled to 50x50 would return 0.25. 301 */ 302 double getImageScaling(Image img, Point p0, Point p1) 303 { 304 int realWidth = img.getWidth(this); 305 int realHeight = img.getHeight(this); 306 if (realWidth == -1 || realHeight == -1) 307 return 1.0; 308 int drawWidth = p1.x - p0.x; 309 int drawHeight = p1.x - p0.x; 310 311 double drawArea = drawWidth * drawHeight; 312 double realArea = realWidth * realHeight; 313 314 return drawArea / realArea; 315 } 316 317 /** 318 */ 319 @Override 320 public void paint(Graphics g, MapView mv) { 321 long start = System.currentTimeMillis(); 322 LatLon topLeft = mv.getLatLon(0, 0); 323 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight()); 324 Graphics oldg = g; 325 326 if (botRight.lon() == 0.0 || botRight.lat() == 0) { 327 // probably still initializing 328 return; 329 } 330 if (lastTopLeft != null && lastBotRight != null 331 && topLeft.equalsEpsilon(lastTopLeft) 332 && botRight.equalsEpsilon(lastBotRight) && bufferImage != null 333 && mv.getWidth() == bufferImage.getWidth(null) 334 && mv.getHeight() == bufferImage.getHeight(null) && !needRedraw) { 335 336 g.drawImage(bufferImage, 0, 0, null); 337 return; 338 } 339 340 needRedraw = false; 341 lastTopLeft = topLeft; 342 lastBotRight = botRight; 343 bufferImage = mv.createImage(mv.getWidth(), mv.getHeight()); 344 g = bufferImage.getGraphics(); 345 346 z12x0 = lonToTileX(topLeft.lon()); 347 z12x1 = lonToTileX(botRight.lon()); 348 z12y0 = latToTileY(topLeft.lat()); 349 z12y1 = latToTileY(botRight.lat()); 350 351 if (z12x0 > z12x1) { 352 int tmp = z12x0; 353 z12x0 = z12x1; 354 z12x1 = tmp; 355 } 356 if (z12y0 > z12y1) { 357 int tmp = z12y0; 358 z12y0 = z12y1; 359 z12y1 = tmp; 360 } 361 362 if (z12x1 - z12x0 > 18) 363 return; 364 if (z12y1 - z12y0 > 18) 365 return; 366 367 for (int x = z12x0 - 1; x <= z12x1 + 1; x++) { 368 double lon = tileXToLon(x); 369 for (int y = z12y0 - 1; y <= z12y1 + 1; y++) { 370 LatLon tmpLL = new LatLon(tileYToLat(y), lon); 371 pixelpos[x - z12x0 + 1][y - z12y0 + 1] = mv.getPoint(Main.proj 372 .latlon2eastNorth(tmpLL)); 373 } 374 } 375 376 int fontHeight = g.getFontMetrics().getHeight(); 377 378 g.setColor(Color.DARK_GRAY); 379 380 float fadeBackground = SlippyMapPreferences.getFadeBackground(); 381 382 Double imageScale = null; 383 int count = 0; 384 for (int x = z12x0 - 1; x <= z12x1; x++) { 385 for (int y = z12y0 - 1; y <= z12y1; y++) { 386 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y); 387 SlippyMapTile tile; 388 tile = tileStorage.get(key); 389 if (!key.valid) { 390 System.out.println("loadAllTiles() made invalid key"); 391 continue; 392 } 393 if (tile == null) { 394 tile = new SlippyMapTile(x, y, currentZoomLevel); 395 tileStorage.put(key, tile); 396 if (SlippyMapPreferences.getAutoloadTiles()) { 397 // TODO probably do on background 398 loadSingleTile(tile); 399 } 400 checkTileStorage(); 401 } 402 Image img = tile.getImage(); 403 404 if (img != null) { 405 Point p = pixelpos[x - z12x0 + 1][y - z12y0 + 1]; 406 Point p2 = pixelpos[x - z12x0 + 2][y - z12y0 + 2]; 407 g.drawImage(img, p.x, p.y, p2.x - p.x, p2.y - p.y, this); 408 if (imageScale == null) 409 imageScale = new Double(getImageScaling(img, p, p2)); 410 count++; 411 if (fadeBackground != 0f) { 412 // dimm by painting opaque rect... 413 g.setColor(new Color(1f, 1f, 1f, fadeBackground)); 414 g.fillRect(p.x, p.y, p2.x - p.x, p2.y - p.y); 415 }// end of if dim != 0 416 }// end of if img != null 417 }// end of for 418 }// end of for 419 g.setColor(Color.red); 420 for (int x = z12x0 - 1; x <= z12x1; x++) { 421 Point p = pixelpos[x - z12x0 + 1][0]; 422 423 if(SlippyMapPreferences.getDrawDebug()) { 424 if (x % 32 == 0) { 425 // level 7 tile boundary 426 g.fillRect(p.x - 1, 0, 3, mv.getHeight()); 427 } else { 428 g.drawLine(p.x, 0, p.x, mv.getHeight()); 429 } 430 }//end of if draw debug 431 432 for (int y = z12y0 - 1; y <= z12y1; y++) { 433 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, x, y); 434 int texty = p.y + 2 + fontHeight; 435 SlippyMapTile tile = tileStorage.get(key); 436 if (tile == null) { 437 continue; 438 } 439 if (!key.valid) { 440 System.out.println("paint-0() made invalid key"); 441 continue; 442 } 443 if (tile.getImage() == null) { 444 loadSingleTile(tile); 445 } 446 p = pixelpos[x - z12x0 + 1][y - z12y0 + 2]; 447 448 if(SlippyMapPreferences.getDrawDebug()) { 449 g.drawString("x=" + x + " y=" + y + " z=" + currentZoomLevel 450 + "", p.x + 2, texty); 451 texty += 1 + fontHeight; 452 if ((x % 32 == 0) && (y % 32 == 0)) { 453 g.drawString("x=" + x / 32 + " y=" + y / 32 + " z=7", 454 p.x + 2, texty); 455 texty += 1 + fontHeight; 456 } 457 }//end of if draw debug 458 459 String md = tile.getMetadata(); 460 if (md != null) { 461 g.drawString(md, p.x + 2, texty); 462 texty += 1 + fontHeight; 463 } 464 465 if (tile.getImage() == null) { 466 g.drawString(tr("image not loaded"), p.x + 2, texty); 467 texty += 1 + fontHeight; 468 } 469 470 if(SlippyMapPreferences.getDrawDebug()) { 471 if (x == z12x0 - 1) { 472 if (y % 32 == 31) { 473 g.fillRect(0, p.y - 1, mv.getWidth(), 3); 474 } else { 475 g.drawLine(0, p.y, mv.getWidth(), p.y); 476 } 477 } 478 }//end of if draw debug 479 } // end of for 480 } 481 482 oldg.drawImage(bufferImage, 0, 0, null); 483 484 if (imageScale != null) { 485 // If each source image pixel is being stretched into > 3 486 // drawn pixels, zoom in... getting too pixelated 487 if (imageScale > 3) { 488 if (SlippyMapPreferences.getAutozoom()) { 489 Main.debug("autozoom increase: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0 490 + topLeft + " " + botRight + " scale: " + imageScale); 491 increaseZoomLevel(); 492 } 493 this.paint(oldg, mv); 494 } 495 496 // If each source image pixel is being squished into > 0.32 497 // of a drawn pixels, zoom out. 498 if (imageScale < 0.32) { 499 if (SlippyMapPreferences.getAutozoom()) { 500 Main.debug("autozoom decrease: "+z12x1+" " + z12x0 + " " + z12y1 + " " + z12y0 501 + topLeft + " " + botRight + " scale: " + imageScale); 502 decreaseZoomLevel(); 503 } 504 this.paint(oldg, mv); 505 } 506 } 507 g.setColor(Color.black); 508 g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120); 509 }// end of paint metod 510 511 SlippyMapTile getTileForPixelpos(int px, int py) { 512 int tilex = z12x1; 513 int tiley = z12y1; 514 for (int x = z12x0; x <= z12x1; x++) { 515 516 if (pixelpos[x - z12x0 + 1][0].x > px) { 517 tilex = x - 1; 518 break; 519 } 520 } 521 if (tilex == -1) 522 return null; 523 for (int y = z12y0; y <= z12y1; y++) { 524 525 if (pixelpos[0][y - z12y0 + 1].y > py) { 526 tiley = y - 1; 527 break; 528 } 529 } 530 if (tiley == -1) { 531 return null; 532 } 533 534 SlippyMapKey key = new SlippyMapKey(currentZoomLevel, tilex, tiley); 535 if (!key.valid) { 536 System.err.println("getTileForPixelpos("+px+","+py+") made invalid key"); 537 return null; 538 } 539 SlippyMapTile tile = tileStorage.get(key); 540 if (tile == null) 541 tileStorage.put(key, tile = new SlippyMapTile(tilex, tiley, currentZoomLevel)); 542 checkTileStorage(); 543 return tile; 544 } 545 546 @Override 547 public Icon getIcon() { 548 return ImageProvider.get("slippymap"); 549 } 550 551 @Override 552 public Object getInfoComponent() { 553 return null; 554 } 555 556 @Override 557 public Component[] getMenuEntries() { 558 return new Component[] { 559 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 560 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 561 new JSeparator(), 562 // color, 563 new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)), 564 new JSeparator(), 565 new JMenuItem(new LayerListPopup.InfoAction(this)) }; 566 } 567 568 @Override 569 public String getToolTipText() { 570 return null; 571 } 572 573 @Override 574 public boolean isMergable(Layer other) { 575 return false; 576 } 577 578 @Override 579 public void mergeFrom(Layer from) { 580 } 581 582 @Override 583 public void visitBoundingBox(BoundingXYVisitor v) { 584 } 585 586 private int latToTileY(double lat) { 587 double l = lat / 180 * Math.PI; 588 double pf = Math.log(Math.tan(l) + (1 / Math.cos(l))); 589 return (int) (Math.pow(2.0, currentZoomLevel - 1) * (Math.PI - pf) / Math.PI); 590 } 591 592 private int lonToTileX(double lon) { 593 return (int) (Math.pow(2.0, currentZoomLevel - 3) * (lon + 180.0) / 45.0); 594 } 595 596 private double tileYToLat(int y) { 597 return Math.atan(Math.sinh(Math.PI 598 - (Math.PI * y / Math.pow(2.0, currentZoomLevel - 1)))) 599 * 180 / Math.PI; 600 } 601 602 private double tileXToLon(int x) { 603 return x * 45.0 / Math.pow(2.0, currentZoomLevel - 3) - 180.0; 604 } 605 606 public boolean imageUpdate(Image img, int infoflags, int x, int y, 607 int width, int height) { 608 boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0); 609 if ((infoflags & ERROR) != 0) { 610 String url = "unknown"; 611 for (SlippyMapTile tile : tileStorage.values()) { 612 if (tile.getImage() != img) 613 continue; 614 url = tile.getImageURL().toString(); 615 } 616 System.err.println("imageUpdate(" + img + ") error " + url +")"); 617 } 618 if ((infoflags & SOMEBITS) != 0) { 619 //if (y%100 == 0) 620 // System.out.println("imageUpdate("+img+") SOMEBITS ("+x+","+y+")"); 621 } 622 // Repaint immediately if we are done, otherwise batch up 623 // repaint requests every 100 milliseconds 624 needRedraw = true; 625 Main.map.repaint(done ? 0 : 100); 626 return !done; 627 } 628 629 /* 630 * (non-Javadoc) 631 * 632 * @seeorg.openstreetmap.josm.data.Preferences.PreferenceChangedListener# 633 * preferenceChanged(java.lang.String, java.lang.String) 634 */ 635 public void preferenceChanged(String key, String newValue) { 636 if (key.startsWith(SlippyMapPreferences.PREFERENCE_PREFIX)) { 637 // System.err.println(this + ".preferenceChanged('" + key + "', '" 638 // + newValue + "') called"); 639 // when fade background changed, no need to clear tile storage 640 // TODO move this code to SlippyMapPreferences class. 641 if (!key.equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) { 642 clearTileStorage(); 643 } 644 } 645 } 646 647 @Override 648 public void destroy() { 649 Main.pref.listener.remove(SlippyMapLayer.this); 650 } 651 651 }
Note:
See TracChangeset
for help on using the changeset viewer.