View Javadoc

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