View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perceptionold;
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  import java.util.SortedSet;
12  import java.util.TreeSet;
13  
14  import org.djunits.unit.LengthUnit;
15  import org.djunits.unit.SpeedUnit;
16  import org.djunits.value.vdouble.scalar.Length;
17  import org.djunits.value.vdouble.scalar.Speed;
18  import org.djunits.value.vdouble.scalar.Time;
19  import org.opentrafficsim.core.Throw;
20  import org.opentrafficsim.core.gtu.GTUDirectionality;
21  import org.opentrafficsim.core.gtu.GTUException;
22  import org.opentrafficsim.core.gtu.RelativePosition;
23  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
24  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
25  import org.opentrafficsim.core.gtu.perception.AbstractPerception;
26  import org.opentrafficsim.core.gtu.perception.TimeStampedObject;
27  import org.opentrafficsim.core.network.LateralDirectionality;
28  import org.opentrafficsim.core.network.NetworkException;
29  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
30  import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
31  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
32  import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
33  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
34  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
35  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayDistance;
36  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
37  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
38  import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
39  import org.opentrafficsim.road.gtu.lane.tactical.LanePathInfo;
40  import org.opentrafficsim.road.network.lane.Lane;
41  import org.opentrafficsim.road.network.lane.LaneDirection;
42  import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
43  import org.opentrafficsim.road.network.speed.SpeedLimitTypes;
44  
45  /**
46   * The perception module of a GTU based on lanes. It is responsible for perceiving (sensing) the environment of the GTU, which
47   * includes the locations of other GTUs. Perception is done at a certain time, and the perceived information might have a
48   * limited validity. In that sense, Perception is stateful. Information can be requested as often as needed, but will only be
49   * recalculated when asked explicitly. This abstract class provides the building blocks for lane-based perception. <br>
50   * Perception for lane-based GTUs involves information about GTUs in front of the owner GTU on the same lane (the 'leader' GTU),
51   * parallel vehicles (important if we want to change lanes), distance to other vehicles on parallel lanes, as well in front as
52   * to the back (important if we want to change lanes), and information about obstacles, traffic lights, speed signs, and ending
53   * lanes.
54   * <p>
55   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
56   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
57   * </p>
58   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
59   * initial version Nov 15, 2015 <br>
60   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
61   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
62   */
63  public abstract class AbstractLanePerception extends AbstractPerception implements LanePerception
64  {
65      /** */
66      private static final long serialVersionUID = 20151128L;
67  
68      /** The forward headway and (leader) object. */
69      private TimeStampedObject<Headway> forwardHeadway;
70  
71      /** The backward headway and (follower) object. */
72      private TimeStampedObject<Headway> backwardHeadway;
73  
74      /** The minimum speed limit of all lanes where the GTU is registered. */
75      private TimeStampedObject<Speed> speedLimit;
76  
77      /** The adjacent lanes that are accessible for the GTU at the left side. */
78      private TimeStampedObject<Map<Lane, Set<Lane>>> accessibleAdjacentLanesLeft;
79  
80      /** The adjacent lanes that are accessible for the GTU at the right side. */
81      private TimeStampedObject<Map<Lane, Set<Lane>>> accessibleAdjacentLanesRight;
82  
83      /** The objects parallel to us on the left side. */
84      private TimeStampedObject<Collection<Headway>> parallelHeadwaysLeft;
85  
86      /** The objects parallel to us on the right side. */
87      private TimeStampedObject<Collection<Headway>> parallelHeadwaysRight;
88  
89      /** The GTUs on the left side. */
90      private TimeStampedObject<Collection<Headway>> neighboringHeadwaysLeft;
91  
92      /** The GTUs on the right side. */
93      private TimeStampedObject<Collection<Headway>> neighboringHeadwaysRight;
94  
95      /** The lanes and path we expect to take if we do not change lanes. */
96      private TimeStampedObject<LanePathInfo> lanePathInfo;
97  
98      /** The structure of the lanes in front of the GTU. */
99      // TODO private LaneStructure laneStructure;
100 
101     /**
102      * Create a new LanePerception module. Because the constructor is often called inside the constructor of a GTU, this
103      * constructor does not ask for the pointer to the GTU, as it is often impossible to provide at the time of construction.
104      * Use the setter of the GTU instead.
105      * @param gtu GTU
106      */
107     public AbstractLanePerception(final LaneBasedGTU gtu)
108     {
109         super(gtu);
110     }
111 
112     /**
113      * Check whether the GTU has been initialized, and returns the current time.
114      * @return the current time according to the simulator.
115      * @throws GTUException when the GTU was not initialized yet.
116      */
117     private Time getTimestamp() throws GTUException
118     {
119         if (getGtu() == null)
120         {
121             throw new GTUException("gtu value has not been initialized for LanePerception when perceiving.");
122         }
123         return getGtu().getSimulator().getSimulatorTime().getTime();
124     }
125 
126     /**
127      * @throws GTUException when the GTU was not initialized yet.
128      * @throws NetworkException when the speed limit for a GTU type cannot be retrieved from the network.
129      * @throws ParameterException in case of not being able to retrieve parameter ParameterTypes.LOOKAHEAD
130      */
131     public final void updateLanePathInfo() throws GTUException, NetworkException, ParameterException
132     {
133         Time timestamp = getTimestamp();
134         this.lanePathInfo = new TimeStampedObject<LanePathInfo>(AbstractLaneBasedTacticalPlanner.buildLanePathInfo(getGtu(),
135                 getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD)), timestamp);
136     }
137 
138     /** {@inheritDoc} */
139     @Override
140     public final void updateSpeedLimit() throws GTUException, NetworkException
141     {
142         Time timestamp = getTimestamp();
143         // assess the speed limit where we are right now
144         this.speedLimit = new TimeStampedObject<>(new Speed(Double.MAX_VALUE, SpeedUnit.SI), timestamp);
145         for (Lane lane : getGtu().getLanes().keySet())
146         {
147             if (lane.getSpeedLimit(getGtu().getGTUType()).lt(this.speedLimit.getObject()))
148             {
149                 this.speedLimit = new TimeStampedObject<>(lane.getSpeedLimit(getGtu().getGTUType()), timestamp);
150             }
151         }
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public final void updateForwardHeadway() throws GTUException, NetworkException, ParameterException
157     {
158         Time timestamp = getTimestamp();
159         if (this.lanePathInfo == null || this.lanePathInfo.getTimestamp().ne(timestamp))
160         {
161             updateLanePathInfo();
162         }
163         Length maximumForwardHeadway = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
164         this.forwardHeadway = new TimeStampedObject<>(forwardHeadway(maximumForwardHeadway), timestamp);
165     }
166 
167     /** {@inheritDoc} */
168     @Override
169     public final void updateBackwardHeadway() throws GTUException, NetworkException, ParameterException
170     {
171         Time timestamp = getTimestamp();
172         Length maximumReverseHeadway = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACKOLD);
173         this.backwardHeadway = new TimeStampedObject<>(backwardHeadway(maximumReverseHeadway), timestamp);
174     }
175 
176     /** {@inheritDoc} */
177     @Override
178     public final void updateAccessibleAdjacentLanesLeft() throws GTUException
179     {
180         Time timestamp = getTimestamp();
181         Map<Lane, Set<Lane>> accessibleAdjacentLanesMap = new HashMap<>();
182         for (Lane lane : getGtu().getLanes().keySet())
183         {
184             Set<Lane> adjacentLanes = new HashSet<Lane>(1);
185             adjacentLanes.addAll(lane.accessibleAdjacentLanes(LateralDirectionality.LEFT, getGtu().getGTUType()));
186             accessibleAdjacentLanesMap.put(lane, adjacentLanes);
187         }
188         this.accessibleAdjacentLanesLeft = new TimeStampedObject<>(accessibleAdjacentLanesMap, timestamp);
189     }
190 
191     /** {@inheritDoc} */
192     @Override
193     public final void updateAccessibleAdjacentLanesRight() throws GTUException
194     {
195         Time timestamp = getTimestamp();
196         Map<Lane, Set<Lane>> accessibleAdjacentLanesMap = new HashMap<>();
197         for (Lane lane : getGtu().getLanes().keySet())
198         {
199             Set<Lane> adjacentLanes = new HashSet<Lane>(1);
200             adjacentLanes.addAll(lane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, getGtu().getGTUType()));
201             accessibleAdjacentLanesMap.put(lane, adjacentLanes);
202         }
203         this.accessibleAdjacentLanesRight = new TimeStampedObject<>(accessibleAdjacentLanesMap, timestamp);
204     }
205 
206     /** {@inheritDoc} */
207     @Override
208     public final void updateParallelHeadwaysLeft() throws GTUException
209     {
210         Time timestamp = getTimestamp();
211         if (this.accessibleAdjacentLanesLeft == null || !timestamp.equals(this.accessibleAdjacentLanesLeft.getTimestamp()))
212         {
213             updateAccessibleAdjacentLanesLeft();
214         }
215         Set<Headway> parallelHeadwaySet = new HashSet<>();
216         for (Lane lane : this.accessibleAdjacentLanesLeft.getObject().keySet())
217         {
218             for (Lane adjacentLane : this.accessibleAdjacentLanesLeft.getObject().get(lane))
219             {
220                 parallelHeadwaySet.addAll(parallel(adjacentLane, timestamp));
221             }
222         }
223         this.parallelHeadwaysLeft = new TimeStampedObject<>(parallelHeadwaySet, timestamp);
224     }
225 
226     /** {@inheritDoc} */
227     @Override
228     public final void updateParallelHeadwaysRight() throws GTUException
229     {
230         Time timestamp = getTimestamp();
231         if (this.accessibleAdjacentLanesRight == null || !timestamp.equals(this.accessibleAdjacentLanesRight.getTimestamp()))
232         {
233             updateAccessibleAdjacentLanesRight();
234         }
235         Set<Headway> parallelHeadwaySet = new HashSet<>();
236         for (Lane lane : this.accessibleAdjacentLanesRight.getObject().keySet())
237         {
238             for (Lane adjacentLane : this.accessibleAdjacentLanesRight.getObject().get(lane))
239             {
240                 parallelHeadwaySet.addAll(parallel(adjacentLane, timestamp));
241             }
242         }
243         this.parallelHeadwaysRight = new TimeStampedObject<>(parallelHeadwaySet, timestamp);
244     }
245 
246     /** {@inheritDoc} */
247     @Override
248     public final void updateLaneTrafficLeft() throws GTUException, NetworkException, ParameterException
249     {
250         Time timestamp = getTimestamp();
251         if (this.accessibleAdjacentLanesLeft == null || !timestamp.equals(this.accessibleAdjacentLanesLeft.getTimestamp()))
252         {
253             updateAccessibleAdjacentLanesLeft();
254         }
255 
256         if (this.parallelHeadwaysLeft == null || !timestamp.equals(this.parallelHeadwaysLeft.getTimestamp()))
257         {
258             updateParallelHeadwaysLeft();
259         }
260 
261         // for the accessible lanes, see who is ahead of us and in front of us
262         Length maximumForwardHeadway = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
263         Length maximumReverseHeadway = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACKOLD);
264         this.neighboringHeadwaysLeft = new TimeStampedObject<>(
265                 collectNeighborLaneTraffic(LateralDirectionality.LEFT, timestamp, maximumForwardHeadway, maximumReverseHeadway),
266                 timestamp);
267     }
268 
269     /** {@inheritDoc} */
270     @Override
271     public final void updateLaneTrafficRight() throws GTUException, NetworkException, ParameterException
272     {
273         Time timestamp = getTimestamp();
274         if (this.accessibleAdjacentLanesRight == null || !timestamp.equals(this.accessibleAdjacentLanesRight.getTimestamp()))
275         {
276             updateAccessibleAdjacentLanesRight();
277         }
278 
279         if (this.parallelHeadwaysRight == null || !timestamp.equals(this.parallelHeadwaysRight.getTimestamp()))
280         {
281             updateParallelHeadwaysRight();
282         }
283 
284         // for the accessible lanes, see who is ahead of us and in front of us
285         Length maximumForwardHeadway = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
286         Length maximumReverseHeadway = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACKOLD);
287         this.neighboringHeadwaysRight = new TimeStampedObject<>(collectNeighborLaneTraffic(LateralDirectionality.RIGHT,
288                 timestamp, maximumForwardHeadway, maximumReverseHeadway), timestamp);
289     }
290 
291     /** {@inheritDoc} */
292     @Override
293     public final Map<Lane, Set<Lane>> accessibleAdjacentLaneMap(final LateralDirectionality lateralDirection)
294     {
295         return lateralDirection.equals(LateralDirectionality.LEFT) ? this.accessibleAdjacentLanesLeft.getObject()
296                 : this.accessibleAdjacentLanesRight.getObject();
297     }
298 
299     /** {@inheritDoc} */
300     @Override
301     public final Collection<Headway> getNeighboringHeadways(final LateralDirectionality lateralDirection)
302     {
303         return lateralDirection.equals(LateralDirectionality.LEFT) ? this.parallelHeadwaysLeft.getObject()
304                 : this.parallelHeadwaysRight.getObject();
305     }
306 
307     /** {@inheritDoc} */
308     @Override
309     public final Collection<Headway> getParallelHeadways(final LateralDirectionality lateralDirection)
310     {
311         return lateralDirection.equals(LateralDirectionality.LEFT) ? this.neighboringHeadwaysLeft.getObject()
312                 : this.neighboringHeadwaysRight.getObject();
313     }
314 
315     /**************************************************************************************************************************/
316     /**************************************************** HEADWAY ALGORITHMS **************************************************/
317     /**************************************************************************************************************************/
318 
319     /**
320      * Determine which GTU is in front of this GTU. This method looks in all lanes where this GTU is registered, and not further
321      * than the value of the given maxDistance. The minimum headway is returned of all Lanes where the GTU is registered. When
322      * no GTU is found within the given maxDistance, a HeadwayGTU with <b>null</b> as the gtuId and maxDistance as the distance
323      * is returned. The search will extend into successive lanes if the maxDistance is larger than the remaining length on the
324      * lane. When Lanes (or underlying CrossSectionLinks) diverge, a route planner may be used to determine which kinks and
325      * lanes to take into account and which ones not. When the Lanes (or underlying CrossSectionLinks) converge, "parallel"
326      * traffic is not taken into account in the headway calculation. Instead, gap acceptance algorithms or their equivalent
327      * should guide the merging behavior.<br>
328      * <b>Note:</b> Headway is the net headway and calculated on a front-to-back basis.
329      * @param maxDistance the maximum distance to look for the nearest GTU; positive values search forwards; negative values
330      *            search backwards
331      * @return HeadwayGTU; the headway and the GTU information
332      * @throws GTUException when there is an error with the next lanes in the network.
333      * @throws NetworkException when there is a problem with the route planner
334      */
335     private Headway forwardHeadway(final Length maxDistance) throws GTUException, NetworkException
336     {
337         LanePathInfo lpi = getLanePathInfo();
338         return forwardHeadway(lpi, maxDistance);
339     }
340 
341     /**
342      * Determine which GTU is in front of this GTU. This method uses a given lanePathInfo to look forward, but not further than
343      * the value of the given maxDistance. The minimum headway is returned of all Lanes where the GTU is registered. When no GTU
344      * is found within the given maxDistance, a HeadwayGTU with <b>null</b> as the gtuId and maxDistance as the distance is
345      * returned. The search will extend into successive lanes if the maxDistance is larger than the remaining length on the
346      * lane. When Lanes (or underlying CrossSectionLinks) diverge, a route planner may be used to determine which kinks and
347      * lanes to take into account and which ones not. When the Lanes (or underlying CrossSectionLinks) converge, "parallel"
348      * traffic is not taken into account in the headway calculation. Instead, gap acceptance algorithms or their equivalent
349      * should guide the merging behavior.<br>
350      * <b>Note:</b> Headway is the net headway and calculated on a front-to-back basis.
351      * @param lpi the lanePathInfo object that informs the headway algorithm in which lanes to look, and from which position on
352      *            the first lane.
353      * @param maxDistance the maximum distance to look for the nearest GTU; positive values search forwards; negative values
354      *            search backwards
355      * @return HeadwayGTU; the headway and the GTU information
356      * @throws GTUException when there is an error with the next lanes in the network.
357      * @throws NetworkException when there is a problem with the route planner
358      */
359     private Headway forwardHeadway(final LanePathInfo lpi, final Length maxDistance) throws GTUException, NetworkException
360     {
361         Throw.when(maxDistance.le(Length.ZERO), GTUException.class, "forwardHeadway: maxDistance should be positive");
362 
363         int ldIndex = 0;
364         LaneDirection ld = lpi.getReferenceLaneDirection();
365         double gtuPosFrontSI = lpi.getReferencePosition().si;
366         if (lpi.getReferenceLaneDirection().getDirection().isPlus())
367         {
368             gtuPosFrontSI += getGtu().getFront().getDx().si;
369         }
370         else
371         {
372             gtuPosFrontSI -= getGtu().getFront().getDx().si;
373         }
374 
375         // TODO end of lanepath
376 
377         while ((gtuPosFrontSI > ld.getLane().getLength().si || gtuPosFrontSI < 0.0)
378                 && ldIndex < lpi.getLaneDirectionList().size() - 1)
379         {
380             ldIndex++;
381             if (ld.getDirection().isPlus()) // e.g. 1005 on length of lane = 1000
382             {
383                 if (lpi.getLaneDirectionList().get(ldIndex).getDirection().isPlus())
384                 {
385                     gtuPosFrontSI -= ld.getLane().getLength().si;
386                 }
387                 else
388                 {
389                     gtuPosFrontSI = lpi.getLaneDirectionList().get(ldIndex).getLane().getLength().si - gtuPosFrontSI;
390                 }
391                 ld = lpi.getLaneDirectionList().get(ldIndex);
392             }
393             else
394             // e.g. -5 on lane of whatever length
395             {
396                 if (lpi.getLaneDirectionList().get(ldIndex).getDirection().isPlus())
397                 {
398                     gtuPosFrontSI += ld.getLane().getLength().si;
399                 }
400                 else
401                 {
402                     gtuPosFrontSI += lpi.getLaneDirectionList().get(ldIndex).getLane().getLength().si;
403                 }
404                 ld = lpi.getLaneDirectionList().get(ldIndex);
405             }
406         }
407 
408         double maxDistanceSI = maxDistance.si;
409         Time time = getGtu().getSimulator().getSimulatorTime().getTime();
410 
411         // look forward based on the provided lanePathInfo.
412         Headway closest = headwayLane(ld, gtuPosFrontSI, 0.0, time);
413         if (closest != null)
414         {
415             if (closest.getDistance().si > maxDistanceSI)
416             {
417                 return new HeadwayDistance(maxDistanceSI);
418             }
419             return closest;
420         }
421         double cumDistSI = ld.getDirection().isPlus() ? ld.getLane().getLength().si - gtuPosFrontSI : gtuPosFrontSI;
422         for (int i = ldIndex + 1; i < lpi.getLaneDirectionList().size(); i++)
423         {
424             ld = lpi.getLaneDirectionList().get(i);
425             closest = headwayLane(ld, ld.getDirection().isPlus() ? 0.0 : ld.getLane().getLength().si, cumDistSI, time);
426             if (closest != null)
427             {
428                 if (closest.getDistance().si > maxDistanceSI)
429                 {
430                     return new HeadwayDistance(maxDistanceSI);
431                 }
432                 return closest;
433             }
434             cumDistSI += ld.getLane().getLength().si;
435         }
436         return new HeadwayDistance(maxDistanceSI);
437     }
438 
439     /**
440      * Determine the positive headway on a lane, or null if no GTU can be found on this lane.
441      * @param laneDirection the lane and direction to look
442      * @param startPosSI the start position to look from in meters
443      * @param cumDistSI the cumulative distance that has already been observed on other lanes
444      * @param now the current time to determine the GTU positions on the lane
445      * @return the HeadwayGTU, containing information on a GTU that is ahead of the given start position, or null if no GTU can
446      *         be found on this lane
447      * @throws GTUException when the GTUs ahead on the lane cannot be determined
448      */
449     private Headway headwayLane(final LaneDirection laneDirection, final double startPosSI, final double cumDistSI,
450             final Time now) throws GTUException
451     {
452         Lane lane = laneDirection.getLane();
453         LaneBasedGTU laneBasedGTU = lane.getGtuAhead(new Length(startPosSI, LengthUnit.SI), laneDirection.getDirection(),
454                 RelativePosition.REAR, now);
455         if (laneBasedGTU == null)
456         {
457             return null;
458         }
459         double distanceSI = Math.abs(laneBasedGTU.position(lane, laneBasedGTU.getRear()).si - startPosSI);
460         return new HeadwayGTUSimple(laneBasedGTU.getId(), laneBasedGTU.getGTUType(),
461                 new Length(cumDistSI + distanceSI, LengthUnit.SI), laneBasedGTU.getLength(), laneBasedGTU.getSpeed(),
462                 laneBasedGTU.getAcceleration());
463     }
464 
465     /**
466      * Determine which GTU is behind this GTU. This method looks in all lanes where this GTU is registered, and not further back
467      * than the absolute value of the given maxDistance. The minimum net headway is returned of all Lanes where the GTU is
468      * registered. When no GTU is found within the given maxDistance, <b>null</b> is returned. The search will extend into
469      * successive lanes if the maxDistance is larger than the remaining length on the lane. When Lanes (or underlying
470      * CrossSectionLinks) diverge, the headway algorithms have to look at multiple Lanes and return the minimum headway in each
471      * of the Lanes. When the Lanes (or underlying CrossSectionLinks) converge, "parallel" traffic is not taken into account in
472      * the headway calculation. Instead, gap acceptance algorithms or their equivalent should guide the merging behavior.<br>
473      * <b>Note:</b> Headway is the net headway and calculated on a back-to-front basis.
474      * @param maxDistance the maximum distance to look for the nearest GTU; it should have a negative value to search backwards
475      * @return HeadwayGTU; the headway and the GTU information
476      * @throws GTUException when there is an error with the next lanes in the network.
477      * @throws NetworkException when there is a problem with the route planner
478      */
479     private Headway backwardHeadway(final Length maxDistance) throws GTUException, NetworkException
480     {
481         Throw.when(maxDistance.ge(Length.ZERO), GTUException.class, "backwardHeadway: maxDistance should be negative");
482         Time time = getGtu().getSimulator().getSimulatorTime().getTime();
483         double maxDistanceSI = maxDistance.si;
484         Headway foundHeadway = new HeadwayDistance(-maxDistanceSI);
485         for (Lane lane : getGtu().positions(getGtu().getRear()).keySet())
486         {
487             Headway closest = headwayRecursiveBackwardSI(lane, getGtu().getLanes().get(lane),
488                     getGtu().position(lane, getGtu().getRear(), time).getSI(), 0.0, -maxDistanceSI, time);
489             if (closest.getDistance().si < -maxDistanceSI && closest.getDistance().si < -foundHeadway.getDistance().si)
490             {
491                 foundHeadway = closest;
492             }
493         }
494         if (foundHeadway instanceof AbstractHeadwayGTU)
495         {
496             return new HeadwayGTUSimple(foundHeadway.getId(), ((AbstractHeadwayGTU) foundHeadway).getGtuType(),
497                     foundHeadway.getDistance().multiplyBy(-1.0), foundHeadway.getLength(), foundHeadway.getSpeed(), null);
498         }
499         if (foundHeadway instanceof HeadwayDistance)
500         {
501             return new HeadwayDistance(foundHeadway.getDistance().multiplyBy(-1.0));
502         }
503         // TODO allow observation of other objects as well.
504         throw new GTUException("backwardHeadway not implemented yet for other object types than GTU");
505     }
506 
507     /**
508      * Calculate the minimum headway, possibly on subsequent lanes, in backward direction (so between our back, and the other
509      * GTU's front). Note: this method returns a POSITIVE number.
510      * @param lane the lane where we are looking right now
511      * @param direction the direction we are driving on that lane
512      * @param lanePositionSI from which position on this lane do we start measuring? This is the current position of the rear of
513      *            the GTU when we measure in the lane where the original GTU is positioned, and lane.getLength() for each
514      *            subsequent lane.
515      * @param cumDistanceSI the distance we have already covered searching on previous lanes. Note: This is a POSITIVE number.
516      * @param maxDistanceSI the maximum distance to look for in SI units; stays the same in subsequent calls. Note: this is a
517      *            POSITIVE number.
518      * @param when the current or future time for which to calculate the headway
519      * @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
520      *         no other GTU could not be found within maxDistanceSI meters
521      * @throws GTUException when there is a problem with the geometry of the network
522      */
523     private Headway headwayRecursiveBackwardSI(final Lane lane, final GTUDirectionality direction, final double lanePositionSI,
524             final double cumDistanceSI, final double maxDistanceSI, final Time when) throws GTUException
525     {
526         LaneBasedGTU otherGTU =
527                 lane.getGtuBehind(new Length(lanePositionSI, LengthUnit.SI), direction, RelativePosition.FRONT, when);
528         if (otherGTU != null)
529         {
530             double distanceM = cumDistanceSI + lanePositionSI - otherGTU.position(lane, otherGTU.getFront(), when).getSI();
531             if (distanceM > 0 && distanceM <= maxDistanceSI)
532             {
533                 return new HeadwayGTUSimple(otherGTU.getId(), otherGTU.getGTUType(), new Length(distanceM, LengthUnit.SI),
534                         otherGTU.getLength(), otherGTU.getSpeed(), null);
535             }
536             return new HeadwayDistance(Double.MAX_VALUE);
537         }
538 
539         // Continue search on predecessor lanes.
540         if (cumDistanceSI + lanePositionSI < maxDistanceSI)
541         {
542             // is there a predecessor link?
543             if (lane.prevLanes(getGtu().getGTUType()).size() > 0)
544             {
545                 Headway foundMaxGTUDistanceSI = new HeadwayDistance(Double.MAX_VALUE);
546                 for (Lane prevLane : lane.prevLanes(getGtu().getGTUType()).keySet())
547                 {
548                     // What is behind us is INDEPENDENT of the followed route!
549                     double traveledDistanceSI = cumDistanceSI + lanePositionSI;
550                     // WRONG - adapt method to forward perception method!
551                     Headway closest = headwayRecursiveBackwardSI(prevLane, direction, prevLane.getLength().getSI(),
552                             traveledDistanceSI, maxDistanceSI, when);
553                     if (closest.getDistance().si < maxDistanceSI
554                             && closest.getDistance().si < foundMaxGTUDistanceSI.getDistance().si)
555                     {
556                         foundMaxGTUDistanceSI = closest;
557                     }
558                 }
559                 return foundMaxGTUDistanceSI;
560             }
561         }
562 
563         // No other GTU was not on one of the current lanes or their successors.
564         return new HeadwayDistance(Double.MAX_VALUE);
565     }
566 
567     /**************************************************************************************************************************/
568     /************************************************ ADJACENT LANE TRAFFIC ***************************************************/
569     /**************************************************************************************************************************/
570 
571     /**
572      * Determine which GTUs are parallel with us on another lane, based on fractional positions. <br>
573      * Note: When the GTU that calls the method is also registered on the given lane, it is excluded from the return set.
574      * @param lane the lane to look for parallel (partial or full overlapping) GTUs.
575      * @param when the future time for which to calculate the headway
576      * @return the set of GTUs parallel to us on the other lane (partial overlap counts as parallel), based on fractional
577      *         positions, or an empty set when no GTUs were found.
578      * @throws GTUException when the vehicle's route is inconclusive, when vehicles are not registered correctly on their lanes,
579      *             or when the given lane is not parallel to one of the lanes where we are registered.
580      */
581     private Collection<Headway> parallel(final Lane lane, final Time when) throws GTUException
582     {
583         Collection<Headway> headwayCollection = new LinkedHashSet<Headway>();
584         for (Lane l : getGtu().getLanes().keySet())
585         {
586             // only take lanes that we can compare based on a shared design line
587             if (l.getParentLink().equals(lane.getParentLink()))
588             {
589                 // compare based on fractional positions.
590                 double posFractionRef = getGtu().fractionalPosition(l, getGtu().getReference(), when);
591                 double posFractionFront = Math.max(0.0, posFractionRef + getGtu().getFront().getDx().si / lane.getLength().si);
592                 double posFractionRear = Math.min(1.0, posFractionRef + getGtu().getRear().getDx().si / lane.getLength().si);
593                 // double posFractionFront = Math.max(0.0, getGtu().fractionalPosition(l, getGtu().getFront(), when));
594                 // double posFractionRear = Math.min(1.0, getGtu().fractionalPosition(l, getGtu().getRear(), when));
595                 double posMin = Math.min(posFractionFront, posFractionRear);
596                 double posMax = Math.max(posFractionFront, posFractionRear);
597                 for (LaneBasedGTU otherGTU : lane.getGtuList())
598                 {
599                     if (!otherGTU.equals(this)) // TODO
600                     {
601                         /*- cater for: *-----*         *-----*       *-----*       *----------*
602                          *                *-----*    *----*      *------------*       *-----*
603                          * where the GTUs can each drive in two directions (!)
604                          */
605                         double gtuFractionRef = otherGTU.fractionalPosition(lane, otherGTU.getReference(), when);
606                         double gtuFractionFront =
607                                 Math.max(0.0, gtuFractionRef + otherGTU.getFront().getDx().si / lane.getLength().si);
608                         double gtuFractionRear =
609                                 Math.min(1.0, gtuFractionRef + otherGTU.getRear().getDx().si / lane.getLength().si);
610                         double gtuMin = Math.min(gtuFractionFront, gtuFractionRear);
611                         double gtuMax = Math.max(gtuFractionFront, gtuFractionRear);
612                         // TODO calculate real overlaps
613                         Length overlapFront = new Length(1.0, LengthUnit.SI);
614                         Length overlap = new Length(1.0, LengthUnit.SI);
615                         Length overlapRear = new Length(1.0, LengthUnit.SI);
616                         if ((gtuMin >= posMin && gtuMin <= posMax) || (gtuMax >= posMin && gtuMax <= posMax)
617                                 || (posMin >= gtuMin && posMin <= gtuMax) || (posMax >= gtuMin && posMax <= gtuMax))
618                         {
619                             headwayCollection.add(new HeadwayGTUSimple(otherGTU.getId(), otherGTU.getGTUType(), overlapFront,
620                                     overlap, overlapRear, otherGTU.getLength(), otherGTU.getSpeed(),
621                                     otherGTU.getAcceleration()));
622                         }
623                     }
624                 }
625             }
626         }
627         return headwayCollection;
628     }
629 
630     /**
631      * Determine which GTUs are parallel with us in a certain lateral direction, based on fractional positions. <br>
632      * Note 1: This method will look to the adjacent lanes of all lanes where the vehicle has been registered.<br>
633      * Note 2: When the GTU that calls the method is also registered on the given lane, it is excluded from the return set.
634      * @param lateralDirection the direction of the adjacent lane(s) to look for parallel (partial or full overlapping) GTUs.
635      * @param when the future time for which to calculate the headway
636      * @return the set of GTUs parallel to us on other lane(s) in the given direction (partial overlap counts as parallel),
637      *         based on fractional positions, or an empty set when no GTUs were found.
638      * @throws GTUException when the vehicle's route is inconclusive, when vehicles are not registered correctly on their lanes,
639      *             or when there are no lanes parallel to one of the lanes where we are registered in the given direction.
640      */
641     private Collection<Headway> parallel(final LateralDirectionality lateralDirection, final Time when) throws GTUException
642     {
643         Collection<Headway> gtuSet = new LinkedHashSet<Headway>();
644         for (Lane lane : getGtu().getLanes().keySet())
645         {
646             for (Lane adjacentLane : accessibleAdjacentLaneMap(lateralDirection).get(lane))
647             {
648                 gtuSet.addAll(parallel(adjacentLane, when));
649             }
650         }
651         return gtuSet;
652     }
653 
654     /**
655      * Determine whether there is a lane to the left or to the right of this lane, which is accessible from this lane, or null
656      * if no lane could be found. The method takes the LongitidinalDirectionality of the lane into account. In other words, if
657      * 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
658      * or BOTH, null will be returned.<br>
659      * A lane is called adjacent to another lane if the lateral edges are not more than a delta distance apart. This means that
660      * a lane that <i>overlaps</i> with another lane is <b>not</b> returned as an adjacent lane. <br>
661      * The algorithm also looks for RoadMarkerAcross elements between the lanes to determine the lateral permeability for a GTU.
662      * A RoadMarkerAcross is seen as being between two lanes if its center line is not more than delta distance from the
663      * relevant lateral edges of the two adjacent lanes. <br>
664      * When there are multiple lanes that are adjacent, which could e.g. be the case if an overlapping tram lane and a car lane
665      * are adjacent to the current lane, the widest lane that best matches the GTU accessibility of the provided GTUType is
666      * returned. <br>
667      * <b>Note:</b> LEFT is seen as a negative lateral direction, RIGHT as a positive lateral direction. <br>
668      * FIXME In other places in OTS LEFT is positive (and RIGHT is negative). This should be made more consistent.
669      * @param currentLane the lane to look for the best accessible adjacent lane
670      * @param lateralDirection the direction (LEFT, RIGHT) to look at
671      * @param longitudinalPosition Length; the position of the GTU along <cite>currentLane</cite>
672      * @return the lane if it is accessible, or null if there is no lane, it is not accessible, or the driving direction does
673      *         not match.
674      */
675     public final Lane bestAccessibleAdjacentLane(final Lane currentLane, final LateralDirectionality lateralDirection,
676             final Length longitudinalPosition)
677     {
678         Set<Lane> candidates = accessibleAdjacentLaneMap(lateralDirection).get(currentLane);
679         if (candidates.isEmpty())
680         {
681             return null; // There is no adjacent Lane that this GTU type can cross into
682         }
683         if (candidates.size() == 1)
684         {
685             return candidates.iterator().next(); // There is exactly one adjacent Lane that this GTU type can cross into
686         }
687         // There are several candidates; find the one that is widest at the beginning.
688         Lane bestLane = null;
689         double widestSeen = Double.NEGATIVE_INFINITY;
690         for (Lane lane : candidates)
691         {
692             if (lane.getWidth(longitudinalPosition).getSI() > widestSeen)
693             {
694                 widestSeen = lane.getWidth(longitudinalPosition).getSI();
695                 bestLane = lane;
696             }
697         }
698         return bestLane;
699     }
700 
701     /**
702      * Collect relevant traffic in adjacent lanes. Parallel traffic is included with headway equal to Double.NaN.
703      * @param directionality LateralDirectionality; either <cite>LateralDirectionality.LEFT</cite>, or
704      *            <cite>LateralDirectionality.RIGHT</cite>
705      * @param when DoubleScalar.Abs&lt;TimeUnit&gt;; the (current) time
706      * @param maximumForwardHeadway DoubleScalar.Rel&lt;LengthUnit&gt;; the maximum forward search distance
707      * @param maximumReverseHeadway DoubleScalar.Rel&lt;LengthUnit&gt;; the maximum reverse search distance
708      * @return Collection&lt;LaneBasedGTU&gt;;
709      * @throws NetworkException on network inconsistency
710      * @throws GTUException on problems with the GTU state (e.g., position)
711      * @throws ParameterException in case of a parameter problem
712      */
713     private Collection<Headway> collectNeighborLaneTraffic(final LateralDirectionality directionality, final Time when,
714             final Length maximumForwardHeadway, final Length maximumReverseHeadway)
715             throws NetworkException, GTUException, ParameterException
716     {
717         Collection<Headway> result = new HashSet<Headway>();
718         for (Headway p : parallel(directionality, when))
719         {
720             // TODO expand for other types of Headways
721             result.add(new HeadwayGTUSimple(p.getId(), ((AbstractHeadwayGTU) p).getGtuType(),
722                     new Length(Double.NaN, LengthUnit.SI), p.getLength(), p.getSpeed(), p.getAcceleration()));
723         }
724 
725         // forward
726         for (Lane adjacentLane : accessibleAdjacentLaneMap(directionality).get(getLanePathInfo().getReferenceLane()))
727         {
728             LanePathInfo lpiAdjacent = buildLanePathInfoAdjacent(adjacentLane, directionality, when);
729             Headway leader = forwardHeadway(lpiAdjacent, maximumForwardHeadway);
730             if (null != leader.getId() && !result.contains(leader))
731             {
732                 result.add(leader);
733             }
734         }
735 
736         // backward
737         for (Lane lane : getGtu().getLanes().keySet())
738         {
739             for (Lane adjacentLane : accessibleAdjacentLaneMap(directionality).get(lane))
740             {
741                 Headway follower = headwayRecursiveBackwardSI(adjacentLane, getGtu().getLanes().get(lane),
742                         getGtu().projectedPosition(adjacentLane, getGtu().getRear(), when).getSI(), 0.0,
743                         -maximumReverseHeadway.getSI(), when);
744                 if (follower instanceof AbstractHeadwayGTU)
745                 {
746                     boolean found = false;
747                     for (Headway headway : result)
748                     {
749                         if (headway.getId().equals(follower.getId()))
750                         {
751                             found = true;
752                         }
753                     }
754                     if (!found)
755                     {
756                         result.add(new HeadwayGTUSimple(follower.getId(), ((AbstractHeadwayGTU) follower).getGtuType(),
757                                 follower.getDistance().multiplyBy(-1.0), follower.getLength(), follower.getSpeed(), null));
758                     }
759                 }
760                 else if (follower instanceof HeadwayDistance) // always add for potential lane drop
761                 {
762                     result.add(new HeadwayDistance(follower.getDistance().multiplyBy(-1.0)));
763                 }
764                 else
765                 {
766                     throw new GTUException(
767                             "collectNeighborLaneTraffic not yet suited to observe obstacles on neighboring lanes");
768                 }
769             }
770         }
771         return result;
772     }
773 
774     /**
775      * Find a lanePathInfo left or right of the current LanePath.
776      * @param adjacentLane the start adjacent lane for which we calculate the LanePathInfo
777      * @param direction LateralDirectionality; either <cite>LateralDirectionality.LEFT</cite>, or
778      *            <cite>LateralDirectionality.RIGHT</cite>
779      * @param when DoubleScalar.Abs&lt;TimeUnit&gt;; the (current) time
780      * @return the adjacent LanePathInfo
781      * @throws GTUException when the GTU was not initialized yet.
782      * @throws NetworkException when the speed limit for a GTU type cannot be retrieved from the network.
783      * @throws ParameterException in case of a parameter problem
784      */
785     private LanePathInfo buildLanePathInfoAdjacent(final Lane adjacentLane, final LateralDirectionality direction,
786             final Time when) throws GTUException, NetworkException, ParameterException
787     {
788         if (this.lanePathInfo == null || this.lanePathInfo.getTimestamp().ne(when))
789         {
790             updateLanePathInfo();
791         }
792         LanePathInfo lpi = getLanePathInfo();
793         List<LaneDirection> laneDirectionList = new ArrayList<>();
794         laneDirectionList.add(new LaneDirection(adjacentLane, lpi.getReferenceLaneDirection().getDirection()));
795         Length referencePosition = getGtu().projectedPosition(adjacentLane, getGtu().getReference(), when);
796         for (int i = 1; i < lpi.getLaneDirectionList().size(); i++)
797         {
798             LaneDirection ld = lpi.getLaneDirectionList().get(i);
799             Set<Lane> accessibleLanes = ld.getLane().accessibleAdjacentLanes(direction, getGtu().getGTUType());
800             Lane adjLane = null;
801             for (Lane lane : accessibleLanes)
802             {
803                 if (lane.getParentLink().equals(ld.getLane().getParentLink()))
804                 {
805                     adjLane = lane;
806                 }
807             }
808             if (adjLane == null)
809             {
810                 break;
811             }
812             laneDirectionList.add(new LaneDirection(adjLane, ld.getDirection()));
813         }
814         return new LanePathInfo(null, laneDirectionList, referencePosition);
815     }
816 
817     /**************************************************************************************************************************/
818     /*************************************************** GETTERS FOR THE INFORMATION ******************************************/
819     /**************************************************************************************************************************/
820 
821     /** {@inheritDoc} */
822     @Override
823     public final LaneBasedGTU getGtu()
824     {
825         return (LaneBasedGTU) super.getGtu();
826     }
827 
828     /**
829      * Retrieve the last perceived lane path info.
830      * @return LanePathInfo
831      */
832     public final LanePathInfo getLanePathInfo()
833     {
834         return this.lanePathInfo.getObject();
835     }
836 
837     /** {@inheritDoc} */
838     @Override
839     public final Headway getForwardHeadway()
840     {
841         return this.forwardHeadway.getObject();
842     }
843 
844     /** {@inheritDoc} */
845     @Override
846     public final Headway getBackwardHeadway()
847     {
848         return this.backwardHeadway.getObject();
849     }
850 
851     /** {@inheritDoc} */
852     @Override
853     public final Map<Lane, Set<Lane>> getAccessibleAdjacentLanesLeft()
854     {
855         return this.accessibleAdjacentLanesLeft.getObject();
856     }
857 
858     /** {@inheritDoc} */
859     @Override
860     public final Map<Lane, Set<Lane>> getAccessibleAdjacentLanesRight()
861     {
862         return this.accessibleAdjacentLanesRight.getObject();
863     }
864 
865     /** {@inheritDoc} */
866     @Override
867     public final Collection<Headway> getNeighboringHeadwaysLeft()
868     {
869         return this.neighboringHeadwaysLeft.getObject();
870     }
871 
872     /** {@inheritDoc} */
873     @Override
874     public final Collection<Headway> getNeighboringHeadwaysRight()
875     {
876         return this.neighboringHeadwaysRight.getObject();
877     }
878 
879     /** {@inheritDoc} */
880     @Override
881     public final Collection<Headway> getParallelHeadwaysLeft()
882     {
883         return this.parallelHeadwaysLeft.getObject();
884     }
885 
886     /** {@inheritDoc} */
887     @Override
888     public final Collection<Headway> getParallelHeadwaysRight()
889     {
890         return this.parallelHeadwaysRight.getObject();
891     }
892 
893     /** {@inheritDoc} */
894     @Override
895     public final Speed getSpeedLimit()
896     {
897         return this.speedLimit.getObject();
898     }
899 
900     /** {@inheritDoc} */
901     @Override
902     public final TimeStampedObject<Headway> getTimeStampedForwardHeadway()
903     {
904         return this.forwardHeadway;
905     }
906 
907     /** {@inheritDoc} */
908     @Override
909     public final TimeStampedObject<Headway> getTimeStampedBackwardHeadway()
910     {
911         return this.backwardHeadway;
912     }
913 
914     /** {@inheritDoc} */
915     @Override
916     public final TimeStampedObject<Map<Lane, Set<Lane>>> getTimeStampedAccessibleAdjacentLanesLeft()
917     {
918         return this.accessibleAdjacentLanesLeft;
919     }
920 
921     /** {@inheritDoc} */
922     @Override
923     public final TimeStampedObject<Map<Lane, Set<Lane>>> getTimeStampedAccessibleAdjacentLanesRight()
924     {
925         return this.accessibleAdjacentLanesRight;
926     }
927 
928     /** {@inheritDoc} */
929     @Override
930     public final TimeStampedObject<Collection<Headway>> getTimeStampedNeighboringHeadwaysLeft()
931     {
932         return this.neighboringHeadwaysLeft;
933     }
934 
935     /** {@inheritDoc} */
936     @Override
937     public final TimeStampedObject<Collection<Headway>> getTimeStampedNeighboringHeadwaysRight()
938     {
939         return this.neighboringHeadwaysRight;
940     }
941 
942     /** {@inheritDoc} */
943     @Override
944     public final TimeStampedObject<Collection<Headway>> getTimeStampedParallelHeadwaysLeft()
945     {
946         return this.parallelHeadwaysLeft;
947     }
948 
949     /** {@inheritDoc} */
950     @Override
951     public final TimeStampedObject<Collection<Headway>> getTimeStampedParallelHeadwaysRight()
952     {
953         return this.parallelHeadwaysRight;
954     }
955 
956     /** {@inheritDoc} */
957     @Override
958     public final TimeStampedObject<Speed> getTimeStampedSpeedLimit()
959     {
960         return this.speedLimit;
961     }
962 
963     /** {@inheritDoc} */
964     @Override
965     public SortedSet<AbstractHeadwayGTU> getFirstLeaders(LateralDirectionality lat)
966     {
967         if (lat == null)
968         {
969             return getLeaders(RelativeLane.CURRENT);
970         }
971         else if (lat.isLeft())
972         {
973             return getLeaders(RelativeLane.LEFT);
974         }
975         return getLeaders(RelativeLane.RIGHT);
976     }
977 
978     /** {@inheritDoc} */
979     @Override
980     public SortedSet<AbstractHeadwayGTU> getFirstFollowers(LateralDirectionality lat)
981     {
982         if (lat == null)
983         {
984             return getFollowers(RelativeLane.CURRENT);
985         }
986         else if (lat.isLeft())
987         {
988             return getFollowers(RelativeLane.LEFT);
989         }
990         return getFollowers(RelativeLane.RIGHT);
991     }
992 
993     /** {@inheritDoc} */
994     @Override
995     public boolean existsGtuAlongside(LateralDirectionality lat)
996     {
997         for (Headway headway : getNeighboringHeadways(lat))
998         {
999             if (headway.isParallel())
1000             {
1001                 return true;
1002             }
1003         }
1004         return false;
1005     }
1006 
1007     /** {@inheritDoc} */
1008     @Override
1009     public SortedSet<AbstractHeadwayGTU> getLeaders(RelativeLane lane)
1010     {
1011         SortedSet<AbstractHeadwayGTU> leaders = new TreeSet<>();
1012         if (lane.isCurrent())
1013         {
1014             if (getForwardHeadway() instanceof AbstractHeadwayGTU)
1015             {
1016                 leaders.add((AbstractHeadwayGTU) getForwardHeadway());
1017             }
1018         }
1019         else
1020         {
1021             for (Headway headway : getNeighboringHeadways(lane.getLateralDirectionality()))
1022             {
1023                 if (headway instanceof AbstractHeadwayGTU && headway.isAhead())
1024                 {
1025                     leaders.add((AbstractHeadwayGTU) headway);
1026                 }
1027             }
1028         }
1029         return leaders;
1030     }
1031 
1032     /** {@inheritDoc} */
1033     @Override
1034     public SortedSet<AbstractHeadwayGTU> getFollowers(RelativeLane lane)
1035     {
1036         SortedSet<AbstractHeadwayGTU> followers = new TreeSet<>();
1037         if (lane.isCurrent())
1038         {
1039             if (getBackwardHeadway() instanceof AbstractHeadwayGTU)
1040             {
1041                 followers.add((AbstractHeadwayGTU) getBackwardHeadway());
1042             }
1043         }
1044         else
1045         {
1046             for (Headway headway : getNeighboringHeadways(lane.getLateralDirectionality()))
1047             {
1048                 if (headway instanceof AbstractHeadwayGTU && headway.isBehind())
1049                 {
1050                     followers.add((AbstractHeadwayGTU) headway);
1051                 }
1052             }
1053         }
1054         return followers;
1055     }
1056 
1057     /** {@inheritDoc} */
1058     @Override
1059     public SortedSet<InfrastructureLaneChangeInfo> getInfrastructureLaneChangeInfo(RelativeLane lane)
1060     {
1061         return new TreeSet<>();
1062     }
1063 
1064     /** {@inheritDoc} */
1065     @Override
1066     public int getSplitNumber(InfrastructureLaneChangeInfo info)
1067     {
1068         return 0;
1069     }
1070 
1071     /** {@inheritDoc} */
1072     @Override
1073     public SpeedLimitProspect getSpeedLimitProspect(RelativeLane lane)
1074     {
1075         SpeedLimitProspect slp = new SpeedLimitProspect();
1076         slp.addSpeedInfo(Length.ZERO, SpeedLimitTypes.FIXED_SIGN, getSpeedLimit());
1077         slp.addSpeedInfo(Length.ZERO, SpeedLimitTypes.MAX_VEHICLE_SPEED, getGtu().getMaximumSpeed());
1078         return slp;
1079     }
1080 
1081     /** {@inheritDoc} */
1082     @Override
1083     public Length getLegalLaneChangePossibility(RelativeLane fromLane, LateralDirectionality lat)
1084     {
1085         return Length.ZERO;
1086     }
1087 
1088     /** {@inheritDoc} */
1089     @Override
1090     public Length getPhysicalLaneChangePossibility(RelativeLane fromLane, LateralDirectionality lat)
1091     {
1092         return Length.ZERO;
1093     }
1094 
1095     /** {@inheritDoc} */
1096     @Override
1097     public SortedSet<RelativeLane> getCurrentCrossSection()
1098     {
1099         return new TreeSet<>();
1100     }
1101 
1102     /** {@inheritDoc} */
1103     @Override
1104     public SortedSet<HeadwayTrafficLight> getTrafficLights()
1105     {
1106         return new TreeSet<>();
1107     }
1108 
1109     /** {@inheritDoc} */
1110     @Override
1111     public SortedSet<HeadwayConflict> getIntersectionConflicts(RelativeLane lane)
1112     {
1113         return new TreeSet<>();
1114     }
1115 
1116     /** {@inheritDoc} */
1117     @Override
1118     public TimeStampedObject<SortedSet<AbstractHeadwayGTU>> getTimeStampedFirstLeaders(LateralDirectionality lat)
1119     {
1120         return null;
1121     }
1122 
1123     /** {@inheritDoc} */
1124     @Override
1125     public TimeStampedObject<SortedSet<AbstractHeadwayGTU>> getTimeStampedFirstFollowers(LateralDirectionality lat)
1126     {
1127         return null;
1128     }
1129 
1130     /** {@inheritDoc} */
1131     @Override
1132     public TimeStampedObject<Boolean> existsGtuAlongsideTimeStamped(LateralDirectionality lat)
1133     {
1134         return null;
1135     }
1136 
1137     /** {@inheritDoc} */
1138     @Override
1139     public TimeStampedObject<SortedSet<AbstractHeadwayGTU>> getTimeStampedLeaders(RelativeLane lane)
1140     {
1141         return null;
1142     }
1143 
1144     /** {@inheritDoc} */
1145     @Override
1146     public TimeStampedObject<SortedSet<AbstractHeadwayGTU>> getTimeStampedFollowers(RelativeLane lane)
1147     {
1148         return null;
1149     }
1150 
1151     /** {@inheritDoc} */
1152     @Override
1153     public TimeStampedObject<SortedSet<InfrastructureLaneChangeInfo>> getTimeStampedInfrastructureLaneChangeInfo(
1154             RelativeLane lane)
1155     {
1156         return null;
1157     }
1158 
1159     /** {@inheritDoc} */
1160     @Override
1161     public TimeStampedObject<Integer> getTimeStampedSplitNumber(InfrastructureLaneChangeInfo info)
1162     {
1163         return null;
1164     }
1165 
1166     /** {@inheritDoc} */
1167     @Override
1168     public TimeStampedObject<SpeedLimitProspect> getTimeStampedSpeedLimitProspect(RelativeLane lane)
1169     {
1170         return null;
1171     }
1172 
1173     /** {@inheritDoc} */
1174     @Override
1175     public TimeStampedObject<Length> getTimeStampedLegalLaneChangePossibility(RelativeLane fromLane, LateralDirectionality lat)
1176     {
1177         return null;
1178     }
1179 
1180     /** {@inheritDoc} */
1181     @Override
1182     public TimeStampedObject<Length> getTimeStampedPhysicalLaneChangePossibility(RelativeLane fromLane,
1183             LateralDirectionality lat)
1184     {
1185         return null;
1186     }
1187 
1188     /** {@inheritDoc} */
1189     @Override
1190     public TimeStampedObject<SortedSet<RelativeLane>> getTimeStampedCurrentCrossSection()
1191     {
1192         return null;
1193     }
1194 
1195     /** {@inheritDoc} */
1196     @Override
1197     public TimeStampedObject<SortedSet<HeadwayTrafficLight>> getTimeStampedTrafficLights()
1198     {
1199         return null;
1200     }
1201 
1202     /** {@inheritDoc} */
1203     @Override
1204     public TimeStampedObject<SortedSet<HeadwayConflict>> getTimeStampedIntersectionConflicts(RelativeLane lane)
1205     {
1206         return null;
1207     }
1208 
1209     /** {@inheritDoc} */
1210     @Override
1211     public void updateFirstLeaders()
1212     {
1213     }
1214 
1215     /** {@inheritDoc} */
1216     @Override
1217     public void updateFirstFollowers()
1218     {
1219     }
1220 
1221     /** {@inheritDoc} */
1222     @Override
1223     public void updateGtuAlongside()
1224     {
1225     }
1226 
1227     /** {@inheritDoc} */
1228     @Override
1229     public void updateLeaders()
1230     {
1231     }
1232 
1233     /** {@inheritDoc} */
1234     @Override
1235     public void updateFollowers()
1236     {
1237     }
1238 
1239     /** {@inheritDoc} */
1240     @Override
1241     public void updateInfrastructureLaneChangeInfo()
1242     {
1243     }
1244 
1245     /** {@inheritDoc} */
1246     @Override
1247     public void updateSplitNumber()
1248     {
1249     }
1250 
1251     /** {@inheritDoc} */
1252     @Override
1253     public void updateSpeedLimitProspect()
1254     {
1255     }
1256 
1257     /** {@inheritDoc} */
1258     @Override
1259     public void updateLegalLaneChangePossibility()
1260     {
1261     }
1262 
1263     /** {@inheritDoc} */
1264     @Override
1265     public void updatePhysicalLaneChangePossibility()
1266     {
1267     }
1268 
1269     /** {@inheritDoc} */
1270     @Override
1271     public void updateCurrentCrossSection()
1272     {
1273     }
1274 
1275     /** {@inheritDoc} */
1276     @Override
1277     public void updateTrafficLights()
1278     {
1279     }
1280 
1281     /** {@inheritDoc} */
1282     @Override
1283     public void updateIntersectionConflicts()
1284     {
1285     }
1286 
1287 }