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 java.util.Calendar;
030: import java.util.Date;
031: import java.util.Iterator;
032: import java.util.Set;
033:
034: import org.cougaar.glm.ldm.GLMFactory;
035: import org.cougaar.glm.ldm.asset.Organization;
036: import org.cougaar.glm.ldm.plan.GeolocLocation;
037: import org.cougaar.glm.ldm.plan.NewGeolocLocation;
038: import org.cougaar.lib.util.UTILPluginException;
039: import org.cougaar.planning.ldm.measure.Distance;
040: import org.cougaar.planning.ldm.measure.Latitude;
041: import org.cougaar.planning.ldm.measure.Longitude;
042: import org.cougaar.util.log.Logger;
043:
044: /**
045: * This class contains utility functions for measurements.
046: */
047:
048: public class GLMMeasure {
049: private static String myName = "GLMMeasure";
050:
051: // This section is for calculating great-circle distances for timing estimates
052: // useful constants
053: private static double EARTH_RADIUS = 3437.75d; // nmi
054: private static double DEGREES_TO_RADIANS = (3.1415927d / 180.0d);
055:
056: public GLMMeasure(Logger logger) {
057: this .logger = logger;
058: // assetHelper = new AssetUtil (logger);
059: }
060:
061: /**
062: * Utility function to calculate the distance between two locations
063: * @param start GeolocLocation starting point
064: * @param end GeolocLocation ending point
065: * @return Distance between the two points
066: */
067: public Distance distanceBetween(GeolocLocation start,
068: GeolocLocation end) {
069: return distanceBetween(start, end, 1.0);
070: }
071:
072: /**
073: * Utility function to calculate the distance between two locations
074: * @param start GeolocLocation starting point
075: * @param end GeolocLocation ending point
076: * @param multiplier Multiplier for the final dist result
077: * @return Distance between the two points
078: */
079: public Distance distanceBetween(GeolocLocation start,
080: GeolocLocation end, double multiplier) {
081:
082: if (start == null)
083: throw new UTILPluginException("start geoloc is null");
084: if (end == null)
085: throw new UTILPluginException("end geoloc is null");
086:
087: // get Long/Lat
088: Longitude startlong = start.getLongitude();
089: Latitude startlat = start.getLatitude();
090: Longitude endlong = end.getLongitude();
091: Latitude endlat = end.getLatitude();
092:
093: if (startlong == null)
094: throw new UTILPluginException(
095: "startlong is null in start GeolocLocation");
096: if (startlat == null)
097: throw new UTILPluginException(
098: "startlat is null in start GeolocLocation");
099: if (endlong == null)
100: throw new UTILPluginException(
101: "endlong is null in end GeolocLocation");
102: if (endlat == null)
103: throw new UTILPluginException(
104: "endlat is null in end GeolocLocation");
105: if ((startlong.getDegrees() == 0.0d)
106: && (startlat.getDegrees() == 0.0d))
107: logger.debug("distanceBetween - Geoloc " + start
108: + " has lat = lon = 0.0?");
109: if ((endlong.getDegrees() == 0.0d)
110: && (endlat.getDegrees() == 0.0d))
111: logger.debug("distanceBetween - Geoloc " + end
112: + " has lat = lon = 0.0?");
113:
114: // get radian measures
115: double startlongrad = (startlong.getDegrees() * DEGREES_TO_RADIANS);
116: double startlatrad = (startlat.getDegrees() * DEGREES_TO_RADIANS);
117: double endlongrad = (endlong.getDegrees() * DEGREES_TO_RADIANS);
118: double endlatrad = (endlat.getDegrees() * DEGREES_TO_RADIANS);
119:
120: // calculate distance
121: double deltalong = startlongrad - endlongrad;
122: double distinradians = Math.acos(Math.sin(startlatrad)
123: * Math.sin(endlatrad) + Math.cos(startlatrad)
124: * Math.cos(endlatrad) * Math.cos(deltalong));
125: double retval = EARTH_RADIUS * distinradians;
126:
127: return Distance.newNauticalMiles(retval * multiplier);
128: }
129:
130: /**
131: * This is a helper function to generate geo loc codes.
132: * It is used in TOPSGlobalGroundAllocatorPlugin.getOrgLocation()
133: * to initialize the static locations
134: * of the known organizations. In the long term, it is hoped that
135: * this can be phased out in favor of a native ALPINE mechanism.
136: *
137: * @param code String representation of Geoloc code
138: * @param name String name of organization
139: * @param longd double degrees longitude
140: * @param latd double degrees latitude
141: * @return GeolocLocation initialized to input values
142: * @deprecated
143: */
144: public GeolocLocation makeGeoloc(GLMFactory fac, String code,
145: String name, double longd, double latd) {
146:
147: NewGeolocLocation retval = fac.newGeolocLocation();
148: retval.setGeolocCode(code);
149: retval.setName(name);
150: retval.setLatitude(Latitude.newDegrees(latd));
151: retval.setLongitude(Longitude.newDegrees(longd));
152: return retval;
153: }
154:
155: /**
156: * Utility function to shift dates around. This is helpful
157: * if you need to make a Date object that is "x hours before/after
158: * some reference date".
159: * For example, READYAT dates could be 1 day before mission date.
160: * @param d Date object representing the reference date
161: * @param factor float representing number of hours to shift
162: * @param direction int, should be either 1 or -1.
163: * 1 indicates shifting forward, -1 indicates shifting backward.
164: * @return Date the shifted date.
165: */
166: public Date dateShift(Date d, float factor, int direction) {
167: long x = (long) (factor * 60 * 60 * 1000 * direction);
168: Date newDate = new Date();
169: if ((direction == 1) || (direction == -1)) {
170: newDate.setTime(d.getTime() + x);
171: return (newDate);
172: } else {
173: throw new UTILPluginException("illegal direction received.");
174: }
175: }
176:
177: /**
178: * Utility functions to decode Date objects.
179: * Given a Date object representing anytime of the day,
180: * returns a Date object representing 12:00AM of that
181: * same day.
182: * @param d Date object representing the reference date
183: * @return Date, midnight of d.
184: */
185: public Date decodeDate(Date d) {
186: // long millis = d.getTime();
187: // Date midnight = new Date(millis - (millis % 86400000));
188: Calendar cal = Calendar.getInstance();
189: cal.setTime(d);
190: int year = cal.get(Calendar.YEAR);
191: int month = cal.get(Calendar.MONTH);
192: int day = cal.get(Calendar.DAY_OF_MONTH);
193: cal.clear();
194: cal.set(year, month, day);
195:
196: // Date midnight = new Date(millis - secondsToday);
197: Date midnight = cal.getTime();
198: return midnight;
199: }
200:
201: /**
202: * test if location is US or foreign
203: * What we really want to know is if the port is a POE or POD; is there a
204: * better way? Should we just create a list of all ports and find the
205: * closest each time we need one?
206: * @return true if is a foreign port
207: */
208: public boolean isForeignLoc(GeolocLocation org_loc) {
209: boolean foreign_org = true;
210:
211: // For the sake of this demo, the US is bounded by the following cities:
212: // Morgan, MT 48:59:36 N (above) 48.9933
213: // Quoddy, ME 67:01:51 W (east) -67.0308
214: // Key West, FL 24:33:00 N (below) 24.5500
215: // Eureka, CA 124:09:45 W (west) -124.1625
216: // (data from www.mit.edu:8001/geo)
217:
218: double lat = org_loc.getLatitude().getDegrees();
219: double lon = org_loc.getLongitude().getDegrees();
220:
221: if (lon < -67.0308d && lon > -124.1625d && lat > 24.5500d
222: && lat < 48.9933d)
223: foreign_org = false;
224:
225: return foreign_org;
226: }
227:
228: /**
229: * given a geoloclocation and set of possible orgs, suggest
230: * the best (closest) org.
231: *
232: * @param orgs Set of possible orgs
233: * @param clusterName cluster ident. for logger.isDebugEnabled()ging purposes
234: * @return Organization object indicating best org
235: */
236: public Organization bestOrg(GeolocLocation loc, Set orgs,
237: String clusterName) {
238:
239: if (orgs == null || orgs.size() == 0) {
240: throw new UTILPluginException(clusterName
241: + "GLMMeasure: no orgs available");
242: }
243:
244: if (loc == null) {
245: // This is a lie- it doesn't actually return anything, does it? it
246: // just dies...
247: throw new UTILPluginException(
248: clusterName
249: + "GLMMeasure: null loc while checking for orgs, returning default org");
250: }
251:
252: Iterator orgse = orgs.iterator();
253: Organization best = null;
254: double distance = 0.0d;
255:
256: while (orgse.hasNext()) {
257: Organization nextorg = (Organization) orgse.next();
258:
259: double nextdistance = 9999.0d;
260: GeolocLocation orgloc = (GeolocLocation) nextorg
261: .getMilitaryOrgPG().getHomeLocation();
262:
263: // if string codes match, we don't have to look at distances...
264: if (orgloc.getGeolocCode().equals(loc.getGeolocCode()))
265: return nextorg;
266:
267: if (orgloc != null && orgloc.getLongitude() != null
268: && orgloc.getLatitude() != null
269: && loc.getLongitude() != null
270: && loc.getLatitude() != null) {
271: nextdistance = distanceBetween(loc, orgloc).getMiles();
272: if (logger.isDebugEnabled())
273: logger.debug("Dist between loc " + loc
274: + " and orglog " + orgloc + " is "
275: + nextdistance);
276: }
277:
278: if (best == null) {
279: best = nextorg;
280: distance = nextdistance;
281: }
282:
283: else if (nextdistance < distance) {
284: best = nextorg;
285: distance = nextdistance;
286: if (logger.isDebugEnabled())
287: logger.debug("Best is " + best);
288: }
289: }
290:
291: if (best == null) {
292: throw new UTILPluginException(clusterName
293: + " GLMMeasure saw no appropriate orgs");
294: }
295: return best;
296: }
297:
298: protected Logger logger;
299: // protected AssetUtil assetHelper;
300: }
|