View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception.categories.neighbors;
2   
3   import java.util.Collection;
4   import java.util.Comparator;
5   import java.util.ConcurrentModificationException;
6   import java.util.Iterator;
7   import java.util.LinkedHashSet;
8   import java.util.Set;
9   import java.util.SortedMap;
10  import java.util.SortedSet;
11  import java.util.TreeMap;
12  import java.util.TreeSet;
13  
14  import org.djunits.value.vdouble.scalar.Length;
15  import org.djunits.value.vdouble.scalar.Time;
16  import org.djutils.exceptions.Try;
17  import org.opentrafficsim.base.parameters.ParameterException;
18  import org.opentrafficsim.core.gtu.GTUException;
19  import org.opentrafficsim.core.gtu.RelativePosition;
20  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
21  import org.opentrafficsim.road.gtu.lane.perception.LaneStructureRecord;
22  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
23  
24  /**
25   * Utilities to perceive neighbors.
26   * <p>
27   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
29   * <p>
30   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 okt. 2018 <br>
31   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
32   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
33   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
34   */
35  public final class NeighborsUtil
36  {
37  
38      /**
39       * Constructor.
40       */
41      private NeighborsUtil()
42      {
43          //
44      }
45  
46      /**
47       * Returns a set of first leaders per branch, relative to given relative position. Helper method to find first leaders and
48       * GTU's alongside.
49       * @param startRecord LaneStructureRecord; lane structure record to start the search from
50       * @param egoRelativePosition RelativePosition; position of GTU to start search from
51       * @param egoFrontPosition RelativePosition; front position of GTU to determine headway
52       * @param otherRelativePosition RelativePosition.TYPE; position of other GTU that has to be downstream
53       * @param now Time; current time
54       * @return set of first leaders per branch
55       * @throws GTUException if the GTU was not initialized
56       * @throws ParameterException if a parameter was not present or out of bounds
57       */
58      public static SortedSet<DistanceGTU> getFirstDownstreamGTUs(final LaneStructureRecord startRecord,
59              final RelativePosition egoRelativePosition, final RelativePosition egoFrontPosition,
60              final RelativePosition.TYPE otherRelativePosition, final Time now) throws GTUException, ParameterException
61      {
62          SortedSet<DistanceGTU> headwaySet = new TreeSet<>();
63          Set<LaneStructureRecord> currentSet = new LinkedHashSet<>();
64          Set<LaneStructureRecord> nextSet = new LinkedHashSet<>();
65          Length dxSearch = egoRelativePosition.getDx();
66          Length dxHeadway = egoFrontPosition.getDx();
67          LaneStructureRecord record = startRecord;
68          branchUpstream(record, dxSearch, currentSet);
69          // move downstream over branches as long as no vehicles are found
70          while (!currentSet.isEmpty())
71          {
72              Iterator<LaneStructureRecord> iterator = currentSet.iterator();
73              while (iterator.hasNext())
74              {
75                  record = iterator.next();
76                  /*-
77                   *                                             _ _ _ ______________________ _ _ _
78                   *                                                             _|___    |
79                   * find any vehicle downstream of this point on lane A |      |__o__| A |
80                   *                                             _ _ _ ___________|_______|__ _ _ _ 
81                   *                                                     (--------) negative distance
82                   */
83                  LaneBasedGTU down = record.getLane().getGtuAhead(record.getStartDistance().neg().plus(dxSearch),
84                          record.getDirection(), otherRelativePosition, now);
85                  if (down != null)
86                  {
87                      // GTU found, add to set
88                      headwaySet.add(new DistanceGTU(down,
89                              record.getStartDistance().plus(down.position(record.getLane(), down.getRear())).minus(dxHeadway)));
90                  }
91                  else
92                  {
93                      // no GTU found, search on next lanes in next loop and maintain cumulative length
94                      for (LaneStructureRecord next : record.getNext())
95                      {
96                          nextSet.add(next);
97                      }
98                  }
99              }
100             currentSet = nextSet;
101             nextSet = new LinkedHashSet<>();
102         }
103         return headwaySet;
104     }
105 
106     /**
107      * Returns a set of lanes to start from for a downstream search, upstream of the reference lane if the tail is before this
108      * lane.
109      * @param record LaneStructureRecord; start record
110      * @param dx Length; distance between reference point and point to search from
111      * @param set Set&lt;LaneStructureRecord&gt;; set of lanes that is recursively built up, starting with the reference record
112      */
113     private static void branchUpstream(final LaneStructureRecord record, final Length dx, final Set<LaneStructureRecord> set)
114     {
115         Length pos = record.getStartDistance().neg().minus(dx);
116         if (pos.lt0() && !record.getPrev().isEmpty())
117         {
118             for (LaneStructureRecord prev : record.getPrev())
119             {
120                 branchUpstream(prev, dx, set);
121             }
122         }
123         else
124         {
125             set.add(record);
126         }
127     }
128 
129     /**
130      * Returns a set of first followers per branch, relative to given relative position. Helper method to find first followers
131      * and GTU's alongside.
132      * @param startRecord LaneStructureRecord; lane structure record to start the search from
133      * @param egoRelativePosition RelativePosition; position of GTU to start search from
134      * @param egoRearPosition RelativePosition; rear position of GTU to determine headway
135      * @param otherRelativePosition RelativePosition.TYPE; type of position of other GTU that has to be upstream
136      * @param now Time; current time
137      * @return set of first followers per branch
138      * @throws GTUException if the GTU was not initialized
139      * @throws ParameterException if a parameter was not present or out of bounds
140      */
141     public static SortedSet<DistanceGTU> getFirstUpstreamGTUs(final LaneStructureRecord startRecord,
142             final RelativePosition egoRelativePosition, final RelativePosition egoRearPosition,
143             final RelativePosition.TYPE otherRelativePosition, final Time now) throws GTUException, ParameterException
144     {
145         SortedSet<DistanceGTU> headwaySet = new TreeSet<>();
146         Set<LaneStructureRecord> currentSet = new LinkedHashSet<>();
147         Set<LaneStructureRecord> prevSet = new LinkedHashSet<>();
148         Length dxSearch = egoRelativePosition.getDx();
149         Length dxHeadway = egoRearPosition.getDx();
150         LaneStructureRecord record = startRecord;
151         branchDownstream(record, dxSearch, currentSet);
152         // move upstream over branches as long as no vehicles are found
153         while (!currentSet.isEmpty())
154         {
155             Iterator<LaneStructureRecord> iterator = currentSet.iterator();
156             while (iterator.hasNext())
157             {
158                 record = iterator.next();
159                 /*-
160                  * _ _ _ ______________________ _ _ _ 
161                  *         |    ___|_   
162                  *         | A |__o__|      | find any upstream of this point on lane A
163                  * _ _ _ __|_______|___________ _ _ _ 
164                  *         (----------------) distance
165                  */
166                 LaneBasedGTU up = record.getLane().getGtuBehind(record.getStartDistance().neg().plus(dxSearch),
167                         record.getDirection(), otherRelativePosition, now);
168                 if (up != null)
169                 {
170                     // GTU found, add to set
171                     headwaySet.add(new DistanceGTU(up, record.getStartDistance().neg()
172                             .minus(up.position(record.getLane(), up.getFront())).plus(dxHeadway)));
173                 }
174                 else
175                 {
176                     // no GTU found, search on next lanes in next loop and maintain cumulative length
177                     for (LaneStructureRecord prev : record.getPrev())
178                     {
179                         prevSet.add(prev);
180                     }
181                 }
182             }
183             currentSet = prevSet;
184             prevSet = new LinkedHashSet<>();
185         }
186         return headwaySet;
187     }
188 
189     /**
190      * Returns a set of lanes to start from for an upstream search, downstream of the reference lane if the front is after this
191      * lane.
192      * @param record LaneStructureRecord; start record
193      * @param dx Length; distance between reference point and point to search from
194      * @param set Set&lt;LaneStructureRecord&gt;; set of lanes that is recursively built up, starting with the reference record
195      */
196     private static void branchDownstream(final LaneStructureRecord record, final Length dx, final Set<LaneStructureRecord> set)
197     {
198         Length pos = record.getStartDistance().neg().plus(dx);
199         if (pos.gt(record.getLane().getLength()))
200         {
201             for (LaneStructureRecord next : record.getNext())
202             {
203                 branchDownstream(next, dx, set);
204             }
205         }
206         else
207         {
208             set.add(record);
209         }
210     }
211 
212     /**
213      * Translation from a set of {@code DistanceGTU}'s, to a sorted set of {@code HeadwayGTU}'s. This bridges the gap between a
214      * raw network search, and the perceived result.
215      * @param base SortedSet&lt;DistanceGTU&gt;; base set of GTU's at distance
216      * @param headwayGtuType HeadwayGtuType; headway type for perceived GTU's
217      * @param perceivingGtu LaneBasedGTU; perceiving GTU
218      * @param downstream boolean; whether the GTU's are downstream
219      * @return SortedSet&lt;HeadwayGTU&gt;; set of perceived GTU's
220      */
221     public static SortedSet<HeadwayGTU> perceive(final SortedSet<DistanceGTU> base, final HeadwayGtuType headwayGtuType,
222             final LaneBasedGTU perceivingGtu, final boolean downstream)
223     {
224         return new SortedNeighborsSet(base, headwayGtuType, perceivingGtu, downstream);
225     }
226 
227     /**
228      * GTU at a distance, as preliminary info towards perceiving it. For instance, as a set from a search algorithm.
229      * <p>
230      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
231      * <br>
232      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
233      * <p>
234      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 22 apr. 2018 <br>
235      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
236      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
237      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
238      */
239     public static class DistanceGTU implements Comparable<DistanceGTU>
240     {
241 
242         /** GTU. */
243         private LaneBasedGTU gtu;
244 
245         /** Distance. */
246         private Length distance;
247 
248         /**
249          * Constructor.
250          * @param gtu LaneBasedGTU; GTU
251          * @param distance Length; distance
252          */
253         DistanceGTU(final LaneBasedGTU gtu, final Length distance)
254         {
255             this.gtu = gtu;
256             this.distance = distance;
257         }
258 
259         /**
260          * Returns the GTU.
261          * @return LaneBasedGTU; GTU
262          */
263         public LaneBasedGTU getGTU()
264         {
265             return this.gtu;
266         }
267 
268         /**
269          * Returns the distance.
270          * @return Length; distance
271          */
272         public Length getDistance()
273         {
274             return this.distance;
275         }
276 
277         /** {@inheritDoc} */
278         @Override
279         public int compareTo(final DistanceGTU o)
280         {
281             return this.distance.compareTo(o.distance);
282         }
283     }
284 
285     /**
286      * Translation from a set of {@code DistanceGTU}'s, to a sorted set of {@code HeadwayGTU}'s. This bridges the gap between a
287      * raw network search, and the perceived result.
288      * <p>
289      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
290      * <br>
291      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
292      * <p>
293      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 22 apr. 2018 <br>
294      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
295      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
296      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
297      */
298     private static class SortedNeighborsSet implements SortedSet<HeadwayGTU>
299     {
300 
301         /** Base set of GTU's at distance. */
302         private final SortedSet<DistanceGTU> base;
303 
304         /** Headway type for perceived GTU's. */
305         private final HeadwayGtuType headwayGtuType;
306 
307         /** Perceiving GTU. */
308         private final LaneBasedGTU perceivingGtu;
309 
310         /** Whether the GTU's are downstream. */
311         private final boolean downstream;
312 
313         /** Contains all GTU's perceived so far, to prevent re-perception. */
314         private final SortedMap<String, HeadwayGTU> all = new TreeMap<>();
315 
316         /**
317          * Constructor.
318          * @param base SortedSet&lt;DistanceGTU&gt;; base set of GTU's at distance
319          * @param headwayGtuType HeadwayGtuType; headway type for perceived GTU's
320          * @param perceivingGtu LaneBasedGTU; perceiving GTU
321          * @param downstream boolean; whether the GTU's are downstream
322          */
323         SortedNeighborsSet(final SortedSet<DistanceGTU> base, final HeadwayGtuType headwayGtuType,
324                 final LaneBasedGTU perceivingGtu, final boolean downstream)
325         {
326             this.base = base;
327             this.headwayGtuType = headwayGtuType;
328             this.perceivingGtu = perceivingGtu;
329             this.downstream = downstream;
330         }
331 
332         /** {@inheritDoc} */
333         @Override
334         public int size()
335         {
336             return this.base.size();
337         }
338 
339         /** {@inheritDoc} */
340         @Override
341         public boolean isEmpty()
342         {
343             return this.base.isEmpty();
344         }
345 
346         /**
347          * Make sure all GTU are available in perceived for. Helper method.
348          */
349         private void getAll()
350         {
351             Iterator<HeadwayGTU> it = iterator();
352             while (it.hasNext())
353             {
354                 @SuppressWarnings("unused")
355                 HeadwayGTU gtu = it.next(); // iterator creates all HeadwayGTU's
356             }
357         }
358 
359         /** {@inheritDoc} */
360         @Override
361         public boolean contains(final Object o)
362         {
363             getAll();
364             return this.all.containsValue(o);
365         }
366 
367         /** {@inheritDoc} */
368         @Override
369         public Iterator<HeadwayGTU> iterator()
370         {
371             return new Iterator<HeadwayGTU>()
372             {
373                 @SuppressWarnings("synthetic-access")
374                 private Iterator<DistanceGTU> it = SortedNeighborsSet.this.base.iterator();
375 
376                 @Override
377                 public boolean hasNext()
378                 {
379                     return this.it.hasNext();
380                 }
381 
382                 @SuppressWarnings("synthetic-access")
383                 @Override
384                 public HeadwayGTU next()
385                 {
386                     DistanceGTU next = this.it.next();
387                     if (next == null)
388                     {
389                         throw new ConcurrentModificationException();
390                     }
391                     HeadwayGTU out = SortedNeighborsSet.this.all.get(next.getGTU().getId());
392                     if (out == null)
393                     {
394                         out = Try.assign(() -> SortedNeighborsSet.this.headwayGtuType.createHeadwayGtu(
395                                 SortedNeighborsSet.this.perceivingGtu, next.getGTU(), next.getDistance(),
396                                 SortedNeighborsSet.this.downstream), "Exception while perceiving a neighbor.");
397                         SortedNeighborsSet.this.all.put(next.getGTU().getId(), out);
398                     }
399                     return out;
400                 }
401             };
402         }
403 
404         /** {@inheritDoc} */
405         @Override
406         public Object[] toArray()
407         {
408             getAll();
409             return this.all.values().toArray();
410         }
411 
412         /** {@inheritDoc} */
413         @Override
414         public <T> T[] toArray(final T[] a)
415         {
416             getAll();
417             return this.all.values().toArray(a);
418         }
419 
420         /** {@inheritDoc} */
421         @Override
422         public boolean add(final HeadwayGTU e)
423         {
424             throw new UnsupportedOperationException();
425         }
426 
427         /** {@inheritDoc} */
428         @Override
429         public boolean remove(final Object o)
430         {
431             throw new UnsupportedOperationException();
432         }
433 
434         /** {@inheritDoc} */
435         @Override
436         public boolean containsAll(final Collection<?> c)
437         {
438             getAll();
439             return this.all.values().containsAll(c);
440         }
441 
442         /** {@inheritDoc} */
443         @Override
444         public boolean addAll(final Collection<? extends HeadwayGTU> c)
445         {
446             throw new UnsupportedOperationException();
447         }
448 
449         /** {@inheritDoc} */
450         @Override
451         public boolean retainAll(final Collection<?> c)
452         {
453             throw new UnsupportedOperationException();
454         }
455 
456         /** {@inheritDoc} */
457         @Override
458         public boolean removeAll(final Collection<?> c)
459         {
460             throw new UnsupportedOperationException();
461         }
462 
463         /** {@inheritDoc} */
464         @Override
465         public void clear()
466         {
467             throw new UnsupportedOperationException();
468         }
469 
470         /** {@inheritDoc} */
471         @Override
472         public Comparator<? super HeadwayGTU> comparator()
473         {
474             return null;
475         }
476 
477         /**
478          * Helper method for sub-lists to find distance-GTU from the perceived GTU.
479          * @param element HeadwayGTU; perceived GTU
480          * @return DistanceGTU; pertaining to given GTU
481          */
482         private DistanceGTU getGTU(final HeadwayGTU element)
483         {
484             for (DistanceGTU distanceGtu : this.base)
485             {
486                 if (distanceGtu.getGTU().getId().equals(element.getId()))
487                 {
488                     return distanceGtu;
489                 }
490             }
491             throw new IllegalArgumentException("GTU used to obtain a subset is not in the set.");
492         }
493 
494         /** {@inheritDoc} */
495         @Override
496         public SortedSet<HeadwayGTU> subSet(final HeadwayGTU fromElement, final HeadwayGTU toElement)
497         {
498             return new SortedNeighborsSet(this.base.subSet(getGTU(fromElement), getGTU(toElement)), this.headwayGtuType,
499                     this.perceivingGtu, this.downstream);
500         }
501 
502         /** {@inheritDoc} */
503         @Override
504         public SortedSet<HeadwayGTU> headSet(final HeadwayGTU toElement)
505         {
506             return new SortedNeighborsSet(this.base.headSet(getGTU(toElement)), this.headwayGtuType, this.perceivingGtu,
507                     this.downstream);
508         }
509 
510         /** {@inheritDoc} */
511         @Override
512         public SortedSet<HeadwayGTU> tailSet(final HeadwayGTU fromElement)
513         {
514             return new SortedNeighborsSet(this.base.tailSet(getGTU(fromElement)), this.headwayGtuType, this.perceivingGtu,
515                     this.downstream);
516         }
517 
518         /** {@inheritDoc} */
519         @Override
520         public HeadwayGTU first()
521         {
522             return iterator().next();
523         }
524 
525         /** {@inheritDoc} */
526         @Override
527         public HeadwayGTU last()
528         {
529             getAll();
530             return this.all.get(this.all.lastKey());
531         }
532 
533     }
534 
535 }