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