001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.coor;
003
004/**
005 * Northing, Easting of the projected coordinates.
006 *
007 * This class is immutable.
008 *
009 * @author Imi
010 */
011public class EastNorth extends Coordinate {
012
013    private static final long serialVersionUID = 1L;
014
015    /**
016     * Constructs a new {@code EastNorth}.
017     * @param east easting
018     * @param north northing
019     */
020    public EastNorth(double east, double north) {
021        super(east, north);
022    }
023
024    /**
025     * Returns easting.
026     * @return easting
027     */
028    public double east() {
029        return x;
030    }
031
032    /**
033     * Returns northing.
034     * @return northing
035     */
036    public double north() {
037        return y;
038    }
039
040    /**
041     * Adds an offset to this {@link EastNorth} instance and returns the result.
042     * @param dEast The offset to add in east direction.
043     * @param dNorth The offset to add in north direction.
044     * @return The result.
045     */
046    public EastNorth add(double dEast, double dNorth) {
047        return new EastNorth(east()+dEast, north()+dNorth);
048    }
049
050    /**
051     * Adds the coordinates of an other EastNorth instance to this one.
052     * @param other The other instance.
053     * @return The new EastNorth position.
054     */
055    public EastNorth add(EastNorth other) {
056        return new EastNorth(x+other.x, y+other.y);
057    }
058
059    /**
060     * Subtracts an east/north value from this point.
061     * @param other The other value to subtract from this.
062     * @return A point with the new coordinates.
063     */
064    public EastNorth subtract(EastNorth other) {
065        return new EastNorth(x-other.x, y-other.y);
066    }
067
068    /**
069     * Scales this {@link EastNorth} instance to a given factor and returns the result.
070     * @param s factor
071     * @return The result.
072     */
073    public EastNorth scale(double s) {
074        return new EastNorth(s * x, s * y);
075    }
076
077    /**
078     * Does a linear interpolation between two EastNorth instances.
079     * @param en2 The other EstNort instance.
080     * @param proportion The proportion the other instance influences the result.
081     * @return The new {@link EastNorth} position.
082     */
083    public EastNorth interpolate(EastNorth en2, double proportion) {
084        return new EastNorth(this.x + proportion * (en2.x - this.x),
085                this.y + proportion * (en2.y - this.y));
086    }
087
088    /**
089     * Gets the center between two {@link EastNorth} instances.
090     * @param en2 The other instance.
091     * @return The center between this and the other instance.
092     */
093    public EastNorth getCenter(EastNorth en2) {
094        return new EastNorth((this.x + en2.x)/2.0, (this.y + en2.y)/2.0);
095    }
096
097    /**
098     * Returns the euclidean distance from this {@code EastNorth} to a specified {@code EastNorth}.
099     *
100     * @param en the specified coordinate to be measured against this {@code EastNorth}
101     * @return the euclidean distance from this {@code EastNorth} to a specified {@code EastNorth}
102     * @since 6166
103     */
104    public double distance(final EastNorth en) {
105        return super.distance(en);
106    }
107
108    /**
109     * Returns the square of the euclidean distance from this {@code EastNorth} to a specified {@code EastNorth}.
110     *
111     * @param en the specified coordinate to be measured against this {@code EastNorth}
112     * @return the square of the euclidean distance from this {@code EastNorth} to a specified {@code EastNorth}
113     * @since 6166
114     */
115    public double distanceSq(final EastNorth en) {
116        return super.distanceSq(en);
117    }
118
119    /**
120     * Counts length (distance from [0,0]) of this.
121     *
122     * @return length of this
123     */
124    public double length() {
125        return Math.sqrt(x*x + y*y);
126    }
127
128    /**
129     * Returns the heading, in radians, that you have to use to get from
130     * this EastNorth to another. Heading is mapped into [0, 2pi)
131     *
132     * @param other the "destination" position
133     * @return heading
134     */
135    public double heading(EastNorth other) {
136        double hd = Math.atan2(other.east() - east(), other.north() - north());
137        if (hd < 0) {
138            hd = 2 * Math.PI + hd;
139        }
140        return hd;
141    }
142
143    /**
144     * Replies true if east and north are different from Double.NaN and not infinite
145     *
146     * @return true if east and north are different from Double.NaN and not infinite
147     */
148    public boolean isValid() {
149        return !Double.isNaN(x) && !Double.isNaN(y) && !Double.isInfinite(x) && !Double.isInfinite(y);
150    }
151
152    /**
153     * Returns an EastNorth representing the this EastNorth rotated around
154     * a given EastNorth by a given angle
155     * @param pivot the center of the rotation
156     * @param angle the angle of the rotation
157     * @return EastNorth rotated object
158     */
159    public EastNorth rotate(EastNorth pivot, double angle) {
160        double cosPhi = Math.cos(angle);
161        double sinPhi = Math.sin(angle);
162        double x = east() - pivot.east();
163        double y = north() - pivot.north();
164        // CHECKSTYLE.OFF: SingleSpaceSeparator
165        double nx =  cosPhi * x + sinPhi * y + pivot.east();
166        double ny = -sinPhi * x + cosPhi * y + pivot.north();
167        // CHECKSTYLE.ON: SingleSpaceSeparator
168        return new EastNorth(nx, ny);
169    }
170
171    @Override
172    public String toString() {
173        return "EastNorth[e="+x+", n="+y+']';
174    }
175
176    /**
177     * Compares two EastNorth values
178     * @param other other east.north
179     * @param e epsilon
180     *
181     * @return true if "x" and "y" values are within epsilon {@code e} of each other
182     */
183    public boolean equalsEpsilon(EastNorth other, double e) {
184        return Math.abs(x - other.x) < e && Math.abs(y - other.y) < e;
185    }
186}