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