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