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