001: /*
002: * <copyright>
003: *
004: * Copyright 2001-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026: package org.cougaar.tools.csmart.util;
027:
028: /**
029: * Interface containing constants for doing math on the Earth:
030: * Computing distances between points largely
031: * Constants here taken from
032: * <a href="http://openmap.bbn.com">OpenMap</a><br>
033: * Files: <code>com.bbn.openmap.proj.ProjMath, com.bbn.openmap.proj.MoreMath
034: * com.bbn.openmap.proj.GreatCircle and com.bbn.openmap.proj.Planet</code>
035: * <br>
036: * Those files in turn rely in part on John Snyder's
037: * <i>Map Projection --A Working Manual</i><br>
038: *
039: * Some routines taken from <a href="http://www.cougaar.org">Cougaar</a>
040: * and the class <code>org.cougaar.glm.util.GeoUtils</code><br>
041: *
042: */
043: public final class EarthConstants {
044: // From com.bbn.openmap.proj.MoreMath
045: public static final float TWO_PI = (float) Math.PI * 2.0f;
046:
047: // From com.bbn.openmap.proj.Planet
048: // WGS84 / GRS80 datums
049: public static final transient float polarRadiusMeters = 6356752.3142f;
050: public static final transient float equatorialRadiusMeters = 6378137.0f;
051: public static final transient float equatorialCircumferenceMeters = EarthConstants.TWO_PI
052: * EarthConstants.equatorialRadiusMeters;
053:
054: // from org.cougaar.glm.util, by Ken A:
055: // Modified to remove reliance on other Cougaar classes
056: /**
057: * Compute great-circle distance (expressed as float Kilometers)
058: * between two points on globe (expressed as LatLonPoint)
059: * @param position1 LatLonPoint of first point
060: * @param position2 LatLonPoint of second point
061: * @return double distance in KM of great-circle distance between points
062: */
063: public static float DistanceBetweenLatLonPoints(
064: LatLonPoint position1, LatLonPoint position2) {
065: // Get distance as KM
066: return EarthConstants.DistanceBetweenPoints(position1
067: .getLatitude(), position1.getLongitude(), position2
068: .getLatitude(), position2.getLongitude());
069: }
070:
071: /**
072: * Compute great-circle distance in KM between two points on globe
073: * expressed as latitude and longitude.
074: * @param latitude1 float latitude of first point (decimal degrees)
075: * @param longitude1 float longitude of first point (decimal degrees)
076: * @param latitude2 float latitude of second point (decimal degrees)
077: * @param longitude2 float longitude of second point (decimal degrees)
078: * @return float great-circle distance between two points in KM
079: */
080: public static float DistanceBetweenPoints(float latitude1,
081: float longitude1, float latitude2, float longitude2) {
082: // Convert arguments to Radians
083: double lon1_rad = Math.toRadians(longitude1);
084: double lat1_rad = Math.toRadians(latitude1);
085: double lon2_rad = Math.toRadians(longitude2);
086: double lat2_rad = Math.toRadians(latitude2);
087:
088: // Convert to 3-D Cartesian coordinates (X,Y,Z with earth center at 0,0,0)
089: double node_1_x = Math.cos(lat1_rad) * Math.cos(lon1_rad);
090: double node_1_y = Math.cos(lat1_rad) * Math.sin(lon1_rad);
091: double node_1_z = Math.sin(lat1_rad);
092:
093: double node_2_x = Math.cos(lat2_rad) * Math.cos(lon2_rad);
094: double node_2_y = Math.cos(lat2_rad) * Math.sin(lon2_rad);
095: double node_2_z = Math.sin(lat2_rad);
096:
097: // Calculate Cross-Product
098: double cross_x = (node_1_y * node_2_z) - (node_1_z * node_2_y);
099: double cross_y = (node_1_z * node_2_x) - (node_1_x * node_2_z);
100: double cross_z = (node_1_x * node_2_y) - (node_1_y * node_2_x);
101:
102: // Calculate the length of the Cross-Product
103: double norm_cross = Math.sqrt((cross_x * cross_x)
104: + (cross_y * cross_y) + (cross_z * cross_z));
105:
106: // Calculate the Dot-Product
107: double dot_product = (node_1_x * node_2_x)
108: + (node_1_y * node_2_y) + (node_1_z * node_2_z);
109:
110: // Calculate the central angle
111: double angle = Math.atan2(norm_cross, dot_product);
112:
113: // Calculate the great-circle distance
114: float distance = (float) (EarthConstants.equatorialRadiusMeters
115: * angle / 1000.0d);
116:
117: return distance;
118: }
119:
120: // Great circle calculations
121: // From Openmap: com.bbn.openmap.proj.GreatCircle:
122: // uses LatLonPoint
123: /**
124: * Calculate spherical arc distance between two points.
125: * <p>
126: * Computes arc distance `c' on the sphere. equation
127: * (5-3a). (0 <= c <= PI)
128: * <p>
129: * @param phi1 latitude in radians of start point
130: * @param lambda0 longitude in radians of start point
131: * @param phi latitude in radians of end point
132: * @param lambda longitude in radians of end point
133: * @return float arc distance `c' in radians
134: */
135: public static final float spherical_distance(float phi1,
136: float lambda0, float phi, float lambda) {
137: float pdiff = (float) Math.sin(((phi - phi1) / 2));
138: float ldiff = (float) Math.sin((lambda - lambda0) / 2);
139: float rval = (float) Math.sqrt((pdiff * pdiff)
140: + (float) Math.cos(phi1) * (float) Math.cos(phi)
141: * (ldiff * ldiff));
142:
143: return 2.0f * (float) Math.asin(rval);
144: }
145:
146: /**
147: * Calculate spherical arc distance between two points.
148: * <p>
149: * Computes arc distance `c' on the sphere. equation
150: * (5-3a). (0 <= c <= PI)
151: * <p>
152: * @param p1 <code>LatLonPoint</code> of first point
153: * @param p2 <code>LatLonPoint</code> of second point
154: * @return float arc distance `c' in radians
155: */
156: public static final float spherical_distance(LatLonPoint p1,
157: LatLonPoint p2) {
158: return spherical_distance(p1.radlat_, p1.radlon_, p2.radlat_,
159: p2.radlon_);
160: }
161:
162: /**
163: * Calculate spherical arc distance between two points.
164: * <p>
165: * Computes arc distance `c' on the sphere. equation
166: * (5-3a). (0 <= c <= PI)
167: * <p>
168: * @param lat1 latitude in degrees of start point
169: * @param lon1 longitude in degrees of start point
170: * @param lat2 latitude in degrees of end point
171: * @param lon2 longitude in degrees of end point
172: * @return float arc distance `c' in radians
173: */
174: public static final float spherical_distance_deg(float lat1,
175: float lon1, float lat2, float lon2) {
176: return spherical_distance(EarthConstants.degToRad(lat1),
177: EarthConstants.degToRad(lon1), EarthConstants
178: .degToRad(lat2), EarthConstants.degToRad(lon2));
179: }
180:
181: /**
182: * Calculate point at azimuth and distance from another point.
183: * <p>
184: * Returns a LatLonPoint at arc distance `c' in direction `Az'
185: * from start point.
186: * <p>
187: * @param phi1 latitude in radians of start point
188: * @param lambda0 longitude in radians of start point
189: * @param c arc radius in radians (0 < c <= PI)
190: * @param Az azimuth (direction) east of north (-PI <= Az < PI)
191: * @return LatLonPoint
192: */
193: public static final LatLonPoint spherical_between(float phi1,
194: float lambda0, float c, float Az) {
195: float cosphi1 = (float) Math.cos(phi1);
196: float sinphi1 = (float) Math.sin(phi1);
197: float cosAz = (float) Math.cos(Az);
198: float sinAz = (float) Math.sin(Az);
199: float sinc = (float) Math.sin(c);
200: float cosc = (float) Math.cos(c);
201:
202: return new LatLonPoint(EarthConstants.radToDeg((float) Math
203: .asin(sinphi1 * cosc + cosphi1 * sinc * cosAz)),
204: EarthConstants.radToDeg((float) Math.atan2(
205: sinc * sinAz, cosphi1 * cosc - sinphi1 * sinc
206: * cosAz)
207: + lambda0));
208: }
209:
210: /**
211: * Calculate point at azimuth and distance from another point.
212: * <p>
213: * Returns a LatLonPoint at arc distance `c' in direction `Az'
214: * from start point.
215: * <p>
216: * @param point <code>LatLonPoint</code> of start
217: * @param c arc radius in radians (0 < c <= PI)
218: * @param Az azimuth (direction) east of north (-PI <= Az < PI) in radians
219: * @return LatLonPoint
220: */
221: public static final LatLonPoint spherical_between(
222: LatLonPoint point, float c, float Az) {
223: return spherical_between(point.radlat_, point.radlon_, c, Az);
224: }
225:
226: /**
227: * Calculate point at azimuth and distance from another point.
228: * <p>
229: * Returns a LatLonPoint at arc distance `c' in direction `Az'
230: * from start point.
231: * <p>
232: * @param lat latitude in degrees of start point
233: * @param lon longitude in degrees of start point
234: * @param c arc radius in radians (0 < c <= PI)
235: * @param Az azimuth (direction) east of north (-PI <= Az < PI)
236: * @return LatLonPoint
237: */
238: public static final LatLonPoint spherical_between_degs(float lat,
239: float lon, float c, float Az) {
240: return spherical_between(EarthConstants.degToRad(lat),
241: EarthConstants.degToRad(lon), c, Az);
242: }
243:
244: // From Openmaps' com.bbn.openmap.proj.ProjMath:
245: /**
246: * Convert radians to degrees.
247: * @param rad radians
248: * @return double decimal degrees
249: */
250: public static final double radToDeg(double rad) {
251: return (rad * (180.0d / Math.PI));
252: }
253:
254: /**
255: * Convert radians to degrees.
256: * @param rad radians
257: * @return float decimal degrees
258: */
259: public static final float radToDeg(float rad) {
260: return (rad * (180.0f / (float) Math.PI));
261: }
262:
263: /**
264: * Convert degrees to radians.
265: * @param deg degrees
266: * @return double radians
267: */
268: public static final double degToRad(double deg) {
269: return (deg * (Math.PI / 180.0d));
270: }
271:
272: /**
273: * Convert degrees to radians.
274: * @param deg degrees
275: * @return float radians
276: */
277: public static final float degToRad(float deg) {
278: return (deg * ((float) Math.PI / 180.0f));
279: }
280:
281: /**
282: * Convert the meters distance into arcRadius on the earth.
283: * @param meters distance, should be <= the Earth's Circumference.
284: * @return float radians arc radius (0 < ret <= PI)
285: */
286: public static final float arcRadius(float meters) {
287: float arc = meters / EarthConstants.equatorialRadiusMeters;
288:
289: if (arc > Math.PI) {
290: arc = (float) (2 * Math.PI - arc);
291: }
292:
293: return arc;
294: }
295:
296: /**
297: * Generate a hashCode value for a lat/lon pair.
298: * @param lat latitude
299: * @param lon longitude
300: * @return int hashcode
301: */
302: public static final int hashLatLon(float lat, float lon) {
303: if (lat == -0f)
304: lat = 0f;//handle negative zero (anything else?)
305: if (lon == -0f)
306: lon = 0f;
307: int tmp = Float.floatToIntBits(lat);
308: int hash = (tmp << 5) | (tmp >> 27);//rotate the lat bits
309: return hash ^ Float.floatToIntBits(lon);//XOR with lon
310: }
311:
312: // From com.bbn.openmap.proj.MoreMath
313: /**
314: * Checks if a ~= b.
315: * Use this to test equality of floating point numbers.
316: * <p>
317: * @param a double
318: * @param b double
319: * @param epsilon the allowable error
320: * @return boolean
321: */
322: public static final boolean approximately_equal(double a, double b,
323: double epsilon) {
324: return (Math.abs(a - b) <= epsilon);
325: }
326:
327: /**
328: * Checks if a ~= b.
329: * Use this to test equality of floating point numbers.
330: * <p>
331: * @param a float
332: * @param b float
333: * @param epsilon the allowable error
334: * @return boolean
335: */
336: public static final boolean approximately_equal(float a, float b,
337: float epsilon) {
338: return (Math.abs(a - b) <= epsilon);
339: }
340: } // EarthConstants.java
|