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