001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package 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     */
011    public class EastNorth extends Coordinate {
012    
013        public EastNorth(double east, double north) {
014            super(east,north);
015        }
016    
017        public double east() {
018            return x;
019        }
020    
021        public double north() {
022            return y;
023        }
024    
025        public EastNorth add(double dx, double dy) {
026            return new EastNorth(x+dx, y+dy);
027        }
028    
029        public EastNorth add(EastNorth other) {
030            return new EastNorth(x+other.x, y+other.y);
031        }
032    
033        public EastNorth scale(double s) {
034            return new EastNorth(s * x, s * y);
035        }
036    
037        public EastNorth interpolate(EastNorth en2, double proportion) {
038            return new EastNorth(this.x + proportion * (en2.x - this.x),
039                this.y + proportion * (en2.y - this.y));
040        }
041    
042        public EastNorth getCenter(EastNorth en2) {
043            return new EastNorth((this.x + en2.x)/2.0, (this.y + en2.y)/2.0);
044        }
045    
046        public double distance(EastNorth en2) {
047            return Math.sqrt((this.x-en2.x)*(this.x-en2.x) + (this.y-en2.y)*(this.y-en2.y));
048        }
049    
050        /**
051         * Returns the heading, in radians, that you have to use to get from
052         * this EastNorth to another. Heading is mapped into [0, 2pi)
053         *
054         * @param other the "destination" position
055         * @return heading
056         */
057        public double heading(EastNorth other) {
058            double hd = Math.atan2(other.east() - east(), other.north() - north());
059            if(hd < 0) hd = 2 * Math.PI + hd;
060            return hd;
061        }
062        
063        /**
064         * Replies true if east and north are different from Double.NaN
065         * 
066         * @return true if east and north are different from Double.NaN
067         */
068        public boolean isValid() {
069            return !java.lang.Double.isNaN(x) && !java.lang.Double.isNaN(y);
070        }
071    
072        public EastNorth sub(EastNorth en) {
073            return new EastNorth(en.east() - east(), en.north() - north());
074        }
075    
076        /**
077         * Returns an EastNorth representing the this EastNorth rotated around
078         * a given EastNorth by a given angle
079         * @param pivot the center of the rotation
080         * @param angle the angle of the rotation
081         * @return EastNorth rotated object
082         */
083        public EastNorth rotate(EastNorth pivot, double angle) {
084            double cosPhi = Math.cos(angle);
085            double sinPhi = Math.sin(angle);
086            double x = east() - pivot.east();
087            double y = north() - pivot.north();
088            double nx =  cosPhi * x + sinPhi * y + pivot.east();
089            double ny = -sinPhi * x + cosPhi * y + pivot.north();
090            return new EastNorth(nx, ny);
091        }
092    
093        @Override public String toString() {
094            return "EastNorth[e="+x+", n="+y+"]";
095        }
096    
097        /**
098         * Compares two EastNorth values
099         *
100         * @return true if "x" and "y" values are within 1E-6 of each other
101         */
102        public boolean equalsEpsilon(EastNorth other, double e) {
103            return (Math.abs(x - other.x) < e && Math.abs(y - other.y) < e);
104        }
105    }