View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.HashMap;
6   import java.util.HashSet;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.unit.LengthUnit;
13  import org.djunits.unit.SpeedUnit;
14  import org.djunits.value.vdouble.scalar.Length;
15  import org.djunits.value.vdouble.scalar.Speed;
16  import org.djunits.value.vdouble.scalar.Time;
17  import org.opentrafficsim.core.gtu.GTUDirectionality;
18  import org.opentrafficsim.core.gtu.GTUException;
19  import org.opentrafficsim.core.gtu.RelativePosition;
20  import org.opentrafficsim.core.network.LateralDirectionality;
21  import org.opentrafficsim.core.network.NetworkException;
22  import org.opentrafficsim.core.perception.PerceivedObject;
23  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
24  import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
25  import org.opentrafficsim.road.gtu.lane.tactical.LanePathInfo;
26  import org.opentrafficsim.road.gtu.lane.tactical.following.HeadwayGTU;
27  import org.opentrafficsim.road.network.lane.Lane;
28  import org.opentrafficsim.road.network.lane.LaneDirection;
29  
30  /**
31   * The perception module of a GTU based on lanes. It is responsible for perceiving (sensing) the environment of the GTU, which
32   * includes the locations of other GTUs. Perception is done at a certain time, and the perceived information might have a
33   * limited validity. In that sense, Perception is stateful. Information can be requested as often as needed, but will only be
34   * recalculated when asked explicitly. This abstract class provides the building blocks for lane-based perception. <br>
35   * Perception for lane-based GTUs involves information about GTUs in front of the owner GTU on the same lane (the 'leader' GTU),
36   * parallel vehicles (important if we want to change lanes), distance to other vehicles on parallel lanes, as well in front as
37   * to the back (important if we want to change lanes), and information about obstacles, traffic lights, speed signs, and ending
38   * lanes.
39   * <p>
40   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
41   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
42   * </p>
43   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
44   * initial version Nov 15, 2015 <br>
45   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
46   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
47   */
48  public abstract class AbstractLanePerception implements LanePerception
49  {
50      /** */
51      private static final long serialVersionUID = 20151128L;
52  
53      /** The lane based GTU for which this perception module stores information. */
54      private LaneBasedGTU gtu;
55  
56      /** The forward headway and (leader) GTU. */
57      private TimeStampedObject<HeadwayGTU> forwardHeadwayGTU;
58  
59      /** The backward headway and (follower) GTU. */
60      private TimeStampedObject<HeadwayGTU> backwardHeadwayGTU;
61  
62      /** The minimum speed limit of all lanes where the GTU is registered. */
63      private TimeStampedObject<Speed> speedLimit;
64  
65      /** The adjacent lanes that are accessible for the GTU at the left side. */
66      private TimeStampedObject<Map<Lane, Set<Lane>>> accessibleAdjacentLanesLeft;
67  
68      /** The adjacent lanes that are accessible for the GTU at the right side. */
69      private TimeStampedObject<Map<Lane, Set<Lane>>> accessibleAdjacentLanesRight;
70  
71      /** The GTUs parallel to us on the left side. */
72      private TimeStampedObject<Set<LaneBasedGTU>> parallelGTUsLeft;
73  
74      /** The GTUs parallel to us on the right side. */
75      private TimeStampedObject<Set<LaneBasedGTU>> parallelGTUsRight;
76  
77      /** The GTUs on the left side. */
78      private TimeStampedObject<Collection<HeadwayGTU>> neighboringGTUsLeft;
79  
80      /** The GTUs on the right side. */
81      private TimeStampedObject<Collection<HeadwayGTU>> neighboringGTUsRight;
82  
83      /** The lanes and path we expect to take if we do not change lanes. */
84      private TimeStampedObject<LanePathInfo> lanePathInfo;
85  
86      /** The structure of the lanes in front of the GTU. */
87      // TODO private LaneStructure laneStructure;
88  
89      /**
90       * Create a new LanePerception module. Because the constructor is often called inside the constructor of a GTU, this
91       * constructor does not ask for the pointer to the GTU, as it is often impossible to provide at the time of construction.
92       * Use the setter of the GTU instead.
93       */
94      public AbstractLanePerception()
95      {
96          super();
97      }
98  
99      /** {@inheritDoc} */
100     @Override
101     public final void setGTU(final LaneBasedGTU laneBasedGtu)
102     {
103         this.gtu = laneBasedGtu;
104     }
105 
106     /**
107      * Check whether the GTU has been initialized, and returns the current time.
108      * @return the current time according to the simulator.
109      * @throws GTUException when the GTU was not initialized yet.
110      */
111     private Time.Abs getTimestamp() throws GTUException
112     {
113         if (this.gtu == null)
114         {
115             throw new GTUException("gtu value has not been initialized for LanePerception when perceiving.");
116         }
117         return this.gtu.getSimulator().getSimulatorTime().getTime();
118     }
119 
120     /**
121      * @throws GTUException when the GTU was not initialized yet.
122      * @throws NetworkException when the speed limit for a GTU type cannot be retrieved from the network.
123      */
124     public final void updateLanePathInfo() throws GTUException, NetworkException
125     {
126         Time.Abs timestamp = getTimestamp();
127         this.lanePathInfo =
128                 new TimeStampedObject<LanePathInfo>(AbstractLaneBasedTacticalPlanner.buildLanePathInfo(this.gtu, this.gtu
129                         .getBehavioralCharacteristics().getForwardHeadwayDistance()), timestamp);
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public final void updateSpeedLimit() throws GTUException, NetworkException
135     {
136         Time.Abs timestamp = getTimestamp();
137         // assess the speed limit where we are right now
138         this.speedLimit = new TimeStampedObject<>(new Speed(Double.MAX_VALUE, SpeedUnit.SI), timestamp);
139         for (Lane lane : this.gtu.getLanes().keySet())
140         {
141             if (lane.getSpeedLimit(this.gtu.getGTUType()).lt(this.speedLimit.getObject()))
142             {
143                 this.speedLimit = new TimeStampedObject<>(lane.getSpeedLimit(this.gtu.getGTUType()), timestamp);
144             }
145         }
146     }
147 
148     /** {@inheritDoc} */
149     @Override
150     public final void updateForwardHeadwayGTU() throws GTUException, NetworkException
151     {
152         Time.Abs timestamp = getTimestamp();
153         if (this.lanePathInfo == null || this.lanePathInfo.getTimestamp().ne(timestamp))
154         {
155             updateLanePathInfo();
156         }
157         Length.Rel maximumForwardHeadway = this.gtu.getBehavioralCharacteristics().getForwardHeadwayDistance();
158         this.forwardHeadwayGTU = new TimeStampedObject<>(forwardHeadway(maximumForwardHeadway), timestamp);
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     public final void updateBackwardHeadwayGTU() throws GTUException, NetworkException
164     {
165         Time.Abs timestamp = getTimestamp();
166         Length.Rel maximumReverseHeadway = this.gtu.getBehavioralCharacteristics().getBackwardHeadwayDistance();
167         this.backwardHeadwayGTU = new TimeStampedObject<>(backwardHeadway(maximumReverseHeadway), timestamp);
168     }
169 
170     /** {@inheritDoc} */
171     @Override
172     public final void updateAccessibleAdjacentLanesLeft() throws GTUException
173     {
174         Time.Abs timestamp = getTimestamp();
175         Map<Lane, Set<Lane>> accessibleAdjacentLanesMap = new HashMap<>();
176         for (Lane lane : this.gtu.getLanes().keySet())
177         {
178             Set<Lane> adjacentLanes = new HashSet<Lane>(1);
179             adjacentLanes.addAll(lane.accessibleAdjacentLanes(LateralDirectionality.LEFT, this.gtu.getGTUType()));
180             accessibleAdjacentLanesMap.put(lane, adjacentLanes);
181         }
182         this.accessibleAdjacentLanesLeft = new TimeStampedObject<>(accessibleAdjacentLanesMap, timestamp);
183     }
184 
185     /** {@inheritDoc} */
186     @Override
187     public final void updateAccessibleAdjacentLanesRight() throws GTUException
188     {
189         Time.Abs timestamp = getTimestamp();
190         Map<Lane, Set<Lane>> accessibleAdjacentLanesMap = new HashMap<>();
191         for (Lane lane : this.gtu.getLanes().keySet())
192         {
193             Set<Lane> adjacentLanes = new HashSet<Lane>(1);
194             adjacentLanes.addAll(lane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, this.gtu.getGTUType()));
195             accessibleAdjacentLanesMap.put(lane, adjacentLanes);
196         }
197         this.accessibleAdjacentLanesRight = new TimeStampedObject<>(accessibleAdjacentLanesMap, timestamp);
198     }
199 
200     /** {@inheritDoc} */
201     @Override
202     public final void updateParallelGTUsLeft() throws GTUException
203     {
204         Time.Abs timestamp = getTimestamp();
205         if (this.accessibleAdjacentLanesLeft == null || !timestamp.equals(this.accessibleAdjacentLanesLeft.getTimestamp()))
206         {
207             updateAccessibleAdjacentLanesLeft();
208         }
209         Set<LaneBasedGTU> parallelGTUSet = new HashSet<>();
210         for (Lane lane : this.accessibleAdjacentLanesLeft.getObject().keySet())
211         {
212             for (Lane adjacentLane : this.accessibleAdjacentLanesLeft.getObject().get(lane))
213             {
214                 parallelGTUSet.addAll(parallel(adjacentLane, timestamp));
215             }
216         }
217         this.parallelGTUsLeft = new TimeStampedObject<>(parallelGTUSet, timestamp);
218     }
219 
220     /** {@inheritDoc} */
221     @Override
222     public final void updateParallelGTUsRight() throws GTUException
223     {
224         Time.Abs timestamp = getTimestamp();
225         if (this.accessibleAdjacentLanesRight == null || !timestamp.equals(this.accessibleAdjacentLanesRight.getTimestamp()))
226         {
227             updateAccessibleAdjacentLanesRight();
228         }
229         Set<LaneBasedGTU> parallelGTUSet = new HashSet<>();
230         for (Lane lane : this.accessibleAdjacentLanesRight.getObject().keySet())
231         {
232             for (Lane adjacentLane : this.accessibleAdjacentLanesRight.getObject().get(lane))
233             {
234                 parallelGTUSet.addAll(parallel(adjacentLane, timestamp));
235             }
236         }
237         this.parallelGTUsRight = new TimeStampedObject<>(parallelGTUSet, timestamp);
238     }
239 
240     /** {@inheritDoc} */
241     @Override
242     public final void updateLaneTrafficLeft() throws GTUException, NetworkException
243     {
244         Time.Abs timestamp = getTimestamp();
245         if (this.accessibleAdjacentLanesLeft == null || !timestamp.equals(this.accessibleAdjacentLanesLeft.getTimestamp()))
246         {
247             updateAccessibleAdjacentLanesLeft();
248         }
249 
250         if (this.parallelGTUsLeft == null || !timestamp.equals(this.parallelGTUsLeft.getTimestamp()))
251         {
252             updateParallelGTUsLeft();
253         }
254 
255         // for the accessible lanes, see who is ahead of us and in front of us
256         Length.Rel maximumForwardHeadway = this.gtu.getBehavioralCharacteristics().getForwardHeadwayDistance();
257         Length.Rel maximumReverseHeadway = this.gtu.getBehavioralCharacteristics().getBackwardHeadwayDistance();
258         this.neighboringGTUsLeft =
259                 new TimeStampedObject<>(collectNeighborLaneTraffic(LateralDirectionality.LEFT, timestamp,
260                         maximumForwardHeadway, maximumReverseHeadway), timestamp);
261     }
262 
263     /** {@inheritDoc} */
264     @Override
265     public final void updateLaneTrafficRight() throws GTUException, NetworkException
266     {
267         Time.Abs timestamp = getTimestamp();
268         if (this.accessibleAdjacentLanesRight == null || !timestamp.equals(this.accessibleAdjacentLanesRight.getTimestamp()))
269         {
270             updateAccessibleAdjacentLanesRight();
271         }
272 
273         if (this.parallelGTUsRight == null || !timestamp.equals(this.parallelGTUsRight.getTimestamp()))
274         {
275             updateParallelGTUsRight();
276         }
277 
278         // for the accessible lanes, see who is ahead of us and in front of us
279         Length.Rel maximumForwardHeadway = this.gtu.getBehavioralCharacteristics().getForwardHeadwayDistance();
280         Length.Rel maximumReverseHeadway = this.gtu.getBehavioralCharacteristics().getBackwardHeadwayDistance();
281         this.neighboringGTUsRight =
282                 new TimeStampedObject<>(collectNeighborLaneTraffic(LateralDirectionality.RIGHT, timestamp,
283                         maximumForwardHeadway, maximumReverseHeadway), timestamp);
284     }
285 
286     /** {@inheritDoc} */
287     @Override
288     public final Map<Lane, Set<Lane>> accessibleAdjacentLaneMap(final LateralDirectionality lateralDirection)
289     {
290         return lateralDirection.equals(LateralDirectionality.LEFT) ? this.accessibleAdjacentLanesLeft.getObject()
291                 : this.accessibleAdjacentLanesRight.getObject();
292     }
293 
294     /** {@inheritDoc} */
295     @Override
296     public final Set<LaneBasedGTU> parallelGTUs(final LateralDirectionality lateralDirection)
297     {
298         return lateralDirection.equals(LateralDirectionality.LEFT) ? this.parallelGTUsLeft.getObject() : this.parallelGTUsRight
299                 .getObject();
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     public final Collection<HeadwayGTU> neighboringGTUCollection(final LateralDirectionality lateralDirection)
305     {
306         return lateralDirection.equals(LateralDirectionality.LEFT) ? this.neighboringGTUsLeft.getObject()
307                 : this.neighboringGTUsRight.getObject();
308     }
309 
310     /**************************************************************************************************************************/
311     /**************************************************** HEADWAY ALGORITHMS **************************************************/
312     /**************************************************************************************************************************/
313 
314     /**
315      * Determine which GTU is in front of this GTU. This method looks in all lanes where this GTU is registered, and not further
316      * than the value of the given maxDistance. The minimum headway is returned of all Lanes where the GTU is registered. When
317      * no GTU is found within the given maxDistance, a HeadwayGTU with <b>null</b> as the gtuId and maxDistance as the distance
318      * is returned. The search will extend into successive lanes if the maxDistance is larger than the remaining length on the
319      * lane. When Lanes (or underlying CrossSectionLinks) diverge, a route planner may be used to determine which kinks and
320      * lanes to take into account and which ones not. When the Lanes (or underlying CrossSectionLinks) converge, "parallel"
321      * traffic is not taken into account in the headway calculation. Instead, gap acceptance algorithms or their equivalent
322      * should guide the merging behavior.<br>
323      * <b>Note:</b> Headway is the net headway and calculated on a front-to-back basis.
324      * @param maxDistance the maximum distance to look for the nearest GTU; positive values search forwards; negative values
325      *            search backwards
326      * @return HeadwayGTU; the headway and the GTU information
327      * @throws GTUException when there is an error with the next lanes in the network.
328      * @throws NetworkException when there is a problem with the route planner
329      */
330     private HeadwayGTU forwardHeadway(final Length.Rel maxDistance) throws GTUException, NetworkException
331     {
332         LanePathInfo lpi = getLanePathInfo();
333         return forwardHeadway(lpi, maxDistance);
334     }
335 
336     /**
337      * Determine which GTU is in front of this GTU. This method uses a given lanePathInfo to look forward, but not further than
338      * the value of the given maxDistance. The minimum headway is returned of all Lanes where the GTU is registered. When no GTU
339      * is found within the given maxDistance, a HeadwayGTU with <b>null</b> as the gtuId and maxDistance as the distance is
340      * returned. The search will extend into successive lanes if the maxDistance is larger than the remaining length on the
341      * lane. When Lanes (or underlying CrossSectionLinks) diverge, a route planner may be used to determine which kinks and
342      * lanes to take into account and which ones not. When the Lanes (or underlying CrossSectionLinks) converge, "parallel"
343      * traffic is not taken into account in the headway calculation. Instead, gap acceptance algorithms or their equivalent
344      * should guide the merging behavior.<br>
345      * <b>Note:</b> Headway is the net headway and calculated on a front-to-back basis.
346      * @param lpi the lanePathInfo object that informs the headway algorithm in which lanes to look, and from which position on
347      *            the first lane.
348      * @param maxDistance the maximum distance to look for the nearest GTU; positive values search forwards; negative values
349      *            search backwards
350      * @return HeadwayGTU; the headway and the GTU information
351      * @throws GTUException when there is an error with the next lanes in the network.
352      * @throws NetworkException when there is a problem with the route planner
353      */
354     private HeadwayGTU forwardHeadway(final LanePathInfo lpi, final Length.Rel maxDistance) throws GTUException,
355             NetworkException
356     {
357         GTUException.failIf(maxDistance.le(Length.Rel.ZERO), "forwardHeadway: maxDistance should be positive");
358 
359         int ldIndex = 0;
360         LaneDirection ld = lpi.getReferenceLaneDirection();
361         double gtuPosFrontSI = lpi.getReferencePosition().si;
362         if (lpi.getReferenceLaneDirection().getDirection().isPlus())
363         {
364             gtuPosFrontSI += this.gtu.getFront().getDx().si;
365         }
366         else
367         {
368             gtuPosFrontSI -= this.gtu.getFront().getDx().si;
369         }
370         
371         // TODO end of lanepath
372         
373         while ((gtuPosFrontSI > ld.getLane().getLength().si || gtuPosFrontSI < 0.0) && ldIndex < lpi.getLaneDirectionList().size() - 1)
374         {
375             ldIndex++;
376             if (ld.getDirection().isPlus()) // e.g. 1005 on length of lane = 1000
377             {            
378                 if (lpi.getLaneDirectionList().get(ldIndex).getDirection().isPlus())
379                     gtuPosFrontSI -= ld.getLane().getLength().si;
380                 else
381                     gtuPosFrontSI = lpi.getLaneDirectionList().get(ldIndex).getLane().getLength().si - gtuPosFrontSI;
382                 ld = lpi.getLaneDirectionList().get(ldIndex);
383             }
384             else // e.g. -5 on lane of whatever length
385             {
386                 if (lpi.getLaneDirectionList().get(ldIndex).getDirection().isPlus())
387                     gtuPosFrontSI += ld.getLane().getLength().si;
388                 else
389                     gtuPosFrontSI += lpi.getLaneDirectionList().get(ldIndex).getLane().getLength().si;
390                 ld = lpi.getLaneDirectionList().get(ldIndex);
391             }
392         }
393 
394         double maxDistanceSI = maxDistance.si;
395         Time.Abs time = this.gtu.getSimulator().getSimulatorTime().getTime();
396 
397         // look forward based on the provided lanePathInfo.
398         HeadwayGTU closest = headwayGTULane(ld, gtuPosFrontSI, 0.0, time);
399         if (closest != null)
400         {
401             if (closest.getDistance().si > maxDistanceSI)
402             {
403                 return new HeadwayGTU(null, null, maxDistanceSI, null);
404             }
405             return closest;
406         }
407         double cumDistSI = ld.getDirection().isPlus() ? ld.getLane().getLength().si - gtuPosFrontSI : gtuPosFrontSI;
408         for (int i = ldIndex + 1; i < lpi.getLaneDirectionList().size(); i++)
409         {
410             ld = lpi.getLaneDirectionList().get(i);
411             closest = headwayGTULane(ld, ld.getDirection().isPlus() ? 0.0 : ld.getLane().getLength().si, cumDistSI, time);
412             if (closest != null)
413             {
414                 if (closest.getDistance().si > maxDistanceSI)
415                 {
416                     return new HeadwayGTU(null, null, maxDistanceSI, null);
417                 }
418                 return closest;
419             }
420             cumDistSI += ld.getLane().getLength().si;
421         }
422         return new HeadwayGTU(null, null, maxDistanceSI, null);
423     }
424 
425     /**
426      * Determine the positive headway on a lane, or null if no GTU can be found on this lane.
427      * @param laneDirection the lane and direction to look
428      * @param startPosSI the start position to look from in meters
429      * @param cumDistSI the cumulative distance that has already been observed on other lanes
430      * @param now the current time to determine the GTU positions on the lane
431      * @return the HeadwayGTU, containing information on a GTU that is ahead of the given start position, or null if no GTU can
432      *         be found on this lane
433      * @throws GTUException when the GTUs ahead on the lane cannot be determined
434      */
435     private HeadwayGTU headwayGTULane(final LaneDirection laneDirection, final double startPosSI, final double cumDistSI,
436             final Time.Abs now) throws GTUException
437     {
438         Lane lane = laneDirection.getLane();
439         LaneBasedGTU laneBasedGTU =
440                 lane.getGtuAhead(new Length.Rel(startPosSI, LengthUnit.SI), laneDirection.getDirection(),
441                         RelativePosition.REAR, now);
442         if (laneBasedGTU == null)
443         {
444             return null;
445         }
446         double distanceSI = Math.abs(laneBasedGTU.position(lane, laneBasedGTU.getRear()).si - startPosSI);
447         return new HeadwayGTU(laneBasedGTU.getId(), laneBasedGTU.getVelocity(), cumDistSI + distanceSI,
448                 laneBasedGTU.getGTUType());
449     }
450 
451     /**
452      * Determine which GTU is behind this GTU. This method looks in all lanes where this GTU is registered, and not further back
453      * than the absolute value of the given maxDistance. The minimum net headway is returned of all Lanes where the GTU is
454      * registered. When no GTU is found within the given maxDistance, <b>null</b> is returned. The search will extend into
455      * successive lanes if the maxDistance is larger than the remaining length on the lane. When Lanes (or underlying
456      * CrossSectionLinks) diverge, the headway algorithms have to look at multiple Lanes and return the minimum headway in each
457      * of the Lanes. When the Lanes (or underlying CrossSectionLinks) converge, "parallel" traffic is not taken into account in
458      * the headway calculation. Instead, gap acceptance algorithms or their equivalent should guide the merging behavior.<br>
459      * <b>Note:</b> Headway is the net headway and calculated on a back-to-front basis.
460      * @param maxDistance the maximum distance to look for the nearest GTU; it should have a negative value to search backwards
461      * @return HeadwayGTU; the headway and the GTU information
462      * @throws GTUException when there is an error with the next lanes in the network.
463      * @throws NetworkException when there is a problem with the route planner
464      */
465     private HeadwayGTU backwardHeadway(final Length.Rel maxDistance) throws GTUException, NetworkException
466     {
467         GTUException.failIf(maxDistance.ge(Length.Rel.ZERO), "backwardHeadway: maxDistance should be negative");
468         Time.Abs time = this.gtu.getSimulator().getSimulatorTime().getTime();
469         double maxDistanceSI = maxDistance.si;
470         HeadwayGTU foundMaxGTUDistanceSI = new HeadwayGTU(null, null, -maxDistanceSI, null);
471         for (Lane lane : this.gtu.positions(this.gtu.getRear()).keySet())
472         {
473             HeadwayGTU closest =
474                     headwayRecursiveBackwardSI(lane, this.gtu.getLanes().get(lane),
475                             this.gtu.position(lane, this.gtu.getRear(), time).getSI(), 0.0, -maxDistanceSI, time);
476             if (closest.getDistance().si < -maxDistanceSI && closest.getDistance().si < -foundMaxGTUDistanceSI.getDistance().si)
477             {
478                 foundMaxGTUDistanceSI = closest;
479             }
480         }
481         return new HeadwayGTU(foundMaxGTUDistanceSI.getGtuId(), foundMaxGTUDistanceSI.getGtuSpeed(), foundMaxGTUDistanceSI
482                 .getDistance().multiplyBy(-1.0), foundMaxGTUDistanceSI.getGtuType());
483     }
484 
485     /**
486      * Calculate the minimum headway, possibly on subsequent lanes, in backward direction (so between our back, and the other
487      * GTU's front). Note: this method returns a POSITIVE number.
488      * @param lane the lane where we are looking right now
489      * @param direction the direction we are driving on that lane
490      * @param lanePositionSI from which position on this lane do we start measuring? This is the current position of the rear of
491      *            the GTU when we measure in the lane where the original GTU is positioned, and lane.getLength() for each
492      *            subsequent lane.
493      * @param cumDistanceSI the distance we have already covered searching on previous lanes. Note: This is a POSITIVE number.
494      * @param maxDistanceSI the maximum distance to look for in SI units; stays the same in subsequent calls. Note: this is a
495      *            POSITIVE number.
496      * @param when the current or future time for which to calculate the headway
497      * @return the headway in SI units when we have found the GTU, or a null GTU with a distance of Double.MAX_VALUE meters when
498      *         no other GTU could not be found within maxDistanceSI meters
499      * @throws GTUException when there is a problem with the geometry of the network
500      */
501     private HeadwayGTU headwayRecursiveBackwardSI(final Lane lane, final GTUDirectionality direction,
502             final double lanePositionSI, final double cumDistanceSI, final double maxDistanceSI, final Time.Abs when)
503             throws GTUException
504     {
505         LaneBasedGTU otherGTU =
506                 lane.getGtuBehind(new Length.Rel(lanePositionSI, LengthUnit.SI), direction, RelativePosition.FRONT, when);
507         if (otherGTU != null)
508         {
509             double distanceM = cumDistanceSI + lanePositionSI - otherGTU.position(lane, otherGTU.getFront(), when).getSI();
510             if (distanceM > 0 && distanceM <= maxDistanceSI)
511             {
512                 return new HeadwayGTU(otherGTU.getId(), otherGTU.getVelocity(), distanceM, otherGTU.getGTUType());
513             }
514             return new HeadwayGTU(null, null, Double.MAX_VALUE, null);
515         }
516 
517         // Continue search on predecessor lanes.
518         if (cumDistanceSI + lanePositionSI < maxDistanceSI)
519         {
520             // is there a predecessor link?
521             if (lane.prevLanes(this.gtu.getGTUType()).size() > 0)
522             {
523                 HeadwayGTU foundMaxGTUDistanceSI = new HeadwayGTU(null, null, Double.MAX_VALUE, null);
524                 for (Lane prevLane : lane.prevLanes(this.gtu.getGTUType()).keySet())
525                 {
526                     // What is behind us is INDEPENDENT of the followed route!
527                     double traveledDistanceSI = cumDistanceSI + lanePositionSI;
528                     // WRONG - adapt method to forward perception method!
529                     HeadwayGTU closest =
530                             headwayRecursiveBackwardSI(prevLane, direction, prevLane.getLength().getSI(), traveledDistanceSI,
531                                     maxDistanceSI, when);
532                     if (closest.getDistance().si < maxDistanceSI
533                             && closest.getDistance().si < foundMaxGTUDistanceSI.getDistance().si)
534                     {
535                         foundMaxGTUDistanceSI = closest;
536                     }
537                 }
538                 return foundMaxGTUDistanceSI;
539             }
540         }
541 
542         // No other GTU was not on one of the current lanes or their successors.
543         return new HeadwayGTU(null, null, Double.MAX_VALUE, null);
544     }
545 
546     /**************************************************************************************************************************/
547     /************************************************ ADJACENT LANE TRAFFIC ***************************************************/
548     /**************************************************************************************************************************/
549 
550     /**
551      * Determine which GTUs are parallel with us on another lane, based on fractional positions. <br>
552      * Note: When the GTU that calls the method is also registered on the given lane, it is excluded from the return set.
553      * @param lane the lane to look for parallel (partial or full overlapping) GTUs.
554      * @param when the future time for which to calculate the headway
555      * @return the set of GTUs parallel to us on the other lane (partial overlap counts as parallel), based on fractional
556      *         positions, or an empty set when no GTUs were found.
557      * @throws GTUException when the vehicle's route is inconclusive, when vehicles are not registered correctly on their lanes,
558      *             or when the given lane is not parallel to one of the lanes where we are registered.
559      */
560     private Set<LaneBasedGTU> parallel(final Lane lane, final Time.Abs when) throws GTUException
561     {
562         Set<LaneBasedGTU> gtuSet = new LinkedHashSet<LaneBasedGTU>();
563         for (Lane l : this.gtu.getLanes().keySet())
564         {
565             // only take lanes that we can compare based on a shared design line
566             if (l.getParentLink().equals(lane.getParentLink()))
567             {
568                 // compare based on fractional positions.
569                 double posFractionRef = this.gtu.fractionalPosition(l, this.gtu.getReference(), when);
570                 double posFractionFront = Math.max(0.0, posFractionRef + this.gtu.getFront().getDx().si / lane.getLength().si);
571                 double posFractionRear = Math.min(1.0, posFractionRef + this.gtu.getRear().getDx().si / lane.getLength().si);
572                 // double posFractionFront = Math.max(0.0, this.gtu.fractionalPosition(l, this.gtu.getFront(), when));
573                 // double posFractionRear = Math.min(1.0, this.gtu.fractionalPosition(l, this.gtu.getRear(), when));
574                 double posMin = Math.min(posFractionFront, posFractionRear);
575                 double posMax = Math.max(posFractionFront, posFractionRear);
576                 for (LaneBasedGTU otherGTU : lane.getGtuList())
577                 {
578                     if (!otherGTU.equals(this))
579                     {
580                         /*- cater for: *-----*         *-----*       *-----*       *----------*
581                          *                *-----*    *----*      *------------*       *-----*
582                          * where the GTUs can each drive in two directions (!)
583                          */
584                         double gtuFractionRef = otherGTU.fractionalPosition(lane, otherGTU.getReference(), when);
585                         double gtuFractionFront = Math.max(0.0, gtuFractionRef + otherGTU.getFront().getDx().si / lane.getLength().si);
586                         double gtuFractionRear = Math.min(1.0, gtuFractionRef + otherGTU.getRear().getDx().si / lane.getLength().si);
587                         // double gtuFractionFront = Math.max(0.0, otherGTU.fractionalPosition(lane, otherGTU.getFront(), when));
588                         // double gtuFractionRear = Math.min(1.0, otherGTU.fractionalPosition(lane, otherGTU.getRear(), when));
589                         double gtuMin = Math.min(gtuFractionFront, gtuFractionRear);
590                         double gtuMax = Math.max(gtuFractionFront, gtuFractionRear);
591                         if ((gtuMin >= posMin && gtuMin <= posMax) || (gtuMax >= posMin && gtuMax <= posMax)
592                                 || (posMin >= gtuMin && posMin <= gtuMax) || (posMax >= gtuMin && posMax <= gtuMax))
593                         {
594                             gtuSet.add(otherGTU);
595                         }
596                     }
597                 }
598             }
599         }
600         return gtuSet;
601     }
602 
603     /**
604      * Determine which GTUs are parallel with us in a certain lateral direction, based on fractional positions. <br>
605      * Note 1: This method will look to the adjacent lanes of all lanes where the vehicle has been registered.<br>
606      * Note 2: When the GTU that calls the method is also registered on the given lane, it is excluded from the return set.
607      * @param lateralDirection the direction of the adjacent lane(s) to look for parallel (partial or full overlapping) GTUs.
608      * @param when the future time for which to calculate the headway
609      * @return the set of GTUs parallel to us on other lane(s) in the given direction (partial overlap counts as parallel),
610      *         based on fractional positions, or an empty set when no GTUs were found.
611      * @throws GTUException when the vehicle's route is inconclusive, when vehicles are not registered correctly on their lanes,
612      *             or when there are no lanes parallel to one of the lanes where we are registered in the given direction.
613      */
614     private Set<LaneBasedGTU> parallel(final LateralDirectionality lateralDirection, final Time.Abs when) throws GTUException
615     {
616         Set<LaneBasedGTU> gtuSet = new LinkedHashSet<LaneBasedGTU>();
617         for (Lane lane : this.gtu.getLanes().keySet())
618         {
619             for (Lane adjacentLane : accessibleAdjacentLaneMap(lateralDirection).get(lane))
620             {
621                 gtuSet.addAll(parallel(adjacentLane, when));
622             }
623         }
624         return gtuSet;
625     }
626 
627     /**
628      * Determine whether there is a lane to the left or to the right of this lane, which is accessible from this lane, or null
629      * if no lane could be found. The method takes the LongitidinalDirectionality of the lane into account. In other words, if
630      * we drive FORWARD and look for a lane on the LEFT, and there is a lane but the Directionality of that lane is not FORWARD
631      * or BOTH, null will be returned.<br>
632      * A lane is called adjacent to another lane if the lateral edges are not more than a delta distance apart. This means that
633      * a lane that <i>overlaps</i> with another lane is <b>not</b> returned as an adjacent lane. <br>
634      * The algorithm also looks for RoadMarkerAcross elements between the lanes to determine the lateral permeability for a GTU.
635      * A RoadMarkerAcross is seen as being between two lanes if its center line is not more than delta distance from the
636      * relevant lateral edges of the two adjacent lanes. <br>
637      * When there are multiple lanes that are adjacent, which could e.g. be the case if an overlapping tram lane and a car lane
638      * are adjacent to the current lane, the widest lane that best matches the GTU accessibility of the provided GTUType is
639      * returned. <br>
640      * <b>Note:</b> LEFT is seen as a negative lateral direction, RIGHT as a positive lateral direction. <br>
641      * FIXME In other places in OTS LEFT is positive (and RIGHT is negative). This should be made more consistent.
642      * @param currentLane the lane to look for the best accessible adjacent lane
643      * @param lateralDirection the direction (LEFT, RIGHT) to look at
644      * @param longitudinalPosition Length.Rel; the position of the GTU along <cite>currentLane</cite>
645      * @return the lane if it is accessible, or null if there is no lane, it is not accessible, or the driving direction does
646      *         not match.
647      */
648     public final Lane bestAccessibleAdjacentLane(final Lane currentLane, final LateralDirectionality lateralDirection,
649             final Length.Rel longitudinalPosition)
650     {
651         Set<Lane> candidates = accessibleAdjacentLaneMap(lateralDirection).get(currentLane);
652         if (candidates.isEmpty())
653         {
654             return null; // There is no adjacent Lane that this GTU type can cross into
655         }
656         if (candidates.size() == 1)
657         {
658             return candidates.iterator().next(); // There is exactly one adjacent Lane that this GTU type can cross into
659         }
660         // There are several candidates; find the one that is widest at the beginning.
661         Lane bestLane = null;
662         double widestSeen = Double.NEGATIVE_INFINITY;
663         for (Lane lane : candidates)
664         {
665             if (lane.getWidth(longitudinalPosition).getSI() > widestSeen)
666             {
667                 widestSeen = lane.getWidth(longitudinalPosition).getSI();
668                 bestLane = lane;
669             }
670         }
671         return bestLane;
672     }
673 
674     /**
675      * Collect relevant traffic in adjacent lanes. Parallel traffic is included with headway equal to Double.NaN.
676      * @param directionality LateralDirectionality; either <cite>LateralDirectionality.LEFT</cite>, or
677      *            <cite>LateralDirectionality.RIGHT</cite>
678      * @param when DoubleScalar.Abs&lt;TimeUnit&gt;; the (current) time
679      * @param maximumForwardHeadway DoubleScalar.Rel&lt;LengthUnit&gt;; the maximum forward search distance
680      * @param maximumReverseHeadway DoubleScalar.Rel&lt;LengthUnit&gt;; the maximum reverse search distance
681      * @return Collection&lt;LaneBasedGTU&gt;;
682      * @throws NetworkException on network inconsistency
683      * @throws GTUException on problems with the GTU state (e.g., position)
684      */
685     private Collection<HeadwayGTU> collectNeighborLaneTraffic(final LateralDirectionality directionality, final Time.Abs when,
686             final Length.Rel maximumForwardHeadway, final Length.Rel maximumReverseHeadway) throws NetworkException,
687             GTUException
688     {
689         Collection<HeadwayGTU> result = new HashSet<HeadwayGTU>();
690         for (LaneBasedGTU p : parallel(directionality, when))
691         {
692             result.add(new HeadwayGTU(p.getId(), p.getVelocity(), Double.NaN, p.getGTUType()));
693         }
694 
695         // forward
696         for (Lane adjacentLane : accessibleAdjacentLaneMap(directionality).get(getLanePathInfo().getReferenceLane()))
697         {
698             LanePathInfo lpiAdjacent = buildLanePathInfoAdjacent(adjacentLane, directionality, when);
699             HeadwayGTU leader = forwardHeadway(lpiAdjacent, maximumForwardHeadway);
700             if (null != leader.getGtuId() && !result.contains(leader))
701             {
702                 result.add(leader);
703             }
704         }
705 
706         // backward
707         for (Lane lane : this.gtu.getLanes().keySet())
708         {
709             for (Lane adjacentLane : accessibleAdjacentLaneMap(directionality).get(lane))
710             {
711                 HeadwayGTU follower =
712                         headwayRecursiveBackwardSI(adjacentLane, this.gtu.getLanes().get(lane),
713                                 this.gtu.projectedPosition(adjacentLane, this.gtu.getRear(), when).getSI(), 0.0,
714                                 -maximumReverseHeadway.getSI(), when);
715                 if (null != follower.getGtuId())
716                 {
717                     boolean found = false;
718                     for (HeadwayGTU hg : result)
719                     {
720                         if (hg.getGtuId().equals(follower.getGtuId()))
721                         {
722                             found = true;
723                         }
724                     }
725                     if (!found)
726                     {
727                         result.add(new HeadwayGTU(follower.getGtuId(), follower.getGtuSpeed(), -follower.getDistance().si,
728                                 follower.getGtuType()));
729                     }
730                 }
731             }
732         }
733         return result;
734     }
735 
736     /**
737      * Find a lanePathInfo left or right of the current LanePath.
738      * @param adjacentLane the start adjacent lane for which we calculate the LanePathInfo
739      * @param direction LateralDirectionality; either <cite>LateralDirectionality.LEFT</cite>, or
740      *            <cite>LateralDirectionality.RIGHT</cite>
741      * @param when DoubleScalar.Abs&lt;TimeUnit&gt;; the (current) time
742      * @return the adjacent LanePathInfo
743      * @throws GTUException when the GTU was not initialized yet.
744      * @throws NetworkException when the speed limit for a GTU type cannot be retrieved from the network.
745      */
746     private LanePathInfo buildLanePathInfoAdjacent(final Lane adjacentLane, final LateralDirectionality direction,
747             final Time.Abs when) throws GTUException, NetworkException
748     {
749         if (this.lanePathInfo == null || this.lanePathInfo.getTimestamp().ne(when))
750         {
751             updateLanePathInfo();
752         }
753         LanePathInfo lpi = getLanePathInfo();
754         List<LaneDirection> laneDirectionList = new ArrayList<>();
755         laneDirectionList.add(new LaneDirection(adjacentLane, lpi.getReferenceLaneDirection().getDirection()));
756         Length.Rel referencePosition = this.gtu.projectedPosition(adjacentLane, this.gtu.getReference(), when);
757         for (int i = 1; i < lpi.getLaneDirectionList().size(); i++)
758         {
759             LaneDirection ld = lpi.getLaneDirectionList().get(i);
760             Set<Lane> accessibleLanes = ld.getLane().accessibleAdjacentLanes(direction, this.gtu.getGTUType());
761             Lane adjLane = null;
762             for (Lane lane : accessibleLanes)
763             {
764                 if (lane.getParentLink().equals(ld.getLane().getParentLink()))
765                 {
766                     adjLane = lane;
767                 }
768             }
769             if (adjLane == null)
770             {
771                 break;
772             }
773             laneDirectionList.add(new LaneDirection(adjLane, ld.getDirection()));
774         }
775         return new LanePathInfo(null, laneDirectionList, referencePosition);
776     }
777 
778     /**************************************************************************************************************************/
779     /*************************************************** GETTERS FOR THE INFORMATION ******************************************/
780     /**************************************************************************************************************************/
781 
782     /** {@inheritDoc} */
783     @Override
784     public final LaneBasedGTU getGTU()
785     {
786         return this.gtu;
787     }
788 
789     /**
790      * Retrieve the last perceived lane path info.
791      * @return LanePathInfo
792      */
793     public final LanePathInfo getLanePathInfo()
794     {
795         return this.lanePathInfo.getObject();
796     }
797 
798     /** {@inheritDoc} */
799     @Override
800     public final HeadwayGTU getForwardHeadwayGTU()
801     {
802         return this.forwardHeadwayGTU.getObject();
803     }
804 
805     /** {@inheritDoc} */
806     @Override
807     public final HeadwayGTU getBackwardHeadwayGTU()
808     {
809         return this.backwardHeadwayGTU.getObject();
810     }
811 
812     /** {@inheritDoc} */
813     @Override
814     public final Map<Lane, Set<Lane>> getAccessibleAdjacentLanesLeft()
815     {
816         return this.accessibleAdjacentLanesLeft.getObject();
817     }
818 
819     /** {@inheritDoc} */
820     @Override
821     public final Map<Lane, Set<Lane>> getAccessibleAdjacentLanesRight()
822     {
823         return this.accessibleAdjacentLanesRight.getObject();
824     }
825 
826     /** {@inheritDoc} */
827     @Override
828     public final Collection<HeadwayGTU> getNeighboringGTUsLeft()
829     {
830         return this.neighboringGTUsLeft.getObject();
831     }
832 
833     /** {@inheritDoc} */
834     @Override
835     public final Collection<HeadwayGTU> getNeighboringGTUsRight()
836     {
837         return this.neighboringGTUsRight.getObject();
838     }
839 
840     /** {@inheritDoc} */
841     @Override
842     public final Set<LaneBasedGTU> getParallelGTUsLeft()
843     {
844         return this.parallelGTUsLeft.getObject();
845     }
846 
847     /** {@inheritDoc} */
848     @Override
849     public final Set<LaneBasedGTU> getParallelGTUsRight()
850     {
851         return this.parallelGTUsRight.getObject();
852     }
853 
854     /** {@inheritDoc} */
855     @Override
856     public final Speed getSpeedLimit()
857     {
858         return this.speedLimit.getObject();
859     }
860 
861     /** {@inheritDoc} */
862     @Override
863     public final Set<PerceivedObject> getPerceivedObjects()
864     {
865         // TODO getPerceivedObjects() in LanePerception
866         return new HashSet<PerceivedObject>();
867     }
868 
869     /** {@inheritDoc} */
870     @Override
871     public final TimeStampedObject<HeadwayGTU> getTimeStampedForwardHeadwayGTU()
872     {
873         return this.forwardHeadwayGTU;
874     }
875 
876     /** {@inheritDoc} */
877     @Override
878     public final TimeStampedObject<HeadwayGTU> getTimeStampedBackwardHeadwayGTU()
879     {
880         return this.backwardHeadwayGTU;
881     }
882 
883     /** {@inheritDoc} */
884     @Override
885     public final TimeStampedObject<Map<Lane, Set<Lane>>> getTimeStampedAccessibleAdjacentLanesLeft()
886     {
887         return this.accessibleAdjacentLanesLeft;
888     }
889 
890     /** {@inheritDoc} */
891     @Override
892     public final TimeStampedObject<Map<Lane, Set<Lane>>> getTimeStampedAccessibleAdjacentLanesRight()
893     {
894         return this.accessibleAdjacentLanesRight;
895     }
896 
897     /** {@inheritDoc} */
898     @Override
899     public final TimeStampedObject<Collection<HeadwayGTU>> getTimeStampedNeighboringGTUsLeft()
900     {
901         return this.neighboringGTUsLeft;
902     }
903 
904     /** {@inheritDoc} */
905     @Override
906     public final TimeStampedObject<Collection<HeadwayGTU>> getTimeStampedNeighboringGTUsRight()
907     {
908         return this.neighboringGTUsRight;
909     }
910 
911     /** {@inheritDoc} */
912     @Override
913     public final TimeStampedObject<Set<LaneBasedGTU>> getTimeStampedParallelGTUsLeft()
914     {
915         return this.parallelGTUsLeft;
916     }
917 
918     /** {@inheritDoc} */
919     @Override
920     public final TimeStampedObject<Set<LaneBasedGTU>> getTimeStampedParallelGTUsRight()
921     {
922         return this.parallelGTUsRight;
923     }
924 
925     /** {@inheritDoc} */
926     @Override
927     public final TimeStampedObject<Speed> getTimeStampedSpeedLimit()
928     {
929         return this.speedLimit;
930     }
931 
932     /** {@inheritDoc} */
933     @Override
934     public TimeStampedObject<Set<PerceivedObject>> getTimeStampedPerceivedObjects() throws GTUException
935     {
936         // TODO getPerceivedObjects() in LanePerception
937         return new TimeStampedObject<Set<PerceivedObject>>(new HashSet<PerceivedObject>(), getTimestamp());
938     }
939 
940 }