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    public TileAnchor(IProjected tileOrigin, IProjected nextTileOrigin) {
043        this.tileOrigin = new Point2D.Double(tileOrigin.getEast(), tileOrigin.getNorth());
044        this.nextTileOrigin = new Point2D.Double(nextTileOrigin.getEast(), nextTileOrigin.getNorth());
045    }
046
047    public Point2D getTileOrigin() {
048        return tileOrigin;
049    }
050
051    public Point2D getNextTileOrigin() {
052        return nextTileOrigin;
053    }
054
055    @Override
056    public String toString() {
057        return "TileAnchor{" + tileOrigin + "; " + nextTileOrigin + '}';
058    }
059
060    /**
061     * Create a transformation that converts points from this coordinate space
062     * to another coordinate space.
063     * @param other tile anchor of the tile in the target coordinate space
064     * @return affine transformation from this coordinate space to the target
065     * coordinate space
066     */
067    public AffineTransform convert(TileAnchor other) {
068        Point2D src1 = this.getTileOrigin();
069        Point2D src2 = this.getNextTileOrigin();
070        Point2D dest1 = other.getTileOrigin();
071        Point2D dest2 = other.getNextTileOrigin();
072
073        double scaleX = (dest2.getX() - dest1.getX()) / (src2.getX() - src1.getX());
074        double scaleY = (dest2.getY() - dest1.getY()) / (src2.getY() - src1.getY());
075        double offsetX0 = dest1.getX() - scaleX * src1.getX();
076        double offsetY0 = dest1.getY() - scaleY * src1.getY();
077        return new AffineTransform(scaleX, 0, 0, scaleY, offsetX0, offsetY0);
078    }
079}