View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception;
2   
3   import java.io.Serializable;
4   import java.util.HashMap;
5   import java.util.HashSet;
6   import java.util.Iterator;
7   import java.util.Map;
8   import java.util.Set;
9   import java.util.SortedSet;
10  import java.util.TreeMap;
11  import java.util.TreeSet;
12  
13  import org.djunits.value.vdouble.scalar.Length;
14  import org.opentrafficsim.core.gtu.GTU;
15  import org.opentrafficsim.core.gtu.GTUException;
16  import org.opentrafficsim.core.gtu.RelativePosition;
17  import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
18  
19  import nl.tudelft.simulation.language.Throw;
20  
21  /**
22   * This data structure can clearly indicate the lane structure ahead of us, e.g. in the following situation:
23   * 
24   * <pre>
25   *     (---- a ----)(---- b ----)(---- c ----)(---- d ----)(---- e ----)(---- f ----)(---- g ----)  
26   *                                             __________                             __________
27   *                                            / _________ 1                          / _________ 2
28   *                                           / /                                    / /
29   *                                __________/ /             _______________________/ /
30   *  1  ____________ ____________ /_ _ _ _ _ _/____________ /_ _ _ _ _ _ _ _ _ _ _ _ /      
31   *  0 |_ _X_ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ |_ _ _ _ _ _ \____________
32   * -1 |____________|_ _ _ _ _ _ |____________|____________|  __________|____________|____________| 3
33   * -2              / __________/                           \ \  
34   *        ________/ /                                       \ \___________  
35   *      5 _________/                                         \____________  4
36   * </pre>
37   * 
38   * When the GTU is looking ahead, it needs to know that when it continues to destination 3, it needs to shift one lane to the
39   * right at some point, but <b>not</b> two lanes to the right in link b, and not later than at the end of link f. When it needs
40   * to go to destination 1, it needs to shift to the left in link c. When it has to go to destination 2, it has to shift to the
41   * left, but not earlier than at link e. At node [de], it is possible to leave the rightmost lane of link e, and go to
42   * destination 4. The rightmost lane just splits into two lanes at the end of link d, and the GTU can either continue driving to
43   * destination 3, turn right to destination 4. This means that the right lane of link d has <b>two</b> successor lanes.
44   * <p>
45   * In the data structures, lanes are numbered laterally. Suppose that the lane where vehicle X resides would be number 0.
46   * Consistent with "left is positive" for angles, the lane right of X would have number -1, and entry 5 would have number -2.
47   * <p>
48   * In the data structure, this can be indicated as follows (N = next, P = previous, L = left, R = right, D = lane drop, . =
49   * continued but not in this structure). The merge lane in b is considered "off limits" for the GTUs on the "main" lane -1; the
50   * "main" lane 0 is considered off limits from the exit lanes on c, e, and f. Still, we need to maintain pointers to these
51   * lanes, as we are interested in the GTUs potentially driving next to us, feeding into our lane, etc.
52   * 
53   * <pre>
54   *       1                0               -1               -2
55   *       
56   *                       ROOT 
57   *                   _____|_____      ___________      ___________            
58   *                  |_-_|_._|_R_|----|_L_|_._|_-_|    |_-_|_._|_-_|  a           
59   *                        |                |                |
60   *                   _____V_____      _____V_____      _____V_____            
61   *                  |_-_|_N_|_R_|----|_L_|_N_|_R_|&lt;---|_L_|_D_|_-_|  b           
62   *                        |                |                 
63   *  ___________      _____V_____      _____V_____                 
64   * |_-_|_N_|_R_|&lt;---|_L_|_N_|_R_|----|_L_|_N_|_-_|                   c
65   *       |                |                |                 
66   *  _____V_____      _____V_____      _____V_____                 
67   * |_-_|_._|_-_|    |_-_|_N_|_R_|----|_L_|_NN|_-_|                   d          
68   *                        |                ||_______________ 
69   *  ___________      _____V_____      _____V_____      _____V_____            
70   * |_-_|_N_|_R_|&lt;---|_L_|_N_|_R_|----|_L_|_N_|_-_|    |_-_|_N_|_-_|  e          
71   *       |                |                |                |
72   *  _____V_____      _____V_____      _____V_____      _____V_____            
73   * |_-_|_N_|_R_|&lt;---|_L_|_D_|_R_|----|_L_|_N_|_-_|    |_-_|_._|_-_|  f          
74   *       |                                 |                 
75   *  _____V_____                       _____V_____                             
76   * |_-_|_._|_-_|                     |_-_|_._|_-_|                   g
77   * </pre>
78   * <p>
79   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
80   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
81   * </p>
82   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
83   * initial version Feb 20, 2016 <br>
84   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
85   */
86  public class LaneStructure implements Serializable
87  {
88      /** */
89      private static final long serialVersionUID = 20160400L;
90  
91      /** The lanes from which we observe the situation. */
92      private LaneStructureRecord rootLSR;
93  
94      /** Look ahead distance. */
95      private Length lookAhead;
96  
97      /** Lane structure records of the cross section. */
98      private TreeMap<RelativeLane, LaneStructureRecord> crossSectionRecords = new TreeMap<>();
99  
100     /** Lane structure records grouped per relative lane. */
101     private final Map<RelativeLane, Set<LaneStructureRecord>> relativeLaneMap = new HashMap<>();
102 
103     /**
104      * @param rootLSR the root record.
105      * @param lookAhead look ahead distance
106      */
107     public LaneStructure(final LaneStructureRecord rootLSR, final Length lookAhead)
108     {
109         this.rootLSR = rootLSR;
110         this.lookAhead = lookAhead;
111     }
112 
113     /**
114      * @return rootLSR
115      */
116     public final LaneStructureRecord getRootLSR()
117     {
118         return this.rootLSR;
119     }
120 
121     /**
122      * Returns the cross section.
123      * @return cross section
124      */
125     public final SortedSet<RelativeLane> getCrossSection()
126     {
127         return this.crossSectionRecords.navigableKeySet();
128     }
129 
130     /**
131      * @param lane lane to check
132      * @return record at given lane
133      * @throws GTUException if the lane is not in the cross section
134      */
135     public final LaneStructureRecord getLaneLSR(final RelativeLane lane) throws GTUException
136     {
137         Throw.when(!this.crossSectionRecords.containsKey(lane), GTUException.class,
138                 "The requested lane %s is not in the most recent cross section.", lane);
139         return this.crossSectionRecords.get(lane);
140     }
141 
142     /**
143      * Removes all mappings to relative lanes that are not in the most recent cross section.
144      * @param map map to clear mappings from
145      */
146     public final void removeInvalidMappings(final Map<RelativeLane, ?> map)
147     {
148         Iterator<RelativeLane> iterator = map.keySet().iterator();
149         while (iterator.hasNext())
150         {
151             RelativeLane lane = iterator.next();
152             if (!this.crossSectionRecords.containsKey(lane))
153             {
154                 iterator.remove();
155             }
156         }
157     }
158 
159     /**
160      * Adds a lane structure record in a mapping from relative lanes.
161      * @param lsr lane structure record
162      * @param relativeLane relative lane
163      */
164     public final void addLaneStructureRecord(final LaneStructureRecord lsr, final RelativeLane relativeLane)
165     {
166         if (!this.relativeLaneMap.containsKey(relativeLane))
167         {
168             this.relativeLaneMap.put(relativeLane, new HashSet<>());
169         }
170         this.relativeLaneMap.get(relativeLane).add(lsr);
171         if (lsr.getStartDistance().le(Length.ZERO) && lsr.getStartDistance().plus(lsr.getLane().getLength()).ge(Length.ZERO))
172         {
173             this.crossSectionRecords.put(relativeLane, lsr);
174         }
175     }
176 
177     /**
178      * Retrieve objects on a lane of a specific type. Returns objects over a maximum length of the look ahead distance
179      * downstream from the relative position, or as far as the lane map goes.
180      * @param lane lane
181      * @param clazz class of objects to find
182      * @param gtu gtu
183      * @param pos relative position to start search from
184      * @param <T> type of objects to find
185      * @return Sorted set of objects of requested type
186      * @throws GTUException if lane is not in current set
187      */
188     @SuppressWarnings("unchecked")
189     public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjects(final RelativeLane lane,
190             final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
191     {
192         LaneStructureRecord record = this.getLaneLSR(lane);
193         Length ds = gtu.getRelativePositions().get(pos).getDx().minus(gtu.getReference().getDx());
194         // the list is ordered, but only for DIR_PLUS, need to do our own ordering
195         Length minimumPosition;
196         Length maximumPosition;
197         if (record.getDirection().isPlus())
198         {
199             minimumPosition = ds.minus(record.getStartDistance());
200             maximumPosition = record.getLane().getLength();
201         }
202         else
203         {
204             minimumPosition = Length.ZERO;
205             maximumPosition = record.getLane().getLength().plus(record.getStartDistance()).minus(ds);
206         }
207         SortedSet<Entry<T>> set = new TreeSet<>();
208         Length distance;
209         for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
210         {
211             distance = record.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
212             if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead))
213             {
214                 // unchecked, but the above isAssignableFrom assures correctness
215                 set.add(new Entry<>(distance, (T) object));
216             }
217         }
218         getDownstreamObjectsRecursive(set, record, clazz, ds);
219         return set;
220     }
221 
222     /**
223      * Recursive search for lane based objects downstream.
224      * @param set set to store entries into
225      * @param record current record
226      * @param clazz class of objects to find
227      * @param ds distance from reference to chosen relative position
228      * @param <T> type of objects to find
229      */
230     @SuppressWarnings("unchecked")
231     private <T extends LaneBasedObject> void getDownstreamObjectsRecursive(final SortedSet<Entry<T>> set,
232             final LaneStructureRecord record, final Class<T> clazz, final Length ds)
233     {
234         if (record.getNext().isEmpty() || record.getNext().get(0).getStartDistance().gt(this.lookAhead))
235         {
236             return;
237         }
238         for (LaneStructureRecord next : record.getNext())
239         {
240             Length distance;
241             for (LaneBasedObject object : next.getLane().getLaneBasedObjects())
242             {
243                 distance = next.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
244                 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead))
245                 {
246                     // unchecked, but the above isAssignableFrom assures correctness
247                     set.add(new Entry<>(distance, (T) object));
248                 }
249             }
250             getDownstreamObjectsRecursive(set, next, clazz, ds);
251         }
252     }
253 
254     /**
255      * Retrieve objects on a lane of a specific type. Returns upstream objects from the relative position for as far as the lane
256      * map goes. Distances to upstream objects are given as positive values.
257      * @param lane lane
258      * @param clazz class of objects to find
259      * @param gtu gtu
260      * @param pos relative position to start search from
261      * @param <T> type of objects to find
262      * @return Sorted set of objects of requested type
263      * @throws GTUException if lane is not in current set
264      */
265     @SuppressWarnings("unchecked")
266     public final <T extends LaneBasedObject> SortedSet<Entry<T>> getUpstreamObjects(final RelativeLane lane,
267             final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
268     {
269         LaneStructureRecord record = this.getLaneLSR(lane);
270         Length ds = gtu.getReference().getDx().minus(gtu.getRelativePositions().get(pos).getDx());
271         // the list is ordered, but only for DIR_PLUS, need to do our own ordering
272         Length minimumPosition;
273         Length maximumPosition;
274         if (record.getDirection().isPlus())
275         {
276             minimumPosition = Length.ZERO;
277             maximumPosition = record.getStartDistance().neg().minus(ds);
278         }
279         else
280         {
281             minimumPosition = record.getLane().getLength().plus(record.getStartDistance()).plus(ds);
282             maximumPosition = record.getLane().getLength();
283         }
284         SortedSet<Entry<T>> set = new TreeSet<>();
285         Length distance;
286         for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
287         {
288             if (clazz.isAssignableFrom(object.getClass()))
289             {
290                 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
291                 // unchecked, but the above isAssignableFrom assures correctness
292                 set.add(new Entry<>(distance, (T) object));
293             }
294         }
295         getUpstreamObjectsRecursive(set, record, clazz, ds);
296         return set;
297     }
298 
299     /**
300      * Recursive search for lane based objects upstream.
301      * @param set set to store entries into
302      * @param record current record
303      * @param clazz class of objects to find
304      * @param ds distance from reference to chosen relative position
305      * @param <T> type of objects to find
306      */
307     @SuppressWarnings("unchecked")
308     private <T extends LaneBasedObject> void getUpstreamObjectsRecursive(final SortedSet<Entry<T>> set,
309             final LaneStructureRecord record, final Class<T> clazz, final Length ds)
310     {
311         for (LaneStructureRecord prev : record.getPrev())
312         {
313             Length distance;
314             for (LaneBasedObject object : prev.getLane().getLaneBasedObjects())
315             {
316                 if (clazz.isAssignableFrom(object.getClass()))
317                 {
318                     distance = prev.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
319                     // unchecked, but the above isAssignableFrom assures correctness
320                     set.add(new Entry<>(distance, (T) object));
321                 }
322             }
323             getUpstreamObjectsRecursive(set, prev, clazz, ds);
324         }
325     }
326 
327     /** {@inheritDoc} */
328     @Override
329     public final String toString()
330     {
331         return "LaneStructure [rootLSR=" + this.rootLSR + "]";
332     }
333 
334     /**
335      * <p>
336      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
337      * <br>
338      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
339      * <p>
340      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 15, 2016 <br>
341      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
342      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
343      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
344      * @param <T> class of lane based object contained
345      */
346     public class Entry<T extends LaneBasedObject> implements Comparable<Entry<T>>
347     {
348 
349         /** Distance to lane based object. */
350         private final Length distance;
351 
352         /** Lane based object. */
353         private final T laneBasedObject;
354 
355         /**
356          * @param distance distance to lane based object
357          * @param laneBasedObject lane based object
358          */
359         public Entry(final Length distance, final T laneBasedObject)
360         {
361             this.distance = distance;
362             this.laneBasedObject = laneBasedObject;
363         }
364 
365         /**
366          * @return distance.
367          */
368         public final Length getDistance()
369         {
370             return this.distance;
371         }
372 
373         /**
374          * @return laneBasedObject.
375          */
376         public final T getLaneBasedObject()
377         {
378             return this.laneBasedObject;
379         }
380 
381         /** {@inheritDoc} */
382         @Override
383         public final int hashCode()
384         {
385             final int prime = 31;
386             int result = 1;
387             result = prime * result + ((this.distance == null) ? 0 : this.distance.hashCode());
388             result = prime * result + ((this.laneBasedObject == null) ? 0 : this.laneBasedObject.hashCode());
389             return result;
390         }
391 
392         /** {@inheritDoc} */
393         @Override
394         public final boolean equals(final Object obj)
395         {
396             if (this == obj)
397             {
398                 return true;
399             }
400             if (obj == null)
401             {
402                 return false;
403             }
404             if (getClass() != obj.getClass())
405             {
406                 return false;
407             }
408             Entry<?> other = (Entry<?>) obj;
409             if (this.distance == null)
410             {
411                 if (other.distance != null)
412                 {
413                     return false;
414                 }
415             }
416             else if (!this.distance.equals(other.distance))
417             {
418                 return false;
419             }
420             if (this.laneBasedObject == null)
421             {
422                 if (other.laneBasedObject != null)
423                 {
424                     return false;
425                 }
426             }
427             // laneBasedObject does not implement equals...
428             else if (!this.laneBasedObject.equals(other.laneBasedObject))
429             {
430                 return false;
431             }
432             return true;
433         }
434 
435         /** {@inheritDoc} */
436         @Override
437         public final int compareTo(final Entry<T> arg)
438         {
439             int d = this.distance.compareTo(arg.distance);
440             if (d != 0 || this.laneBasedObject.equals(arg.laneBasedObject))
441             {
442                 return d; // different distance (-1 or 1), or same distance but also equal lane based object (0)
443             }
444             return 1; // same distance, unequal lane based object (1)
445         }
446 
447         /** {@inheritDoc} */
448         @Override
449         public final String toString()
450         {
451             return "LaneStructure.Entry [distance=" + this.distance + ", laneBasedObject=" + this.laneBasedObject + "]";
452         }
453 
454     }
455 
456 }