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