Ticket #13210: patch-mapview-imagery-tile-source-coordinates.patch
File patch-mapview-imagery-tile-source-coordinates.patch, 32.0 KB (added by , 8 years ago) |
---|
-
src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoaderListener.java
diff --git a/src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoaderListener.java b/src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoaderListener.java index e4ab5c8..f698513 100644
a b package org.openstreetmap.gui.jmapviewer.interfaces; 3 3 4 4 import org.openstreetmap.gui.jmapviewer.Tile; 5 5 6 /** 7 * This listener listens to successful tile loads. 8 */ 9 @FunctionalInterface 6 10 public interface TileLoaderListener { 7 11 8 12 /** -
src/org/openstreetmap/josm/data/imagery/CachedTileLoaderFactory.java
diff --git a/src/org/openstreetmap/josm/data/imagery/CachedTileLoaderFactory.java b/src/org/openstreetmap/josm/data/imagery/CachedTileLoaderFactory.java index 2a286b3..d648aa7 100644
a b public class CachedTileLoaderFactory implements TileLoaderFactory { 59 59 } 60 60 61 61 @Override 62 public TileLoader makeTileLoader(TileLoaderListener listener) {63 return makeTileLoader(listener, null);64 }65 66 @Override67 62 public TileLoader makeTileLoader(TileLoaderListener listener, Map<String, String> inputHeaders) { 68 63 Map<String, String> headers = new ConcurrentHashMap<>(); 69 64 headers.put("User-Agent", Version.getInstance().getFullAgentString()); -
src/org/openstreetmap/josm/data/imagery/TileLoaderFactory.java
diff --git a/src/org/openstreetmap/josm/data/imagery/TileLoaderFactory.java b/src/org/openstreetmap/josm/data/imagery/TileLoaderFactory.java index 7818d65..0d3cf0d 100644
a b import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener; 12 12 * @author Wiktor Niesiobędzki 13 13 * @since 8526 14 14 */ 15 @FunctionalInterface 15 16 public interface TileLoaderFactory { 16 17 17 18 /** 18 19 * @param listener that will be notified, when tile has finished loading 19 20 * @return TileLoader that notifies specified listener 21 * @deprecated Use {@link #makeTileLoader(TileLoaderListener, Map)} 20 22 */ 21 TileLoader makeTileLoader(TileLoaderListener listener); 23 @Deprecated 24 default TileLoader makeTileLoader(TileLoaderListener listener) { 25 return makeTileLoader(listener, null); 26 } 22 27 23 28 /** 24 29 * @param listener that will be notified, when tile has finished loading 25 * @param headers that will be sent with requests to TileSource 30 * @param headers that will be sent with requests to TileSource. <code>null</code> indicates none 26 31 * @return TileLoader that uses both of above 27 32 */ 28 33 TileLoader makeTileLoader(TileLoaderListener listener, Map<String, String> headers); -
src/org/openstreetmap/josm/gui/MapViewState.java
diff --git a/src/org/openstreetmap/josm/gui/MapViewState.java b/src/org/openstreetmap/josm/gui/MapViewState.java index 30c423e..043d60e 100644
a b import java.awt.Rectangle; 7 7 import java.awt.geom.AffineTransform; 8 8 import java.awt.geom.Point2D; 9 9 import java.awt.geom.Point2D.Double; 10 import java.awt.geom.Rectangle2D; 10 11 11 12 import javax.swing.JComponent; 12 13 … … public final class MapViewState { 143 144 } 144 145 145 146 /** 147 * Gets the {@link MapViewPoint} for the given {@link LatLon} coordinate. 148 * @param latlon the position 149 * @return The point for that position. 150 */ 151 public MapViewPoint getPointFor(LatLon latlon) { 152 return getPointFor(getProjection().latlon2eastNorth(latlon)); 153 } 154 155 /** 146 156 * Gets a rectangle representing the whole view area. 147 157 * @return The rectangle. 148 158 */ … … public final class MapViewState { 350 360 public LatLon getLatLon() { 351 361 return projection.eastNorth2latlon(getEastNorth()); 352 362 } 363 364 /** 365 * Add the given offset to this point 366 * @param en The offset in east/north space. 367 * @return The new point 368 */ 369 public MapViewPoint add(EastNorth en) { 370 return new MapViewEastNorthPoint(getEastNorth().add(en)); 371 } 353 372 } 354 373 355 374 private class MapViewViewPoint extends MapViewPoint { … … public final class MapViewState { 454 473 public Bounds getLatLonBoundsBox() { 455 474 return projection.getLatLonBoundsBox(getProjectionBounds()); 456 475 } 476 477 /** 478 * Gets this rectangle on the screen. 479 * @return The rectangle. 480 */ 481 public Rectangle2D getInView() { 482 double x1 = p1.getInViewX(); 483 double y1 = p1.getInViewY(); 484 double x2 = p2.getInViewX(); 485 double y2 = p2.getInViewY(); 486 return new Rectangle2D.Double(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2)); 487 } 457 488 } 458 489 459 490 } -
src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java index c905f44..5d154d1 100644
a b import java.awt.Graphics2D; 12 12 import java.awt.GridBagLayout; 13 13 import java.awt.Image; 14 14 import java.awt.Point; 15 import java.awt.Rectangle;16 15 import java.awt.Toolkit; 17 16 import java.awt.event.ActionEvent; 18 17 import java.awt.event.MouseAdapter; 19 18 import java.awt.event.MouseEvent; 19 import java.awt.geom.Point2D; 20 import java.awt.geom.Rectangle2D; 20 21 import java.awt.image.BufferedImage; 21 22 import java.awt.image.ImageObserver; 22 23 import java.io.File; … … import java.util.Map.Entry; 36 37 import java.util.Set; 37 38 import java.util.concurrent.ConcurrentSkipListSet; 38 39 import java.util.concurrent.atomic.AtomicInteger; 40 import java.util.stream.Stream; 39 41 40 42 import javax.swing.AbstractAction; 41 43 import javax.swing.Action; … … import org.openstreetmap.josm.actions.ImageryAdjustAction; 67 69 import org.openstreetmap.josm.actions.RenameLayerAction; 68 70 import org.openstreetmap.josm.actions.SaveActionBase; 69 71 import org.openstreetmap.josm.data.Bounds; 72 import org.openstreetmap.josm.data.ProjectionBounds; 70 73 import org.openstreetmap.josm.data.coor.EastNorth; 71 74 import org.openstreetmap.josm.data.coor.LatLon; 72 75 import org.openstreetmap.josm.data.imagery.ImageryInfo; … … import org.openstreetmap.josm.data.preferences.IntegerProperty; 77 80 import org.openstreetmap.josm.gui.ExtendedDialog; 78 81 import org.openstreetmap.josm.gui.MapFrame; 79 82 import org.openstreetmap.josm.gui.MapView; 83 import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle; 80 84 import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener; 81 85 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 82 86 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 83 87 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 84 88 import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings.FilterChangeListener; 89 import org.openstreetmap.josm.gui.layer.imagery.TileCoordinateConverter; 85 90 import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings; 86 91 import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings.DisplaySettingsChangeEvent; 87 92 import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings.DisplaySettingsChangeListener; … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 169 174 private final TileSourceDisplaySettings displaySettings = createDisplaySettings(); 170 175 171 176 private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this); 177 // prepared to be moved to the painter 178 private TileCoordinateConverter coordinateConverter; 172 179 173 180 /** 174 181 * Creates Tile Source based Imagery Layer based on Imagery Info … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 223 230 } 224 231 225 232 protected void initTileSource(T tileSource) { 233 coordinateConverter = new TileCoordinateConverter(Main.map.mapView, getDisplaySettings()); 226 234 attribution.initialize(tileSource); 227 235 228 236 currentZoomLevel = getBestZoom(); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 420 428 if (clickedTile != null) { 421 429 ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Tile Info"), new String[]{tr("OK")}); 422 430 JPanel panel = new JPanel(new GridBagLayout()); 423 Rectangle displaySize = tileToRect(clickedTile);431 Rectangle2D displaySize = coordinateConverter.getRectangleForTile(clickedTile); 424 432 String url = ""; 425 433 try { 426 434 url = clickedTile.getUrl(); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 433 441 {"Tile name", clickedTile.getKey()}, 434 442 {"Tile url", url}, 435 443 {"Tile size", getSizeString(clickedTile.getTileSource().getTileSize()) }, 436 {"Tile display size", new StringBuilder().append(displaySize. width).append('x').append(displaySize.height).toString()},444 {"Tile display size", new StringBuilder().append(displaySize.getWidth()).append('x').append(displaySize.getHeight()).toString()}, 437 445 }; 438 446 439 447 for (String[] entry: content) { … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 999 1007 1000 1008 private TileSet getVisibleTileSet() { 1001 1009 MapView mv = Main.map.mapView; 1002 EastNorth topLeft = mv.getEastNorth(0, 0);1003 EastNorth botRight = mv.getEastNorth(mv.getWidth(), mv.getHeight());1004 return new MapWrappingTileSet(topLeft, botRight, currentZoomLevel);1010 MapViewRectangle area = mv.getState().getViewArea(); 1011 ProjectionBounds bounds = area.getProjectionBounds(); 1012 return getTileSet(bounds.getMin(), bounds.getMax(), currentZoomLevel); 1005 1013 } 1006 1014 1007 1015 protected void loadAllTiles(boolean force) { … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1056 1064 return img; 1057 1065 } 1058 1066 1059 private Rectangle tileToRect(Tile t1) {1060 /*1061 * We need to get a box in which to draw, so advance by one tile in1062 * each direction to find the other corner of the box.1063 * Note: this somewhat pollutes the tile cache1064 */1065 Tile t2 = tempCornerTile(t1);1066 Rectangle rect = new Rectangle(pixelPos(t1));1067 rect.add(pixelPos(t2));1068 return rect;1069 }1070 1071 1067 // 'source' is the pixel coordinates for the area that 1072 1068 // the img is capable of filling in. However, we probably 1073 1069 // only want a portion of it. 1074 1070 // 1075 1071 // 'border' is the screen cordinates that need to be drawn. 1076 1072 // We must not draw outside of it. 1077 private void drawImageInside(Graphics g, Image sourceImg, Rectangle source, Rectangleborder) {1078 Rectangle target = source;1073 private void drawImageInside(Graphics g, Image sourceImg, Rectangle2D source, Rectangle2D border) { 1074 Rectangle2D target = source; 1079 1075 1080 1076 // If a border is specified, only draw the intersection 1081 1077 // if what we have combined with what we are supposed to draw. 1082 1078 if (border != null) { 1083 target = source. intersection(border);1079 target = source.createIntersection(border); 1084 1080 if (Main.isDebugEnabled()) { 1085 1081 Main.debug("source: " + source + "\nborder: " + border + "\nintersection: " + target); 1086 1082 } … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1097 1093 double imageXScaling = sourceImg.getWidth(this) / source.getWidth(); 1098 1094 1099 1095 // How many pixels into the 'source' rectangle are we drawing? 1100 int screenXoffset = target.x - source.x;1101 int screenYoffset = target.y - source.y;1096 double screenXoffset = target.getX() - source.getX(); 1097 double screenYoffset = target.getY() - source.getY(); 1102 1098 // And how many pixels into the image itself does that correlate to? 1103 1099 int imgXoffset = (int) (screenXoffset * imageXScaling + 0.5); 1104 1100 int imgYoffset = (int) (screenYoffset * imageYScaling + 0.5); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1111 1107 Main.debug("drawing image into target rect: " + target); 1112 1108 } 1113 1109 g.drawImage(sourceImg, 1114 target.x, target.y,1115 target.x + target.width, target.y + target.height,1110 (int) target.getX(), (int) target.getY(), 1111 (int) target.getMaxX(), (int) target.getMaxY(), 1116 1112 imgXoffset, imgYoffset, 1117 1113 imgXend, imgYend, 1118 1114 this); 1119 1115 if (PROP_FADE_AMOUNT.get() != 0) { 1120 1116 // dimm by painting opaque rect... 1121 1117 g.setColor(getFadeColorWithAlpha()); 1122 g.fillRect(target.x, target.y, 1123 target.width, target.height); 1118 ((Graphics2D)g).fill(target); 1124 1119 } 1125 1120 } 1126 1121 … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1135 1130 // border is null and we draw the entire tile set. 1136 1131 private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) { 1137 1132 if (zoom <= 0) return Collections.emptyList(); 1138 Rectangle borderRect = null;1133 Rectangle2D borderRect = null; 1139 1134 if (border != null) { 1140 borderRect = tileToRect(border);1135 borderRect = coordinateConverter.getRectangleForTile(border); 1141 1136 } 1142 1137 List<Tile> missedTiles = new LinkedList<>(); 1143 1138 // The callers of this code *require* that we return any tiles … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1157 1152 // applying all filters to this layer 1158 1153 img = applyImageProcessors((BufferedImage) img); 1159 1154 1160 Rectangle sourceRect = tileToRect(tile);1155 Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile); 1161 1156 if (borderRect != null && !sourceRect.intersects(borderRect)) { 1162 1157 continue; 1163 1158 } … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1194 1189 } 1195 1190 1196 1191 private void paintTileText(TileSet ts, Tile tile, Graphics g, MapView mv, int zoom, Tile t) { 1197 int fontHeight = g.getFontMetrics().getHeight(); 1198 if (tile == null) 1192 if (tile == null) { 1199 1193 return; 1200 Point p = pixelPos(t); 1201 int texty = p.y + 2 + fontHeight; 1194 } 1195 Point2D p = coordinateConverter.getPixelForTile(t); 1196 int fontHeight = g.getFontMetrics().getHeight(); 1197 int x = (int) p.getX(); 1198 int y = (int) p.getY(); 1199 int texty = y + 2 + fontHeight; 1202 1200 1203 1201 /*if (PROP_DRAW_DEBUG.get()) { 1204 1202 myDrawString(g, "x=" + t.getXtile() + " y=" + t.getYtile() + " z=" + zoom + "", p.x + 2, texty); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1216 1214 }*/ 1217 1215 1218 1216 if (tile.hasError() && getDisplaySettings().isShowErrors()) { 1219 myDrawString(g, tr("Error") + ": " + tr(tile.getErrorMessage()), p.x + 2, texty);1217 myDrawString(g, tr("Error") + ": " + tr(tile.getErrorMessage()), x + 2, texty); 1220 1218 //texty += 1 + fontHeight; 1221 1219 } 1222 1220 … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1225 1223 if (Main.isDebugEnabled()) { 1226 1224 if (yCursor < t.getYtile()) { 1227 1225 if (t.getYtile() % 32 == 31) { 1228 g.fillRect(0, p.y - 1, mv.getWidth(), 3);1226 g.fillRect(0, y - 1, mv.getWidth(), 3); 1229 1227 } else { 1230 g.drawLine(0, p.y, mv.getWidth(), p.y);1228 g.drawLine(0, y, mv.getWidth(), y); 1231 1229 } 1232 1230 //yCursor = t.getYtile(); 1233 1231 } … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1235 1233 if (xCursor < t.getXtile()) { 1236 1234 if (t.getXtile() % 32 == 0) { 1237 1235 // level 7 tile boundary 1238 g.fillRect( p.x - 1, 0, 3, mv.getHeight());1236 g.fillRect(x - 1, 0, 3, mv.getHeight()); 1239 1237 } else { 1240 g.drawLine( p.x, 0, p.x, mv.getHeight());1238 g.drawLine(x, 0, x, mv.getHeight()); 1241 1239 } 1242 1240 //xCursor = t.getXtile(); 1243 1241 } 1244 1242 } 1245 1243 } 1246 1244 1247 private Point pixelPos(LatLon ll) {1248 return Main.map.mapView.getPoint(Main.getProjection().latlon2eastNorth(ll).add(getDx(), getDy()));1249 }1250 1251 private Point pixelPos(Tile t) {1252 ICoordinate coord = tileSource.tileXYToLatLon(t);1253 return pixelPos(new LatLon(coord));1254 }1255 1256 1245 private LatLon getShiftedLatLon(EastNorth en) { 1257 1246 return Main.getProjection().eastNorth2latlon(en.add(-getDx(), -getDy())); 1258 1247 } … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1268 1257 1269 1258 private final TileSet nullTileSet = new TileSet(); 1270 1259 1271 private final class MapWrappingTileSet extends TileSet { 1272 private MapWrappingTileSet(EastNorth topLeft, EastNorth botRight, int zoom) { 1273 this(getShiftedLatLon(topLeft), getShiftedLatLon(botRight), zoom); 1274 } 1275 1276 private MapWrappingTileSet(LatLon topLeft, LatLon botRight, int zoom) { 1277 super(topLeft, botRight, zoom); 1278 double centerLon = getShiftedLatLon(Main.map.mapView.getCenter()).lon(); 1260 /** 1261 * This is a rectangular range of tiles. 1262 */ 1263 private static class TileRange { 1264 int minX, maxX, minY, maxY; 1265 int zoom; 1279 1266 1280 if (topLeft.lon() > centerLon) { 1281 x0 = tileSource.getTileXMin(zoom); 1282 } 1283 if (botRight.lon() < centerLon) { 1284 x1 = tileSource.getTileXMax(zoom); 1285 } 1286 sanitize(); 1267 private TileRange() { 1287 1268 } 1288 }1289 1269 1290 private class TileSet { 1291 int x0, x1, y0, y1; 1292 int zoom; 1270 protected TileRange(TileXY t1, TileXY t2, int zoom) { 1271 minX = (int) Math.floor(Math.min(t1.getX(), t2.getX())); 1272 minY = (int) Math.floor(Math.min(t1.getY(), t2.getY())); 1273 maxX = (int) Math.ceil(Math.max(t1.getX(), t2.getX())); 1274 maxY = (int) Math.ceil(Math.max(t1.getY(), t2.getY())); 1275 this.zoom = zoom; 1276 } 1293 1277 1294 /** 1295 * Create a TileSet by EastNorth bbox taking a layer shift in account 1296 * @param topLeft top-left lat/lon 1297 * @param botRight bottom-right lat/lon 1298 * @param zoom zoom level 1299 */ 1300 protected TileSet(EastNorth topLeft, EastNorth botRight, int zoom) { 1301 this(getShiftedLatLon(topLeft), getShiftedLatLon(botRight), zoom); 1278 protected double tilesSpanned() { 1279 return Math.sqrt(1.0 * this.size()); 1302 1280 } 1303 1281 1304 /** 1305 * Create a TileSet by known LatLon bbox without layer shift correction 1306 * @param topLeft top-left lat/lon 1307 * @param botRight bottom-right lat/lon 1308 * @param zoom zoom level 1309 */ 1310 protected TileSet(LatLon topLeft, LatLon botRight, int zoom) { 1311 this.zoom = zoom; 1312 if (zoom == 0) 1313 return; 1282 protected int size() { 1283 int xSpan = maxX - minX + 1; 1284 int ySpan = maxY - minY + 1; 1285 return xSpan * ySpan; 1286 } 1287 } 1314 1288 1315 TileXY t1 = tileSource.latLonToTileXY(topLeft.toCoordinate(), zoom); 1316 TileXY t2 = tileSource.latLonToTileXY(botRight.toCoordinate(), zoom); 1289 private class TileSet extends TileRange { 1317 1290 1318 x0 = (int) Math.floor(t1.getX()); 1319 y0 = (int) Math.floor(t1.getY()); 1320 x1 = (int) Math.ceil(t2.getX()); 1321 y1 = (int) Math.ceil(t2.getY()); 1291 protected TileSet(TileXY t1, TileXY t2, int zoom) { 1292 super(t1, t2, zoom); 1322 1293 sanitize(); 1323 1324 1294 } 1325 1295 1326 1296 /** … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1331 1301 } 1332 1302 1333 1303 protected void sanitize() { 1334 if (x0 > x1) { 1335 int tmp = x0; 1336 x0 = x1; 1337 x1 = tmp; 1338 } 1339 if (y0 > y1) { 1340 int tmp = y0; 1341 y0 = y1; 1342 y1 = tmp; 1304 if (minX < tileSource.getTileXMin(zoom)) { 1305 minX = tileSource.getTileXMin(zoom); 1343 1306 } 1344 1345 if (x0 < tileSource.getTileXMin(zoom)) { 1346 x0 = tileSource.getTileXMin(zoom); 1307 if (minY < tileSource.getTileYMin(zoom)) { 1308 minY = tileSource.getTileYMin(zoom); 1347 1309 } 1348 if ( y0 < tileSource.getTileYMin(zoom)) {1349 y0 = tileSource.getTileYMin(zoom);1310 if (maxX > tileSource.getTileXMax(zoom)) { 1311 maxX = tileSource.getTileXMax(zoom); 1350 1312 } 1351 if (x1 > tileSource.getTileXMax(zoom)) { 1352 x1 = tileSource.getTileXMax(zoom); 1353 } 1354 if (y1 > tileSource.getTileYMax(zoom)) { 1355 y1 = tileSource.getTileYMax(zoom); 1313 if (maxY > tileSource.getTileYMax(zoom)) { 1314 maxY = tileSource.getTileYMax(zoom); 1356 1315 } 1357 1316 } 1358 1317 … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1368 1327 return tileCache == null || size() > tileCache.getCacheSize(); 1369 1328 } 1370 1329 1371 private double tilesSpanned() {1372 return Math.sqrt(1.0 * this.size());1373 }1374 1375 private int size() {1376 int xSpan = x1 - x0 + 1;1377 int ySpan = y1 - y0 + 1;1378 return xSpan * ySpan;1379 }1380 1381 1330 /* 1382 1331 * Get all tiles represented by this TileSet that are 1383 1332 * already in the tileCache. … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1395 1344 if (zoom == 0 || this.insane()) 1396 1345 return Collections.emptyList(); 1397 1346 List<Tile> ret = new ArrayList<>(); 1398 for (int x = x0; x <= x1; x++) {1399 for (int y = y0; y <= y1; y++) {1347 for (int x = minX; x <= maxX; x++) { 1348 for (int y = minY; y <= maxY; y++) { 1400 1349 Tile t; 1401 1350 if (create) { 1402 1351 t = getOrCreateTile(x, y, zoom); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1424 1373 * @return comparator, that sorts the tiles from the center to the edge of the current screen 1425 1374 */ 1426 1375 private Comparator<Tile> getTileDistanceComparator() { 1427 final int centerX = (int) Math.ceil(( x0 + x1) / 2d);1428 final int centerY = (int) Math.ceil(( y0 + y1) / 2d);1376 final int centerX = (int) Math.ceil((minX + maxX) / 2d); 1377 final int centerY = (int) Math.ceil((minY + maxY) / 2d); 1429 1378 return new Comparator<Tile>() { 1430 1379 private int getDistance(Tile t) { 1431 1380 return Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1462 1411 1463 1412 @Override 1464 1413 public String toString() { 1465 return getClass().getName() + ": zoom: " + zoom + " X(" + x0 + ", " + x1 + ") Y(" + y0 + ", " + y1+ ") size: " + size();1414 return getClass().getName() + ": zoom: " + zoom + " X(" + minX + ", " + maxX + ") Y(" + minY + ", " + maxY + ") size: " + size(); 1466 1415 } 1467 1416 } 1468 1417 1418 /** 1419 * Create a TileSet by EastNorth bbox taking a layer shift in account 1420 * @param topLeft top-left lat/lon 1421 * @param botRight bottom-right lat/lon 1422 * @param zoom zoom level 1423 * @return the tile set 1424 */ 1425 protected TileSet getTileSet(EastNorth topLeft, EastNorth botRight, int zoom) { 1426 return getTileSet(getShiftedLatLon(topLeft), getShiftedLatLon(botRight), zoom); 1427 } 1428 1429 /** 1430 * Create a TileSet by known LatLon bbox without layer shift correction 1431 * @param topLeft top-left lat/lon 1432 * @param botRight bottom-right lat/lon 1433 * @param zoom zoom level 1434 * @return the tile set 1435 */ 1436 protected TileSet getTileSet(LatLon topLeft, LatLon botRight, int zoom) { 1437 if (zoom == 0) 1438 return new TileSet(); 1439 1440 TileXY t1 = tileSource.latLonToTileXY(topLeft.toCoordinate(), zoom); 1441 TileXY t2 = tileSource.latLonToTileXY(botRight.toCoordinate(), zoom); 1442 return new TileSet(t1, t2, zoom); 1443 } 1444 1469 1445 private static class TileSetInfo { 1470 1446 public boolean hasVisibleTiles; 1471 1447 public boolean hasOverzoomedTiles; … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1493 1469 } 1494 1470 1495 1471 private class DeepTileSet { 1496 private final EastNorth topLeft, botRight;1472 private final ProjectionBounds bounds; 1497 1473 private final int minZoom, maxZoom; 1498 1474 private final TileSet[] tileSets; 1499 1475 private final TileSetInfo[] tileSetInfos; 1500 1476 1501 1477 @SuppressWarnings("unchecked") 1502 DeepTileSet(EastNorth topLeft, EastNorth botRight, int minZoom, int maxZoom) { 1503 this.topLeft = topLeft; 1504 this.botRight = botRight; 1478 DeepTileSet(ProjectionBounds bounds, int minZoom, int maxZoom) { 1479 this.bounds = bounds; 1505 1480 this.minZoom = minZoom; 1506 1481 this.maxZoom = maxZoom; 1507 1482 this.tileSets = new AbstractTileSourceLayer.TileSet[maxZoom - minZoom + 1]; … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1514 1489 synchronized (tileSets) { 1515 1490 TileSet ts = tileSets[zoom-minZoom]; 1516 1491 if (ts == null) { 1517 ts = new MapWrappingTileSet(topLeft, botRight, zoom);1492 ts = AbstractTileSourceLayer.this.getTileSet(bounds.getMin(), bounds.getMax(), zoom); 1518 1493 tileSets[zoom-minZoom] = ts; 1519 1494 } 1520 1495 return ts; … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1537 1512 1538 1513 @Override 1539 1514 public void paint(Graphics2D g, MapView mv, Bounds bounds) { 1540 EastNorth topLeft = mv.getEastNorth(0, 0); 1541 EastNorth botRight = mv.getEastNorth(mv.getWidth(), mv.getHeight()); 1542 1543 if (botRight.east() == 0 || botRight.north() == 0) { 1544 /*Main.debug("still initializing??");*/ 1545 // probably still initializing 1546 return; 1547 } 1515 ProjectionBounds pb = mv.getState().getViewArea().getProjectionBounds(); 1548 1516 1549 1517 needRedraw = false; 1550 1518 … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1553 1521 zoom = getBestZoom(); 1554 1522 } 1555 1523 1556 DeepTileSet dts = new DeepTileSet( topLeft, botRight, getMinZoomLvl(), zoom);1524 DeepTileSet dts = new DeepTileSet(pb, getMinZoomLvl(), zoom); 1557 1525 TileSet ts = dts.getTileSet(zoom); 1558 1526 1559 1527 int displayZoomLevel = zoom; … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1632 1600 continue; 1633 1601 } 1634 1602 Tile t2 = tempCornerTile(missed); 1635 TileSet ts2 = newTileSet(1603 TileSet ts2 = getTileSet( 1636 1604 getShiftedLatLon(tileSource.tileXYToLatLon(missed)), 1637 1605 getShiftedLatLon(tileSource.tileXYToLatLon(t2)), 1638 1606 newzoom); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1660 1628 this.paintTileText(ts, t, g, mv, displayZoomLevel, t); 1661 1629 } 1662 1630 1663 attribution.paintAttribution(g, mv.getWidth(), mv.getHeight(), getShiftedCoord(topLeft), getShiftedCoord(botRight), 1631 EastNorth min = pb.getMin(); 1632 EastNorth max = pb.getMax(); 1633 attribution.paintAttribution(g, mv.getWidth(), mv.getHeight(), getShiftedCoord(min), getShiftedCoord(max), 1664 1634 displayZoomLevel, this); 1665 1635 1666 1636 //g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120); … … implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 1710 1680 EastNorth topLeft = mv.getEastNorth(0, 0); 1711 1681 EastNorth botRight = mv.getEastNorth(mv.getWidth(), mv.getHeight()); 1712 1682 int z = currentZoomLevel; 1713 TileSet ts = newTileSet(topLeft, botRight, z);1683 TileSet ts = getTileSet(topLeft, botRight, z); 1714 1684 1715 1685 if (!ts.tooLarge()) { 1716 1686 ts.loadAllTiles(false); // make sure there are tile objects for all tiles 1717 1687 } 1718 Tile clickedTile = null; 1719 for (Tile t1 : ts.allExistingTiles()) { 1720 Tile t2 = tempCornerTile(t1); 1721 Rectangle r = new Rectangle(pixelPos(t1)); 1722 r.add(pixelPos(t2)); 1723 if (Main.isDebugEnabled()) { 1724 Main.debug("r: " + r + " clicked: " + clicked); 1725 } 1726 if (!r.contains(clicked)) { 1727 continue; 1728 } 1729 clickedTile = t1; 1730 break; 1731 } 1732 if (clickedTile == null) 1733 return null; 1688 Stream<Tile> clickedTiles = ts.allExistingTiles().stream() 1689 .filter(t -> coordinateConverter.getRectangleForTile(t).contains(clicked)); 1734 1690 if (Main.isTraceEnabled()) { 1735 Main.trace("Clicked on tile: " + clickedTile.getXtile() + ' ' + clickedTile.getYtile() +1736 " currentZoomLevel: " + currentZoomLevel);1691 clickedTiles = clickedTiles.peek(t -> Main.trace("Clicked on tile: " + t.getXtile() + ' ' + t.getYtile() + 1692 " currentZoomLevel: " + currentZoomLevel)); 1737 1693 } 1738 return clickedTile ;1694 return clickedTiles.findAny().orElse(null); 1739 1695 } 1740 1696 1741 1697 @Override -
new file src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java
diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java b/src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java new file mode 100644 index 0000000..f6993fe
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.layer.imagery; 3 4 import java.awt.geom.Point2D; 5 import java.awt.geom.Rectangle2D; 6 7 import org.openstreetmap.gui.jmapviewer.Tile; 8 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 9 import org.openstreetmap.josm.data.coor.LatLon; 10 import org.openstreetmap.josm.gui.MapView; 11 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint; 12 13 /** 14 * This class handles tile coordinate management and computes their position in the map view. 15 * @author Michael Zangl 16 * @since xxx 17 */ 18 public class TileCoordinateConverter { 19 private MapView mapView; 20 private TileSourceDisplaySettings settings; 21 22 /** 23 * Create a new coordinate converter for the map view. 24 * @param mapView The map view. 25 * @param settings displacement settings. 26 */ 27 public TileCoordinateConverter(MapView mapView, TileSourceDisplaySettings settings) { 28 this.mapView = mapView; 29 this.settings = settings; 30 } 31 32 private MapViewPoint pos(ICoordinate ll) { 33 return mapView.getState().getPointFor(new LatLon(ll)).add(settings.getDisplacement()); 34 } 35 36 /** 37 * Gets the top left position of the tile inside the map view. 38 * @param tile The tile 39 * @return The positon. 40 */ 41 public Point2D getPixelForTile(Tile tile) { 42 ICoordinate coord = tile.getTileSource().tileXYToLatLon(tile); 43 return pos(coord).getInView(); 44 } 45 46 /** 47 * Gets the position of the tile inside the map view. 48 * @param tile The tile 49 * @return The positon. 50 */ 51 public Rectangle2D getRectangleForTile(Tile tile) { 52 ICoordinate c1 = tile.getTileSource().tileXYToLatLon(tile); 53 ICoordinate c2 = tile.getTileSource().tileXYToLatLon(tile.getXtile() + 1, tile.getYtile() + 1, tile.getZoom()); 54 55 return pos(c1).rectTo(pos(c2)).getInView(); 56 } 57 }