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