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