Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / geom / LatLon.java
blob41ce6ffa0d035f6a64a9699a9e9e6501c8cf8207
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.geom;
9 import gov.nasa.worldwind.util.Logging;
11 /**
12 * Represents a point on the two-dimensional surface of a globe. Latitude is the degrees North and ranges between [-90,
13 * 90], while longitude refers to degrees East, and ranges between (-180, 180].
14 * <p/>
15 * Instances of <code>LatLon</code> are immutable.
17 * @author Tom Gaskins
18 * @version $Id: LatLon.java 2567 2007-08-16 22:27:38Z tgaskins $
20 public class LatLon
22 private final Angle latitude;
23 private final Angle longitude;
25 /**
26 * Factor method for obtaining a new <code>LatLon</code> from two angles expressed in radians.
28 * @param latitude in radians
29 * @param longitude in radians
30 * @return a new <code>LatLon</code> from the given angles, which are expressed as radians
32 public static LatLon fromRadians(double latitude, double longitude)
34 return new LatLon(Math.toDegrees(latitude), Math.toDegrees(longitude));
37 /**
38 * Factory method for obtaining a new <code>LatLon</code> from two angles expressed in degrees.
40 * @param latitude in degrees
41 * @param longitude in degrees
42 * @return a new <code>LatLon</code> from the given angles, which are expressed as degrees
44 public static LatLon fromDegrees(double latitude, double longitude)
46 return new LatLon(latitude, longitude);
49 private LatLon(double latitude, double longitude)
51 this.latitude = Angle.fromDegrees(latitude);
52 this.longitude = Angle.fromDegrees(longitude);
55 /**
56 * Contructs a new <code>LatLon</code> from two angles. Neither angle may be null.
58 * @param latitude
59 * @param longitude
60 * @throws IllegalArgumentException if <code>latitude</code> or <code>longitude</code> is null
62 public LatLon(Angle latitude, Angle longitude)
64 if (latitude == null || longitude == null)
66 String message = Logging.getMessage("nullValue.LatitudeOrLongitudeIsNull");
67 Logging.logger().severe(message);
68 throw new IllegalArgumentException(message);
71 this.latitude = latitude;
72 this.longitude = longitude;
75 /**
76 * Obtains the latitude of this <code>LatLon</code>.
78 * @return this <code>LatLon</code>'s latitude
80 public final Angle getLatitude()
82 return this.latitude;
85 /**
86 * Obtains the longitude of this <code>LatLon</code>.
88 * @return this <code>LatLon</code>'s longitude
90 public final Angle getLongitude()
92 return this.longitude;
95 public static LatLon interpolate(double amount, LatLon value1, LatLon value2)
97 if ((value1 == null) || (value2 == null))
99 String message = Logging.getMessage("nullValue.LatLonIsNull");
100 Logging.logger().severe(message);
101 throw new IllegalArgumentException(message);
104 if (amount < 0)
105 return value1;
106 else if (amount > 1)
107 return value2;
109 Quaternion beginQuat = Quaternion.fromRotationYPR(value1.getLongitude(), value1.getLatitude(), Angle.ZERO);
110 Quaternion endQuat = Quaternion.fromRotationYPR(value2.getLongitude(), value2.getLatitude(), Angle.ZERO);
111 Quaternion quaternion = Quaternion.slerp(amount, beginQuat, endQuat);
113 Angle lat = quaternion.getRotationY();
114 Angle lon = quaternion.getRotationX();
115 if ((lat == null) || (lon == null))
116 return null;
118 return new LatLon(lat, lon);
122 * Computes the great circle angular distance between two locations
124 * @param begin LatLon of the first location
125 * @param end LatLon of the second location
126 * @return the angular distance between the two locations
128 public static Angle sphericalDistance(LatLon begin, LatLon end)
130 // TODO: Perhaps use more accurate formula described here: http://en.wikipedia.org/wiki/Great-circle_distance
131 double radLatA = begin.getLatitude().radians;
132 double radLatB = end.getLatitude().radians;
133 double radLonA = begin.getLongitude().radians;
134 double radLonB = end.getLongitude().radians;
135 return Angle.fromRadians(
136 Math.acos(Math.cos(radLatA) * Math.cos(radLatB) * Math.cos(radLonA - radLonB)
137 + Math.sin(radLatA) * Math.sin(radLatB)));
140 private static final double HALF_PI = Math.PI / 2d;
142 public static Angle azimuth(LatLon p1, LatLon p2)
144 double lat1 = p1.getLatitude().radians;
145 double lon1 = p1.getLongitude().radians;
146 double lat2 = p2.getLatitude().radians;
147 double lon2 = p2.getLongitude().radians;
149 if (lat1 == lat2 && lon1 == lon2)
150 return Angle.ZERO;
152 if (lon1 == lon2)
153 return lat1 > lat2 ? Angle.POS180 : Angle.ZERO;
155 double c = Math.acos(Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1) + Math.sin(lat1) * Math.sin(lat2));
156 double A = Math.asin(Math.cos(lat2) * Math.sin(lon2 - lon1) / c);
158 if (lat2 < lat1)
159 A = Math.PI - A;
160 else if (lon2 < lon1)
161 A += 2 * Math.PI;
163 return Angle.fromRadians(A);
166 public static LatLon endPosition(LatLon p, double azimuth, double pathLength)
168 double a = Math.acos(Math.cos(pathLength) * Math.cos(HALF_PI - p.getLatitude().radians)
169 + Math.sin(HALF_PI - p.getLatitude().radians) * Math.sin(pathLength) * Math.cos(azimuth));
171 double B = Math.asin(Math.sin(pathLength) * Math.sin(azimuth) / Math.sin(a));
173 return new LatLon(Angle.fromRadians(HALF_PI - a).normalizedLatitude(),
174 Angle.fromRadians(B + p.getLongitude().radians).normalizedLongitude());
177 public LatLon add(LatLon that)
179 if (that == null)
181 String msg = Logging.getMessage("nullValue.AngleIsNull");
182 Logging.logger().severe(msg);
183 throw new IllegalArgumentException(msg);
186 Angle lat = Angle.normalizedLatitude(this.latitude.add(that.latitude));
187 Angle lon = Angle.normalizedLongitude(this.longitude.add(that.longitude));
189 return new LatLon(lat, lon);
192 public LatLon subtract(LatLon that)
194 if (that == null)
196 String msg = Logging.getMessage("nullValue.AngleIsNull");
197 Logging.logger().severe(msg);
198 throw new IllegalArgumentException(msg);
201 Angle lat = Angle.normalizedLatitude(this.latitude.subtract(that.latitude));
202 Angle lon = Angle.normalizedLongitude(this.longitude.subtract(that.longitude));
204 return new LatLon(lat, lon);
207 public LatLon add(Position that)
209 if (that == null)
211 String msg = Logging.getMessage("nullValue.AngleIsNull");
212 Logging.logger().severe(msg);
213 throw new IllegalArgumentException(msg);
216 Angle lat = Angle.normalizedLatitude(this.latitude.add(that.getLatitude()));
217 Angle lon = Angle.normalizedLongitude(this.longitude.add(that.getLongitude()));
219 return new LatLon(lat, lon);
222 public LatLon subtract(Position that)
224 if (that == null)
226 String msg = Logging.getMessage("nullValue.AngleIsNull");
227 Logging.logger().severe(msg);
228 throw new IllegalArgumentException(msg);
231 Angle lat = Angle.normalizedLatitude(this.latitude.subtract(that.getLatitude()));
232 Angle lon = Angle.normalizedLongitude(this.longitude.subtract(that.getLongitude()));
234 return new LatLon(lat, lon);
237 public static boolean positionsCrossDateLine(Iterable<LatLon> positions)
239 if (positions == null)
241 String msg = Logging.getMessage("nullValue.PositionsListIsNull");
242 Logging.logger().severe(msg);
243 throw new IllegalArgumentException(msg);
246 LatLon pos = null;
247 for (LatLon posNext : positions)
249 if (pos != null)
251 // A segment cross the line if end pos have different longitude signs
252 // and are more than 180 degress longitude apart
253 if (Math.signum(pos.getLongitude().degrees) != Math.signum(posNext.getLongitude().degrees))
255 if (Math.abs(pos.getLongitude().degrees - posNext.getLongitude().degrees) > 180)
256 return true;
259 pos = posNext;
262 return false;
265 public static boolean positionsCrossLongitudeBoundary(LatLon p1, LatLon p2)
267 if (p1 == null || p2 == null)
269 String msg = Logging.getMessage("nullValue.PositionsListIsNull");
270 Logging.logger().severe(msg);
271 throw new IllegalArgumentException(msg);
274 // A segment cross the line if end pos have different longitude signs
275 // and are more than 180 degress longitude apart
276 if (Math.signum(p1.getLongitude().degrees) != Math.signum(p2.getLongitude().degrees))
278 if (Math.abs(p1.getLongitude().degrees - p2.getLongitude().degrees) > 180)
279 return true;
282 return false;
285 @Override
286 public String toString()
288 return "(" + this.latitude.toString() + ", " + this.longitude.toString() + ")";
291 @Override
292 public boolean equals(Object o)
294 if (this == o)
295 return true;
296 if (o == null || getClass() != o.getClass())
297 return false;
299 final gov.nasa.worldwind.geom.LatLon latLon = (gov.nasa.worldwind.geom.LatLon) o;
301 if (!latitude.equals(latLon.latitude))
302 return false;
303 //noinspection RedundantIfStatement
304 if (!longitude.equals(latLon.longitude))
305 return false;
307 return true;
310 @Override
311 public int hashCode()
313 int result;
314 result = latitude.hashCode();
315 result = 29 * result + longitude.hashCode();
316 return result;