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 6e9bdc3..35e9558 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
|
| 968 | 973 | return new Tile(tileSource, x, y, zoom); |
| 969 | 974 | } |
| 970 | 975 | |
| | 976 | private Tile getOrCreateTile(TilePosition tilePosition) { |
| | 977 | return getOrCreateTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom()); |
| | 978 | } |
| | 979 | |
| 971 | 980 | private Tile getOrCreateTile(int x, int y, int zoom) { |
| 972 | 981 | Tile tile = getTile(x, y, zoom); |
| 973 | 982 | if (tile == null) { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 981 | 990 | return tile; |
| 982 | 991 | } |
| 983 | 992 | |
| | 993 | private Tile getTile(TilePosition tilePosition) { |
| | 994 | return getTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom()); |
| | 995 | } |
| | 996 | |
| 984 | 997 | /** |
| 985 | 998 | * Returns tile at given position. |
| 986 | 999 | * This can and will return null for tiles that are not already in the cache. |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1121 | 1134 | } |
| 1122 | 1135 | } |
| 1123 | 1136 | |
| | 1137 | private List<Tile> paintTileImages(Graphics g, TileSet ts) { |
| | 1138 | Object paintMutex = new Object(); |
| | 1139 | List<TilePosition> missed = Collections.synchronizedList(new ArrayList<>()); |
| | 1140 | ts.visitTiles(tile -> { |
| | 1141 | Image img = getLoadedTileImage(tile); |
| | 1142 | if (img == null) { |
| | 1143 | missed.add(new TilePosition(tile)); |
| | 1144 | } |
| | 1145 | img = applyImageProcessors((BufferedImage) img); |
| | 1146 | Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile); |
| | 1147 | synchronized (paintMutex) { |
| | 1148 | //cannot paint in parallel |
| | 1149 | drawImageInside(g, img, sourceRect, null); |
| | 1150 | } |
| | 1151 | }, missed::add); |
| | 1152 | |
| | 1153 | return missed.stream().map(this::getOrCreateTile).collect(Collectors.toList()); |
| | 1154 | } |
| | 1155 | |
| 1124 | 1156 | // This function is called for several zoom levels, not just |
| 1125 | 1157 | // the current one. It should not trigger any tiles to be |
| 1126 | 1158 | // downloaded. It should also avoid polluting the tile cache |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1132 | 1164 | // border is null and we draw the entire tile set. |
| 1133 | 1165 | private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) { |
| 1134 | 1166 | if (zoom <= 0) return Collections.emptyList(); |
| 1135 | | Rectangle2D borderRect = null; |
| 1136 | | if (border != null) { |
| 1137 | | borderRect = coordinateConverter.getRectangleForTile(border); |
| 1138 | | } |
| | 1167 | Rectangle2D borderRect = coordinateConverter.getRectangleForTile(border); |
| 1139 | 1168 | List<Tile> missedTiles = new LinkedList<>(); |
| 1140 | 1169 | // The callers of this code *require* that we return any tiles |
| 1141 | 1170 | // that we do not draw in missedTiles. ts.allExistingTiles() by |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1286 | 1315 | int ySpan = maxY - minY + 1; |
| 1287 | 1316 | return xSpan * ySpan; |
| 1288 | 1317 | } |
| | 1318 | |
| | 1319 | /** |
| | 1320 | * Gets a stream of all tile positions in this set |
| | 1321 | * @return A stream of all positions |
| | 1322 | */ |
| | 1323 | public Stream<TilePosition> tilePositions() { |
| | 1324 | if (zoom == 0) { |
| | 1325 | return Stream.empty(); |
| | 1326 | } else { |
| | 1327 | return IntStream.rangeClosed(minX, maxX).mapToObj( |
| | 1328 | x -> IntStream.rangeClosed(minY, maxY).mapToObj(y -> new TilePosition(x, y, zoom)) |
| | 1329 | ).flatMap(Function.identity()); |
| | 1330 | } |
| | 1331 | } |
| | 1332 | } |
| | 1333 | |
| | 1334 | /** |
| | 1335 | * The position of a single tile. |
| | 1336 | * @author Michael Zangl |
| | 1337 | * @since xxx |
| | 1338 | */ |
| | 1339 | private static class TilePosition { |
| | 1340 | private final int x; |
| | 1341 | private final int y; |
| | 1342 | private final int zoom; |
| | 1343 | public TilePosition(int x, int y, int zoom) { |
| | 1344 | super(); |
| | 1345 | this.x = x; |
| | 1346 | this.y = y; |
| | 1347 | this.zoom = zoom; |
| | 1348 | } |
| | 1349 | |
| | 1350 | public TilePosition(Tile tile) { |
| | 1351 | this(tile.getXtile(), tile.getYtile(), tile.getZoom()); |
| | 1352 | } |
| | 1353 | |
| | 1354 | /** |
| | 1355 | * @return the x position |
| | 1356 | */ |
| | 1357 | public int getX() { |
| | 1358 | return x; |
| | 1359 | } |
| | 1360 | |
| | 1361 | /** |
| | 1362 | * @return the y position |
| | 1363 | */ |
| | 1364 | public int getY() { |
| | 1365 | return y; |
| | 1366 | } |
| | 1367 | |
| | 1368 | /** |
| | 1369 | * @return the zoom |
| | 1370 | */ |
| | 1371 | public int getZoom() { |
| | 1372 | return zoom; |
| | 1373 | } |
| | 1374 | |
| | 1375 | @Override |
| | 1376 | public String toString() { |
| | 1377 | return "TilePosition [x=" + x + ", y=" + y + ", zoom=" + zoom + "]"; |
| | 1378 | } |
| 1289 | 1379 | } |
| 1290 | 1380 | |
| 1291 | 1381 | private class TileSet extends TileRange { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1299 | 1389 | * null tile set |
| 1300 | 1390 | */ |
| 1301 | 1391 | private TileSet() { |
| 1302 | | return; |
| | 1392 | // default |
| 1303 | 1393 | } |
| 1304 | 1394 | |
| 1305 | 1395 | protected void sanitize() { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1334 | 1424 | * already in the tileCache. |
| 1335 | 1425 | */ |
| 1336 | 1426 | private List<Tile> allExistingTiles() { |
| 1337 | | return this.__allTiles(false); |
| | 1427 | return allTiles(p -> getTile(p)); |
| 1338 | 1428 | } |
| 1339 | 1429 | |
| 1340 | 1430 | private List<Tile> allTilesCreate() { |
| 1341 | | return this.__allTiles(true); |
| | 1431 | return allTiles(p -> getOrCreateTile(p)); |
| 1342 | 1432 | } |
| 1343 | 1433 | |
| 1344 | | private List<Tile> __allTiles(boolean create) { |
| 1345 | | // Tileset is either empty or too large |
| 1346 | | if (zoom == 0 || this.insane()) |
| 1347 | | return Collections.emptyList(); |
| 1348 | | List<Tile> ret = new ArrayList<>(); |
| 1349 | | for (int x = minX; x <= maxX; x++) { |
| 1350 | | for (int y = minY; y <= maxY; y++) { |
| 1351 | | Tile t; |
| 1352 | | if (create) { |
| 1353 | | t = getOrCreateTile(x, y, zoom); |
| 1354 | | } else { |
| 1355 | | t = getTile(x, y, zoom); |
| 1356 | | } |
| 1357 | | if (t != null) { |
| 1358 | | ret.add(t); |
| 1359 | | } |
| 1360 | | } |
| | 1434 | private List<Tile> allTiles(Function<TilePosition, Tile> mapper) { |
| | 1435 | return tilePositions().map(mapper).filter(Objects::nonNull).collect(Collectors.toList()); |
| | 1436 | } |
| | 1437 | |
| | 1438 | @Override |
| | 1439 | public Stream<TilePosition> tilePositions() { |
| | 1440 | if (this.insane()) { |
| | 1441 | // Tileset is either empty or too large |
| | 1442 | return Stream.empty(); |
| | 1443 | } else { |
| | 1444 | return super.tilePositions(); |
| 1361 | 1445 | } |
| 1362 | | return ret; |
| 1363 | 1446 | } |
| 1364 | 1447 | |
| 1365 | 1448 | private List<Tile> allLoadedTiles() { |
| 1366 | | List<Tile> ret = new ArrayList<>(); |
| 1367 | | for (Tile t : this.allExistingTiles()) { |
| 1368 | | if (t.isLoaded()) |
| 1369 | | ret.add(t); |
| 1370 | | } |
| 1371 | | return ret; |
| | 1449 | return allExistingTiles().stream().filter(Tile::isLoaded).collect(Collectors.toList()); |
| 1372 | 1450 | } |
| 1373 | 1451 | |
| 1374 | 1452 | /** |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1377 | 1455 | private Comparator<Tile> getTileDistanceComparator() { |
| 1378 | 1456 | final int centerX = (int) Math.ceil((minX + maxX) / 2d); |
| 1379 | 1457 | final int centerY = (int) Math.ceil((minY + maxY) / 2d); |
| 1380 | | return new Comparator<Tile>() { |
| 1381 | | private int getDistance(Tile t) { |
| 1382 | | return Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY); |
| 1383 | | } |
| 1384 | | |
| 1385 | | @Override |
| 1386 | | public int compare(Tile o1, Tile o2) { |
| 1387 | | int distance1 = getDistance(o1); |
| 1388 | | int distance2 = getDistance(o2); |
| 1389 | | return Integer.compare(distance1, distance2); |
| 1390 | | } |
| 1391 | | }; |
| | 1458 | return Comparator.comparingInt(t -> Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY)); |
| 1392 | 1459 | } |
| 1393 | 1460 | |
| 1394 | 1461 | private void loadAllTiles(boolean force) { |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1411 | 1478 | } |
| 1412 | 1479 | } |
| 1413 | 1480 | |
| | 1481 | /** |
| | 1482 | * Call the given paint method for all tiles in this tile set. |
| | 1483 | * <p> |
| | 1484 | * Uses a parallel stream. |
| | 1485 | * @param visitor A visitor to call for each tile. |
| | 1486 | * @param missed a consumer to call for each missed tile. |
| | 1487 | */ |
| | 1488 | public void visitTiles(Consumer<Tile> visitor, Consumer<TilePosition> missed) { |
| | 1489 | tilePositions().parallel().forEach(tp -> visitTilePosition(visitor, tp, missed)); |
| | 1490 | } |
| | 1491 | |
| | 1492 | private void visitTilePosition(Consumer<Tile> visitor, TilePosition tp, Consumer<TilePosition> missed) { |
| | 1493 | Tile tile = getTile(tp); |
| | 1494 | if (tile == null) { |
| | 1495 | missed.accept(tp); |
| | 1496 | } else { |
| | 1497 | visitor.accept(tile); |
| | 1498 | } |
| | 1499 | } |
| | 1500 | |
| 1414 | 1501 | @Override |
| 1415 | 1502 | public String toString() { |
| 1416 | 1503 | return getClass().getName() + ": zoom: " + zoom + " X(" + minX + ", " + maxX + ") Y(" + minY + ", " + maxY + ") size: " + size(); |
| … |
… |
implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
|
| 1583 | 1670 | |
| 1584 | 1671 | g.setColor(Color.DARK_GRAY); |
| 1585 | 1672 | |
| 1586 | | List<Tile> missedTiles = this.paintTileImages(g, ts, displayZoomLevel, null); |
| | 1673 | List<Tile> missedTiles = this.paintTileImages(g, ts); |
| 1587 | 1674 | int[] otherZooms = {-1, 1, -2, 2, -3, -4, -5}; |
| 1588 | 1675 | for (int zoomOffset : otherZooms) { |
| 1589 | 1676 | if (!getDisplaySettings().isAutoZoom()) { |