Ticket #13210: patch-mapview-imagery-tile-source-coordinates.patch

File patch-mapview-imagery-tile-source-coordinates.patch, 32.0 KB (added by michael2402, 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;  
    33
    44import org.openstreetmap.gui.jmapviewer.Tile;
    55
     6/**
     7 * This listener listens to successful tile loads.
     8 */
     9@FunctionalInterface
    610public interface TileLoaderListener {
    711
    812    /**
  • 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 {  
    5959    }
    6060
    6161    @Override
    62     public TileLoader makeTileLoader(TileLoaderListener listener) {
    63         return makeTileLoader(listener, null);
    64     }
    65 
    66     @Override
    6762    public TileLoader makeTileLoader(TileLoaderListener listener, Map<String, String> inputHeaders) {
    6863        Map<String, String> headers = new ConcurrentHashMap<>();
    6964        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;  
    1212 * @author Wiktor Niesiobędzki
    1313 * @since 8526
    1414 */
     15@FunctionalInterface
    1516public interface TileLoaderFactory {
    1617
    1718    /**
    1819     * @param listener that will be notified, when tile has finished loading
    1920     * @return TileLoader that notifies specified listener
     21     * @deprecated Use {@link #makeTileLoader(TileLoaderListener, Map)}
    2022     */
    21     TileLoader makeTileLoader(TileLoaderListener listener);
     23    @Deprecated
     24    default TileLoader makeTileLoader(TileLoaderListener listener) {
     25        return makeTileLoader(listener, null);
     26    }
    2227
    2328    /**
    2429     * @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
    2631     * @return TileLoader that uses both of above
    2732     */
    2833    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;  
    77import java.awt.geom.AffineTransform;
    88import java.awt.geom.Point2D;
    99import java.awt.geom.Point2D.Double;
     10import java.awt.geom.Rectangle2D;
    1011
    1112import javax.swing.JComponent;
    1213
    public final class MapViewState {  
    143144    }
    144145
    145146    /**
     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    /**
    146156     * Gets a rectangle representing the whole view area.
    147157     * @return The rectangle.
    148158     */
    public final class MapViewState {  
    350360        public LatLon getLatLon() {
    351361            return projection.eastNorth2latlon(getEastNorth());
    352362        }
     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        }
    353372    }
    354373
    355374    private class MapViewViewPoint extends MapViewPoint {
    public final class MapViewState {  
    454473        public Bounds getLatLonBoundsBox() {
    455474            return projection.getLatLonBoundsBox(getProjectionBounds());
    456475        }
     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        }
    457488    }
    458489
    459490}
  • 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;  
    1212import java.awt.GridBagLayout;
    1313import java.awt.Image;
    1414import java.awt.Point;
    15 import java.awt.Rectangle;
    1615import java.awt.Toolkit;
    1716import java.awt.event.ActionEvent;
    1817import java.awt.event.MouseAdapter;
    1918import java.awt.event.MouseEvent;
     19import java.awt.geom.Point2D;
     20import java.awt.geom.Rectangle2D;
    2021import java.awt.image.BufferedImage;
    2122import java.awt.image.ImageObserver;
    2223import java.io.File;
    import java.util.Map.Entry;  
    3637import java.util.Set;
    3738import java.util.concurrent.ConcurrentSkipListSet;
    3839import java.util.concurrent.atomic.AtomicInteger;
     40import java.util.stream.Stream;
    3941
    4042import javax.swing.AbstractAction;
    4143import javax.swing.Action;
    import org.openstreetmap.josm.actions.ImageryAdjustAction;  
    6769import org.openstreetmap.josm.actions.RenameLayerAction;
    6870import org.openstreetmap.josm.actions.SaveActionBase;
    6971import org.openstreetmap.josm.data.Bounds;
     72import org.openstreetmap.josm.data.ProjectionBounds;
    7073import org.openstreetmap.josm.data.coor.EastNorth;
    7174import org.openstreetmap.josm.data.coor.LatLon;
    7275import org.openstreetmap.josm.data.imagery.ImageryInfo;
    import org.openstreetmap.josm.data.preferences.IntegerProperty;  
    7780import org.openstreetmap.josm.gui.ExtendedDialog;
    7881import org.openstreetmap.josm.gui.MapFrame;
    7982import org.openstreetmap.josm.gui.MapView;
     83import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
    8084import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;
    8185import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    8286import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    8387import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
    8488import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings.FilterChangeListener;
     89import org.openstreetmap.josm.gui.layer.imagery.TileCoordinateConverter;
    8590import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings;
    8691import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings.DisplaySettingsChangeEvent;
    8792import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings.DisplaySettingsChangeListener;
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    169174    private final TileSourceDisplaySettings displaySettings = createDisplaySettings();
    170175
    171176    private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this);
     177    // prepared to be moved to the painter
     178    private TileCoordinateConverter coordinateConverter;
    172179
    173180    /**
    174181     * Creates Tile Source based Imagery Layer based on Imagery Info
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    223230    }
    224231
    225232    protected void initTileSource(T tileSource) {
     233        coordinateConverter = new TileCoordinateConverter(Main.map.mapView, getDisplaySettings());
    226234        attribution.initialize(tileSource);
    227235
    228236        currentZoomLevel = getBestZoom();
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    420428            if (clickedTile != null) {
    421429                ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Tile Info"), new String[]{tr("OK")});
    422430                JPanel panel = new JPanel(new GridBagLayout());
    423                 Rectangle displaySize = tileToRect(clickedTile);
     431                Rectangle2D displaySize = coordinateConverter.getRectangleForTile(clickedTile);
    424432                String url = "";
    425433                try {
    426434                    url = clickedTile.getUrl();
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    433441                        {"Tile name", clickedTile.getKey()},
    434442                        {"Tile url", url},
    435443                        {"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()},
    437445                };
    438446
    439447                for (String[] entry: content) {
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    9991007
    10001008    private TileSet getVisibleTileSet() {
    10011009        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);
    10051013    }
    10061014
    10071015    protected void loadAllTiles(boolean force) {
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    10561064        return img;
    10571065    }
    10581066
    1059     private Rectangle tileToRect(Tile t1) {
    1060         /*
    1061          * We need to get a box in which to draw, so advance by one tile in
    1062          * each direction to find the other corner of the box.
    1063          * Note: this somewhat pollutes the tile cache
    1064          */
    1065         Tile t2 = tempCornerTile(t1);
    1066         Rectangle rect = new Rectangle(pixelPos(t1));
    1067         rect.add(pixelPos(t2));
    1068         return rect;
    1069     }
    1070 
    10711067    // 'source' is the pixel coordinates for the area that
    10721068    // the img is capable of filling in.  However, we probably
    10731069    // only want a portion of it.
    10741070    //
    10751071    // 'border' is the screen cordinates that need to be drawn.
    10761072    //  We must not draw outside of it.
    1077     private void drawImageInside(Graphics g, Image sourceImg, Rectangle source, Rectangle border) {
    1078         Rectangle target = source;
     1073    private void drawImageInside(Graphics g, Image sourceImg, Rectangle2D source, Rectangle2D border) {
     1074        Rectangle2D target = source;
    10791075
    10801076        // If a border is specified, only draw the intersection
    10811077        // if what we have combined with what we are supposed to draw.
    10821078        if (border != null) {
    1083             target = source.intersection(border);
     1079            target = source.createIntersection(border);
    10841080            if (Main.isDebugEnabled()) {
    10851081                Main.debug("source: " + source + "\nborder: " + border + "\nintersection: " + target);
    10861082            }
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    10971093        double imageXScaling = sourceImg.getWidth(this) / source.getWidth();
    10981094
    10991095        // 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();
    11021098        // And how many pixels into the image itself does that correlate to?
    11031099        int imgXoffset = (int) (screenXoffset * imageXScaling + 0.5);
    11041100        int imgYoffset = (int) (screenYoffset * imageYScaling + 0.5);
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    11111107            Main.debug("drawing image into target rect: " + target);
    11121108        }
    11131109        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(),
    11161112                imgXoffset, imgYoffset,
    11171113                imgXend, imgYend,
    11181114                this);
    11191115        if (PROP_FADE_AMOUNT.get() != 0) {
    11201116            // dimm by painting opaque rect...
    11211117            g.setColor(getFadeColorWithAlpha());
    1122             g.fillRect(target.x, target.y,
    1123                     target.width, target.height);
     1118            ((Graphics2D)g).fill(target);
    11241119        }
    11251120    }
    11261121
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    11351130    // border is null and we draw the entire tile set.
    11361131    private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
    11371132        if (zoom <= 0) return Collections.emptyList();
    1138         Rectangle borderRect = null;
     1133        Rectangle2D borderRect = null;
    11391134        if (border != null) {
    1140             borderRect = tileToRect(border);
     1135            borderRect = coordinateConverter.getRectangleForTile(border);
    11411136        }
    11421137        List<Tile> missedTiles = new LinkedList<>();
    11431138        // The callers of this code *require* that we return any tiles
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    11571152            // applying all filters to this layer
    11581153            img = applyImageProcessors((BufferedImage) img);
    11591154
    1160             Rectangle sourceRect = tileToRect(tile);
     1155            Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile);
    11611156            if (borderRect != null && !sourceRect.intersects(borderRect)) {
    11621157                continue;
    11631158            }
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    11941189    }
    11951190
    11961191    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) {
    11991193            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;
    12021200
    12031201        /*if (PROP_DRAW_DEBUG.get()) {
    12041202            myDrawString(g, "x=" + t.getXtile() + " y=" + t.getYtile() + " z=" + zoom + "", p.x + 2, texty);
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    12161214        }*/
    12171215
    12181216        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);
    12201218            //texty += 1 + fontHeight;
    12211219        }
    12221220
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    12251223        if (Main.isDebugEnabled()) {
    12261224            if (yCursor < t.getYtile()) {
    12271225                if (t.getYtile() % 32 == 31) {
    1228                     g.fillRect(0, p.y - 1, mv.getWidth(), 3);
     1226                    g.fillRect(0, y - 1, mv.getWidth(), 3);
    12291227                } else {
    1230                     g.drawLine(0, p.y, mv.getWidth(), p.y);
     1228                    g.drawLine(0, y, mv.getWidth(), y);
    12311229                }
    12321230                //yCursor = t.getYtile();
    12331231            }
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    12351233            if (xCursor < t.getXtile()) {
    12361234                if (t.getXtile() % 32 == 0) {
    12371235                    // level 7 tile boundary
    1238                     g.fillRect(p.x - 1, 0, 3, mv.getHeight());
     1236                    g.fillRect(x - 1, 0, 3, mv.getHeight());
    12391237                } else {
    1240                     g.drawLine(p.x, 0, p.x, mv.getHeight());
     1238                    g.drawLine(x, 0, x, mv.getHeight());
    12411239                }
    12421240                //xCursor = t.getXtile();
    12431241            }
    12441242        }
    12451243    }
    12461244
    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 
    12561245    private LatLon getShiftedLatLon(EastNorth en) {
    12571246        return Main.getProjection().eastNorth2latlon(en.add(-getDx(), -getDy()));
    12581247    }
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    12681257
    12691258    private final TileSet nullTileSet = new TileSet();
    12701259
    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;
    12791266
    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() {
    12871268        }
    1288     }
    12891269
    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        }
    12931277
    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());
    13021280        }
    13031281
    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    }
    13141288
    1315             TileXY t1 = tileSource.latLonToTileXY(topLeft.toCoordinate(), zoom);
    1316             TileXY t2 = tileSource.latLonToTileXY(botRight.toCoordinate(), zoom);
     1289    private class TileSet extends TileRange {
    13171290
    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);
    13221293            sanitize();
    1323 
    13241294        }
    13251295
    13261296        /**
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    13311301        }
    13321302
    13331303        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);
    13431306            }
    1344 
    1345             if (x0 < tileSource.getTileXMin(zoom)) {
    1346                 x0 = tileSource.getTileXMin(zoom);
     1307            if (minY < tileSource.getTileYMin(zoom)) {
     1308                minY = tileSource.getTileYMin(zoom);
    13471309            }
    1348             if (y0 < tileSource.getTileYMin(zoom)) {
    1349                 y0 = tileSource.getTileYMin(zoom);
     1310            if (maxX > tileSource.getTileXMax(zoom)) {
     1311                maxX = tileSource.getTileXMax(zoom);
    13501312            }
    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);
    13561315            }
    13571316        }
    13581317
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    13681327            return tileCache == null || size() > tileCache.getCacheSize();
    13691328        }
    13701329
    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 
    13811330        /*
    13821331         * Get all tiles represented by this TileSet that are
    13831332         * already in the tileCache.
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    13951344            if (zoom == 0 || this.insane())
    13961345                return Collections.emptyList();
    13971346            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++) {
    14001349                    Tile t;
    14011350                    if (create) {
    14021351                        t = getOrCreateTile(x, y, zoom);
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    14241373         * @return comparator, that sorts the tiles from the center to the edge of the current screen
    14251374         */
    14261375        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);
    14291378            return new Comparator<Tile>() {
    14301379                private int getDistance(Tile t) {
    14311380                    return Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY);
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    14621411
    14631412        @Override
    14641413        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();
    14661415        }
    14671416    }
    14681417
     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
    14691445    private static class TileSetInfo {
    14701446        public boolean hasVisibleTiles;
    14711447        public boolean hasOverzoomedTiles;
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    14931469    }
    14941470
    14951471    private class DeepTileSet {
    1496         private final EastNorth topLeft, botRight;
     1472        private final ProjectionBounds bounds;
    14971473        private final int minZoom, maxZoom;
    14981474        private final TileSet[] tileSets;
    14991475        private final TileSetInfo[] tileSetInfos;
    15001476
    15011477        @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;
    15051480            this.minZoom = minZoom;
    15061481            this.maxZoom = maxZoom;
    15071482            this.tileSets = new AbstractTileSourceLayer.TileSet[maxZoom - minZoom + 1];
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    15141489            synchronized (tileSets) {
    15151490                TileSet ts = tileSets[zoom-minZoom];
    15161491                if (ts == null) {
    1517                     ts = new MapWrappingTileSet(topLeft, botRight, zoom);
     1492                    ts = AbstractTileSourceLayer.this.getTileSet(bounds.getMin(), bounds.getMax(), zoom);
    15181493                    tileSets[zoom-minZoom] = ts;
    15191494                }
    15201495                return ts;
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    15371512
    15381513    @Override
    15391514    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();
    15481516
    15491517        needRedraw = false;
    15501518
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    15531521            zoom = getBestZoom();
    15541522        }
    15551523
    1556         DeepTileSet dts = new DeepTileSet(topLeft, botRight, getMinZoomLvl(), zoom);
     1524        DeepTileSet dts = new DeepTileSet(pb, getMinZoomLvl(), zoom);
    15571525        TileSet ts = dts.getTileSet(zoom);
    15581526
    15591527        int displayZoomLevel = zoom;
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    16321600                    continue;
    16331601                }
    16341602                Tile t2 = tempCornerTile(missed);
    1635                 TileSet ts2 = new TileSet(
     1603                TileSet ts2 = getTileSet(
    16361604                        getShiftedLatLon(tileSource.tileXYToLatLon(missed)),
    16371605                        getShiftedLatLon(tileSource.tileXYToLatLon(t2)),
    16381606                        newzoom);
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    16601628            this.paintTileText(ts, t, g, mv, displayZoomLevel, t);
    16611629        }
    16621630
    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),
    16641634                displayZoomLevel, this);
    16651635
    16661636        //g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi  
    17101680        EastNorth topLeft = mv.getEastNorth(0, 0);
    17111681        EastNorth botRight = mv.getEastNorth(mv.getWidth(), mv.getHeight());
    17121682        int z = currentZoomLevel;
    1713         TileSet ts = new TileSet(topLeft, botRight, z);
     1683        TileSet ts = getTileSet(topLeft, botRight, z);
    17141684
    17151685        if (!ts.tooLarge()) {
    17161686            ts.loadAllTiles(false); // make sure there are tile objects for all tiles
    17171687        }
    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));
    17341690        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));
    17371693        }
    1738         return clickedTile;
     1694        return clickedTiles.findAny().orElse(null);
    17391695    }
    17401696
    17411697    @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.
     2package org.openstreetmap.josm.gui.layer.imagery;
     3
     4import java.awt.geom.Point2D;
     5import java.awt.geom.Rectangle2D;
     6
     7import org.openstreetmap.gui.jmapviewer.Tile;
     8import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
     9import org.openstreetmap.josm.data.coor.LatLon;
     10import org.openstreetmap.josm.gui.MapView;
     11import 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 */
     18public 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}