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.logistics.plugin.trans.tools;
027:
028: import java.util.Set;
029: import java.util.HashSet;
030: import java.util.HashMap;
031: import java.util.Map;
032: import java.util.Iterator;
033: import java.util.Enumeration;
034: import java.util.Collection;
035: import java.util.Collections;
036:
037: import org.cougaar.core.blackboard.IncrementalSubscription;
038: import org.cougaar.planning.ldm.PlanningFactory;
039:
040: import org.cougaar.glm.callback.GLMLocationListener;
041: import org.cougaar.glm.callback.GLMLocationCallback;
042:
043: import org.cougaar.glm.ldm.asset.GLMAsset;
044: import org.cougaar.glm.ldm.asset.TransportationNode;
045: import org.cougaar.glm.ldm.asset.TransportationRoute;
046: import org.cougaar.glm.ldm.asset.TransportationSeaLink;
047:
048: import org.cougaar.glm.ldm.plan.GeolocLocation;
049: import org.cougaar.glm.ldm.plan.NewGeolocLocation;
050:
051: import org.cougaar.logistics.plugin.trans.SequentialGlobalAirPlugin;
052:
053: import org.cougaar.glm.util.GLMMeasure;
054: import org.cougaar.glm.util.GLMPrepPhrase;
055:
056: import org.cougaar.lib.filter.UTILPluginAdapter;
057:
058: import org.cougaar.planning.ldm.asset.Asset;
059: import org.cougaar.planning.ldm.plan.Task;
060:
061: import org.cougaar.planning.ldm.measure.Distance;
062: import org.cougaar.planning.ldm.measure.Latitude;
063: import org.cougaar.planning.ldm.measure.Longitude;
064:
065: import org.cougaar.planning.ldm.plan.Location;
066:
067: import org.cougaar.util.UnaryPredicate;
068: import org.cougaar.util.log.Logger;
069:
070: /**
071: * An object for finding the correct POE or POD for a given geoloc
072: */
073: public class LocatorImpl implements Locator {
074: public Set myLocations;
075:
076: protected Map geolocToAsset = new HashMap();
077: protected Map assetToLocation = new HashMap();
078:
079: private BlackboardPlugin myPlugin;
080: protected GLMLocationCallback locationCallback = null;
081: protected RouteFinder routeFinder;
082: protected Logger logger;
083: protected GLMMeasure measureHelper;
084: protected double MAX_GROUND_DISTANCE;
085: private static double EARTH_RADIUS = Distance.newNauticalMiles(
086: 3437.75d).getMiles(); // originally nmi
087:
088: public void setFactory(PlanningFactory ldmf) {
089: routeFinder = new RouteFinder(logger);
090: routeFinder.setFactory(ldmf);
091:
092: try {
093: MAX_GROUND_DISTANCE = (myPlugin.getMyParams()
094: .hasParam("MAX_GROUND_DISTANCE")) ? myPlugin
095: .getMyParams()
096: .getDoubleParam("MAX_GROUND_DISTANCE") : 400.0;
097: } catch (Exception e) {
098: logger.warn("got really unexpected exception " + e);
099: } // never happen
100: }
101:
102: /*
103: * Locator must be instantiated within the setupFilters() method on its
104: * parent plugIn. This is because the LocationCallback has to be
105: * instantiated within the context of an open transaction.
106: */
107: public LocatorImpl(BlackboardPlugin pi, Logger logger) {
108: this .logger = logger;
109: myPlugin = pi;
110: pi.addFilter(locationCallback = new GLMLocationCallback(this ,
111: logger));
112: myLocations = new HashSet();
113:
114: if (pi.getBlackboard().didRehydrate())
115: handleNewLocations(locationCallback.getSubscription()
116: .elements());
117: measureHelper = new GLMMeasure(logger);
118: glmPrepHelper = new GLMPrepPhrase(logger);
119: }
120:
121: /*********** Implemented for GLMLocationListener ************************/
122:
123: // By default is interested in all TransportationNode assets
124: // Override to filter for specific kinds of transportation nodes.
125: public boolean interestingLocation(TransportationNode location) {
126: return true;
127: }
128:
129: // Adds new locations to the set of geolocs the locator know about
130: public void handleNewLocations(Enumeration newLocations) {
131: while (newLocations.hasMoreElements()) {
132: TransportationNode locationAsset = (TransportationNode) newLocations
133: .nextElement();
134: GeolocLocation geoloc = (GeolocLocation) locationAsset
135: .getPositionPG().getPosition();
136: String geolocCode = geoloc.getGeolocCode();
137: geolocToAsset.put(geolocCode, locationAsset);
138: assetToLocation.put(locationAsset, geoloc);
139:
140: if (logger.isDebugEnabled())
141: logger.debug(".handleNewLocations mapping <" + geoloc
142: + "> to <" + locationAsset + ">");
143:
144: myLocations.add(geoloc);
145: }
146: }
147:
148: // Don't worry about changed locations
149: public void handleChangedLocations(Enumeration changedLocations) {
150: }
151:
152: /*********** Implemented for UTILFilterCallbackListener ************/
153:
154: // All listeners must be able to create a subscription
155: public IncrementalSubscription subscribeFromCallback(
156: UnaryPredicate pred) {
157: return myPlugin.subscribeFromCallback(pred);
158: }
159:
160: // All listeners must be able to create a subscription with a special container
161: public IncrementalSubscription subscribeFromCallback(
162: UnaryPredicate pred, Collection specialContainer) {
163: return myPlugin.subscribeFromCallback(pred, specialContainer);
164: }
165:
166: /*********************** Implemented for Locator ************************/
167:
168: public boolean isKnownGeolocCode(String geoloc) {
169: return (geolocToAsset.get(geoloc) != null);
170: }
171:
172: public Set knownGeolocCodes() {
173: return geolocToAsset.keySet();
174: }
175:
176: public Object getAssetAtGeolocCode(String geoloc) {
177: return geolocToAsset.get(geoloc);
178: }
179:
180: public Location getLocationForGeolocCode(String geoloc) {
181: Object asset = geolocToAsset.get(geoloc);
182: return (asset != null) ? (Location) assetToLocation.get(asset)
183: : null;
184: }
185:
186: public Location getLocationOfAsset(Object asset) {
187: return (Location) assetToLocation.get(asset);
188: }
189:
190: public void addLocationOfAsset(Object asset, Location loc) {
191: assetToLocation.put(asset, loc);
192: }
193:
194: /**
195: * First check from to see if it's an airport, otherwise, must be a fort
196: * If so, lookup airbase (APOE) nearest fort.
197: *
198: * @return NULL probably only when no locations have been read in -- an ERROR
199: * otherwise, the nearest location
200: **/
201: public Location getPOENearestToFromLoc(Task parentTask) {
202: String origin = glmPrepHelper.getFromLocation(parentTask)
203: .getGeolocCode();
204: Object airport = getAssetAtGeolocCode(origin);
205: if (airport != null)
206: return getLocationOfAsset(airport);
207:
208: return getPOENearestToFromLoc(parentTask, Collections.EMPTY_SET);
209: }
210:
211: public Location getPOENearestToFromLoc(Task parentTask,
212: Collection exceptions) {
213: String origin = glmPrepHelper.getFromLocation(parentTask)
214: .getGeolocCode();
215: // Object airport = geolocToAirport.get (origin);
216: Object airport = getAssetAtGeolocCode(origin);
217: if (airport != null)
218: // return (Location) airportToLocation.get (airport);
219: return getLocationOfAsset(airport);
220:
221: Location poe = getNearestLocationExcept(glmPrepHelper
222: .getFromLocation(parentTask), exceptions);
223:
224: if (poe == null)
225: logger
226: .error(".getPOENearestToFromLoc - could not find POD for task "
227: + parentTask.getUID()
228: + " going to "
229: + glmPrepHelper.getFromLocation(parentTask)
230: + ", though I can choose "
231: + "from "
232: + getNumKnownLocations()
233: + " known locations.");
234: return poe;
235: }
236:
237: /**
238: * Returns geoloc with the closest cartesian distance as calculated
239: * in an ad hoc way from lat and long.
240: */
241: public GeolocLocation getNearestLocation(GeolocLocation geoloc) {
242: return getNearestLocationExcept(geoloc, Collections.EMPTY_SET);
243: }
244:
245: /**
246: * Returns geoloc with the closest cartesian distance as calculated
247: * in an ad hoc way from lat and long.
248: */
249: public GeolocLocation getNearestLocationExcept(
250: GeolocLocation geoloc, Collection exceptions) {
251: GeolocLocation nearestGeoloc = null;
252: double shortestDistance = 1000000; // larger than any actual cartesian distance
253: Set restrictedLocations = new HashSet(myLocations);
254: restrictedLocations.removeAll(exceptions);
255:
256: for (Iterator i = restrictedLocations.iterator(); i.hasNext();) {
257: GeolocLocation currentGeoloc = (GeolocLocation) i.next();
258: double distance = findCartesianDistance(currentGeoloc,
259: geoloc);
260:
261: // logger.debug("Trying geoloc " + currentGeoloc + " with distance " + distance);
262: if (distance < shortestDistance) {
263: // logger.debug("Previous geoloc was the closest match so far");
264: shortestDistance = distance;
265: nearestGeoloc = currentGeoloc;
266: }
267: }
268:
269: // logger.debug("Location.getNearestLocation() - found location " + nearestGeoloc + " for " + geoloc);
270: return nearestGeoloc;
271: }
272:
273: /**
274: * <pre>
275: * Returns geoloc with the closest distance as calculated
276: * using ship routes
277: *
278: * </pre>
279: */
280: public TransportationRoute getRoute(GeolocLocation fromGeoloc,
281: GeolocLocation toGeoloc, Collection exceptions) {
282: GeolocLocation POE = getNearestLocationExcept(fromGeoloc,
283: exceptions);
284: if (POE == null)
285: logger.warn("Locator chose POE " + POE + " nearest to "
286: + fromGeoloc + " from among " + myLocations
287: + "\nexceptions " + exceptions);
288:
289: GeolocLocation POD = getNearestLocation(toGeoloc);
290: if (POD == null)
291: logger.warn("Locator chose POD " + POD + " nearest to "
292: + toGeoloc + " from among " + myLocations
293: + "\nexceptions " + exceptions);
294:
295: TransportationRoute route = routeFinder.getRoute(POE, POD);
296:
297: return routeFinder.makeRouteWithPOEandPOD(route, POE, POD);
298: }
299:
300: public int getNumKnownLocations() {
301: return myLocations.size();
302: }
303:
304: /**
305: * Note that this deals with the lat. and long. coordinates as if they
306: * existed in a cartesian coordinate plane. It doesn't deal with the
307: * non-Euclidean aspect of it.
308: */
309: private double findCartesianDistance(GeolocLocation geoloc1,
310: GeolocLocation geoloc2) {
311: double dlat = geoloc1.getLatitude().getValue(Latitude.DEGREES)
312: - geoloc2.getLatitude().getValue(Latitude.DEGREES);
313: double dlong = geoloc1.getLongitude().getValue(
314: Longitude.DEGREES)
315: - geoloc2.getLongitude().getValue(Longitude.DEGREES);
316:
317: if (dlong > 180)
318: dlong = 360 - dlong;
319:
320: return Math.sqrt(dlat * dlat + dlong * dlong);
321: }
322:
323: protected GLMPrepPhrase glmPrepHelper;
324: }
|