View Javadoc
1   package org.opentrafficsim.road.network.lane;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.HashMap;
6   import java.util.Iterator;
7   import java.util.LinkedHashMap;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Map.Entry;
12  import java.util.NavigableMap;
13  import java.util.Set;
14  import java.util.SortedMap;
15  import java.util.TreeMap;
16  
17  import org.djunits.unit.LengthUnit;
18  import org.djunits.unit.TimeUnit;
19  import org.djunits.value.vdouble.scalar.Length;
20  import org.djunits.value.vdouble.scalar.Speed;
21  import org.djunits.value.vdouble.scalar.Time;
22  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
23  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
24  import org.opentrafficsim.core.geometry.OTSGeometryException;
25  import org.opentrafficsim.core.gtu.GTUDirectionality;
26  import org.opentrafficsim.core.gtu.GTUException;
27  import org.opentrafficsim.core.gtu.GTUType;
28  import org.opentrafficsim.core.gtu.RelativePosition;
29  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
30  import org.opentrafficsim.core.network.LateralDirectionality;
31  import org.opentrafficsim.core.network.Link;
32  import org.opentrafficsim.core.network.LongitudinalDirectionality;
33  import org.opentrafficsim.core.network.NetworkException;
34  import org.opentrafficsim.core.network.Node;
35  import org.opentrafficsim.core.network.OTSNetwork;
36  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
37  import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
38  import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
39  import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
40  import org.opentrafficsim.road.network.lane.object.sensor.AbstractSensor;
41  import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
42  
43  import nl.tudelft.simulation.dsol.SimRuntimeException;
44  import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
45  import nl.tudelft.simulation.event.EventType;
46  import nl.tudelft.simulation.immutablecollections.Immutable;
47  import nl.tudelft.simulation.immutablecollections.ImmutableArrayList;
48  import nl.tudelft.simulation.immutablecollections.ImmutableList;
49  import nl.tudelft.simulation.language.Throw;
50  
51  /**
52   * The Lane is the CrossSectionElement of a CrossSectionLink on which GTUs can drive. The Lane stores several important
53   * properties, such as the successor lane(s), predecessor lane(s), and adjacent lane(s), all separated per GTU type. It can, for
54   * instance, be that a truck is not allowed to move into an adjacent lane, while a car is allowed to do so. Furthermore, the
55   * lane contains sensors that can be triggered by passing GTUs. The Lane class also contains methods to determine to trigger the
56   * sensors at exactly calculated and scheduled times, given the movement of the GTUs. <br>
57   * Finally, the Lane stores the GTUs on the lane, and contains several access methods to determine successor and predecessor
58   * GTUs, as well as methods to add a GTU to a lane (either at the start or in the middle when changing lanes), and remove a GTU
59   * from the lane (either at the end, or in the middle when changing onto another lane).
60   * <p>
61   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
62   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
63   * <p>
64   * $LastChangedDate: 2015-09-24 14:17:07 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1407 $, by $Author: averbraeck $,
65   * initial version Aug 19, 2014 <br>
66   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
67   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
68   */
69  public class Lane extends CrossSectionElement implements Serializable
70  {
71      /** */
72      private static final long serialVersionUID = 20150826L;
73  
74      /** Type of lane to deduce compatibility with GTU types. */
75      private final LaneType laneType;
76  
77      /**
78       * The direction in which vehicles can drive, i.e., in direction of geometry, reverse, or both. This can differ per GTU
79       * type. In an overtake lane, cars might overtake and trucks not. It might be that the lane (e.g., a street in a city) is
80       * FORWARD (from start node of the link to end node of the link) for the GTU type CAR, but BOTH for the GTU type BICYCLE
81       * (i.e., bicycles can also go in the other direction, opposite to the drawing direction of the Link). If the directionality
82       * for a GTUType is set to NONE, this means that the given GTUType cannot use the Lane. If a Directionality is set for
83       * GTUType.ALL, the getDirectionality will default to these settings when there is no specific entry for a given
84       * directionality. This means that the settings can be used additive, or restrictive. <br>
85       * In <b>additive use</b>, set the directionality for GTUType.ALL to NONE, or do not set the directionality for GTUType.ALL.
86       * Now, one by one, the allowed directionalities can be added. An example is a lane on a highway, which we only open for
87       * CAR, TRUCK and BUS. <br>
88       * In <b>restrictive use</b>, set the directionality for GTUType.ALL to BOTH, FORWARD, or BACKWARD. Override the
89       * directionality for certain GTUTypes to a more restrictive access, e.g. to NONE. An example is a lane that is open for all
90       * road users, except TRUCK.
91       */
92      private final Map<GTUType, LongitudinalDirectionality> directionalityMap;
93  
94      /**
95       * The speed limit of this lane, which can differ per GTU type. Cars might be allowed to drive 120 km/h and trucks 90 km/h.
96       * If the speed limit is the same for all GTU types, GTUType.ALL will be used. This means that the settings can be used
97       * additive, or subtractive. <br>
98       * In <b>additive use</b>, do not set the speed limit for GTUType.ALL. Now, one by one, the allowed maximum speeds for each
99       * of the GTU Types have be added. Do this when there are few GTU types or the speed limits per TU type are very different.
100      * <br>
101      * In <b>subtractive use</b>, set the speed limit for GTUType.ALL to the most common one. Override the speed limit for
102      * certain GTUTypes to a different value. An example is a lane on a highway where all vehicles, except truck (CAR, BUS,
103      * MOTORCYCLE, etc.), can drive 120 km/h, but trucks are allowed only 90 km/h. In that case, set the speed limit for
104      * GTUType.ALL to 120 km/h, and for TRUCK to 90 km/h.
105      */
106     // TODO allow for direction-dependent speed limit
107     private Map<GTUType, Speed> speedLimitMap;
108 
109     /**
110      * Sensors on the lane to trigger behavior of the GTU, sorted by longitudinal position. The triggering of sensors is done
111      * per GTU type, so different GTUs can trigger different sensors.
112      */
113     // TODO allow for direction-dependent sensors
114     private final SortedMap<Double, List<SingleSensor>> sensors = new TreeMap<>();
115 
116     /**
117      * Objects on the lane can be observed by the GTU. Examples are signs, speed signs, blocks, and traffic lights. They are
118      * sorted by longitudinal position.
119      */
120     // TODO allow for direction-dependent lane objects
121     private final SortedMap<Double, List<LaneBasedObject>> laneBasedObjects = new TreeMap<>();
122 
123     /** GTUs ordered by increasing longitudinal position; increasing in the direction of the center line. */
124     private final List<LaneBasedGTU> gtuList = new ArrayList<LaneBasedGTU>();
125 
126     /**
127      * Adjacent left lanes that some GTU types can change onto. Left is defined relative to the direction of the design line of
128      * the link (and the direction of the center line of the lane). In terms of offsets, 'left' lanes always have a more
129      * positive offset than the current lane. Initially null so we can calculate and cache the first time the method is called.
130      */
131     private Map<GTUType, Set<Lane>> leftNeighbors = null;
132 
133     /**
134      * Adjacent right lanes that some GTU types can change onto. Right is defined relative to the direction of the design line
135      * of the link (and the direction of the center line of the lane). In terms of offsets, 'right' lanes always have a more
136      * negative offset than the current lane. Initially null so we can calculate and cache the first time the method is called.
137      */
138     private Map<GTUType, Set<Lane>> rightNeighbors = null;
139 
140     /**
141      * Next lane(s) following this lane that some GTU types can drive from or onto. Next is defined in the direction of the
142      * design line. Initially null so we can calculate and cache the first time the method is called.
143      */
144     private Map<GTUType, Map<Lane, GTUDirectionality>> nextLanes = null;
145 
146     /**
147      * Previous lane(s) preceding this lane that some GTU types can drive from or onto. Previous is defined relative to the
148      * direction of the design line. Initially null so we can calculate and cache the first time the method is called.
149      */
150     private Map<GTUType, Map<Lane, GTUDirectionality>> prevLanes = null;
151 
152     /**
153      * Downstream lane(s) following this lane that some GTU types can drive onto given the direction. Initially null so we can
154      * calculate and cache the first time the method is called.
155      */
156     private Map<GTUType, Map<GTUDirectionality, Map<Lane, GTUDirectionality>>> downLanes = null;
157 
158     /**
159      * Previous lane(s) preceding this lane that some GTU types can drive from given the direction. Initially null so we can
160      * calculate and cache the first time the method is called.
161      */
162     private Map<GTUType, Map<GTUDirectionality, Map<Lane, GTUDirectionality>>> upLanes = null;
163 
164     /** The conditions for overtaking another GTU, viewed from this lane. */
165     // TODO allow for direction-dependent overtaking conditions
166     private final OvertakingConditions overtakingConditions;
167 
168     /**
169      * The <b>timed</b> event type for pub/sub indicating the addition of a GTU to the lane. <br>
170      * Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
171      */
172     public static final EventType GTU_ADD_EVENT = new EventType("GTU.ADD");
173 
174     /**
175      * The <b>timed</b> event type for pub/sub indicating the removal of a GTU from the lane. <br>
176      * Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal, Length position}
177      */
178     public static final EventType GTU_REMOVE_EVENT = new EventType("GTU.REMOVE");
179 
180     /**
181      * The <b>timed</b> event type for pub/sub indicating the addition of a Sensor to the lane. <br>
182      * Payload: Object[] {String sensorId, Sensor sensor}
183      */
184     public static final EventType SENSOR_ADD_EVENT = new EventType("SENSOR.ADD");
185 
186     /**
187      * The <b>timed</b> event type for pub/sub indicating the removal of a Sensor from the lane. <br>
188      * Payload: Object[] {String sensorId, Sensor sensor}
189      */
190     public static final EventType SENSOR_REMOVE_EVENT = new EventType("SENSOR.REMOVE");
191 
192     /**
193      * The event type for pub/sub indicating the addition of a LaneBasedObject to the lane. <br>
194      * Payload: Object[] {LaneBasedObject laneBasedObject}
195      */
196     public static final EventType OBJECT_ADD_EVENT = new EventType("OBJECT.ADD");
197 
198     /**
199      * The event type for pub/sub indicating the removal of a LaneBasedObject from the lane. <br>
200      * Payload: Object[] {LaneBasedObject laneBasedObject}
201      */
202     public static final EventType OBJECT_REMOVE_EVENT = new EventType("OBJECT.REMOVE");
203 
204     /**
205      * Construct a new Lane.
206      * @param parentLink CrossSectionLink; the link to which the new Lane will belong (must be constructed first)
207      * @param id String; the id of this lane within the link; should be unique within the link.
208      * @param lateralOffsetAtStart Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
209      *            design line of the parent Link at the start of the parent Link
210      * @param lateralOffsetAtEnd Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
211      *            design line of the parent Link at the end of the parent Link
212      * @param beginWidth Length; start width, positioned <i>symmetrically around</i> the design line
213      * @param endWidth Length; end width, positioned <i>symmetrically around</i> the design line
214      * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
215      * @param directionalityMap Map&lt;GTUType, LongitudinalDirectionality&gt;; in direction of geometry, reverse, or both,
216      *            specified per GTU Type
217      * @param speedLimitMap Map&lt;GTUType, Speed&gt;; speed limit on this lane, specified per GTU Type
218      * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
219      * @throws OTSGeometryException when creation of the center line or contour geometry fails
220      * @throws NetworkException when id equal to null or not unique
221      */
222     @SuppressWarnings("checkstyle:parameternumber")
223     public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
224             final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
225             final Map<GTUType, LongitudinalDirectionality> directionalityMap, final Map<GTUType, Speed> speedLimitMap,
226             final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
227     {
228         super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
229         this.laneType = laneType;
230         this.directionalityMap = directionalityMap;
231         checkDirectionality();
232         this.speedLimitMap = speedLimitMap;
233         this.overtakingConditions = overtakingConditions;
234     }
235 
236     /**
237      * Construct a new Lane.
238      * @param parentLink CrossSectionLink; the link to which the element will belong (must be constructed first)
239      * @param id String; the id of this lane within the link; should be unique within the link.
240      * @param lateralOffsetAtStart Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
241      *            design line of the parent Link at the start of the parent Link
242      * @param lateralOffsetAtEnd Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
243      *            design line of the parent Link at the end of the parent Link
244      * @param beginWidth Length; start width, positioned <i>symmetrically around</i> the design line
245      * @param endWidth Length; end width, positioned <i>symmetrically around</i> the design line
246      * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
247      * @param directionality LongitudinalDirectionality; in direction of geometry, reverse, or both
248      * @param speedLimit Speed; speed limit on this lane
249      * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
250      * @throws OTSGeometryException when creation of the center line or contour geometry fails
251      * @throws NetworkException when id equal to null or not unique
252      */
253     @SuppressWarnings("checkstyle:parameternumber")
254     public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
255             final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
256             final LongitudinalDirectionality directionality, final Speed speedLimit,
257             final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
258     {
259         super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
260         this.laneType = laneType;
261         this.directionalityMap = new LinkedHashMap<>(1);
262         this.directionalityMap.put(GTUType.ALL, directionality);
263         checkDirectionality();
264         this.speedLimitMap = new LinkedHashMap<>();
265         this.speedLimitMap.put(GTUType.ALL, speedLimit);
266         this.overtakingConditions = overtakingConditions;
267     }
268 
269     /**
270      * Construct a new Lane.
271      * @param parentLink CrossSectionLink; the link to which the element will belong (must be constructed first)
272      * @param id String; the id of this lane within the link; should be unique within the link.
273      * @param lateralOffset Length; the lateral offset of the design line of the new CrossSectionLink with respect to the design
274      *            line of the parent Link
275      * @param width Length; width, positioned <i>symmetrically around</i> the design line
276      * @param laneType type of lane to deduce compatibility with GTU types
277      * @param directionalityMap Map&lt;GTUType, LongitudinalDirectionality&gt;; in direction of geometry, reverse, or both,
278      *            specified per GTU Type
279      * @param speedLimitMap Map&lt;GTUType, Speed&gt;; the speed limit on this lane, specified per GTU Type
280      * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
281      * @throws OTSGeometryException when creation of the center line or contour geometry fails
282      * @throws NetworkException when id equal to null or not unique
283      */
284     @SuppressWarnings("checkstyle:parameternumber")
285     public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
286             final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
287             final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
288             throws OTSGeometryException, NetworkException
289     {
290         super(parentLink, id, lateralOffset, width);
291         this.laneType = laneType;
292         this.directionalityMap = directionalityMap;
293         checkDirectionality();
294         this.speedLimitMap = speedLimitMap;
295         this.overtakingConditions = overtakingConditions;
296     }
297 
298     /**
299      * Construct a new Lane.
300      * @param parentLink CrossSectionLink; the link to which the element belongs (must be constructed first)
301      * @param id String; the id of this lane within the link; should be unique within the link
302      * @param lateralOffset Length; the lateral offset of the design line of the new CrossSectionLink with respect to the design
303      *            line of the parent Link
304      * @param width Length; width, positioned <i>symmetrically around</i> the design line
305      * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
306      * @param directionality in direction of geometry, reverse, or both
307      * @param speedLimit Speed; the speed limit on this lane
308      * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
309      * @throws OTSGeometryException when creation of the center line or contour geometry fails
310      * @throws NetworkException when id equal to null or not unique
311      */
312     @SuppressWarnings("checkstyle:parameternumber")
313     public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
314             final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
315             final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
316     {
317         super(parentLink, id, lateralOffset, width);
318         this.laneType = laneType;
319         this.directionalityMap = new LinkedHashMap<>(1);
320         this.directionalityMap.put(GTUType.ALL, directionality);
321         checkDirectionality();
322         this.speedLimitMap = new LinkedHashMap<>();
323         this.speedLimitMap.put(GTUType.ALL, speedLimit);
324         this.overtakingConditions = overtakingConditions;
325     }
326 
327     /**
328      * Construct a new Lane.
329      * @param parentLink CrossSectionLink; the link to which the element belongs (must be constructed first)
330      * @param id String; the id of this lane within the link; should be unique within the link.
331      * @param crossSectionSlices List&lt;CrossSectionSlice&gt;; the offsets and widths at positions along the line, relative to
332      *            the design line of the parent link. If there is just one with and offset, there should just be one element in
333      *            the list with Length = 0. If there are more slices, the last one should be at the length of the design line.
334      *            If not, a NetworkException is thrown.
335      * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
336      * @param directionalityMap Map&lt;GTUType, LongitudinalDirectionality&gt;; in direction of geometry, reverse, or both,
337      *            specified per GTU Type
338      * @param speedLimitMap Map&lt;GTUType, Speed&gt;; the speed limit on this lane, specified per GTU Type
339      * @param overtakingConditions OvertakingConditions; the conditions for overtaking another GTU, viewed from this lane
340      * @throws OTSGeometryException when creation of the center line or contour geometry fails
341      * @throws NetworkException when id equal to null or not unique
342      */
343     @SuppressWarnings("checkstyle:parameternumber")
344     public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
345             final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
346             final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
347             throws OTSGeometryException, NetworkException
348     {
349         super(parentLink, id, crossSectionSlices);
350         this.laneType = laneType;
351         this.directionalityMap = directionalityMap;
352         checkDirectionality();
353         this.speedLimitMap = speedLimitMap;
354         this.overtakingConditions = overtakingConditions;
355     }
356 
357     /**
358      * Construct a new Lane.
359      * @param parentLink CrossSectionLink; the link to which the element belongs (must be constructed first)
360      * @param id String; the id of this lane within the link; should be unique within the link.
361      * @param crossSectionSlices List&lt;CrossSectionSlice&gt;; the offsets and widths at positions along the line, relative to
362      *            the design line of the parent link. If there is just one with and offset, there should just be one element in
363      *            the list with Length = 0. If there are more slices, the last one should be at the length of the design line.
364      *            If not, a NetworkException is thrown.
365      * @param laneType LaneType; the type of lane to deduce compatibility with GTU types
366      * @param directionality LongitudinalDirectionality; in direction of geometry, reverse, or both
367      * @param speedLimit Speed; the speed limit on this lane
368      * @param overtakingConditions OvertakingCondition; the conditions for overtaking another GTU, viewed from this lane
369      * @throws OTSGeometryException when creation of the center line or contour geometry fails
370      * @throws NetworkException when id equal to null or not unique
371      */
372     @SuppressWarnings("checkstyle:parameternumber")
373     public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
374             final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
375             final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
376     {
377         super(parentLink, id, crossSectionSlices);
378         this.laneType = laneType;
379         this.directionalityMap = new LinkedHashMap<>(1);
380         this.directionalityMap.put(GTUType.ALL, directionality);
381         checkDirectionality();
382         this.speedLimitMap = new LinkedHashMap<>();
383         this.speedLimitMap.put(GTUType.ALL, speedLimit);
384         this.overtakingConditions = overtakingConditions;
385     }
386 
387     /**
388      * Clone a Lane for a new network.
389      * @param newParentLink the new link to which the clone belongs
390      * @param newSimulator the new simulator for this network
391      * @param animation whether to (re)create animation or not
392      * @param cse the element to clone from
393      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
394      *             or the end node of the link are not registered in the network.
395      */
396     protected Lane(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation,
397             final Lane cse) throws NetworkException
398     {
399         super(newParentLink, newSimulator, animation, cse);
400         this.laneType = cse.laneType;
401         this.directionalityMap = new HashMap<GTUType, LongitudinalDirectionality>(cse.directionalityMap);
402         this.speedLimitMap = new HashMap<GTUType, Speed>(cse.speedLimitMap);
403         this.overtakingConditions = cse.overtakingConditions;
404 
405         if (animation)
406         {
407             OTSNetwork.cloneAnimation(cse, this, cse.getParentLink().getSimulator(), newSimulator);
408         }
409     }
410 
411     // TODO constructor calls with this(...)
412 
413     /**
414      * Retrieve one of the sets of neighboring Lanes that is accessible for the given type of GTU. A defensive copy of the
415      * internal data structure is returned.
416      * @param direction LateralDirectionality; either LEFT or RIGHT, relative to the DESIGN LINE of the link (and the direction
417      *            of the center line of the lane). In terms of offsets, 'left' lanes always have a more positive offset than the
418      *            current lane, and 'right' lanes a more negative offset.
419      * @param gtuType GTUType; the GTU type to check the accessibility for
420      * @return Set&lt;Lane&gt;; the indicated set of neighboring Lanes
421      */
422     private Set<Lane> neighbors(final LateralDirectionality direction, final GTUType gtuType)
423     {
424         if (this.leftNeighbors == null || this.rightNeighbors == null)
425         {
426             this.leftNeighbors = new LinkedHashMap<>(1);
427             this.rightNeighbors = new LinkedHashMap<>(1);
428         }
429 
430         if (!this.leftNeighbors.containsKey(gtuType) || !this.rightNeighbors.containsKey(gtuType))
431         {
432             Set<Lane> leftSet = new LinkedHashSet<>(1);
433             Set<Lane> rightSet = new LinkedHashSet<>(1);
434             this.leftNeighbors.put(gtuType, leftSet);
435             this.rightNeighbors.put(gtuType, rightSet);
436             for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
437             {
438                 if (cse instanceof Lane && !cse.equals(this))
439                 {
440                     Lane lane = (Lane) cse;
441                     if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.LEFT, gtuType))
442                     {
443                         leftSet.add(lane);
444                     }
445                     if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.RIGHT, gtuType))
446                     {
447                         rightSet.add(lane);
448                     }
449                 }
450             }
451         }
452 
453         Set<Lane> lanes = new LinkedHashSet<>();
454         if (direction.equals(LateralDirectionality.LEFT))
455         {
456             lanes.addAll(this.leftNeighbors.get(gtuType));
457         }
458         else
459         {
460             lanes.addAll(this.rightNeighbors.get(gtuType));
461         }
462         return lanes;
463     }
464 
465     /** Lateral alignment margin for longitudinally connected Lanes. */
466     static final Length ADJACENT_MARGIN = new Length(0.2, LengthUnit.METER);
467 
468     /**
469      * Determine whether another lane is adjacent to this lane (dependent on distance) and accessible (dependent on stripes) for
470      * a certain GTU type (dependent on usability of the adjacent lane for that GTU type). This method assumes that when there
471      * is NO stripe between two adjacent lanes that are accessible for the GTU type, the GTU can enter that lane. <br>
472      * @param lane Lane; the other lane to evaluate
473      * @param direction LateralDirectionality; the direction to look at, relative to the DESIGN LINE of the link. This is a very
474      *            important aspect to note: all information is stored relative to the direction of the design line, and not in a
475      *            driving direction, which can vary for lanes that can be driven in two directions (e.g. at overtaking).
476      * @param gtuType GTUType; the GTU type to check the accessibility for
477      * @return boolean; true if the other lane is adjacent to this lane and accessible for the given GTU type; false otherwise
478      */
479     private boolean laterallyAdjacentAndAccessible(final Lane lane, final LateralDirectionality direction,
480             final GTUType gtuType)
481     {
482         if (!lane.getLaneType().isCompatible(gtuType) || gtuType.equals(GTUType.ALL) || gtuType.equals(GTUType.NONE))
483         {
484             // not accessible for the given GTU type
485             return false;
486         }
487 
488         if (direction.equals(LateralDirectionality.LEFT))
489         {
490             // TODO take the cross section slices into account...
491             if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
492                     - (lane.getDesignLineOffsetAtBegin().getSI() - lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
493                             .getSI()
494                     && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
495                             - (lane.getDesignLineOffsetAtEnd().getSI() - lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
496                                     .getSI())
497             {
498                 // look at stripes between the two lanes
499                 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
500                 {
501                     if (cse instanceof Stripe)
502                     {
503                         Stripe stripe = (Stripe) cse;
504                         // TODO take the cross section slices into account...
505                         if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
506                                 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
507                                 && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
508                                         - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
509                         {
510                             if (!stripe.isPermeable(gtuType, LateralDirectionality.LEFT))
511                             {
512                                 // there is a stripe forbidding to cross to the adjacent lane
513                                 return false;
514                             }
515                         }
516                     }
517                 }
518                 // the lanes are adjacent, and there is no stripe forbidding us to enter that lane
519                 // or there is no stripe at all
520                 return true;
521             }
522         }
523 
524         else
525         // direction.equals(LateralDirectionality.RIGHT)
526         {
527             // TODO take the cross section slices into account...
528             if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
529                     - (lane.getDesignLineOffsetAtBegin().getSI() + lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
530                             .getSI()
531                     && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
532                             - (lane.getDesignLineOffsetAtEnd().getSI() + lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
533                                     .getSI())
534             {
535                 // look at stripes between the two lanes
536                 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
537                 {
538                     if (cse instanceof Stripe)
539                     {
540                         Stripe stripe = (Stripe) cse;
541                         // TODO take the cross section slices into account...
542                         if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
543                                 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
544                                 && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
545                                         - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
546                         {
547                             if (!stripe.isPermeable(gtuType, LateralDirectionality.RIGHT))
548                             {
549                                 // there is a stripe forbidding to cross to the adjacent lane
550                                 return false;
551                             }
552                         }
553                     }
554                 }
555                 // the lanes are adjacent, and there is no stripe forbidding us to enter that lane
556                 // or there is no stripe at all
557                 return true;
558             }
559         }
560 
561         // no lanes were found that are close enough laterally.
562         return false;
563     }
564 
565     /**
566      * Insert a sensor at the right place in the sensor list of this Lane.
567      * @param sensor Sensor; the sensor to add
568      * @throws NetworkException when the position of the sensor is beyond (or before) the range of this Lane
569      */
570     public final void addSensor(final SingleSensor sensor) throws NetworkException
571     {
572         double position = sensor.getLongitudinalPosition().si;
573         if (position < 0 || position > getLength().getSI())
574         {
575             throw new NetworkException("Illegal position for sensor " + position + " valid range is 0.." + getLength().getSI());
576         }
577         if (this.parentLink.getNetwork().containsObject(sensor.getId()))
578         {
579             throw new NetworkException("Network already contains an object with the name " + sensor.getId());
580         }
581         List<SingleSensor> sensorList = this.sensors.get(position);
582         if (null == sensorList)
583         {
584             sensorList = new ArrayList<SingleSensor>(1);
585             this.sensors.put(position, sensorList);
586         }
587         sensorList.add(sensor);
588         this.parentLink.getNetwork().addObject(sensor);
589         fireTimedEvent(Lane.SENSOR_ADD_EVENT, new Object[] { sensor.getId(), sensor },
590                 sensor.getSimulator().getSimulatorTime());
591     }
592 
593     /**
594      * Remove a sensor from the sensor list of this Lane.
595      * @param sensor Sensoe; the sensor to remove.
596      * @throws NetworkException when the sensor was not found on this Lane
597      */
598     public final void removeSensor(final SingleSensor sensor) throws NetworkException
599     {
600         fireTimedEvent(Lane.SENSOR_REMOVE_EVENT, new Object[] { sensor.getId(), sensor },
601                 sensor.getSimulator().getSimulatorTime());
602         List<SingleSensor> sensorList = this.sensors.get(sensor.getLongitudinalPosition().si);
603         if (null == sensorList)
604         {
605             throw new NetworkException("No sensor at " + sensor.getLongitudinalPosition().si);
606         }
607         sensorList.remove(sensor);
608         if (sensorList.size() == 0)
609         {
610             this.sensors.remove(sensor.getLongitudinalPosition().si);
611         }
612         this.parentLink.getNetwork().removeObject(sensor);
613     }
614 
615     /**
616      * Retrieve the list of Sensors of this Lane in the specified distance range for the given GTUType. The sensors that are
617      * triggered by GTUTypes.ALL are added as well. The resulting list is a defensive copy.
618      * @param minimumPosition Length; the minimum distance on the Lane (inclusive)
619      * @param maximumPosition Length; the maximum distance on the Lane (inclusive)
620      * @param gtuType the GTU type to provide the sensors for
621      * @return List&lt;Sensor&gt;; list of the sensor in the specified range. This is a defensive copy.
622      */
623     public final List<SingleSensor> getSensors(final Length minimumPosition, final Length maximumPosition,
624             final GTUType gtuType)
625     {
626         List<SingleSensor> sensorList = new ArrayList<>(1);
627         for (List<SingleSensor> sl : this.sensors.values())
628         {
629             for (SingleSensor sensor : sl)
630             {
631                 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL))
632                         && sensor.getLongitudinalPosition().ge(minimumPosition)
633                         && sensor.getLongitudinalPosition().le(maximumPosition))
634                 {
635                     sensorList.add(sensor);
636                 }
637             }
638         }
639         return sensorList;
640     }
641 
642     /**
643      * Retrieve the list of Sensors of this Lane that are triggered by the given GTUType. The sensors that are triggered by
644      * GTUTypes.ALL are added as well. The resulting list is a defensive copy.
645      * @param gtuType GTUType; the GTU type to provide the sensors for
646      * @return List&lt;Sensor&gt;; list of the sensors, in ascending order for the location on the Lane
647      */
648     public final List<SingleSensor> getSensors(final GTUType gtuType)
649     {
650         List<SingleSensor> sensorList = new ArrayList<>(1);
651         for (List<SingleSensor> sl : this.sensors.values())
652         {
653             for (SingleSensor sensor : sl)
654             {
655                 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
656                 {
657                     sensorList.add(sensor);
658                 }
659             }
660         }
661         return sensorList;
662     }
663 
664     /**
665      * Retrieve the list of all Sensors of this Lane. The resulting list is a defensive copy.
666      * @return List&lt;Sensor&gt;; list of the sensors, in ascending order for the location on the Lane
667      */
668     public final List<SingleSensor> getSensors()
669     {
670         if (this.sensors == null)
671         {
672             return new ArrayList<>();
673         }
674         List<SingleSensor> sensorList = new ArrayList<>(1);
675         for (List<SingleSensor> sl : this.sensors.values())
676         {
677             for (SingleSensor sensor : sl)
678             {
679                 sensorList.add(sensor);
680             }
681         }
682         return sensorList;
683     }
684 
685     /**
686      * Retrieve the list of Sensors of this Lane for the given GTUType. The sensors that are triggered by GTUTypes.ALL are added
687      * as well. The resulting Map is a defensive copy.
688      * @param gtuType GTUType; the GTU type to provide the sensors for
689      * @return SortedMap&lt;Double, List&lt;Sensor&gt;&gt;; all sensors on this lane for the given GTUType as a map per distance
690      */
691     public final SortedMap<Double, List<SingleSensor>> getSensorMap(final GTUType gtuType)
692     {
693         SortedMap<Double, List<SingleSensor>> sensorMap = new TreeMap<>();
694         for (double d : this.sensors.keySet())
695         {
696             List<SingleSensor> sensorList = new ArrayList<>(1);
697             for (List<SingleSensor> sl : this.sensors.values())
698             {
699                 for (SingleSensor sensor : sl)
700                 {
701                     if (sensor.getLongitudinalPosition().si == d && (sensor.getTriggeringGTUTypes().contains(gtuType)
702                             || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
703                     {
704                         sensorList.add(sensor);
705                     }
706                 }
707             }
708             if (sensorList.size() > 0)
709             {
710                 sensorMap.put(d, sensorList);
711             }
712         }
713         // System.out.println("getSensorMap returns");
714         // for (Double key : sensorMap.keySet())
715         // {
716         // System.out.println("\t" + key + " -> " + (sensorMap.get(key).size()) + " sensors");
717         // for (Sensor s : sensorMap.get(key))
718         // {
719         // System.out.println("\t\t" + s);
720         // }
721         // }
722         return sensorMap;
723     }
724 
725     /**
726      * Schedule triggering of the sensors for a certain time step; from now until the nextEvaluationTime of the GTU.
727      * @param gtu LaneBasedGTU; the lane based GTU for which to schedule triggering of the sensors.
728      * @param referenceStartSI double; the SI distance of the GTU reference point on the lane at the current time
729      * @param referenceMoveSI double; the SI distance traveled in the next time step.
730      * @throws NetworkException when GTU not on this lane.
731      * @throws SimRuntimeException when method cannot be scheduled.
732      */
733     public final void scheduleSensorTriggers(final LaneBasedGTU gtu, final double referenceStartSI,
734             final double referenceMoveSI) throws NetworkException, SimRuntimeException
735     {
736         // System.out.println("Getting sensor map for " + gtu.getGTUType() + ":");
737         for (List<SingleSensor> sensorList : getSensorMap(gtu.getGTUType()).values())
738         {
739             // System.out.println("Time=" + gtu.getSimulator().getSimulatorTime().toString() + " SensorList contains "
740             // + (sensorList.size()) + " sensors:");
741             // for (Sensor sensor : sensorList)
742             // {
743             // System.out.println("\t" + sensor);
744             // }
745             for (SingleSensor sensor : sensorList)
746             {
747                 for (RelativePosition relativePosition : gtu.getRelativePositions().values())
748                 {
749                     // System.out.println("GTU relative position " + relativePosition + " sensor relative position "
750                     // + sensor.getPositionType());
751                     if (sensor.getPositionType().equals(relativePosition.getType())
752                             && referenceStartSI + relativePosition.getDx().getSI() <= sensor.getLongitudinalPosition().si
753                             && referenceStartSI + referenceMoveSI
754                                     + relativePosition.getDx().getSI() > sensor.getLongitudinalPosition().si)
755                     {
756                         // the exact time of triggering is based on the distance between the current position of the
757                         // relative position on the GTU and the location of the sensor.
758                         // TODO make sure triggering is done right when driving in DIR_MINUS direction
759                         double d = sensor.getLongitudinalPosition().si - referenceStartSI - relativePosition.getDx().getSI();
760                         if (d < 0)
761                         {
762                             throw new NetworkException("scheduleTriggers for gtu: " + gtu + ", d<0 d=" + d);
763                         }
764 
765                         OperationalPlan oPlan = gtu.getOperationalPlan();
766                         Time triggerTime = oPlan.timeAtDistance(new Length(d, LengthUnit.METER));
767                         if (triggerTime.gt(oPlan.getEndTime()))
768                         {
769                             System.err.println("Time=" + gtu.getSimulator().getSimulatorTime().getTime().getSI()
770                                     + " - Scheduling trigger at " + triggerTime.getSI() + "s. > " + oPlan.getEndTime().getSI()
771                                     + "s. (nextEvalTime) for sensor " + sensor + " , gtu " + gtu);
772                             System.err.println("  v=" + gtu.getSpeed() + ", a=" + gtu.getAcceleration() + ", lane=" + toString()
773                                     + ", refStartSI=" + referenceStartSI + ", moveSI=" + referenceMoveSI);
774                             triggerTime =
775                                     new Time(oPlan.getEndTime().getSI() - Math.ulp(oPlan.getEndTime().getSI()), TimeUnit.SI);
776                             // gtu.timeAtDistance(new Length(-d, METER));
777                             // System.exit(-1);
778                         }
779                         // System.out.println("Scheduling a trigger for relativePosition " + relativePosition);
780                         // System.out.println("Time=" + gtu.getSimulator().getSimulatorTime().toString()
781                         // + " - Scheduling trigger at " + triggerTime + " for sensor " + sensor + " , gtu " + gtu);
782                         SimEvent<OTSSimTimeDouble> event = new SimEvent<OTSSimTimeDouble>(new OTSSimTimeDouble(triggerTime),
783                                 this, sensor, "trigger", new Object[] { gtu });
784                         gtu.getSimulator().scheduleEvent(event);
785                         gtu.addTrigger(this, event);
786                     }
787                 }
788             }
789         }
790     }
791 
792     /**
793      * Insert a laneBasedObject at the right place in the laneBasedObject list of this Lane.
794      * @param laneBasedObject LaneBasedObject; the laneBasedObject to add
795      * @throws NetworkException when the position of the laneBasedObject is beyond (or before) the range of this Lane
796      */
797     public final void addLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
798     {
799         double position = laneBasedObject.getLongitudinalPosition().si;
800         if (position < 0 || position > getLength().getSI())
801         {
802             throw new NetworkException(
803                     "Illegal position for laneBasedObject " + position + " valid range is 0.." + getLength().getSI());
804         }
805         if (this.parentLink.getNetwork().containsObject(laneBasedObject.getId()))
806         {
807             throw new NetworkException("Network already contains an object with the name " + laneBasedObject.getId());
808         }
809         List<LaneBasedObject> laneBasedObjectList = this.laneBasedObjects.get(position);
810         if (null == laneBasedObjectList)
811         {
812             laneBasedObjectList = new ArrayList<LaneBasedObject>(1);
813             this.laneBasedObjects.put(position, laneBasedObjectList);
814         }
815         laneBasedObjectList.add(laneBasedObject);
816         this.parentLink.getNetwork().addObject(laneBasedObject);
817         fireEvent(Lane.OBJECT_ADD_EVENT, new Object[] { laneBasedObject });
818     }
819 
820     /**
821      * Remove a laneBasedObject from the laneBasedObject list of this Lane.
822      * @param laneBasedObject Sensoe; the laneBasedObject to remove.
823      * @throws NetworkException when the laneBasedObject was not found on this Lane
824      */
825     public final void removeLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
826     {
827         fireEvent(Lane.OBJECT_REMOVE_EVENT, new Object[] { laneBasedObject });
828         List<LaneBasedObject> laneBasedObjectList =
829                 this.laneBasedObjects.get(laneBasedObject.getLongitudinalPosition().getSI());
830         if (null == laneBasedObjectList)
831         {
832             throw new NetworkException("No laneBasedObject at " + laneBasedObject.getLongitudinalPosition().si);
833         }
834         laneBasedObjectList.remove(laneBasedObject);
835         if (laneBasedObjectList.isEmpty())
836         {
837             this.laneBasedObjects.remove(laneBasedObject.getLongitudinalPosition().doubleValue());
838         }
839         this.parentLink.getNetwork().removeObject(laneBasedObject);
840     }
841 
842     /**
843      * Retrieve the list of LaneBasedObjects of this Lane in the specified distance range. The resulting list is a defensive
844      * copy.
845      * @param minimumPosition Length; the minimum distance on the Lane (inclusive)
846      * @param maximumPosition Length; the maximum distance on the Lane (inclusive)
847      * @return List&lt;LaneBasedObject&gt;; list of the laneBasedObject in the specified range. This is a defensive copy.
848      */
849     public final List<LaneBasedObject> getLaneBasedObjects(final Length minimumPosition, final Length maximumPosition)
850     {
851         List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
852         for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
853         {
854             for (LaneBasedObject lbo : lbol)
855             {
856                 if (lbo.getLongitudinalPosition().ge(minimumPosition) && lbo.getLongitudinalPosition().le(maximumPosition))
857                 {
858                     laneBasedObjectList.add(lbo);
859                 }
860             }
861         }
862         return laneBasedObjectList;
863     }
864 
865     /**
866      * Retrieve the list of all LaneBasedObjects of this Lane. The resulting list is a defensive copy.
867      * @return List&lt;LaneBasedObject&gt;; list of the laneBasedObjects, in ascending order for the location on the Lane
868      */
869     public final List<LaneBasedObject> getLaneBasedObjects()
870     {
871         if (this.laneBasedObjects == null)
872         {
873             return new ArrayList<>();
874         }
875         List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
876         for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
877         {
878             for (LaneBasedObject lbo : lbol)
879             {
880                 laneBasedObjectList.add(lbo);
881             }
882         }
883         return laneBasedObjectList;
884     }
885 
886     /**
887      * Retrieve the list of LaneBasedObjects of this Lane. The resulting Map is a defensive copy.
888      * @return SortedMap&lt;Double, List&lt;LaneBasedObject&gt;&gt;; all laneBasedObjects on this lane
889      */
890     public final SortedMap<Double, List<LaneBasedObject>> getLaneBasedObjectMap()
891     {
892         SortedMap<Double, List<LaneBasedObject>> laneBasedObjectMap = new TreeMap<>();
893         for (double d : this.laneBasedObjects.keySet())
894         {
895             List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
896             for (LaneBasedObject lbo : this.laneBasedObjects.get(d))
897             {
898                 laneBasedObjectList.add(lbo);
899             }
900             laneBasedObjectMap.put(d, laneBasedObjectList);
901         }
902         return laneBasedObjectMap;
903     }
904 
905     /**
906      * Transform a fraction on the lane to a relative length (can be less than zero or larger than the lane length).
907      * @param fraction double; fraction relative to the lane length.
908      * @return Length; the longitudinal length corresponding to the fraction.
909      */
910     public final Length position(final double fraction)
911     {
912         if (this.length.getUnit().isBaseSIUnit())
913         {
914             return new Length(this.length.si * fraction, LengthUnit.SI);
915         }
916         return new Length(this.length.getInUnit() * fraction, this.length.getUnit());
917     }
918 
919     /**
920      * Transform a fraction on the lane to a relative length in SI units (can be less than zero or larger than the lane length).
921      * @param fraction double; fraction relative to the lane length.
922      * @return double; length corresponding to the fraction, in SI units.
923      */
924     public final double positionSI(final double fraction)
925     {
926         return this.length.si * fraction;
927     }
928 
929     /**
930      * Transform a position on the lane (can be less than zero or larger than the lane length) to a fraction.
931      * @param position Length; relative length on the lane (may be less than zero or larger than the lane length).
932      * @return fraction double; fraction relative to the lane length.
933      */
934     public final double fraction(final Length position)
935     {
936         return position.si / this.length.si;
937     }
938 
939     /**
940      * Transform a position on the lane in SI units (can be less than zero or larger than the lane length) to a fraction.
941      * @param positionSI double; relative length on the lane in SI units (may be less than zero or larger than the lane length).
942      * @return double; fraction relative to the lane length.
943      */
944     public final double fractionSI(final double positionSI)
945     {
946         return positionSI / this.length.si;
947     }
948 
949     /**
950      * Add a LaneBasedGTU to the list of this Lane.
951      * @param gtu LaneBasedGTU; the GTU to add
952      * @param fractionalPosition double; the fractional position that the newly added GTU will have on this Lane
953      * @return int; the rank that the newly added GTU has on this Lane (should be 0, except when the GTU enters this Lane due to
954      *         a lane change operation)
955      * @throws GTUException when the fractionalPosition is outside the range 0..1, or the GTU is already registered on this Lane
956      */
957     public final int addGTU(final LaneBasedGTU gtu, final double fractionalPosition) throws GTUException
958     {
959         int index;
960         // check if we are the first
961         if (this.gtuList.size() == 0)
962         {
963             this.gtuList.add(gtu);
964             index = 0;
965         }
966         else
967         {
968             // check if we can add at the end
969             LaneBasedGTU lastGTU = this.gtuList.get(this.gtuList.size() - 1);
970             if (fractionalPosition > lastGTU.fractionalPosition(this, lastGTU.getFront()))
971             {
972                 this.gtuList.add(gtu);
973                 index = this.gtuList.size() - 1;
974             }
975             else
976             {
977                 // figure out the rank for the new GTU
978                 for (index = 0; index < this.gtuList.size(); index++)
979                 {
980                     LaneBasedGTU otherGTU = this.gtuList.get(index);
981                     if (gtu == otherGTU)
982                     {
983                         throw new GTUException(gtu + " already registered on Lane " + this + " [registered lanes: "
984                                 + gtu.positions(gtu.getFront()).keySet() + "] locations: "
985                                 + gtu.positions(gtu.getFront()).values() + " time: "
986                                 + gtu.getSimulator().getSimulatorTime().getTime());
987                     }
988                     if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
989                     {
990                         break;
991                     }
992                 }
993                 this.gtuList.add(index, gtu);
994             }
995         }
996         fireTimedEvent(Lane.GTU_ADD_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size() },
997                 gtu.getSimulator().getSimulatorTime());
998         getParentLink().addGTU(gtu);
999         return index;
1000     }
1001 
1002     /**
1003      * Add a LaneBasedGTU to the list of this Lane.
1004      * @param gtu LaneBasedGTU; the GTU to add
1005      * @param longitudinalPosition Length; the longitudinal position that the newly added GTU will have on this Lane
1006      * @return int; the rank that the newly added GTU has on this Lane (should be 0, except when the GTU enters this Lane due to
1007      *         a lane change operation)
1008      * @throws GTUException when longitudinalPosition is negative or exceeds the length of this Lane
1009      */
1010     public final int addGTU(final LaneBasedGTU gtu, final Length longitudinalPosition) throws GTUException
1011     {
1012         return addGTU(gtu, longitudinalPosition.getSI() / getLength().getSI());
1013     }
1014 
1015     /**
1016      * Remove a GTU from the GTU list of this lane.
1017      * @param gtu the GTU to remove.
1018      * @param removeFromParentLink when the GTU leaves the last lane of the parentLink of this Lane
1019      * @param position Length; last position of the GTU
1020      */
1021     public final void removeGTU(final LaneBasedGTU gtu, final boolean removeFromParentLink, final Length position)
1022     {
1023         this.gtuList.remove(gtu);
1024         fireTimedEvent(Lane.GTU_REMOVE_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size(), position },
1025                 gtu.getSimulator().getSimulatorTime());
1026         if (removeFromParentLink)
1027         {
1028             this.parentLink.removeGTU(gtu);
1029         }
1030     }
1031 
1032     /**
1033      * Get the last GTU on the lane, relative to a driving direction on this lane.
1034      * @param direction GTUDirectionality; whether we are looking in the the design line direction or against the center line
1035      *            direction.
1036      * @return LaneBasedGTU; the last GTU on this lane in the given direction, or null if no GTU could be found.
1037      * @throws GTUException when there is a problem with the position of the GTUs on the lane.
1038      */
1039     public final LaneBasedGTU getLastGtu(final GTUDirectionality direction) throws GTUException
1040     {
1041         if (this.gtuList.size() == 0)
1042         {
1043             return null;
1044         }
1045         if (direction.equals(GTUDirectionality.DIR_PLUS))
1046         {
1047             return this.gtuList.get(this.gtuList.size() - 1);
1048         }
1049         else
1050         {
1051             return this.gtuList.get(0);
1052         }
1053     }
1054 
1055     /**
1056      * Get the first GTU on the lane, relative to a driving direction on this lane.
1057      * @param direction GTUDirectionality; whether we are looking in the the design line direction or against the center line
1058      *            direction.
1059      * @return LaneBasedGTU; the first GTU on this lane in the given direction, or null if no GTU could be found.
1060      * @throws GTUException when there is a problem with the position of the GTUs on the lane.
1061      */
1062     public final LaneBasedGTU getFirstGtu(final GTUDirectionality direction) throws GTUException
1063     {
1064         if (this.gtuList.size() == 0)
1065         {
1066             return null;
1067         }
1068         if (direction.equals(GTUDirectionality.DIR_PLUS))
1069         {
1070             return this.gtuList.get(0);
1071         }
1072         else
1073         {
1074             return this.gtuList.get(this.gtuList.size() - 1);
1075         }
1076     }
1077 
1078     /**
1079      * Get the first GTU where the relativePosition is in front of another GTU on the lane, in a driving direction on this lane,
1080      * compared to the DESIGN LINE.
1081      * @param position Length; the position before which the relative position of a GTU will be searched.
1082      * @param direction GTUDirectionality; whether we are looking in the the center line direction or against the center line
1083      *            direction.
1084      * @param relativePosition RelativePosition.TYPE; the relative position we want to compare against
1085      * @param when Time; the time for which to evaluate the positions.
1086      * @return LaneBasedGTU; the first GTU before a position on this lane in the given direction, or null if no GTU could be
1087      *         found.
1088      * @throws GTUException when there is a problem with the position of the GTUs on the lane.
1089      */
1090     public final LaneBasedGTU getGtuAhead(final Length position, final GTUDirectionality direction,
1091             final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1092     {
1093         if (direction.equals(GTUDirectionality.DIR_PLUS))
1094         {
1095             for (LaneBasedGTU gtu : this.gtuList)
1096             {
1097                 Length gtuPos = gtu.position(this, gtu.getRelativePositions().get(relativePosition), when);
1098                 if (gtuPos.gt(position))
1099                 {
1100                     return gtu;
1101                 }
1102             }
1103         }
1104         else
1105         {
1106             for (int i = this.gtuList.size() - 1; i >= 0; i--)
1107             {
1108                 LaneBasedGTU gtu = this.gtuList.get(i);
1109                 if (gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).lt(position))
1110                 {
1111                     return gtu;
1112                 }
1113             }
1114         }
1115         return null;
1116     }
1117 
1118     /**
1119      * Get the first object where the relativePosition is in front of a certain position on the lane, in a driving direction on
1120      * this lane, compared to the DESIGN LINE. Perception should iterate over results from this method to see what is most
1121      * limiting.
1122      * @param position Length; the position after which the relative position of an object will be searched.
1123      * @param direction GTUDirectionality; whether we are looking in the the center line direction or against the center line
1124      *            direction.
1125      * @return List&lt;LaneBasedObject&gt;; the first object(s) before a position on this lane in the given direction, or null
1126      *         if no object could be found.
1127      */
1128     public final List<LaneBasedObject> getObjectAhead(final Length position, final GTUDirectionality direction)
1129     {
1130         if (direction.equals(GTUDirectionality.DIR_PLUS))
1131         {
1132             for (double distance : this.laneBasedObjects.keySet())
1133             {
1134                 if (distance > position.si)
1135                 {
1136                     return new ArrayList<>(this.laneBasedObjects.get(distance));
1137                 }
1138             }
1139         }
1140         else
1141         {
1142             NavigableMap<Double, List<LaneBasedObject>> reverseLBO =
1143                     (NavigableMap<Double, List<LaneBasedObject>>) this.laneBasedObjects;
1144             for (double distance : reverseLBO.descendingKeySet())
1145             {
1146                 if (distance < position.si)
1147                 {
1148                     return new ArrayList<>(this.laneBasedObjects.get(distance));
1149                 }
1150             }
1151         }
1152         return null;
1153     }
1154 
1155     /**
1156      * Get the first object where the relativePosition is behind of a certain position on the lane, in a driving direction on
1157      * this lane, compared to the DESIGN LINE. Perception should iterate over results from this method to see what is most
1158      * limiting.
1159      * @param position Length; the position after which the relative position of an object will be searched.
1160      * @param direction GTUDirectionality; whether we are looking in the the center line direction or against the center line
1161      *            direction.
1162      * @return List&lt;LaneBasedObject&gt;; the first object(s) after a position on this lane in the given direction, or null if
1163      *         no object could be found.
1164      */
1165     public final List<LaneBasedObject> getObjectBehind(final Length position, final GTUDirectionality direction)
1166     {
1167         if (direction.equals(GTUDirectionality.DIR_PLUS))
1168         {
1169             return getObjectAhead(position, GTUDirectionality.DIR_MINUS);
1170         }
1171         return getObjectAhead(position, GTUDirectionality.DIR_PLUS);
1172     }
1173 
1174     /**
1175      * Get the first GTU where the relativePosition is behind a certain position on the lane, in a driving direction on this
1176      * lane, compared to the DESIGN LINE.
1177      * @param position Length; the position before which the relative position of a GTU will be searched.
1178      * @param direction GTUDirectionality; whether we are looking in the the center line direction or against the center line
1179      *            direction.
1180      * @param relativePosition RelativePosition.TYPE; the relative position of the GTU we are looking for.
1181      * @param when Time; the time for which to evaluate the positions.
1182      * @return LaneBasedGTU; the first GTU after a position on this lane in the given direction, or null if no GTU could be
1183      *         found.
1184      * @throws GTUException when there is a problem with the position of the GTUs on the lane.
1185      */
1186     public final LaneBasedGTU getGtuBehind(final Length position, final GTUDirectionality direction,
1187             final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1188     {
1189         if (direction.equals(GTUDirectionality.DIR_PLUS))
1190         {
1191             return getGtuAhead(position, GTUDirectionality.DIR_MINUS, relativePosition, when);
1192         }
1193         return getGtuAhead(position, GTUDirectionality.DIR_PLUS, relativePosition, when);
1194     }
1195 
1196     /*
1197      * TODO only center position? Or also width? What is a good cutoff? Base on average width of the GTU type that can drive on
1198      * this Lane? E.g., for a Tram or Train, a 5 cm deviation is a problem; for a Car or a Bicycle, more deviation is
1199      * acceptable.
1200      */
1201     /** Lateral alignment margin for longitudinally connected Lanes. */
1202     public static final Length MARGIN = new Length(0.5, LengthUnit.METER);
1203 
1204     /**
1205      * NextLanes returns the successor lane(s) in the design line direction, if any exist.<br>
1206      * The next lane(s) are cached, as it is too expensive to make the calculation every time. There are several possibilities:
1207      * (1) Returning an empty set when there is no successor lane in the design direction or there is no longitudinal transfer
1208      * possible to a successor lane in the design direction. (2) Returning a set with just one lane if the lateral position of
1209      * the successor lane matches the lateral position of this lane (based on an overlap of the lateral positions of the two
1210      * joining lanes of more than a certain percentage). (3) Multiple lanes in case the Node where the underlying Link for this
1211      * Lane has multiple "outgoing" Links, and there are multiple lanes that match the lateral position of this lane.<br>
1212      * The next lanes can differ per GTU type. For instance, a lane where cars and buses are allowed can have a next lane where
1213      * only buses are allowed, forcing the cars to leave that lane.
1214      * @param gtuType the GTU type for which we return the next lanes.
1215      * @return set of Lanes following this lane for the given GTU type.
1216      */
1217     public final Map<Lane, GTUDirectionality> nextLanes(final GTUType gtuType)
1218     {
1219         if (this.nextLanes == null)
1220         {
1221             this.nextLanes = new LinkedHashMap<>(1);
1222         }
1223         if (!this.nextLanes.containsKey(gtuType))
1224         {
1225             Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1226             this.nextLanes.put(gtuType, laneMap);
1227             // Construct (and cache) the result.
1228             for (Link link : getParentLink().getEndNode().getLinks())
1229             {
1230                 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1231                 {
1232                     for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1233                     {
1234                         if (cse instanceof Lane)
1235                         {
1236                             Lane lane = (Lane) cse;
1237                             Length df = this.getCenterLine().getLast().distance(lane.getCenterLine().getFirst());
1238                             Length dl = this.getCenterLine().getLast().distance(lane.getCenterLine().getLast());
1239                             // this, parentLink ---> O ---> lane, link
1240                             if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getEndNode()))
1241                             {
1242                                 // does the GTU move in the design line direction or against it?
1243                                 // TODO And is it aligned with its next lane?
1244                                 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1245                                 {
1246                                     laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1247                                 }
1248                                 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1249                                 {
1250                                     laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1251                                 }
1252                             }
1253                             // this, parentLink ---> O <--- lane, link
1254                             else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getEndNode()))
1255                             {
1256                                 // does the GTU move in the design line direction or against it?
1257                                 // TODO And is it aligned with its next lane?
1258                                 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1259                                 {
1260                                     laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1261                                 }
1262                                 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1263                                 {
1264                                     laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1265                                 }
1266                             }
1267                         }
1268                     }
1269                 }
1270             }
1271         }
1272         return this.nextLanes.get(gtuType);
1273     }
1274 
1275     /**
1276      * PrevLanes returns the predecessor lane(s) relative to the design line direction, if any exist.<br>
1277      * The previous lane(s) are cached, as it is too expensive to make the calculation every time. There are several
1278      * possibilities: (1) Returning an empty set when there is no predecessor lane relative to the design direction or there is
1279      * no longitudinal transfer possible to a predecessor lane relative to the design direction. (2) Returning a set with just
1280      * one lane if the lateral position of the predecessor lane matches the lateral position of this lane (based on an overlap
1281      * of the lateral positions of the two joining lanes of more than a certain percentage). (3) Multiple lanes in case the Node
1282      * where the underlying Link for this Lane has multiple "incoming" Links, and there are multiple lanes that match the
1283      * lateral position of this lane.<br>
1284      * The previous lanes can differ per GTU type. For instance, a lane where cars and buses are allowed can be preceded by a
1285      * lane where only buses are allowed.
1286      * @param gtuType the GTU type for which we return the next lanes.
1287      * @return set of Lanes following this lane for the given GTU type.
1288      */
1289     public final Map<Lane, GTUDirectionality> prevLanes(final GTUType gtuType)
1290     {
1291         if (this.prevLanes == null)
1292         {
1293             this.prevLanes = new LinkedHashMap<>(1);
1294         }
1295         if (!this.prevLanes.containsKey(gtuType))
1296         {
1297             Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1298             this.prevLanes.put(gtuType, laneMap);
1299             // Construct (and cache) the result.
1300             for (Link link : getParentLink().getStartNode().getLinks())
1301             {
1302                 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1303                 {
1304                     for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1305                     {
1306                         if (cse instanceof Lane)
1307                         {
1308                             Lane lane = (Lane) cse;
1309                             Length df = this.getCenterLine().getFirst().distance(lane.getCenterLine().getFirst());
1310                             Length dl = this.getCenterLine().getFirst().distance(lane.getCenterLine().getLast());
1311                             // this, parentLink <--- O ---> lane, link
1312                             if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getStartNode()))
1313                             {
1314                                 // does the GTU move in the design line direction or against it?
1315                                 // TODO And is it aligned with its next lane?
1316                                 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1317                                 {
1318                                     laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1319                                 }
1320                                 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1321                                 {
1322                                     laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1323                                 }
1324                             }
1325                             // this, parentLink <--- O <--- lane, link
1326                             else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getStartNode()))
1327                             {
1328                                 // does the GTU move in the design line direction or against it?
1329                                 // TODO And is it aligned with its next lane?
1330                                 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1331                                 {
1332                                     laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1333                                 }
1334                                 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1335                                 {
1336                                     laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1337                                 }
1338                             }
1339                         }
1340                     }
1341                 }
1342             }
1343         }
1344         return this.prevLanes.get(gtuType);
1345     }
1346 
1347     /**
1348      * Returns the lanes that could be followed in a given direction and for the given GTU type.
1349      * @param direction GTUDirectionality; gtu direction
1350      * @param gtuType GTUType; gtu type
1351      * @return lanes that can be followed in a given direction and for the given GTU type
1352      */
1353     public final Map<Lane, GTUDirectionality> downstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1354     {
1355         if (this.downLanes == null)
1356         {
1357             this.downLanes = new LinkedHashMap<>(1);
1358         }
1359         if (!this.downLanes.containsKey(gtuType))
1360         {
1361             this.downLanes.put(gtuType, new HashMap<>());
1362         }
1363         if (!this.downLanes.get(gtuType).containsKey(direction))
1364         {
1365             Map<Lane, GTUDirectionality> down = direction.isPlus() ? nextLanes(gtuType) : prevLanes(gtuType);
1366             down = new LinkedHashMap<>(down); // safe copy
1367             Node downNode = direction.isPlus() ? getParentLink().getEndNode() : getParentLink().getStartNode();
1368             Iterator<Entry<Lane, GTUDirectionality>> iterator = down.entrySet().iterator();
1369             while (iterator.hasNext())
1370             {
1371                 Entry<Lane, GTUDirectionality> entry = iterator.next();
1372                 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getStartNode().equals(downNode))
1373                         || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getEndNode().equals(downNode)))
1374                 {
1375                     // cannot move onto this lane
1376                     iterator.remove();
1377                 }
1378             }
1379             this.downLanes.get(gtuType).put(direction, down);
1380         }
1381         return this.downLanes.get(gtuType).get(direction);
1382     }
1383 
1384     /**
1385      * Returns the lanes that could precede in a given direction and for the given GTU type.
1386      * @param direction GTUDirectionality; gtu direction
1387      * @param gtuType GTUType; gtu type
1388      * @return lanes that can be followed in a given direction and for the given GTU type
1389      */
1390     public final Map<Lane, GTUDirectionality> upstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1391     {
1392         if (this.upLanes == null)
1393         {
1394             this.upLanes = new LinkedHashMap<>(1);
1395         }
1396         if (!this.upLanes.containsKey(gtuType))
1397         {
1398             this.upLanes.put(gtuType, new HashMap<>());
1399         }
1400         if (!this.upLanes.get(gtuType).containsKey(direction))
1401         {
1402             Map<Lane, GTUDirectionality> up = direction.isPlus() ? prevLanes(gtuType) : nextLanes(gtuType);
1403             up = new LinkedHashMap<>(up); // safe copy
1404             Node upNode = direction.isPlus() ? getParentLink().getStartNode() : getParentLink().getEndNode();
1405             Iterator<Entry<Lane, GTUDirectionality>> iterator = up.entrySet().iterator();
1406             while (iterator.hasNext())
1407             {
1408                 Entry<Lane, GTUDirectionality> entry = iterator.next();
1409                 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getEndNode().equals(upNode))
1410                         || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getStartNode().equals(upNode)))
1411                 {
1412                     // cannot have come from this lane
1413                     iterator.remove();
1414                 }
1415             }
1416             this.upLanes.get(gtuType).put(direction, up);
1417         }
1418         return this.upLanes.get(gtuType).get(direction);
1419     }
1420 
1421     /**
1422      * Determine the set of lanes to the left or to the right of this lane, which are accessible from this lane, or an empty set
1423      * if no lane could be found. The method takes the LongitidinalDirectionality of the lane into account. In other words, if
1424      * we drive in the DIR_PLUS direction and look for a lane on the LEFT, and there is a lane but the Directionality of that
1425      * lane is not DIR_PLUS or DIR_BOTH, it will not be included.<br>
1426      * A lane is called adjacent to another lane if the lateral edges are not more than a delta distance apart. This means that
1427      * a lane that <i>overlaps</i> with another lane is <b>not</b> returned as an adjacent lane. <br>
1428      * <b>Note:</b> LEFT and RIGHT are seen from the direction of the GTU, in its forward driving direction. <br>
1429      * @param lateralDirection LEFT or RIGHT.
1430      * @param gtuType the type of GTU for which to return the adjacent lanes.
1431      * @return the set of lanes that are accessible, or null if there is no lane that is accessible with a matching driving
1432      *         direction.
1433      */
1434     public final Set<Lane> accessibleAdjacentLanes(final LateralDirectionality lateralDirection, final GTUType gtuType)
1435     {
1436         Set<Lane> candidates = new LinkedHashSet<>(1);
1437         LateralDirectionality dir = this.getDirectionality(gtuType).isForwardOrBoth() ? lateralDirection
1438                 : lateralDirection.isLeft() ? LateralDirectionality.RIGHT : LateralDirectionality.LEFT;
1439         for (Lane lane : neighbors(dir, gtuType))
1440         {
1441             if (lane.getDirectionality(gtuType).equals(LongitudinalDirectionality.DIR_BOTH)
1442                     || lane.getDirectionality(gtuType).equals(this.getDirectionality(gtuType)))
1443             {
1444                 candidates.add(lane);
1445             }
1446         }
1447         return candidates;
1448     }
1449 
1450     /**
1451      * Get the speed limit of this lane, which can differ per GTU type. E.g., cars might be allowed to drive 120 km/h and trucks
1452      * 90 km/h.
1453      * @param gtuType the GTU type to provide the speed limit for
1454      * @return the speedLimit.
1455      * @throws NetworkException on network inconsistency
1456      */
1457     public final Speed getSpeedLimit(final GTUType gtuType) throws NetworkException
1458     {
1459         if (this.speedLimitMap.containsKey(gtuType))
1460         {
1461             return this.speedLimitMap.get(gtuType);
1462         }
1463         if (gtuType.getParent() != null)
1464         {
1465             return getSpeedLimit(gtuType.getParent());
1466         }
1467         throw new NetworkException("No speed limit set for GTUType " + gtuType + " on lane " + toString());
1468     }
1469 
1470     /**
1471      * Get the highest speed limit of this lane.
1472      * @return the highest speedLimit.
1473      * @throws NetworkException on network inconsistency
1474      */
1475     public final Speed getHighestSpeedLimit() throws NetworkException
1476     {
1477         Throw.when(this.speedLimitMap.isEmpty(), NetworkException.class, "Lane %s has no speed limits set.", toString());
1478         Speed out = Speed.ZERO;
1479         for (GTUType gtuType : this.speedLimitMap.keySet())
1480         {
1481             out = Speed.max(out, this.speedLimitMap.get(gtuType));
1482         }
1483         return out;
1484     }
1485 
1486     /**
1487      * Set the speed limit of this lane, which can differ per GTU type. Cars might be allowed to drive 120 km/h and trucks 90
1488      * km/h. If the speed limit is the same for all GTU types, GTUType.ALL will be used. This means that the settings can be
1489      * used additive, or subtractive. <br>
1490      * In <b>additive use</b>, do not set the speed limit for GTUType.ALL. Now, one by one, the allowed maximum speeds for each
1491      * of the GTU Types have be added. Do this when there are few GTU types or the speed limits per TU type are very different.
1492      * <br>
1493      * In <b>subtractive use</b>, set the speed limit for GTUType.ALL to the most common one. Override the speed limit for
1494      * certain GTUTypes to a different value. An example is a lane on a highway where all vehicles, except truck (CAR, BUS,
1495      * MOTORCYCLE, etc.), can drive 120 km/h, but trucks are allowed only 90 km/h. In that case, set the speed limit for
1496      * GTUType.ALL to 120 km/h, and for TRUCK to 90 km/h.
1497      * @param gtuType the GTU type to provide the speed limit for
1498      * @param speedLimit the speed limit for this gtu type
1499      */
1500     public final void setSpeedLimit(final GTUType gtuType, final Speed speedLimit)
1501     {
1502         this.speedLimitMap.put(gtuType, speedLimit);
1503     }
1504 
1505     /**
1506      * Remove the set speed limit for a GTUType. If the speed limit for GTUType.ALL will be removed, there will not be a
1507      * 'default' speed limit anymore. If the speed limit for a certain GTUType is removed, its speed limit will default to the
1508      * speed limit of GTUType.ALL. <br>
1509      * <b>Note</b>: if no speed limit is known for a GTUType, getSpeedLimit will throw a NetworkException when the speed limit
1510      * is retrieved for that GTUType.
1511      * @param gtuType the GTU type to provide the speed limit for
1512      */
1513     public final void removeSpeedLimit(final GTUType gtuType)
1514     {
1515         this.speedLimitMap.remove(gtuType);
1516     }
1517 
1518     /**
1519      * @return laneType.
1520      */
1521     public final LaneType getLaneType()
1522     {
1523         return this.laneType;
1524     }
1525 
1526     /**
1527      * The direction in which vehicles can drive, i.e., in direction of geometry, reverse, or both. This can differ per GTU
1528      * type. In an overtake lane, cars might overtake and trucks not. It might be that the lane (e.g., a street in a city) is
1529      * FORWARD (from start node of the link to end node of the link) for the GTU type CAR, but BOTH for the GTU type BICYCLE
1530      * (i.e., bicycles can also go in the other direction, opposite to the drawing direction of the Link). If the directionality
1531      * for a GTUType is set to NONE, this means that the given GTUType cannot use the Lane. If a Directionality is set for
1532      * GTUType.ALL, the getDirectionality will default to these settings when there is no specific entry for a given
1533      * directionality. This means that the settings can be used additive, or restrictive. <br>
1534      * In <b>additive use</b>, set the directionality for GTUType.ALL to NONE, or do not set the directionality for GTUType.ALL.
1535      * Now, one by one, the allowed directionalities can be added. An example is a lane on a highway, which we only open for
1536      * CAR, TRUCK and BUS. <br>
1537      * In <b>restrictive use</b>, set the directionality for GTUType.ALL to BOTH, FORWARD, or BACKWARD. Override the
1538      * directionality for certain GTUTypes to a more restrictive access, e.g. to NONE. An example is a lane that is open for all
1539      * road users, except TRUCK.
1540      * @param gtuType the GTU type to provide the directionality for
1541      * @return the directionality.
1542      */
1543     public final LongitudinalDirectionality getDirectionality(final GTUType gtuType)
1544     {
1545         if (this.directionalityMap.containsKey(gtuType))
1546         {
1547             return this.directionalityMap.get(gtuType);
1548         }
1549         if (this.directionalityMap.containsKey(GTUType.ALL))
1550         {
1551             return this.directionalityMap.get(GTUType.ALL);
1552         }
1553         return LongitudinalDirectionality.DIR_NONE;
1554     }
1555 
1556     /**
1557      * This method sets the directionality of the lane for a GTU type. It might be that the driving direction in the lane is
1558      * FORWARD (from start node of the link to end node of the link) for the GTU type CAR, but BOTH for the GTU type BICYCLE
1559      * (i.e., bicycles can also go in the other direction; we see this on some city streets). If the directionality for a
1560      * GTUType is set to NONE, this means that the given GTUType cannot use the Lane. If a Directionality is set for
1561      * GTUType.ALL, the getDirectionality will default to these settings when there is no specific entry for a given
1562      * directionality. This means that the settings can be used additive, or restrictive. <br>
1563      * In <b>additive use</b>, set the directionality for GTUType.ALL to NONE, or do not set the directionality for GTUType.ALL.
1564      * Now, one by one, the allowed directionalities can be added. An example is a lane on a highway, which we only open for
1565      * CAR, TRUCK and BUS. <br>
1566      * In <b>restrictive use</b>, set the directionality for GTUType.ALL to BOTH, FORWARD, or BACKWARD. Override the
1567      * directionality for certain GTUTypes to a more restrictive access, e.g. to NONE. An example is a lane that is open for all
1568      * road users, except TRUCK.
1569      * @param gtuType the GTU type to set the directionality for.
1570      * @param directionality the longitudinal directionality of the link (FORWARD, BACKWARD, BOTH or NONE) for the given GTU
1571      *            type.
1572      * @throws NetworkException when the lane directionality for the given GTUType is inconsistent with the Link directionality
1573      *             to which the lane belongs.
1574      */
1575     public void addDirectionality(final GTUType gtuType, final LongitudinalDirectionality directionality)
1576             throws NetworkException
1577     {
1578         this.directionalityMap.put(gtuType, directionality);
1579         checkDirectionality();
1580     }
1581 
1582     /**
1583      * This method removes an earlier provided directionality of the lane for a given GTU type, e.g. for maintenance of the
1584      * lane. After removing, the directionality for the GTU will fall back to the provided directionality for GTUType.ALL (if
1585      * present). Thereby removing a directionality is different from setting the directionality to NONE.
1586      * @param gtuType the GTU type to remove the directionality for on this lane.
1587      */
1588     public void removeDirectionality(final GTUType gtuType)
1589     {
1590         this.directionalityMap.remove(gtuType);
1591     }
1592 
1593     /**
1594      * Check whether the directionalities for the GTU types for this lane are consistent with the directionalities of the
1595      * overarching Link.
1596      * @throws NetworkException when the lane directionality for a given GTUType is inconsistent with the Link directionality to
1597      *             which the lane belongs.
1598      */
1599     private void checkDirectionality() throws NetworkException
1600     {
1601         for (GTUType gtuType : this.directionalityMap.keySet())
1602         {
1603             LongitudinalDirectionality directionality = this.directionalityMap.get(gtuType);
1604             if (!getParentLink().getDirectionality(gtuType).contains(directionality))
1605             {
1606                 throw new NetworkException("Lane " + toString() + " allows " + gtuType + " a directionality of "
1607                         + directionality + " which is not present in the overarching link " + getParentLink().toString());
1608             }
1609         }
1610     }
1611 
1612     /**
1613      * @return gtuList.
1614      */
1615     public final ImmutableList<LaneBasedGTU> getGtuList()
1616     {
1617         return this.gtuList == null ? new ImmutableArrayList<>(new ArrayList<>())
1618                 : new ImmutableArrayList<>(this.gtuList, Immutable.WRAP);
1619     }
1620 
1621     /** {@inheritDoc} */
1622     @Override
1623     @SuppressWarnings("checkstyle:designforextension")
1624     protected double getZ()
1625     {
1626         return 0.0;
1627     }
1628 
1629     /**
1630      * @return overtakingConditions
1631      */
1632     public final OvertakingConditions getOvertakingConditions()
1633     {
1634         return this.overtakingConditions;
1635     }
1636 
1637     /** {@inheritDoc} */
1638     @Override
1639     public final String toString()
1640     {
1641         CrossSectionLink link = getParentLink();
1642         return String.format("Lane %s of %s", getId(), link.getId());
1643     }
1644 
1645     /** Cache of the hashCode. */
1646     private Integer cachedHashCode = null;
1647 
1648     /** {@inheritDoc} */
1649     @SuppressWarnings("checkstyle:designforextension")
1650     @Override
1651     public int hashCode()
1652     {
1653         if (this.cachedHashCode == null)
1654         {
1655             final int prime = 31;
1656             int result = super.hashCode();
1657             result = prime * result + ((this.laneType == null) ? 0 : this.laneType.hashCode());
1658             this.cachedHashCode = result;
1659         }
1660         return this.cachedHashCode;
1661     }
1662 
1663     /** {@inheritDoc} */
1664     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
1665     @Override
1666     public boolean equals(final Object obj)
1667     {
1668         if (this == obj)
1669             return true;
1670         if (!super.equals(obj))
1671             return false;
1672         if (getClass() != obj.getClass())
1673             return false;
1674         Lane other = (Lane) obj;
1675         if (this.laneType == null)
1676         {
1677             if (other.laneType != null)
1678                 return false;
1679         }
1680         else if (!this.laneType.equals(other.laneType))
1681             return false;
1682         return true;
1683     }
1684 
1685     /** {@inheritDoc} */
1686     @Override
1687     @SuppressWarnings("checkstyle:designforextension")
1688     public Lane clone(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation)
1689             throws NetworkException
1690     {
1691         Lane newLane = new Lane(newParentLink, newSimulator, animation, this);
1692         // nextLanes, prevLanes, nextNeighbors, rightNeighbors are filled at first request
1693 
1694         SortedMap<Double, List<SingleSensor>> newSensorMap = new TreeMap<>();
1695         for (double distance : this.sensors.keySet())
1696         {
1697             List<SingleSensor> newSensorList = new ArrayList<>();
1698             for (SingleSensor sensor : this.sensors.get(distance))
1699             {
1700                 SingleSensor newSensor = ((AbstractSensor) sensor).clone(newLane, newSimulator, animation);
1701                 newSensorList.add(newSensor);
1702             }
1703             newSensorMap.put(distance, newSensorList);
1704         }
1705         newLane.sensors.clear();
1706         newLane.sensors.putAll(newSensorMap);
1707 
1708         SortedMap<Double, List<LaneBasedObject>> newLaneBasedObjectMap = new TreeMap<>();
1709         for (double distance : this.laneBasedObjects.keySet())
1710         {
1711             List<LaneBasedObject> newLaneBasedObjectList = new ArrayList<>();
1712             for (LaneBasedObject lbo : this.laneBasedObjects.get(distance))
1713             {
1714                 AbstractLaneBasedObject laneBasedObject = (AbstractLaneBasedObject) lbo;
1715                 LaneBasedObject newLbo = laneBasedObject.clone(newLane, newSimulator, animation);
1716                 newLaneBasedObjectList.add(newLbo);
1717             }
1718             newLaneBasedObjectMap.put(distance, newLaneBasedObjectList);
1719         }
1720         newLane.laneBasedObjects.clear();
1721         newLane.laneBasedObjects.putAll(newLaneBasedObjectMap);
1722 
1723         return newLane;
1724     }
1725 }