001: /*
002: * <copyright>
003: *
004: * Copyright 1997-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:
027: package org.cougaar.glm.util;
028:
029: import org.cougaar.glm.ldm.plan.Position;
030: import org.cougaar.planning.ldm.measure.Distance;
031: import org.cougaar.planning.ldm.measure.Latitude;
032: import org.cougaar.planning.ldm.measure.Longitude;
033: import org.cougaar.util.log.Logger;
034: import org.cougaar.util.log.LoggerFactory;
035:
036: /**
037: * A class containing static methods for geographic computations,
038: * particularly those related to great-circle distance calculations
039: */
040: public class GeoUtils {
041:
042: // Circumference of earth in KM (around equator)
043: public static final double EARTH_CIRCUMFERENCE = 40075.16;
044:
045: // Radius of earth in KM
046: public static final double EARTH_RADIUS = EARTH_CIRCUMFERENCE
047: / (Math.PI * 2.0);
048:
049: /**
050: * Compute great-circle distance (expressed as COUGAAR Distance measure)
051: * between two points on globe (expressed as COUGAAR Position)
052: * @param position1 of first point
053: * @param position2 of second point
054: * @return Distance of great-circle distance between points
055: */
056: public static Distance DistanceBetweenPositions(Position position1,
057: Position position2) {
058: // Get distance as KM
059: double distance = DistanceBetweenPositions(position1
060: .getLatitude().getDegrees(), position1.getLongitude()
061: .getDegrees(), position2.getLatitude().getDegrees(),
062: position2.getLongitude().getDegrees());
063:
064: // Return as Distance
065: return Distance.newKilometers(distance);
066: }
067:
068: /**
069: * Compute great-circle distance in KM between two points on globe
070: * expressed as latitude and longitude.
071: * @param latitude1 of first point (degrees)
072: * @param longitude1 of first point (degrees)
073: * @param latitude2 of second point (degrees)
074: * @param longitude2 of second point (degrees)
075: * @return double great-circle distance between two points
076: */
077: public static double DistanceBetweenPositions(double latitude1,
078: double longitude1, double latitude2, double longitude2) {
079: // Convert arguments to Radians
080: double lon1_rad = Math.toRadians(longitude1);
081: double lat1_rad = Math.toRadians(latitude1);
082: double lon2_rad = Math.toRadians(longitude2);
083: double lat2_rad = Math.toRadians(latitude2);
084:
085: // Convert to 3-D Cartesian coordinates (X,Y,Z with earth center at 0,0,0)
086: double node_1_x = Math.cos(lat1_rad) * Math.cos(lon1_rad);
087: double node_1_y = Math.cos(lat1_rad) * Math.sin(lon1_rad);
088: double node_1_z = Math.sin(lat1_rad);
089:
090: double node_2_x = Math.cos(lat2_rad) * Math.cos(lon2_rad);
091: double node_2_y = Math.cos(lat2_rad) * Math.sin(lon2_rad);
092: double node_2_z = Math.sin(lat2_rad);
093:
094: // Calculate Cross-Product
095: double cross_x = (node_1_y * node_2_z) - (node_1_z * node_2_y);
096: double cross_y = (node_1_z * node_2_x) - (node_1_x * node_2_z);
097: double cross_z = (node_1_x * node_2_y) - (node_1_y * node_2_x);
098:
099: // Calculate the length of the Cross-Product
100: double norm_cross = Math.sqrt((cross_x * cross_x)
101: + (cross_y * cross_y) + (cross_z * cross_z));
102:
103: // Calculate the Dot-Product
104: double dot_product = (node_1_x * node_2_x)
105: + (node_1_y * node_2_y) + (node_1_z * node_2_z);
106:
107: // Calculate the central angle
108: double angle = Math.atan2(norm_cross, dot_product);
109:
110: // Calculate the great-circle distance
111: double distance = EARTH_RADIUS * angle;
112:
113: return distance;
114: }
115:
116: /* only used for isolated main ()-style testing */
117: private static Logger logger = LoggerFactory.getInstance()
118: .createLogger("GeoUtils");
119:
120: // Test procedure on great-circle calculations
121: public static void main(String[] args) {
122: logger.debug("GeoUtils...");
123:
124: // One degree longitude at equator should be ~111 KM.
125: double one_degree_longitude_at_equator = DistanceBetweenPositions(
126: 0.0, 0.0, 0.0, 1.0);
127: logger.debug("One degree longitude at equator = "
128: + one_degree_longitude_at_equator);
129:
130: // One degree latitude at equator should be ~111 KM.
131: double one_degree_latitude_at_equator = DistanceBetweenPositions(
132: 0.0, 0.0, 1.0, 0.0);
133: logger.debug("One degree latitude at equator = "
134: + one_degree_latitude_at_equator);
135:
136: // Distance between Boston and New York should be 183.9 mi (296 km)
137: // Distance between NYC and LA should be 2464 mi (3943 km)
138: // (as the crow flies...).
139: // Boston is 42-15'N, 71-07'W
140: // NYC is 40.40'N, 73-58'W
141: // LA is 34.03'N, 118.14'W
142: double BOS_LAT = 42.0 + (15.0 / 60.0);
143: double BOS_LON = -(71.0 + (7.0 / 60.0));
144: double NY_LAT = 40.0 + (40.0 / 60.0);
145: double NY_LON = -(73.0 + (58.0 / 60.0));
146: double LA_LAT = 34.0 + (3.0 / 60.0);
147: double LA_LON = -(118.0 + (14.0 / 60.0));
148: logger.debug("Distance between NYC and LA (km) = "
149: + DistanceBetweenPositions(NY_LAT, NY_LON, LA_LAT,
150: LA_LON));
151: logger.debug("Distance between LA and NEW YORK (km) = "
152: + DistanceBetweenPositions(LA_LAT, LA_LON, NY_LAT,
153: NY_LON));
154: logger.debug("Distance between BOSTON and NEW YORK (km) = "
155: + DistanceBetweenPositions(BOS_LAT, BOS_LON, NY_LAT,
156: NY_LON));
157: Position BOSTON = new org.cougaar.glm.ldm.plan.PositionImpl(
158: Latitude.newLatitude(BOS_LAT), Longitude
159: .newLongitude(BOS_LON));
160: Position NEW_YORK = new org.cougaar.glm.ldm.plan.PositionImpl(
161: Latitude.newLatitude(NY_LAT), Longitude
162: .newLongitude(NY_LON));
163: Distance distance_between_BOS_and_NY = DistanceBetweenPositions(
164: BOSTON, NEW_YORK);
165: logger.debug("Distance between BOSTON and NEW YORK (miles) = "
166: + distance_between_BOS_and_NY.getMiles());
167: }
168: }
|