- Timestamp:
- 2011-01-12T19:13:24+01:00 (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
r3778 r3785 72 72 * @author LuVar <lubomir.varga@freemap.sk> 73 73 * @author Dave Hansen <dave@sr71.net> 74 * @author Upliner <upliner@gmail.com> 74 75 * 75 76 */ … … 148 149 */ 149 150 public int currentZoomLevel; 150 /**151 * Optimal TMS Zoomlevel for current mapview.152 * Works correctly only for Mercatator, so currently used only for initial zoom.153 */154 public int bestZoomLevel;155 /**156 * Painting zoomlevel. Set to the currentZoomLevel when first tile at this zoomlevel is loaded.157 */158 public int displayZoomLevel = 0;159 151 160 152 private Tile clickedTile; 161 153 private boolean needRedraw; 162 private boolean overZoomed;163 private boolean overZoomedFlag;164 154 private JPopupMenu tileOptionMenu; 165 155 JCheckBoxMenuItem autoZoomPopup; … … 266 256 } 267 257 268 updateBestZoom(); 269 currentZoomLevel = bestZoomLevel; 258 currentZoomLevel = getBestZoom(); 270 259 271 260 clearTileCache(); … … 289 278 } 290 279 291 private double getPPDeg() { 280 /** 281 * Returns average number of screen pixels per tile pixel for current mapview 282 */ 283 private double getScaleFactor(int zoom) { 284 if (Main.map == null || Main.map.mapView == null) return 1; 292 285 MapView mv = Main.map.mapView; 293 return mv.getWidth()/(mv.getLatLon(mv.getWidth(), mv.getHeight()/2).lon()-mv.getLatLon(0, mv.getHeight()/2).lon()); 286 LatLon topLeft = mv.getLatLon(0, 0); 287 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight()); 288 double x1 = lonToTileX(topLeft.lon(), zoom); 289 double y1 = latToTileY(topLeft.lat(), zoom); 290 double x2 = lonToTileX(botRight.lon(), zoom); 291 double y2 = latToTileY(botRight.lat(), zoom); 292 293 int screenPixels = mv.getWidth()*mv.getHeight(); 294 double tilePixels = Math.abs((y2-y1)*(x2-x1)*tileSource.getTileSize()*tileSource.getTileSize()); 295 if (screenPixels == 0 || tilePixels == 0) return 1; 296 return screenPixels/tilePixels; 294 297 } 295 298 296 299 private int getBestZoom() { 297 if (Main.map == null || Main.map.mapView == null) return 3; 298 double ret = Math.log(getPPDeg()*360/tileSource.getTileSize())/Math.log(2); 299 return (int)Math.round(ret); 300 } 301 302 private void updateBestZoom() { 303 bestZoomLevel = getBestZoom(); 304 if (bestZoomLevel > getMaxZoomLvl()) { 305 bestZoomLevel = getMaxZoomLvl(); 306 } 307 if (bestZoomLevel < getMinZoomLvl()) { 308 bestZoomLevel = getMinZoomLvl(); 309 } 300 double factor = getScaleFactor(1); 301 double result = Math.log(factor)/Math.log(2)/2+1; 302 int intResult = (int)Math.round(result); 303 if (intResult > getMaxZoomLvl()) 304 return getMaxZoomLvl(); 305 if (intResult < getMinZoomLvl()) 306 return getMinZoomLvl(); 307 return intResult; 310 308 } 311 309 … … 413 411 @Override 414 412 public void actionPerformed(ActionEvent ae) { 415 if (lastImageScale == null) { 416 out("please wait for a tile to be loaded before snapping"); 417 return; 418 } 419 double new_factor = Math.sqrt(lastImageScale); 420 if (debug) { 421 out("tile snap: scale was: " + lastImageScale + ", new factor: " + new_factor); 422 } 413 double new_factor = Math.sqrt(getScaleFactor(currentZoomLevel)); 423 414 Main.map.mapView.zoomToFactor(new_factor); 424 415 redraw(); … … 517 508 } 518 509 519 boolean isOverZoomed() {520 return overZoomed || overZoomedFlag;521 }522 523 510 /** 524 511 * Zoom in, go closer to map. … … 528 515 public boolean zoomIncreaseAllowed() 529 516 { 530 boolean zia = currentZoomLevel < this.getMaxZoomLvl() && !isOverZoomed();517 boolean zia = currentZoomLevel < this.getMaxZoomLvl(); 531 518 if (debug) { 532 519 out("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + this.getMaxZoomLvl() ); … … 536 523 public boolean increaseZoomLevel() 537 524 { 538 lastImageScale = null;539 525 if (zoomIncreaseAllowed()) { 540 526 currentZoomLevel++; … … 553 539 public boolean setZoomLevel(int zoom) 554 540 { 541 if (zoom == currentZoomLevel) return true; 555 542 if (zoom > this.getMaxZoomLvl()) return false; 556 543 if (zoom < this.getMinZoomLvl()) return false; 557 544 currentZoomLevel = zoom; 558 lastImageScale = null;559 545 zoomChanged(); 560 546 return true; … … 572 558 public boolean decreaseZoomLevel() { 573 559 int minZoom = this.getMinZoomLvl(); 574 lastImageScale = null;575 560 if (zoomDecreaseAllowed()) { 576 561 if (debug) { … … 689 674 } 690 675 691 double getImageScaling(Image img, Rectangle r) {692 int realWidth = -1;693 int realHeight = -1;694 if (img != null) {695 realWidth = img.getHeight(this);696 realWidth = img.getWidth(this);697 }698 if (realWidth == -1 || realHeight == -1) {699 /*700 * We need a good image against which to work. If701 * the current one isn't loaded, then try the last one.702 * Should be good enough. If we've never seen one, then703 * guess.704 */705 if (lastScaledImage != null)706 return getImageScaling(lastScaledImage, r);707 realWidth = 256;708 realHeight = 256;709 } else {710 lastScaledImage = img;711 }712 /*713 * If the zoom scale gets really, really off, these can get into714 * the millions, so make this a double to prevent integer715 * overflows.716 */717 double drawWidth = r.width;718 double drawHeight = r.height;719 // stem.out.println("drawWidth: " + drawWidth + " drawHeight: " +720 // drawHeight);721 722 double drawArea = drawWidth * drawHeight;723 double realArea = realWidth * realHeight;724 725 return drawArea / realArea;726 }727 728 676 LatLon tileLatLon(Tile t) 729 677 { … … 733 681 } 734 682 735 int paintFromOtherZooms(Graphics g, Tile topLeftTile, Tile botRightTile)736 {737 LatLon topLeft = tileLatLon(topLeftTile);738 LatLon botRight = tileLatLon(botRightTile);739 740 741 /*742 * Go looking for tiles in zoom levels *other* than the current743 * one. Even if they might look bad, they look better than a744 * blank tile.745 *746 * Make darn sure that the tilesCache can either hold all of747 * these "fake" tiles or that they don't get inserted in it to748 * begin with.749 */750 //int otherZooms[] = {-5, -4, -3, 2, -2, 1, -1};751 int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5};752 int painted = 0;753 debug = true;754 for (int zoomOff : otherZooms) {755 int zoom = displayZoomLevel + zoomOff;756 if ((zoom < this.getMinZoomLvl()) ||757 (zoom > this.getMaxZoomLvl())) {758 continue;759 }760 TileSet ts = new TileSet(topLeft, botRight, zoom);761 int zoom_painted = 0;762 this.paintTileImages(g, ts, zoom, null);763 if (debug && zoom_painted > 0) {764 out("painted " + zoom_painted + "/"+ ts.size() +765 " tiles from zoom("+zoomOff+"): " + zoom);766 }767 painted += zoom_painted;768 if (zoom_painted >= ts.size()) {769 if (debug) {770 out("broke after drawing " + zoom_painted + "/"+ ts.size() + " at zoomOff: " + zoomOff);771 }772 break;773 }774 }775 debug = false;776 return painted;777 }778 683 Rectangle tileToRect(Tile t1) 779 684 { … … 847 752 } 848 753 } 849 Double lastImageScale = null;850 754 // This function is called for several zoom levels, not just 851 755 // the current one. It should not trigger any tiles to be … … 864 768 } 865 769 List<Tile> missedTiles = new LinkedList<Tile>(); 866 boolean imageScaleRecorded = false;867 770 for (Tile tile : ts.allTiles()) { 868 771 Image img = getLoadedTileImage(tile); … … 879 782 } 880 783 drawImageInside(g, img, sourceRect, borderRect); 881 if (!imageScaleRecorded && zoom == displayZoomLevel) {882 lastImageScale = new Double(getImageScaling(img, sourceRect));883 imageScaleRecorded = true;884 }885 784 }// end of for 886 785 return missedTiles; … … 978 877 return new Coordinate(ll.lat(),ll.lon()); 979 878 } 879 private final TileSet nullTileSet = new TileSet((LatLon)null, (LatLon)null, 0); 980 880 private class TileSet { 981 881 int x0, x1, y0, y1; … … 995 895 TileSet(LatLon topLeft, LatLon botRight, int zoom) { 996 896 this.zoom = zoom; 997 998 x0 = lonToTileX(topLeft.lon(), zoom); 999 y0 = latToTileY(topLeft.lat(), zoom); 1000 x1 = lonToTileX(botRight.lon(), zoom); 1001 y1 = latToTileY(botRight.lat(), zoom); 897 if (zoom == 0) 898 return; 899 900 x0 = (int)lonToTileX(topLeft.lon(), zoom); 901 y0 = (int)latToTileY(topLeft.lat(), zoom); 902 x1 = (int)lonToTileX(botRight.lon(), zoom); 903 y1 = (int)latToTileY(botRight.lat(), zoom); 1002 904 if (x0 > x1) { 1003 905 int tmp = x0; … … 1037 939 } 1038 940 1039 doublesize() {1040 double x_span = x1 - x0 + 1.0;1041 double y_span = y1 - y0 + 1.0;941 int size() { 942 int x_span = x1 - x0 + 1; 943 int y_span = y1 - y0 + 1; 1042 944 return x_span * y_span; 1043 945 } … … 1053 955 private List<Tile> allTiles(boolean create) 1054 956 { 957 // Tileset is either empty or too large 958 if (zoom == 0 || this.insane()) 959 return Collections.emptyList(); 1055 960 List<Tile> ret = new ArrayList<Tile>(); 1056 // Don't even try to iterate over the set.1057 // Someone created a crazy number of them1058 if (this.insane())1059 return ret;1060 961 for (int x = x0; x <= x1; x++) { 1061 962 for (int y = y0; y <= y1; y++) { … … 1074 975 } 1075 976 1076 int totalTiles() {1077 return (y1 - y0 + 1) * (x1 - x0 + 1);1078 }1079 1080 977 void loadAllTiles(boolean force) 1081 978 { … … 1096 993 } 1097 994 1098 boolean az_disable = false; 1099 boolean autoZoomEnabled() 1100 { 1101 if (az_disable) 1102 return false; 1103 return autoZoom; 1104 } 995 996 private static class TileSetInfo { 997 public boolean hasVisibleTiles = false; 998 public boolean hasOverzoomedTiles = false; 999 public boolean hasLoadingTiles = false; 1000 } 1001 1002 private static TileSetInfo getTileSetInfo(TileSet ts) { 1003 List<Tile> allTiles = ts.allTiles(); 1004 TileSetInfo result = new TileSetInfo(); 1005 result.hasLoadingTiles = allTiles.size() < ts.size(); 1006 for (Tile t : allTiles) { 1007 if (t.isLoaded()) { 1008 if (!t.hasError()) { 1009 result.hasVisibleTiles = true; 1010 } 1011 if ("no-tile".equals(t.getValue("tile-info"))) { 1012 result.hasOverzoomedTiles = true; 1013 } 1014 } else { 1015 result.hasLoadingTiles = true; 1016 } 1017 } 1018 return result; 1019 } 1020 1021 private class DeepTileSet { 1022 final EastNorth topLeft, botRight; 1023 final int minZoom; 1024 private final TileSet[] tileSets; 1025 private final TileSetInfo[] tileSetInfos; 1026 public DeepTileSet(EastNorth topLeft, EastNorth botRight, int minZoom, int maxZoom) { 1027 this.topLeft = topLeft; 1028 this.botRight = botRight; 1029 this.minZoom = minZoom; 1030 this.tileSets = new TileSet[maxZoom - minZoom + 1]; 1031 this.tileSetInfos = new TileSetInfo[maxZoom - minZoom + 1]; 1032 } 1033 public TileSet getTileSet(int zoom) { 1034 if (zoom < minZoom) 1035 return nullTileSet; 1036 TileSet ts = tileSets[zoom-minZoom]; 1037 if (ts == null) { 1038 ts = new TileSet(topLeft, botRight, zoom); 1039 tileSets[zoom-minZoom] = ts; 1040 } 1041 return ts; 1042 } 1043 public TileSetInfo getTileSetInfo(int zoom) { 1044 if (zoom < minZoom) 1045 return new TileSetInfo(); 1046 TileSetInfo tsi = tileSetInfos[zoom-minZoom]; 1047 if (tsi == null) { 1048 tsi = TMSLayer.getTileSetInfo(getTileSet(zoom)); 1049 tileSetInfos[zoom-minZoom] = tsi; 1050 } 1051 return tsi; 1052 } 1053 } 1054 1105 1055 /** 1106 1056 */ … … 1120 1070 1121 1071 int zoom = currentZoomLevel; 1122 TileSet ts = new TileSet(topLeft, botRight, zoom); 1123 1124 if (autoZoomEnabled()) { 1125 if (zoomDecreaseAllowed() && ts.tooLarge()) { 1126 if (debug) { 1127 out("too many tiles, decreasing zoom from " + currentZoomLevel); 1128 } 1129 if (decreaseZoomLevel()) { 1130 this.paint(g, mv, bounds); 1131 return; 1132 } 1133 } 1134 1135 // Auto-detection of Bing zoomlevel 1136 if (tileSource instanceof BingAerialTileSource) { 1137 List<Tile> allTiles = ts.allTiles(); 1138 boolean hasVisibleTiles = false; 1139 boolean hasOverzoomedTiles = false; 1140 boolean hasLoadingTiles = allTiles.size() < ts.totalTiles(); 1141 for (Tile t : allTiles) { 1142 if (t.isLoaded()) { 1143 if (!t.hasError()) { 1144 hasVisibleTiles = true; 1145 } 1146 if ("no-tile".equals(t.getValue("tile-info"))) { 1147 hasOverzoomedTiles = true; 1148 } 1149 } else { 1150 hasLoadingTiles = true; 1151 } 1152 } 1153 if (!hasLoadingTiles) { 1154 overZoomed = false; 1155 } 1156 if (!hasVisibleTiles && hasOverzoomedTiles) { 1157 overZoomed = true; 1158 if (displayZoomLevel == 0 || !hasLoadingTiles) { 1159 boolean tmp = overZoomedFlag; 1160 overZoomedFlag = true; 1161 if (decreaseZoomLevel()) { 1162 this.paint(g, mv, bounds); 1163 overZoomedFlag = tmp; 1164 return; 1165 } 1166 overZoomedFlag = tmp; 1167 } 1168 } else if (hasVisibleTiles) { 1169 displayZoomLevel = currentZoomLevel; 1170 } 1171 } else { 1172 displayZoomLevel = currentZoomLevel; 1173 if (zoomIncreaseAllowed() && ts.tooSmall()) { 1174 if (debug) { 1175 out("too zoomed in, (" + ts.tilesSpanned() 1176 + "), increasing zoom from " + currentZoomLevel); 1177 } 1178 // This is a hack. ts.tooSmall() is proabably a bad thing, and this works 1179 // around it. If we have a very small window, the tileSet may be well 1180 // less than 1 real tile wide, but that's expected. But, this sees the 1181 // tile set as too small and zooms in. The code below that checks for 1182 // pixel stretching disagrees and tries to zoom out. Both calls recurse, 1183 // hillarity ensues, and the stack overflows. 1184 // 1185 // This really needs to get fixed properly. We probably shouldn't even 1186 // have the tooSmall() check on tileSets. But, this also helps the zoom 1187 // converge to the correct place much faster. 1188 boolean tmp = az_disable; 1189 az_disable = true; 1190 increaseZoomLevel(); 1191 this.paint(g, mv, bounds); 1192 az_disable = tmp; 1193 return; 1194 } 1195 } 1196 } else { 1197 displayZoomLevel = currentZoomLevel; 1072 if (autoZoom) { 1073 double pixelScaling = getScaleFactor(zoom); 1074 if (pixelScaling > 3 || pixelScaling < 0.45) { 1075 zoom = getBestZoom(); 1076 } 1077 } 1078 1079 DeepTileSet dts = new DeepTileSet(topLeft, botRight, getMinZoomLvl(), getMaxZoomLvl()); 1080 TileSet ts = dts.getTileSet(zoom); 1081 1082 int displayZoomLevel = zoom; 1083 1084 boolean noTilesAtZoom = false; 1085 if (autoZoom && autoLoad) { 1086 // Auto-detection of tilesource maxzoom (currently fully works only for Bing) 1087 TileSetInfo tsi = dts.getTileSetInfo(zoom); 1088 if (!tsi.hasVisibleTiles && (!tsi.hasLoadingTiles || tsi.hasOverzoomedTiles)) { 1089 noTilesAtZoom = true; 1090 } 1091 if (!tsi.hasVisibleTiles && tsi.hasOverzoomedTiles) { 1092 while (displayZoomLevel > dts.minZoom && !tsi.hasVisibleTiles && tsi.hasOverzoomedTiles){ 1093 displayZoomLevel--; 1094 tsi = dts.getTileSetInfo(displayZoomLevel); 1095 } 1096 if (zoom > displayZoomLevel && !dts.getTileSetInfo(displayZoomLevel+1).hasLoadingTiles) { 1097 zoom = displayZoomLevel+1; 1098 } else { 1099 zoom = displayZoomLevel; 1100 } 1101 while (displayZoomLevel >= dts.minZoom && !tsi.hasVisibleTiles){ 1102 displayZoomLevel--; 1103 tsi = dts.getTileSetInfo(displayZoomLevel); 1104 } 1105 if (displayZoomLevel < dts.minZoom) { 1106 displayZoomLevel = 0; 1107 } 1108 tsi = dts.getTileSetInfo(zoom); 1109 } 1110 setZoomLevel(zoom); 1111 // When we have overzoomed tiles and all tiles at current zoomlevel is loaded, 1112 // load tiles at previovus zoomlevels until we have all tiles on screen is loaded. 1113 while (zoom > dts.minZoom && tsi.hasOverzoomedTiles && !tsi.hasLoadingTiles) { 1114 zoom--; 1115 tsi = dts.getTileSetInfo(zoom); 1116 } 1117 ts = dts.getTileSet(zoom); 1118 } else if (autoZoom) { 1119 setZoomLevel(zoom); 1198 1120 } 1199 1121 … … 1205 1127 1206 1128 if (displayZoomLevel != zoom) { 1207 ts = new TileSet(topLeft, botRight,displayZoomLevel);1129 ts = dts.getTileSet(displayZoomLevel); 1208 1130 } 1209 1131 … … 1213 1135 int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5}; 1214 1136 for (int zoomOffset : otherZooms) { 1215 if (!autoZoom Enabled()) {1137 if (!autoZoom) { 1216 1138 break; 1217 1139 } … … 1225 1147 List<Tile> newlyMissedTiles = new LinkedList<Tile>(); 1226 1148 for (Tile missed : missedTiles) { 1149 if ("no-tile".equals(missed.getValue("tile-info")) && zoomOffset > 0) { 1150 // Don't try to paint from higher zoom levels when tile is overzoomed 1151 newlyMissedTiles.add(missed); 1152 continue; 1153 } 1227 1154 Tile t2 = tempCornerTile(missed); 1228 1155 LatLon topLeft2 = tileLatLon(missed); … … 1289 1216 } 1290 1217 1291 if (autoZoomEnabled() && lastImageScale != null) {1292 // If each source image pixel is being stretched into > 31293 // drawn pixels, zoom in... getting too pixelated1294 if (lastImageScale > 3 && zoomIncreaseAllowed()) {1295 if (debug) {1296 out("autozoom increase: scale: " + lastImageScale);1297 }1298 increaseZoomLevel();1299 this.paint(g, mv, bounds);1300 // If each source image pixel is being squished into > 0.321301 // of a drawn pixels, zoom out.1302 } else if ((lastImageScale < 0.45) && (lastImageScale > 0) && zoomDecreaseAllowed()) {1303 if (debug) {1304 out("autozoom decrease: scale: " + lastImageScale);1305 }1306 decreaseZoomLevel();1307 this.paint(g, mv, bounds);1308 }1309 }1310 1218 //g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120); 1311 1219 g.setColor(Color.lightGray); … … 1319 1227 } 1320 1228 } 1321 if ( isOverZoomed()) {1229 if (noTilesAtZoom) { 1322 1230 myDrawString(g, tr("No tiles at this zoom level"), 120, 120); 1323 1231 } … … 1325 1233 myDrawString(g, tr("Current zoom: {0}", currentZoomLevel), 50, 140); 1326 1234 myDrawString(g, tr("Display zoom: {0}", displayZoomLevel), 50, 155); 1235 myDrawString(g, tr("Pixel scale: {0}", getScaleFactor(currentZoomLevel)), 50, 170); 1236 myDrawString(g, tr("Best zoom: {0}", Math.log(getScaleFactor(1))/Math.log(2)/2+1), 50, 185); 1327 1237 } 1328 1238 }// end of paint method … … 1363 1273 return null; 1364 1274 System.out.println("clicked on tile: " + clickedTile.getXtile() + " " + clickedTile.getYtile() + 1365 " scale: " + lastImageScale + "currentZoomLevel: " + currentZoomLevel);1275 " currentZoomLevel: " + currentZoomLevel); 1366 1276 return clickedTile; 1367 1277 } … … 1394 1304 } 1395 1305 1396 private intlatToTileY(double lat, int zoom) {1306 private static double latToTileY(double lat, int zoom) { 1397 1307 double l = lat / 180 * Math.PI; 1398 1308 double pf = Math.log(Math.tan(l) + (1 / Math.cos(l))); 1399 return (int) (Math.pow(2.0, zoom - 1) * (Math.PI - pf) / Math.PI);1400 } 1401 1402 private intlonToTileX(double lon, int zoom) {1403 return (int) (Math.pow(2.0, zoom - 3) * (lon + 180.0) / 45.0);1404 } 1405 1406 private double tileYToLat(int y, int zoom) {1309 return Math.pow(2.0, zoom - 1) * (Math.PI - pf) / Math.PI; 1310 } 1311 1312 private static double lonToTileX(double lon, int zoom) { 1313 return Math.pow(2.0, zoom - 3) * (lon + 180.0) / 45.0; 1314 } 1315 1316 private static double tileYToLat(int y, int zoom) { 1407 1317 return Math.atan(Math.sinh(Math.PI 1408 1318 - (Math.PI * y / Math.pow(2.0, zoom - 1)))) … … 1410 1320 } 1411 1321 1412 private double tileXToLon(int x, int zoom) {1322 private static double tileXToLon(int x, int zoom) { 1413 1323 return x * 45.0 / Math.pow(2.0, zoom - 3) - 180.0; 1414 1324 }
Note:
See TracChangeset
for help on using the changeset viewer.