diff --git a/src/org/openstreetmap/gui/jmapviewer/Tile.java b/src/org/openstreetmap/gui/jmapviewer/Tile.java
index ba59ee0..00685f9 100644
|
a
|
b
|
public class Tile {
|
| 228 | 228 | } |
| 229 | 229 | |
| 230 | 230 | /** |
| 231 | | * @return tile indexes as TileXY object |
| | 231 | * @return tile indexes of the top left corner as TileXY object |
| 232 | 232 | */ |
| 233 | 233 | public TileXY getTileXY() { |
| 234 | 234 | return new TileXY(xtile, ytile); |
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
index 8faa3fc..a885172 100644
|
a
|
b
|
import java.util.LinkedList;
|
| 34 | 34 | import java.util.List; |
| 35 | 35 | import java.util.Map; |
| 36 | 36 | import java.util.Map.Entry; |
| | 37 | import java.util.Objects; |
| 37 | 38 | import java.util.Set; |
| 38 | 39 | import java.util.concurrent.ConcurrentSkipListSet; |
| 39 | 40 | import java.util.concurrent.atomic.AtomicInteger; |
| | 41 | import java.util.function.Consumer; |
| | 42 | import java.util.function.Function; |
| | 43 | import java.util.stream.Collectors; |
| | 44 | import java.util.stream.IntStream; |
| 40 | 45 | import java.util.stream.Stream; |
| 41 | 46 | |
| 42 | 47 | import javax.swing.AbstractAction; |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 957 | 962 | return new Tile(tileSource, x, y, zoom); |
| 958 | 963 | } |
| 959 | 964 | |
| | 965 | private Tile getOrCreateTile(TilePosition tilePosition) { |
| | 966 | return getOrCreateTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom()); |
| | 967 | } |
| | 968 | |
| 960 | 969 | private Tile getOrCreateTile(int x, int y, int zoom) { |
| 961 | 970 | Tile tile = getTile(x, y, zoom); |
| 962 | 971 | if (tile == null) { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 970 | 979 | return tile; |
| 971 | 980 | } |
| 972 | 981 | |
| | 982 | private Tile getTile(TilePosition tilePosition) { |
| | 983 | return getTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom()); |
| | 984 | } |
| | 985 | |
| 973 | 986 | /** |
| 974 | 987 | * Returns tile at given position. |
| 975 | 988 | * This can and will return null for tiles that are not already in the cache. |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1126 | 1139 | } |
| 1127 | 1140 | } |
| 1128 | 1141 | |
| | 1142 | private List<Tile> paintTileImages(Graphics g, TileSet ts) { |
| | 1143 | Object paintMutex = new Object(); |
| | 1144 | List<TilePosition> missed = Collections.synchronizedList(new ArrayList<>()); |
| | 1145 | ts.visitTiles(tile -> { |
| | 1146 | Image img = getLoadedTileImage(tile); |
| | 1147 | if (img == null) { |
| | 1148 | missed.add(new TilePosition(tile)); |
| | 1149 | } |
| | 1150 | img = applyImageProcessors((BufferedImage) img); |
| | 1151 | Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile); |
| | 1152 | synchronized (paintMutex) { |
| | 1153 | //cannot paint in parallel |
| | 1154 | drawImageInside(g, img, sourceRect, null); |
| | 1155 | } |
| | 1156 | }, missed::add); |
| | 1157 | |
| | 1158 | return missed.stream().map(this::getOrCreateTile).collect(Collectors.toList()); |
| | 1159 | } |
| | 1160 | |
| 1129 | 1161 | // This function is called for several zoom levels, not just |
| 1130 | 1162 | // the current one. It should not trigger any tiles to be |
| 1131 | 1163 | // downloaded. It should also avoid polluting the tile cache |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1137 | 1169 | // border is null and we draw the entire tile set. |
| 1138 | 1170 | private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) { |
| 1139 | 1171 | if (zoom <= 0) return Collections.emptyList(); |
| 1140 | | Rectangle2D borderRect = null; |
| 1141 | | if (border != null) { |
| 1142 | | borderRect = coordinateConverter.getRectangleForTile(border); |
| 1143 | | } |
| | 1172 | Rectangle2D borderRect = coordinateConverter.getRectangleForTile(border); |
| 1144 | 1173 | List<Tile> missedTiles = new LinkedList<>(); |
| 1145 | 1174 | // The callers of this code *require* that we return any tiles |
| 1146 | 1175 | // that we do not draw in missedTiles. ts.allExistingTiles() by |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1294 | 1323 | int ySpan = maxY - minY + 1; |
| 1295 | 1324 | return xSpan * ySpan; |
| 1296 | 1325 | } |
| | 1326 | |
| | 1327 | /** |
| | 1328 | * Gets a stream of all tile positions in this set |
| | 1329 | * @return A stream of all positions |
| | 1330 | */ |
| | 1331 | public Stream<TilePosition> tilePositions() { |
| | 1332 | if (zoom == 0) { |
| | 1333 | return Stream.empty(); |
| | 1334 | } else { |
| | 1335 | return IntStream.rangeClosed(minX, maxX).mapToObj( |
| | 1336 | x -> IntStream.rangeClosed(minY, maxY).mapToObj(y -> new TilePosition(x, y, zoom)) |
| | 1337 | ).flatMap(Function.identity()); |
| | 1338 | } |
| | 1339 | } |
| | 1340 | } |
| | 1341 | |
| | 1342 | /** |
| | 1343 | * The position of a single tile. |
| | 1344 | * @author Michael Zangl |
| | 1345 | * @since xxx |
| | 1346 | */ |
| | 1347 | private static class TilePosition { |
| | 1348 | private final int x; |
| | 1349 | private final int y; |
| | 1350 | private final int zoom; |
| | 1351 | public TilePosition(int x, int y, int zoom) { |
| | 1352 | super(); |
| | 1353 | this.x = x; |
| | 1354 | this.y = y; |
| | 1355 | this.zoom = zoom; |
| | 1356 | } |
| | 1357 | |
| | 1358 | public TilePosition(Tile tile) { |
| | 1359 | this(tile.getXtile(), tile.getYtile(), tile.getZoom()); |
| | 1360 | } |
| | 1361 | |
| | 1362 | /** |
| | 1363 | * @return the x position |
| | 1364 | */ |
| | 1365 | public int getX() { |
| | 1366 | return x; |
| | 1367 | } |
| | 1368 | |
| | 1369 | /** |
| | 1370 | * @return the y position |
| | 1371 | */ |
| | 1372 | public int getY() { |
| | 1373 | return y; |
| | 1374 | } |
| | 1375 | |
| | 1376 | /** |
| | 1377 | * @return the zoom |
| | 1378 | */ |
| | 1379 | public int getZoom() { |
| | 1380 | return zoom; |
| | 1381 | } |
| | 1382 | |
| | 1383 | @Override |
| | 1384 | public String toString() { |
| | 1385 | return "TilePosition [x=" + x + ", y=" + y + ", zoom=" + zoom + "]"; |
| | 1386 | } |
| 1297 | 1387 | } |
| 1298 | 1388 | |
| 1299 | 1389 | private class TileSet extends TileRange { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1307 | 1397 | * null tile set |
| 1308 | 1398 | */ |
| 1309 | 1399 | private TileSet() { |
| 1310 | | return; |
| | 1400 | // default |
| 1311 | 1401 | } |
| 1312 | 1402 | |
| 1313 | 1403 | protected void sanitize() { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1342 | 1432 | * already in the tileCache. |
| 1343 | 1433 | */ |
| 1344 | 1434 | private List<Tile> allExistingTiles() { |
| 1345 | | return this.findAllTiles(false); |
| | 1435 | return allTiles(p -> getTile(p)); |
| 1346 | 1436 | } |
| 1347 | 1437 | |
| 1348 | 1438 | private List<Tile> allTilesCreate() { |
| 1349 | | return this.findAllTiles(true); |
| | 1439 | return allTiles(p -> getOrCreateTile(p)); |
| 1350 | 1440 | } |
| 1351 | 1441 | |
| 1352 | | private List<Tile> findAllTiles(boolean create) { |
| 1353 | | // Tileset is either empty or too large |
| 1354 | | if (zoom == 0 || this.insane()) |
| 1355 | | return Collections.emptyList(); |
| 1356 | | List<Tile> ret = new ArrayList<>(); |
| 1357 | | for (int x = minX; x <= maxX; x++) { |
| 1358 | | for (int y = minY; y <= maxY; y++) { |
| 1359 | | Tile t; |
| 1360 | | if (create) { |
| 1361 | | t = getOrCreateTile(x, y, zoom); |
| 1362 | | } else { |
| 1363 | | t = getTile(x, y, zoom); |
| 1364 | | } |
| 1365 | | if (t != null) { |
| 1366 | | ret.add(t); |
| 1367 | | } |
| 1368 | | } |
| | 1442 | private List<Tile> allTiles(Function<TilePosition, Tile> mapper) { |
| | 1443 | return tilePositions().map(mapper).filter(Objects::nonNull).collect(Collectors.toList()); |
| | 1444 | } |
| | 1445 | |
| | 1446 | @Override |
| | 1447 | public Stream<TilePosition> tilePositions() { |
| | 1448 | if (this.insane()) { |
| | 1449 | // Tileset is either empty or too large |
| | 1450 | return Stream.empty(); |
| | 1451 | } else { |
| | 1452 | return super.tilePositions(); |
| 1369 | 1453 | } |
| 1370 | | return ret; |
| 1371 | 1454 | } |
| 1372 | 1455 | |
| 1373 | 1456 | private List<Tile> allLoadedTiles() { |
| 1374 | | List<Tile> ret = new ArrayList<>(); |
| 1375 | | for (Tile t : this.allExistingTiles()) { |
| 1376 | | if (t.isLoaded()) |
| 1377 | | ret.add(t); |
| 1378 | | } |
| 1379 | | return ret; |
| | 1457 | return allExistingTiles().stream().filter(Tile::isLoaded).collect(Collectors.toList()); |
| 1380 | 1458 | } |
| 1381 | 1459 | |
| 1382 | 1460 | /** |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1385 | 1463 | private Comparator<Tile> getTileDistanceComparator() { |
| 1386 | 1464 | final int centerX = (int) Math.ceil((minX + maxX) / 2d); |
| 1387 | 1465 | final int centerY = (int) Math.ceil((minY + maxY) / 2d); |
| 1388 | | return new Comparator<Tile>() { |
| 1389 | | private int getDistance(Tile t) { |
| 1390 | | return Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY); |
| 1391 | | } |
| 1392 | | |
| 1393 | | @Override |
| 1394 | | public int compare(Tile o1, Tile o2) { |
| 1395 | | int distance1 = getDistance(o1); |
| 1396 | | int distance2 = getDistance(o2); |
| 1397 | | return Integer.compare(distance1, distance2); |
| 1398 | | } |
| 1399 | | }; |
| | 1466 | return Comparator.comparingInt(t -> Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY)); |
| 1400 | 1467 | } |
| 1401 | 1468 | |
| 1402 | 1469 | private void loadAllTiles(boolean force) { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1419 | 1486 | } |
| 1420 | 1487 | } |
| 1421 | 1488 | |
| | 1489 | /** |
| | 1490 | * Call the given paint method for all tiles in this tile set. |
| | 1491 | * <p> |
| | 1492 | * Uses a parallel stream. |
| | 1493 | * @param visitor A visitor to call for each tile. |
| | 1494 | * @param missed a consumer to call for each missed tile. |
| | 1495 | */ |
| | 1496 | public void visitTiles(Consumer<Tile> visitor, Consumer<TilePosition> missed) { |
| | 1497 | tilePositions().parallel().forEach(tp -> visitTilePosition(visitor, tp, missed)); |
| | 1498 | } |
| | 1499 | |
| | 1500 | private void visitTilePosition(Consumer<Tile> visitor, TilePosition tp, Consumer<TilePosition> missed) { |
| | 1501 | Tile tile = getTile(tp); |
| | 1502 | if (tile == null) { |
| | 1503 | missed.accept(tp); |
| | 1504 | } else { |
| | 1505 | visitor.accept(tile); |
| | 1506 | } |
| | 1507 | } |
| | 1508 | |
| 1422 | 1509 | @Override |
| 1423 | 1510 | public String toString() { |
| 1424 | 1511 | return getClass().getName() + ": zoom: " + zoom + " X(" + minX + ", " + maxX + ") Y(" + minY + ", " + maxY + ") size: " + size(); |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1591 | 1678 | |
| 1592 | 1679 | g.setColor(Color.DARK_GRAY); |
| 1593 | 1680 | |
| 1594 | | List<Tile> missedTiles = this.paintTileImages(g, ts, displayZoomLevel, null); |
| | 1681 | List<Tile> missedTiles = this.paintTileImages(g, ts); |
| 1595 | 1682 | int[] otherZooms = {-1, 1, -2, 2, -3, -4, -5}; |
| 1596 | 1683 | for (int zoomOffset : otherZooms) { |
| 1597 | 1684 | if (!getDisplaySettings().isAutoZoom()) { |