| 543 | | painter.render(data, virtual, box); |
| | 555 | final double scale = mv.getScale(); |
| | 556 | final int tileSize = 256; |
| | 557 | // We might have to fall back to the old method if user is reprojecting |
| | 558 | final double topResolution = 2 * Math.PI * OsmMercator.EARTH_RADIUS / tileSize; |
| | 559 | // Use to invalidate cache in the future |
| | 560 | int zoom; |
| | 561 | for (zoom = 0; zoom < 30; zoom++) {// Use something like imagery.{generic|tms}.max_zoom_lvl (20 is a bit too low for our needs) |
| | 562 | if (scale > topResolution / (Math.pow(2, zoom))) { |
| | 563 | zoom = zoom > 0 ? zoom - 1 : zoom; |
| | 564 | break; |
| | 565 | } |
| | 566 | } |
| | 567 | if (zoom != lastZoom || data.getMappaintCacheIndex() != lastDataIdx |
| | 568 | || !data.selectionEmpty() |
| | 569 | || !data.getHighlightedVirtualNodes().isEmpty() |
| | 570 | || !data.getHighlightedWaySegments().isEmpty()) { |
| | 571 | cache.clear(); |
| | 572 | lastZoom = zoom; |
| | 573 | lastDataIdx = data.getMappaintCacheIndex(); |
| | 574 | Logging.trace("OsmDataLayer {0} paint cache cleared", this.getName()); |
| | 575 | } |
| | 576 | final List<TileXY> toRender = new ArrayList<>(); |
| | 577 | final TileXY upperRight = latLonToTile(box.getMaxLat(), box.getMaxLon(), zoom); |
| | 578 | final TileXY lowerLeft = latLonToTile(box.getMinLat(), box.getMinLon(), zoom); |
| | 579 | for (int x = lowerLeft.getXIndex(); x <= upperRight.getXIndex(); x++) { |
| | 580 | for (int y = upperRight.getYIndex(); y <= lowerLeft.getYIndex(); y++) { |
| | 581 | toRender.add(new TileXY(x, y)); |
| | 582 | } |
| | 583 | } |
| | 584 | |
| | 585 | for (TileXY tile : toRender) { |
| | 586 | final int actualZoom = zoom; |
| | 587 | final double lat = yToLat(tile.getYIndex(), zoom); |
| | 588 | final double lon = xToLon(tile.getXIndex(), zoom); |
| | 589 | final Point point = mv.getPoint(new LatLon(lat, lon)); |
| | 590 | BufferedImage tileImage = cache.get(tile.toString(), |
| | 591 | () -> generateTile(data, mv, point, inactive, virtual, tile, tileSize, actualZoom)); |
| | 592 | g.drawImage(tileImage, point.x, point.y, tileSize, tileSize, null, null); |
| | 593 | } |
| | 594 | //painter.render(data, virtual, box); |
| | 598 | private static BufferedImage generateTile(DataSet data, MapView mv, Point point, boolean inactive, boolean virtual, TileXY tile, |
| | 599 | int tileSize, int zoom) { |
| | 600 | BufferedImage bufferedImage = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_4BYTE_ABGR); |
| | 601 | Graphics2D g2d = bufferedImage.createGraphics(); |
| | 602 | g2d.setTransform(AffineTransform.getTranslateInstance(-point.x, -point.y)); |
| | 603 | try { |
| | 604 | AbstractMapRenderer tilePainter = MapRendererFactory.getInstance().createActiveRenderer(g2d, mv, inactive); |
| | 605 | tilePainter.render(data, virtual, tileToBounds(tile, zoom)); |
| | 606 | } finally { |
| | 607 | g2d.dispose(); |
| | 608 | } |
| | 609 | return bufferedImage; |
| | 610 | } |
| | 611 | |
| | 612 | private static Bounds tileToBounds(TileXY tile, int zoom) { |
| | 613 | return new Bounds(yToLat(tile.getYIndex() + 1, zoom), xToLon(tile.getXIndex(), zoom), |
| | 614 | yToLat(tile.getYIndex(), zoom), xToLon(tile.getXIndex() + 1, zoom)); |
| | 615 | } |
| | 616 | |
| | 617 | private static double xToLon(int x, int zoom) { |
| | 618 | return (x / Math.pow(2, zoom)) * 360 - 180; |
| | 619 | } |
| | 620 | |
| | 621 | private static double yToLat(int y, int zoom) { |
| | 622 | double t = Math.PI - (2 * Math.PI * y) / (Math.pow(2, zoom)); |
| | 623 | return 180 / Math.PI * Math.atan((Math.exp(t) - Math.exp(-t)) / 2); |
| | 624 | } |
| | 625 | |
| | 626 | private static TileXY latLonToTile(double lat, double lon, int zoom) { |
| | 627 | int xCoord = (int) Math.floor(Math.pow(2, zoom) * (180 + lon) / 360); |
| | 628 | int yCoord = (int) Math.floor(Math.pow(2, zoom) * (1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2); |
| | 629 | return new TileXY(xCoord, yCoord); |
| | 630 | } |
| | 631 | |