View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.navigation.floydwarshall;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.logging.Level;
9   import java.util.logging.Logger;
10  
11  import javax.vecmath.Point3d;
12  
13  import cz.cuni.amis.pogamut.base.agent.IGhostAgent;
14  import cz.cuni.amis.pogamut.base.agent.module.SensorModule;
15  import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture;
16  import cz.cuni.amis.pogamut.base.agent.navigation.IPathPlanner;
17  import cz.cuni.amis.pogamut.base.agent.navigation.impl.PrecomputedPathFuture;
18  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
19  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
20  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.NavigationGraphBuilder;
21  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Item;
22  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
23  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
24  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.MapPointListObtained;
25  import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
26  import cz.cuni.amis.utils.IFilter;
27  import cz.cuni.amis.utils.collections.MyCollections;
28  import cz.cuni.amis.utils.exception.PogamutException;
29  
30  /**
31   * Private map using Floyd-Warshall for path-finding.
32   * <p><p>
33   * It should be initialized upon receiving {@link MapPointListObtained} event.
34   * It precomputes all the paths inside the environment using Floyd-Warshall
35   * algorithm (O(n^3)). Use getReachable(), getDistance(), getPath() to obtain
36   * the info about the path.
37   * <p><p>
38   * If needed you may call {@link FloydWarshallMap#refreshPathMatrix()} to recompute Floyd-Warshall. Especially useful
39   * if you're using {@link NavigationGraphBuilder} to modify the underlying navpoints/edges.
40   * <p><p>
41   * Based upon the implementation from Martin Krulis with his kind consent -
42   * Thank you!
43   * <p><p>
44   * NOTE: requires O(navpoints.size^3) of memory ~ which is 10000^3 at max for
45   * UT2004 (usually the biggest maps have 3000 navpoints max, but small maps, e.g., DM-1on1-Albatross has 200 navpoints).
46   * <p><p>
47   * Because the algorithm is so space-hungery-beast, there is option to disable it
48   * by calling {@link FloydWarshallMap#setEnabled(boolean)} with 'false' argument. See the method for
49   * further documentation about the object behavior. 
50   * 
51   * @author Martin Krulis
52   * @author Jimmy
53   */
54  public class FloydWarshallMap extends SensorModule<IGhostAgent> implements IPathPlanner<NavPoint> {
55  
56  	public static class PathMatrixNode {
57  
58  		private float distance = Float.POSITIVE_INFINITY;
59  		private Integer viaNode = null;
60  		private List<NavPoint> path = null;
61  
62  		/**
63  		 * Doesn't leading anywhere
64  		 */
65  		public PathMatrixNode() {
66  		}
67  
68  		public PathMatrixNode(float distance) {
69  			this.distance = distance;
70  		}
71  
72  		public float getDistance() {
73  			return distance;
74  		}
75  
76  		public void setDistance(float distance) {
77  			this.distance = distance;
78  		}
79  
80  		/**
81  		 * Returns indice.
82  		 * 
83  		 * @return
84  		 */
85  		public Integer getViaNode() {
86  			return viaNode;
87  		}
88  
89  		public void setViaNode(Integer indice) {
90  			this.viaNode = indice;
91  		}
92  
93  		public List<NavPoint> getPath() {
94  			return path;
95  		}
96  
97  		public void setPath(List<NavPoint> path) {
98  			this.path = path;
99  		}
100 
101 	}
102 
103 	private IWorldEventListener<MapPointListObtained> mapListener = new IWorldEventListener<MapPointListObtained>() {
104 
105 		@Override
106 		public void notify(MapPointListObtained event) {
107 			if (log.isLoggable(Level.INFO)) log.info("Map point list obtained.");
108 			performFloydWarshall(event);
109 		}
110 	};
111 
112 	/**
113 	 * Flag mask representing unusable edge.
114 	 */
115 	public static final int BAD_EDGE_FLAG = LinkFlag.FLY.get()
116 			| LinkFlag.LADDER.get() | LinkFlag.PROSCRIBED.get()
117 			| LinkFlag.SWIM.get() | LinkFlag.PLAYERONLY.get();
118 	
119 	public static boolean isWalkable(int flag) {
120 		return ((flag & BAD_EDGE_FLAG) == 0) && ((flag & LinkFlag.SPECIAL.get()) == 0); 
121 	}
122 
123 	/**
124 	 * Prohibited edges.
125 	 */
126 	protected int badEdgeFlag = 0;
127 
128 	/**
129 	 * Hash table converting navPoint IDs to our own indices.
130 	 */
131 	protected Map<UnrealId, Integer> navPointIndices;
132 
133 	/**
134 	 * Mapping indices to nav points.
135 	 * <p>
136 	 * <p>
137 	 * WILL BE NULL AFTER CONSTRUCTION! SERVES AS A TEMPORARY "GLOBAL VARIABLE"
138 	 * DURING FLOYD-WARSHALL COMPUTATION AND PATH RECONSTRUCTION.
139 	 */
140 	protected Map<Integer, NavPoint> indicesNavPoints;
141 
142 	// Warshall's matrix of distances.
143 	protected PathMatrixNode[][] pathMatrix;
144 	
145 	/**
146 	 * Whether the this object is enabled and the path is going to be actually computed.
147 	 * <p><p>
148 	 * Enabled as default. 
149 	 */
150 	private boolean enabled = true;
151 	
152 	/**
153 	 * Synchronizing access to object with respect to {@link FloydWarshallMap#enabled}.
154 	 */
155 	protected Object mutex = new Object();
156 
157 	public FloydWarshallMap(IGhostAgent bot) {
158 		this(bot, BAD_EDGE_FLAG, null);
159 	}
160 
161 	public FloydWarshallMap(IGhostAgent bot, Logger log) {
162 		this(bot, BAD_EDGE_FLAG, log);
163 	}
164 
165 	public FloydWarshallMap(IGhostAgent bot, int badEdgeFlag, Logger log) {
166 		super(bot, log);
167 		this.badEdgeFlag = badEdgeFlag;
168 		worldView.addEventListener(MapPointListObtained.class, mapListener);
169 	}
170 	
171 	/**
172 	 * Whether the object is active, see {@link FloydWarshallMap#setEnabled(boolean)} for more documentation.
173 	 * <p><p>
174 	 * Default: true
175 	 * 
176 	 * @return
177 	 */
178 	public boolean isEnabled() {
179 		return enabled;
180 	}
181 
182 	/**
183 	 * Enables/disables object. As default, object is initialized as 'enabled'. 
184 	 * <p><p>
185 	 * If you "disable" the object (passing 'false' as an argument), it will {@link FloydWarshallMap#cleanUp()} itself
186 	 * dropping any info it has about paths, i.e., method {@link FloydWarshallMap#computePath(NavPoint, NavPoint)} will start throwing exceptions
187 	 * at you.
188 	 * <p><p>
189 	 * Note that if you "enable" the object (passing 'true' as an argument), it won't AUTOMATICALLY trigger the computation of the algorithm,
190 	 * you should manually {@link FloydWarshallMap#refreshPathMatrix()} when it is appropriate (unless you enable it before list of navpoints
191 	 * is received, in that case the path will get computed automatically).
192 	 * 
193 	 * @param enabled
194 	 */
195 	public void setEnabled(boolean enabled) {
196 		synchronized(mutex) {
197 			this.enabled = enabled;
198 			if (!enabled) {
199 				cleanUp();
200 			}
201 		}
202 	}
203 	
204 	
205 	/**
206 	 * Force FloydWarshall to run again, useful if you modify navpoints using {@link NavigationGraphBuilder}.
207 	 */
208 	public void refreshPathMatrix() {
209 		synchronized(mutex) {
210 			if (!enabled) {
211 				if (log.isLoggable(Level.WARNING)) log.fine(this + ": Won't refresh path matrix, object is disabled.");
212 				return;
213 			}
214 			if (log.isLoggable(Level.FINE)) log.fine(this + ": Refreshing path matrix...");
215 			List<NavPoint> navPoints = MyCollections.asList(agent.getWorldView().getAll(NavPoint.class).values());
216 			performFloydWarshall(navPoints);
217 			if (log.isLoggable(Level.WARNING)) log.warning(this + ": Path matrix refreshed!");
218 		}		
219 	}
220 	
221 	protected void performFloydWarshall(MapPointListObtained map) {
222 		List<NavPoint> navPoints = MyCollections.asList(map.getNavPoints().values());
223 		performFloydWarshall(navPoints);
224 	}
225 
226 	protected void performFloydWarshall(List<NavPoint> navPoints) {
227 		if (!enabled) {
228 			if (log.isLoggable(Level.WARNING)) log.fine(this + ": Should not be running Floyd-Warshall, object disabled.");
229 			return;
230 		}
231 		if (log.isLoggable(Level.FINE)) log.fine(this + ": Beginning Floyd-Warshall algorithm...");
232 		long start = System.currentTimeMillis();
233 
234 		// prepares data structures
235 		int size = navPoints.size();
236 		navPointIndices = new HashMap<UnrealId, Integer>(size);
237 		indicesNavPoints = new HashMap<Integer, NavPoint>(size);
238 		pathMatrix = new PathMatrixNode[size][size];
239 
240 		// Initialize navPoint indices mapping.
241 		for (int i = 0; i < navPoints.size(); ++i) {
242 			navPointIndices.put(navPoints.get(i).getId(), i);
243 			indicesNavPoints.put(i, navPoints.get(i));
244 		}
245 
246 		// Initialize distance matrix.
247 		for (int i = 0; i < size; i++) {
248 			for (int j = 0; j < size; j++) {
249 				pathMatrix[i][j] = new PathMatrixNode((i == j) ? 0.0f
250 						: Float.POSITIVE_INFINITY);
251 			}
252 		}
253 
254 		// Set edge lengths into distance matrix.
255 		for (int i = 0; i < size; i++) {
256 			Point3d location = navPoints.get(i).getLocation().getPoint3d();
257 			for (NavPointNeighbourLink link : navPoints.get(i)
258 					.getOutgoingEdges().values()) {
259 				if (checkLink(link)) {
260 					pathMatrix[i][navPointIndices.get(link.getToNavPoint()
261 							.getId())].setDistance((float) location
262 							.distance(link.getToNavPoint().getLocation()
263 									.getPoint3d()));
264 				}
265 			}
266 		}
267 
268 		// Perform Floyd-Warshall.
269 		for (int k = 0; k < size; k++) {
270 			for (int i = 0; i < size; i++) {
271 				for (int j = 0; j < size; j++) {
272 					float newLen = pathMatrix[i][k].getDistance()
273 							+ pathMatrix[k][j].getDistance();
274 					if (pathMatrix[i][j].getDistance() > newLen) {
275 						pathMatrix[i][j].setDistance(newLen);
276 						pathMatrix[i][j].setViaNode(k);
277 					}
278 				}
279 			}
280 		}
281 
282 		// Construct paths + log unreachable paths.
283 		int count = 0;
284 		for (int i = 0; i < size; i++) {
285 			for (int j = 0; j < size; j++) {
286 				if (pathMatrix[i][j].getDistance() == Float.POSITIVE_INFINITY) {
287 					if (log.isLoggable(Level.FINER)) log.finer("Unreachable path from " + navPoints.get(i).getId().getStringId()
288 							+ " -> " + navPoints.get(j).getId().getStringId());
289 					count++;
290 				} else {
291 					// path exists ... retrieve it
292 					pathMatrix[i][j].setPath(retrievePath(i, j));
293 				}
294 			}
295 		}
296 
297 		if (count > 0) {
298 			if (log.isLoggable(Level.WARNING)) log.warning(this + ": There are " + count + " unreachable nav point pairs (if you wish to see more, set logging to Level.FINER).");
299 		}
300 
301 		if (log.isLoggable(Level.INFO)) log.info(this + ": computation for " + size + " navpoints took " + (System.currentTimeMillis() - start) + " millis");
302 
303 		// null unneeded field to free some memory
304 		indicesNavPoints = null;
305 	}
306 
307 	/**
308 	 * Checks whether the edge is usable.
309 	 * 
310 	 * @param from
311 	 *            Starting nav point.
312 	 * @param edge
313 	 *            NeighNav object representing the edge.
314 	 * @return boolean
315 	 */
316 	public boolean checkLink(NavPointNeighbourLink edge) {
317 		// Bad flags (prohibited edges, swimming, flying...).
318 		if ((edge.getFlags() & badEdgeFlag) != 0)
319 			return false;
320 
321 		// Lift flags.
322 		if ((edge.getFlags() & LinkFlag.SPECIAL.get()) != 0)
323 			return true;
324 
325 		// Check whether the climbing angle is not so steep.
326 //		if ((edge.getFromNavPoint().getLocation().getPoint3d().distance(
327 //				edge.getToNavPoint().getLocation().getPoint3d()) < (edge
328 //				.getToNavPoint().getLocation().z - edge.getFromNavPoint()
329 //				.getLocation().z))
330 //				&& (edge.getFromNavPoint().getLocation().getPoint3d().distance(
331 //						edge.getToNavPoint().getLocation().getPoint3d()) > 100)) {
332 //			return false;
333 //		}
334 
335 		// Check whether the jump is not so high.
336 //		if (((edge.getFlags() & LinkFlag.JUMP.get()) != 0)
337 //				&& (edge.getToNavPoint().getLocation().z
338 //						- edge.getFromNavPoint().getLocation().z > 80)) {
339 //			return false;
340 //		}
341         //Check whether there is NeededJump attribute set - this means the bot has to 
342         //provide the jump himself - if the Z of the jump is too high it means he
343         //needs to rocket jump or ShieldGun jump - we will erase those links
344         //as our bots are not capable of this
345         if (edge.getNeededJump() != null && edge.getNeededJump().z > 680) {
346         	return false;
347         }
348         
349         //This is a navpoint that requires lift jump - our bots can't do this - banning the link!
350         if (edge.getToNavPoint().isLiftJumpExit()) {
351         	return false;        	        
352         }
353                 
354 		return true;
355 	}
356 
357 	/**
358 	 * Sub-routine of retrievePath - do not use! ... Well you may, it returns
359 	 * path without 'from', 'to' or null if such path dosn't exist.
360 	 * <p>
361 	 * <p>
362 	 * DO NOT USE OUTSIDE CONSTRUCTOR (relies on indicesNavPoints).
363 	 * 
364 	 * @param from
365 	 * @param to
366 	 * @return
367 	 */
368 	private List<NavPoint> retrievePathInner(Integer from, Integer to) {
369 		PathMatrixNode node = pathMatrix[from][to];
370 		if (node.getDistance() == Float.POSITIVE_INFINITY)
371 			return null;
372 		if (node.getViaNode() == null) {
373 			return new ArrayList<NavPoint>(0);
374 		}
375 		if (node.getViaNode() == null)
376 			return new ArrayList<NavPoint>(0);
377 
378 		List<NavPoint> path = new ArrayList<NavPoint>();
379 		path.addAll(retrievePathInner(from, node.getViaNode()));
380 		path.add(indicesNavPoints.get(node.getViaNode()));
381 		path.addAll(retrievePathInner(node.getViaNode(), to));
382 
383 		return path;
384 	}
385 
386 	/**
387 	 * Returns path between from-to or null if path doesn't exist. Path begins
388 	 * with 'from' and ends with 'to'.
389 	 * <p>
390 	 * <p>
391 	 * DO NOT USE OUTSIDE CONSTRUCTOR (relies on indicesNavPoints).
392 	 * 
393 	 * @param from
394 	 * @param to
395 	 * @return
396 	 */
397 	private List<NavPoint> retrievePath(Integer from, Integer to) {
398 		List<NavPoint> path = new ArrayList<NavPoint>();
399 		path.add(indicesNavPoints.get(from));
400 		path.addAll(retrievePathInner(from, to));
401 		path.add(indicesNavPoints.get(to));
402 		return path;
403 	}
404 
405 	protected PathMatrixNode getPathMatrixNode(NavPoint np1, NavPoint np2) {
406 		return pathMatrix[navPointIndices.get(np1.getId())][navPointIndices
407 				.get(np2.getId())];
408 	}
409 
410 	/**
411 	 * Whether navpoint 'to' is reachable from the navpoint 'from'.
412 	 * <p><p>
413 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
414 	 * is enabled by default.
415 	 * 
416 	 * @param from
417 	 * @param to
418 	 * @return
419 	 */
420 	public boolean reachable(NavPoint from, NavPoint to) {
421 		if ((from == null) || (to == null))
422 			return false;
423 		return getPathMatrixNode(from, to).getDistance() != Float.POSITIVE_INFINITY;
424 	}
425 
426 	/**
427 	 * Calculate's distance between two nav points (using pathfinding).
428 	 * <p><p>
429 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
430 	 * is enabled by default.
431 	 * 
432 	 * @return Distance or POSITIVE_INFINITY if there's no path.
433 	 */
434 	public float getDistance(NavPoint from, NavPoint to) {
435 		if ((from == null) || (to == null))
436 			return Float.POSITIVE_INFINITY;
437 		return getPathMatrixNode(from, to).getDistance();
438 	}
439 
440 	/**
441 	 * Returns path between navpoints 'from' -> 'to'. The path begins with
442 	 * 'from' and ends with 'to'. If such path doesn't exist, returns null.
443 	 * <p><p>
444 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
445 	 * is enabled by default.
446 	 * 
447 	 * @param from
448 	 * @param to
449 	 * @return
450 	 */
451 	public List<NavPoint> getPath(NavPoint from, NavPoint to) {
452 		synchronized(mutex) {
453 			if (!enabled) {
454 				throw new PogamutException("Can't return path as the object is disabled, call .setEnabled(true) & .refreshPathMatrix() first!", log, this);
455 			}
456 			if ((from == null) || (to == null))
457 				return null;
458 			if (log.isLoggable(Level.FINE)) log.fine("Retrieving path: " + from.getId().getStringId() + "[" + from.getLocation() + "] -> " + to.getId().getStringId() + "[" + to.getLocation() + "]");
459 			List<NavPoint> path = getPathMatrixNode(from, to).getPath();
460 			if (path == null) {
461 				if (log.isLoggable(Level.WARNING)) log.warning("PATH NOT EXIST: " + from.getId().getStringId() + "[" + from.getLocation() + "] -> " + to.getId().getStringId() + "[" + to.getLocation() + "]");
462 			} else {
463 				if (log.isLoggable(Level.FINE)) log.fine("Path exists - " + path.size() + " navpoints.");
464 			}
465 			return path;
466 		}
467 	}
468 
469 	@Override
470 	protected void cleanUp() {
471 		super.cleanUp();
472 		pathMatrix = null;
473 		navPointIndices = null;
474 	}
475 	
476 	@Override
477 	public String toString() {
478 		return "FloydWarshallMap";
479 	}
480 
481 	/**
482 	 * Returns path between navpoints 'from' -> 'to'. The path begins with
483 	 * 'from' and ends with 'to'. If such path does not exist, it returns zero-sized path.
484 	 * <p><p>
485 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
486 	 * is enabled by default.
487 	 * 
488 	 * @param from
489 	 * @param to
490 	 * @return
491 	 */
492 	@Override
493 	public IPathFuture<NavPoint> computePath(NavPoint from, NavPoint to) {
494 		return new PrecomputedPathFuture<NavPoint>(from, to, getPath(from, to));
495 	}
496 	
497 	// ===============================
498 	// PATH-DISTANCE FILTERING METHODS
499 	// ===============================
500 	
501 	/**
502      * Returns the nearest target (distance == path distance between 'from' and 'target').
503      * <p><p>
504      * WARNING: O(n) complexity!
505      * 
506      * @param <T>
507      * @param locations
508      * @param target
509      * @return nearest object from collection of objects
510      */
511     public <T extends NavPoint> T getNearestNavPoint(Collection<T> locations, T target) {
512     	if (locations == null) return null;
513     	if (target == null) return null;
514         T nearest = null;
515         double minDistance = Double.MAX_VALUE;
516         double d;
517         for(T l : locations) {
518         	if (l.getLocation() == null) continue;
519             d = getDistance(target, l);
520             if(d < minDistance) {
521                 minDistance = d;
522                 nearest = l;
523             }
524         }
525         return nearest;
526     }
527     
528     /**
529      * Returns the nearest target (distance == path distance between 'from' and 'target') that is not further than 'maxDistance'.
530      * <p><p>
531      * WARNING: O(n) complexity!
532      * 
533      * @param <T>
534      * @param locations
535      * @param target
536      * @param maxDistance
537      * @return nearest object from collection of objects that is not further than 'maxDistance'.
538      */
539     public <T extends NavPoint> T getNearestNavPoint(Collection<T> locations, NavPoint target, double maxDistance) {
540     	if (locations == null) return null;
541     	if (target == null) return null;
542         T nearest = null;
543         double minDistance = Double.MAX_VALUE;
544         double d;
545         for(T l : locations) {
546         	d = getDistance(target, l);
547             if (d > maxDistance) continue;
548             if(d < minDistance) {
549                 minDistance = d;
550                 nearest = l;
551             }
552         }
553         return nearest;
554     }
555     
556     /**
557      * Returns the nearest target (distance == path distance between 'from' and 'target').
558      * <p><p>
559      * WARNING: O(n) complexity!
560      * 
561      * @param <T>
562      * @param locations
563      * @param target
564      * @return nearest object from collection of objects
565      */
566     public <T extends NavPoint> T getNearestFilteredNavPoint(Collection<T> locations, NavPoint target, IFilter<T> filter) {
567     	if (locations == null) return null;
568     	if (target == null) return null;
569         T nearest = null;        
570         double minDistance = Double.MAX_VALUE;
571         double d;
572         for(T l : locations) {
573         	if (!filter.isAccepted(l)) continue;
574             d = getDistance(target, l);
575             if(d < minDistance) {
576                 minDistance = d;
577                 nearest = l;
578             }
579         }
580         return nearest;
581     }
582 
583     /**
584      * Returns the second nearest target (distance == path distance between 'from' and 'target').
585      * <p><p>
586      * WARNING: O(n) complexity!
587      * 
588      * @param <T>
589      * @param locations
590      * @param target
591      * @return nearest object from collection of objects
592      */
593     public <T extends NavPoint> T getSecondNearestNavPoint(Collection<T> locations, NavPoint target) {
594     	if (locations == null) return null;
595     	if (target == null) return null;
596     	T secondNearest = null;
597         T nearest = null;
598         double closestDistance = Double.MAX_VALUE;
599         double secondClosestDistance = Double.MAX_VALUE;
600 
601         for (T l : locations) {
602         	double distance = getDistance(target, l);
603             if (distance < closestDistance) {
604                 secondClosestDistance = closestDistance;
605                 secondNearest = nearest;
606 
607                 closestDistance = distance;
608                 nearest = l;
609             } else {
610                 if(distance < secondClosestDistance) {
611                     secondClosestDistance = distance;
612                     secondNearest = l;
613                 }
614             }
615         }
616         return secondNearest;
617     }
618     
619     /**
620      * Returns the nearest target (distance == path distance between 'from' and 'target').
621      * <p><p>
622      * WARNING: O(n) complexity!
623      * 
624      * @param <T>
625      * @param locations
626      * @param target
627      * @return nearest object from collection of objects
628      */
629     public <T extends Item> T getNearestItem(Collection<T> locations, NavPoint target) {
630     	if (locations == null) return null;
631     	if (target == null) return null;
632         T nearest = null;
633         double minDistance = Double.MAX_VALUE;
634         double d;
635         for(T l : locations) {
636         	if (l.getLocation() == null) continue;
637             d = getDistance(target, l.getNavPoint());
638             if(d < minDistance) {
639                 minDistance = d;
640                 nearest = l;
641             }
642         }
643         return nearest;
644     }
645     
646     /**
647      * Returns the nearest target (distance == path distance between 'from' and 'target') that is not further than 'maxDistance'.
648      * <p><p>
649      * WARNING: O(n) complexity!
650      * 
651      * @param <T>
652      * @param locations
653      * @param target
654      * @param maxDistance
655      * @return nearest object from collection of objects that is not further than 'maxDistance'.
656      */
657     public <T extends Item> T getNearestItem(Collection<T> locations, NavPoint target, double maxDistance) {
658     	if (locations == null) return null;
659     	if (target == null) return null;
660         T nearest = null;
661         double minDistance = Double.MAX_VALUE;
662         double d;
663         for(T l : locations) {
664         	d = getDistance(target, l.getNavPoint());
665             if (d > maxDistance) continue;
666             if(d < minDistance) {
667                 minDistance = d;
668                 nearest = l;
669             }
670         }
671         return nearest;
672     }
673     
674     /**
675      * Returns the nearest target (distance == path distance between 'from' and 'target').
676      * <p><p>
677      * WARNING: O(n) complexity!
678      * 
679      * @param <T>
680      * @param locations
681      * @param target
682      * @return nearest object from collection of objects
683      */
684     public <T extends Item> T getNearestFilteredItem(Collection<T> locations, NavPoint target, IFilter<T> filter) {
685     	if (locations == null) return null;
686     	if (target == null) return null;
687         T nearest = null;        
688         double minDistance = Double.MAX_VALUE;
689         double d;
690         for(T l : locations) {
691         	if (!filter.isAccepted(l)) continue;
692             d = getDistance(target, l.getNavPoint());
693             if(d < minDistance) {
694                 minDistance = d;
695                 nearest = l;
696             }
697         }
698         return nearest;
699     }
700 
701     /**
702      * Returns the second nearest target (distance == path distance between 'from' and 'target').
703      * <p><p>
704      * WARNING: O(n) complexity!
705      * 
706      * @param <T>
707      * @param targets
708      * @param from
709      * @return nearest object from collection of objects
710      */
711     public <T extends Item> T getSecondNearestItem(Collection<T> targets, NavPoint from) {
712     	if (targets == null) return null;
713     	if (from == null) return null;
714     	T secondNearest = null;
715         T nearest = null;
716         double closestDistance = Double.MAX_VALUE;
717         double secondClosestDistance = Double.MAX_VALUE;
718 
719         for (T l : targets) {
720         	double distance = getDistance(from, l.getNavPoint());
721             if (distance < closestDistance) {
722                 secondClosestDistance = closestDistance;
723                 secondNearest = nearest;
724 
725                 closestDistance = distance;
726                 nearest = l;
727             } else {
728                 if(distance < secondClosestDistance) {
729                     secondClosestDistance = distance;
730                     secondNearest = l;
731                 }
732             }
733         }
734         return secondNearest;
735     }
736     
737 }