001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.imagery;
003
004import java.awt.geom.AffineTransform;
005import java.awt.geom.Point2D;
006
007import org.openstreetmap.gui.jmapviewer.interfaces.IProjected;
008
009/**
010 * Class that fixes the position of a tile in a given coordinate space.
011 *
012 * This is done by storing the coordinates of the tile origin and the opposite
013 * tile corner.
014 * <p>
015 * It may represent a reprojected tile, i.e. the tile is rotated / deformed in an
016 * arbitrary way. In general, the tile origin cannot be expected to be the
017 * upper left corner of the rectangle that is spanned by the 2 points.
018 * <p>
019 * The coordinate space may be
020 * <ul>
021 *   <li>pixel coordinates of the image file</li>
022 *   <li>projected coordinates (east / north)</li>
023 *   <li>screen pixel coordinates</li>
024 * </ul>
025 * @since 11846
026 */
027public class TileAnchor {
028
029    protected final Point2D tileOrigin, nextTileOrigin;
030
031    /**
032     * Create a new tile anchor.
033     * @param tileOrigin position of the tile origin
034     * @param nextTileOrigin position of the opposite tile corner, i.e. the
035     * origin of the tile with index (x+1,y+1), when current tile has index (x,y)
036     */
037    public TileAnchor(Point2D tileOrigin, Point2D nextTileOrigin) {
038        this.tileOrigin = tileOrigin;
039        this.nextTileOrigin = nextTileOrigin;
040    }
041
042    /**
043     * Constructs a new {@code TileAnchor}.
044     * @param tileOrigin position of the tile origin
045     * @param nextTileOrigin position of the opposite tile corner, i.e. the
046     * origin of the tile with index (x+1,y+1), when current tile has index (x,y)
047     */
048    public TileAnchor(IProjected tileOrigin, IProjected nextTileOrigin) {
049        this.tileOrigin = new Point2D.Double(tileOrigin.getEast(), tileOrigin.getNorth());
050        this.nextTileOrigin = new Point2D.Double(nextTileOrigin.getEast(), nextTileOrigin.getNorth());
051    }
052
053    /**
054     * Returns the position of the tile origin.
055     * @return the position of the tile origin
056     */
057    public Point2D getTileOrigin() {
058        return tileOrigin;
059    }
060
061    /**
062     * Returns the position of the opposite tile corner.
063     * @return the position of the opposite tile corner, i.e. the
064     * origin of the tile with index (x+1,y+1), when current tile has index (x,y)
065     */
066    public Point2D getNextTileOrigin() {
067        return nextTileOrigin;
068    }
069
070    @Override
071    public String toString() {
072        return "TileAnchor{" + tileOrigin + "; " + nextTileOrigin + '}';
073    }
074
075    /**
076     * Create a transformation that converts points from this coordinate space
077     * to another coordinate space.
078     * @param other tile anchor of the tile in the target coordinate space
079     * @return affine transformation from this coordinate space to the target
080     * coordinate space
081     */
082    public AffineTransform convert(TileAnchor other) {
083        Point2D src1 = this.getTileOrigin();
084        Point2D src2 = this.getNextTileOrigin();
085        Point2D dest1 = other.getTileOrigin();
086        Point2D dest2 = other.getNextTileOrigin();
087
088        double scaleX = (dest2.getX() - dest1.getX()) / (src2.getX() - src1.getX());
089        double scaleY = (dest2.getY() - dest1.getY()) / (src2.getY() - src1.getY());
090        double offsetX0 = dest1.getX() - scaleX * src1.getX();
091        double offsetY0 = dest1.getY() - scaleY * src1.getY();
092        return new AffineTransform(scaleX, 0, 0, scaleY, offsetX0, offsetY0);
093    }
094}