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  
15  import org.djunits.unit.TimeUnit;
16  import org.opentrafficsim.core.OTS_SCALAR;
17  import org.opentrafficsim.core.geometry.OTSGeometryException;
18  import org.opentrafficsim.core.gtu.GTUType;
19  import org.opentrafficsim.core.gtu.RelativePosition;
20  import org.opentrafficsim.core.network.LateralDirectionality;
21  import org.opentrafficsim.core.network.Link;
22  import org.opentrafficsim.core.network.LongitudinalDirectionality;
23  import org.opentrafficsim.core.network.NetworkException;
24  import org.opentrafficsim.graphs.LaneBasedGTUSampler;
25  import org.opentrafficsim.road.gtu.lane.AbstractLaneBasedGTU;
26  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
27  import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
28  
29  /**
30   * The Lane is the CrossSectionElement of a CrossSectionLink on which GTUs can drive. The Lane stores several important
31   * properties, such as the successor lane(s), predecessor lane(s), and adjacent lane(s), all separated per GTU type. It can, for
32   * instance, be that a truck is not allowed to move into an adjacent lane, while a car is allowed to do so. Furthermore, the
33   * lane contains sensors that can be triggered by passing GTUs. The Lane class also contains methods to determine to trigger the
34   * sensors at exactly calculated and scheduled times, given the movement of the GTUs. <br>
35   * Finally, the Lane stores the GTUs on the lane, and contains several access methods to determine successor and predecessor
36   * 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
37   * from the lane (either at the end, or in the middle when changing onto another lane).
38   * <p>
39   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
40   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
41   * <p>
42   * $LastChangedDate: 2015-09-24 14:17:07 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1407 $, by $Author: averbraeck $,
43   * initial version Aug 19, 2014 <br>
44   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
45   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
46   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
47   */
48  public class Lane extends CrossSectionElement implements Serializable, OTS_SCALAR
49  {
50      /** */
51      private static final long serialVersionUID = 20150826L;
52  
53      /** type of lane to deduce compatibility with GTU types. */
54      private final LaneType laneType;
55  
56      /**
57       * The direction in which vehicles can drive, i.e., in direction of geometry, reverse, or both. This can differ per GTU
58       * type. In an overtake lane, cars might overtake and trucks not
59       */
60      private final Map<GTUType, LongitudinalDirectionality> directionalityMap;
61  
62      /**
63       * the speed limit of this lane. This can differ per GTU type. Cars might be allowed to drive 120 km/h and trucks 90 km/h.
64       * If the speed limit is the same for all GTU types, GTUType.ALL will be used.
65       */
66      private Map<GTUType, Speed.Abs> speedLimitMap;
67  
68      /**
69       * Sensors on the lane to trigger behavior of the GTU, sorted by longitudinal position. The triggering of sensors is done
70       * per GTU type, so different GTUs can trigger different sensors.
71       */
72      private final SortedMap<Double, List<GTUTypeSensor>> sensors = new TreeMap<>();
73  
74      /** GTUs ordered by increasing longitudinal position. */
75      private final List<LaneBasedGTU> gtuList = new ArrayList<LaneBasedGTU>();
76  
77      /**
78       * Adjacent left lanes that some GTU types can change onto. Initially null so we can calculate and cache the first time the
79       * method is called.
80       */
81      private Map<GTUType, Set<Lane>> leftNeighbors = null;
82  
83      /**
84       * Adjacent right lanes that some GTU types can change onto. Initially null so we can calculate and cache the first time the
85       * method is called.
86       */
87      private Map<GTUType, Set<Lane>> rightNeighbors = null;
88  
89      /**
90       * Next lane(s) following this lane that some GTU types can drive onto. Initially null so we can calculate and cache the
91       * first time the method is called.
92       */
93      private Map<GTUType, Set<Lane>> nextLanes = null;
94  
95      /**
96       * Previous lane(s) preceding this lane that some GTU types can drive onto. Initially null so we can calculate and cache the
97       * first time the method is called.
98       */
99      private Map<GTUType, Set<Lane>> prevLanes = null;
100 
101     /** List of graphs that want to sample GTUs on this Lane. */
102     private ArrayList<LaneBasedGTUSampler> samplers = new ArrayList<LaneBasedGTUSampler>();
103 
104     /** the conditions for overtaking another GTU, viewed from this lane. */
105     private final OvertakingConditions overtakingConditions;
106 
107     /**
108      * @param parentLink Cross Section Link to which the element belongs.
109      * @param id the id of this lane within the link; should be unique within the link.
110      * @param lateralOffsetAtStart DoubleScalar.Rel&lt;LengthUnit&gt;; the lateral offset of the design line of the new
111      *            CrossSectionLink with respect to the design line of the parent Link at the start of the parent Link
112      * @param lateralOffsetAtEnd DoubleScalar.Rel&lt;LengthUnit&gt;; the lateral offset of the design line of the new
113      *            CrossSectionLink with respect to the design line of the parent Link at the end of the parent Link
114      * @param beginWidth DoubleScalar.Rel&lt;LengthUnit&gt;; start width, positioned <i>symmetrically around</i> the design line
115      * @param endWidth DoubleScalar.Rel&lt;LengthUnit&gt;; end width, positioned <i>symmetrically around</i> the design line
116      * @param laneType type of lane to deduce compatibility with GTU types
117      * @param directionalityMap in direction of geometry, reverse, or both, specified per GTU Type
118      * @param speedLimitMap speed limit on this lane, specified per GTU Type
119      * @param overtakingConditions the conditions for overtaking another GTU, viewed from this lane
120      * @throws OTSGeometryException when creation of the center line or contour geometry fails
121      * @throws NetworkException when id equal to null or not unique
122      */
123     @SuppressWarnings("checkstyle:parameternumber")
124     public Lane(final CrossSectionLink parentLink, final String id, final Length.Rel lateralOffsetAtStart,
125         final Length.Rel lateralOffsetAtEnd, final Length.Rel beginWidth, final Length.Rel endWidth,
126         final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
127         final Map<GTUType, Speed.Abs> speedLimitMap, final OvertakingConditions overtakingConditions)
128         throws OTSGeometryException, NetworkException
129     {
130         super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
131         this.laneType = laneType;
132         this.directionalityMap = directionalityMap;
133         this.speedLimitMap = speedLimitMap;
134         this.overtakingConditions = overtakingConditions;
135     }
136 
137     /**
138      * @param parentLink Cross Section Link to which the element belongs.
139      * @param id the id of this lane within the link; should be unique within the link.
140      * @param lateralOffsetAtStart DoubleScalar.Rel&lt;LengthUnit&gt;; the lateral offset of the design line of the new
141      *            CrossSectionLink with respect to the design line of the parent Link at the start of the parent Link
142      * @param lateralOffsetAtEnd DoubleScalar.Rel&lt;LengthUnit&gt;; the lateral offset of the design line of the new
143      *            CrossSectionLink with respect to the design line of the parent Link at the end of the parent Link
144      * @param beginWidth DoubleScalar.Rel&lt;LengthUnit&gt;; start width, positioned <i>symmetrically around</i> the design line
145      * @param endWidth DoubleScalar.Rel&lt;LengthUnit&gt;; end width, positioned <i>symmetrically around</i> the design line
146      * @param laneType type of lane to deduce compatibility with GTU types
147      * @param directionality in direction of geometry, reverse, or both
148      * @param speedLimit speed limit on this lane
149      * @param overtakingConditions the conditions for overtaking another GTU, viewed from this lane
150      * @throws OTSGeometryException when creation of the center line or contour geometry fails
151      * @throws NetworkException when id equal to null or not unique
152      */
153     @SuppressWarnings("checkstyle:parameternumber")
154     public Lane(final CrossSectionLink parentLink, final String id, final Length.Rel lateralOffsetAtStart,
155         final Length.Rel lateralOffsetAtEnd, final Length.Rel beginWidth, final Length.Rel endWidth,
156         final LaneType laneType, final LongitudinalDirectionality directionality, final Speed.Abs speedLimit,
157         final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
158     {
159         super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
160         this.laneType = laneType;
161         this.directionalityMap = new LinkedHashMap<>(1);
162         this.directionalityMap.put(GTUType.ALL, directionality);
163         this.speedLimitMap = new LinkedHashMap<>();
164         this.speedLimitMap.put(GTUType.ALL, speedLimit);
165         this.overtakingConditions = overtakingConditions;
166     }
167 
168     /**
169      * Retrieve one of the sets of neighboring Lanes that is accessible for the given type of GTU. A defensive copy of the
170      * internal data structure is returned.
171      * @param direction LateralDirectionality; either LEFT or RIGHT, relative to the DESIGN LINE of the link
172      * @param gtuType the GTU type to check the accessibility for
173      * @return Set&lt;Lane&gt;; the indicated set of neighboring Lanes
174      */
175     private Set<Lane> neighbors(final LateralDirectionality direction, final GTUType gtuType)
176     {
177         if (this.leftNeighbors == null || this.rightNeighbors == null)
178         {
179             this.leftNeighbors = new LinkedHashMap<>(1);
180             this.rightNeighbors = new LinkedHashMap<>(1);
181         }
182 
183         if (!this.leftNeighbors.containsKey(gtuType) || !this.rightNeighbors.containsKey(gtuType))
184         {
185             Set<Lane> leftSet = new LinkedHashSet<>(1);
186             Set<Lane> rightSet = new LinkedHashSet<>(1);
187             this.leftNeighbors.put(gtuType, leftSet);
188             this.rightNeighbors.put(gtuType, rightSet);
189             for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
190             {
191                 if (cse instanceof Lane && !cse.equals(this))
192                 {
193                     Lane lane = (Lane) cse;
194                     if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.LEFT, gtuType))
195                     {
196                         leftSet.add(lane);
197                     }
198                     if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.RIGHT, gtuType))
199                     {
200                         rightSet.add(lane);
201                     }
202                 }
203             }
204         }
205 
206         Set<Lane> lanes = new LinkedHashSet<>();
207         if (direction == LateralDirectionality.LEFT)
208         {
209             lanes.addAll(this.leftNeighbors.get(gtuType));
210         }
211         else
212         {
213             lanes.addAll(this.rightNeighbors.get(gtuType));
214         }
215         return lanes;
216     }
217 
218     /** Lateral alignment margin for longitudinally connected Lanes. */
219     static final Length.Rel ADJACENT_MARGIN = new Length.Rel(0.2, METER);
220 
221     /**
222      * Determine whether another lane is adjacent to this lane (dependent on distance) and accessible (dependent on stripes) for
223      * a certain GTU type (dependent on usability of the adjacent lane for that GTU type). This method assumes that when there
224      * is NO stripe between two adjacent lanes that are accessible for the GTU type, the GTU can enter that lane. <br>
225      * @param lane the other lane to evaluate
226      * @param direction the direction to look at, relative to the DESIGN LINE of the link. This is a very important aspect to
227      *            note: all information is stored relative to the direction of the design line, and not in a driving direction,
228      *            which can vary for lanes that can be driven in two directions (e.g. at overtaking).
229      * @param gtuType the GTU type to check the accessibility for
230      * @return whether another lane is adjacent to this lane and accessible for the given GTU type
231      */
232     private boolean laterallyAdjacentAndAccessible(final Lane lane, final LateralDirectionality direction,
233         final GTUType gtuType)
234     {
235         if (!lane.getLaneType().isCompatible(gtuType) || gtuType.equals(GTUType.ALL) || gtuType.equals(GTUType.NONE))
236         {
237             // not accessible for the given GTU type
238             return false;
239         }
240 
241         if (direction.equals(LateralDirectionality.LEFT))
242         {
243             if (Math.abs((this.designLineOffsetAtBegin.getSI() + this.beginWidth.getSI() / 2.0)
244                 - (lane.designLineOffsetAtBegin.getSI() - lane.beginWidth.getSI() / 2.0)) < ADJACENT_MARGIN.getSI()
245                 && Math.abs((this.designLineOffsetAtEnd.getSI() + this.endWidth.getSI() / 2.0)
246                     - (lane.designLineOffsetAtEnd.getSI() - lane.endWidth.getSI() / 2.0)) < ADJACENT_MARGIN.getSI())
247             {
248                 // look at stripes between the two lanes
249                 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
250                 {
251                     if (cse instanceof Stripe)
252                     {
253                         Stripe stripe = (Stripe) cse;
254                         if (Math.abs((this.designLineOffsetAtBegin.getSI() + this.beginWidth.getSI() / 2.0)
255                             - stripe.designLineOffsetAtBegin.getSI()) < ADJACENT_MARGIN.getSI()
256                             && Math.abs((this.designLineOffsetAtEnd.getSI() + this.endWidth.getSI() / 2.0)
257                                 - stripe.designLineOffsetAtEnd.getSI()) < ADJACENT_MARGIN.getSI())
258                         {
259                             if (!stripe.isPermeable(gtuType, LateralDirectionality.LEFT))
260                             {
261                                 // there is a stripe forbidding to cross to the adjacent lane
262                                 return false;
263                             }
264                         }
265                     }
266                 }
267                 // the lanes are adjacent, and there is no stripe forbidding us to enter that lane
268                 // or there is no stripe at all
269                 return true;
270             }
271         }
272 
273         else
274         // direction.equals(LateralDirectionality.RIGHT)
275         {
276             if (Math.abs((this.designLineOffsetAtBegin.getSI() - this.beginWidth.getSI() / 2.0)
277                 - (lane.designLineOffsetAtBegin.getSI() + lane.beginWidth.getSI() / 2.0)) < ADJACENT_MARGIN.getSI()
278                 && Math.abs((this.designLineOffsetAtEnd.getSI() - this.endWidth.getSI() / 2.0)
279                     - (lane.designLineOffsetAtEnd.getSI() + lane.endWidth.getSI() / 2.0)) < ADJACENT_MARGIN.getSI())
280             {
281                 // look at stripes between the two lanes
282                 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
283                 {
284                     if (cse instanceof Stripe)
285                     {
286                         Stripe stripe = (Stripe) cse;
287                         if (Math.abs((this.designLineOffsetAtBegin.getSI() - this.beginWidth.getSI() / 2.0)
288                             - stripe.designLineOffsetAtBegin.getSI()) < ADJACENT_MARGIN.getSI()
289                             && Math.abs((this.designLineOffsetAtEnd.getSI() - this.endWidth.getSI() / 2.0)
290                                 - stripe.designLineOffsetAtEnd.getSI()) < ADJACENT_MARGIN.getSI())
291                         {
292                             if (!stripe.isPermeable(gtuType, LateralDirectionality.RIGHT))
293                             {
294                                 // there is a stripe forbidding to cross to the adjacent lane
295                                 return false;
296                             }
297                         }
298                     }
299                 }
300                 // the lanes are adjacent, and there is no stripe forbidding us to enter that lane
301                 // or there is no stripe at all
302                 return true;
303             }
304         }
305 
306         // no lanes were found that are close enough laterally.
307         return false;
308     }
309 
310     /**
311      * Insert the sensor at the right place in the sensor list of this lane.
312      * @param sensor the sensor to add
313      * @param gtuType the GTU type that triggers this sensor; use GTUType.ALL is all GTUs trigger it
314      * @throws NetworkException when the position of the sensor is beyond (or before) the range of this Lane
315      */
316     public final void addSensor(final Sensor sensor, final GTUType gtuType) throws NetworkException
317     {
318         double position = sensor.getLongitudinalPositionSI();
319         if (position < 0 || position > getLength().getSI())
320         {
321             throw new NetworkException("Illegal position for sensor " + position + " valid range is 0.."
322                 + getLength().getSI());
323         }
324         List<GTUTypeSensor> sensorList = this.sensors.get(position);
325         if (null == sensorList)
326         {
327             sensorList = new ArrayList<GTUTypeSensor>(1);
328             this.sensors.put(position, sensorList);
329         }
330         sensorList.add(new GTUTypeSensor(gtuType, sensor));
331     }
332 
333     /**
334      * Remove a sensor from the sensor list of this lane.
335      * @param sensor the sensor to remove.
336      * @throws NetworkException when the sensor was not found on this Lane
337      */
338     public final void removeSensor(final Sensor sensor) throws NetworkException
339     {
340         List<GTUTypeSensor> sensorList = this.sensors.get(sensor.getLongitudinalPosition().getSI());
341         if (null == sensorList)
342         {
343             throw new NetworkException("No sensor at " + sensor.getLongitudinalPositionSI());
344         }
345         List<GTUTypeSensor> sensorList2 = new ArrayList<GTUTypeSensor>(1);
346         for (GTUTypeSensor gs : sensorList)
347         {
348             if (!gs.getSensor().equals(sensor))
349             {
350                 sensorList2.add(gs);
351             }
352         }
353         if (sensorList2.size() == 0)
354         {
355             this.sensors.remove(sensor.getLongitudinalPosition().doubleValue());
356         }
357         else
358         {
359             this.sensors.put(sensor.getLongitudinalPosition().doubleValue(), sensorList2);
360         }
361     }
362 
363     /**
364      * Retrieve the list of Sensors of this Lane in the specified distance range for the given GTUType. The sensors that are
365      * triggered by GTUTypes.ALL are added as well. The resulting list is a defensive copy.
366      * @param minimumPosition DoubleScalar.Rel&lt;LengthUnit&gt;; the minimum distance on the Lane (exclusive)
367      * @param maximumPosition DoubleScalar.Rel&lt;LengthUnit&gt;; the maximum distance on the Lane (inclusive)
368      * @param gtuType the GTU type to provide the sensors for
369      * @return List&lt;Sensor&gt;; list of the sensor in the specified range
370      */
371     public final List<Sensor> getSensors(final Length.Rel minimumPosition, final Length.Rel maximumPosition,
372         final GTUType gtuType)
373     {
374         List<Sensor> sensorList = new ArrayList<>(1);
375         for (List<GTUTypeSensor> gtsl : this.sensors.values())
376         {
377             for (GTUTypeSensor gs : gtsl)
378             {
379                 if ((gs.getGtuType().equals(gtuType) || gs.getGtuType().equals(GTUType.ALL))
380                     && gs.getSensor().getLongitudinalPosition().gt(minimumPosition)
381                     && gs.getSensor().getLongitudinalPosition().le(maximumPosition))
382                 {
383                     sensorList.add(gs.getSensor());
384                 }
385             }
386         }
387         return sensorList;
388     }
389 
390     /**
391      * Retrieve the list of Sensors of this Lane that are triggered by the given GTUType. The sensors that are triggered by
392      * GTUTypes.ALL are added as well. The resulting list is a defensive copy.
393      * @param gtuType the GTU type to provide the sensors for
394      * @return List&lt;Sensor&gt;; list of the sensors, in ascending order for the location on the Lane
395      */
396     public final List<Sensor> getSensors(final GTUType gtuType)
397     {
398         List<Sensor> sensorList = new ArrayList<>(1);
399         for (List<GTUTypeSensor> gtsl : this.sensors.values())
400         {
401             for (GTUTypeSensor gs : gtsl)
402             {
403                 if ((gs.getGtuType().equals(gtuType) || gs.getGtuType().equals(GTUType.ALL)))
404                 {
405                     sensorList.add(gs.getSensor());
406                 }
407             }
408         }
409         return sensorList;
410     }
411 
412     /**
413      * Retrieve the list of all Sensors of this Lane. The resulting list is a defensive copy.
414      * @return List&lt;Sensor&gt;; list of the sensors, in ascending order for the location on the Lane
415      */
416     public final List<Sensor> getSensors()
417     {
418         List<Sensor> sensorList = new ArrayList<>(1);
419         for (List<GTUTypeSensor> gtsl : this.sensors.values())
420         {
421             for (GTUTypeSensor gs : gtsl)
422             {
423                 sensorList.add(gs.getSensor());
424             }
425         }
426         return sensorList;
427     }
428 
429     /**
430      * Retrieve the list of Sensors of this Lane for the given GTUType. The sensors that are triggered by GTUTypes.ALL are added
431      * as well. The resulting Map is a defensive copy.
432      * @param gtuType the GTU type to provide the sensors for
433      * @return all sensors on this lane for the given GTUType as a map per distance
434      */
435     public final SortedMap<Double, List<Sensor>> getSensorMap(final GTUType gtuType)
436     {
437         SortedMap<Double, List<Sensor>> sensorMap = new TreeMap<>();
438         for (double d : this.sensors.keySet())
439         {
440             List<Sensor> sensorList = new ArrayList<>(1);
441             for (GTUTypeSensor gs : this.sensors.get(d))
442             {
443                 if (gs.getGtuType().equals(gtuType) || gs.getGtuType().equals(GTUType.ALL))
444                 {
445                     sensorList.add(gs.getSensor());
446                 }
447             }
448             if (sensorList.size() > 0)
449             {
450                 sensorMap.put(d, sensorList);
451             }
452         }
453         return sensorMap;
454     }
455 
456     /**
457      * Trigger the sensors for a certain time step; from now until the nextEvaluationTime of the GTU.
458      * @param gtu the LaneBasedGTU for which to trigger the sensors.
459      * @param referenceStartSI the SI distance of the GTU reference point on the lane at the current time
460      * @param referenceMoveSI the SI distance traveled in the next time step.
461      * @throws NetworkException when GTU not on this lane.
462      * @throws SimRuntimeException when method cannot be scheduled.
463      */
464     public final void scheduleTriggers(final LaneBasedGTU gtu, final double referenceStartSI,
465         final double referenceMoveSI) throws NetworkException, SimRuntimeException
466     {
467         for (List<Sensor> sensorList : getSensorMap(gtu.getGTUType()).values())
468         {
469             for (Sensor sensor : sensorList)
470             {
471                 for (RelativePosition relativePosition : gtu.getRelativePositions().values())
472                 {
473                     if (sensor.getPositionType().equals(relativePosition.getType())
474                         && referenceStartSI + relativePosition.getDx().getSI() <= sensor.getLongitudinalPositionSI()
475                         && referenceStartSI + referenceMoveSI + relativePosition.getDx().getSI() > sensor
476                             .getLongitudinalPositionSI())
477                     {
478                         // the exact time of triggering is based on the distance between the current position of the
479                         // relative position on the GTU and the location of the sensor.
480                         double d =
481                             sensor.getLongitudinalPositionSI() - referenceStartSI - relativePosition.getDx().getSI();
482                         if (d < 0)
483                         {
484                             throw new NetworkException("scheduleTriggers for gtu: " + gtu + ", d<0 d=" + d);
485                         }
486 
487                         Time.Abs triggerTime = gtu.timeAtDistance(new Length.Rel(d, METER));
488                         if (triggerTime.gt(gtu.getNextEvaluationTime()))
489                         {
490                             System.err.println("Time=" + gtu.getSimulator().getSimulatorTime().getTime().getSI()
491                                 + " - Scheduling trigger at " + triggerTime.getSI() + "s. > "
492                                 + gtu.getNextEvaluationTime().getSI() + "s. (nextEvalTime) for sensor " + sensor
493                                 + " , gtu " + gtu);
494                             System.err.println("  v=" + gtu.getVelocity() + ", a=" + gtu.getAcceleration() + ", lane="
495                                 + toString() + ", refStartSI=" + referenceStartSI + ", moveSI=" + referenceMoveSI);
496                             triggerTime =
497                                 new Time.Abs(gtu.getNextEvaluationTime().getSI()
498                                     - Math.ulp(gtu.getNextEvaluationTime().getSI()), TimeUnit.SI);
499                             // gtu.timeAtDistance(new Length.Rel(-d, METER));
500                             // System.exit(-1);
501                         }
502                         // System.out.println("Time=" + gtu.getSimulator().getSimulatorTime().toString()
503                         // + " - Scheduling trigger at " + triggerTime + " for sensor " + sensor + " , gtu " + gtu);
504                         gtu.getSimulator().scheduleEventAbs(triggerTime, this, sensor, "trigger", new Object[]{gtu});
505                     }
506                 }
507             }
508         }
509     }
510 
511     /**
512      * Transform a fraction on the lane to a relative length (can be less than zero or larger than the lane length).
513      * @param fraction fraction relative to the lane length.
514      * @return relative length corresponding to the fraction.
515      */
516     public final Length.Rel position(final double fraction)
517     {
518         return new Length.Rel(this.getLength().getInUnit() * fraction, this.getLength().getUnit());
519     }
520 
521     /**
522      * Transform a fraction on the lane to a relative length in SI units (can be less than zero or larger than the lane length).
523      * @param fraction fraction relative to the lane length.
524      * @return relative length corresponding to the fraction, in SI units.
525      */
526     public final double positionSI(final double fraction)
527     {
528         return this.getLength().getSI() * fraction;
529     }
530 
531     /**
532      * Transform a position on the lane (can be less than zero or larger than the lane length) to a fraction.
533      * @param position relative length on the lane (may be less than zero or larger than the lane length).
534      * @return fraction fraction relative to the lane length.
535      */
536     public final double fraction(final Length.Rel position)
537     {
538         return position.getSI() / this.getLength().getSI();
539     }
540 
541     /**
542      * Transform a position on the lane in SI units (can be less than zero or larger than the lane length) to a fraction.
543      * @param positionSI relative length on the lane in SI units (may be less than zero or larger than the lane length).
544      * @return fraction fraction relative to the lane length.
545      */
546     public final double fractionSI(final double positionSI)
547     {
548         return positionSI / this.getLength().getSI();
549     }
550 
551     /**
552      * Add a LaneBasedGTU to the list of this Lane.
553      * @param gtu LaneBasedGTU; the GTU to add
554      * @param fractionalPosition double; the fractional position that the newly added GTU will have on this Lane
555      * @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
556      *         a lane change operation)
557      * @throws NetworkException when the fractionalPosition is outside the range 0..1, or the GTU is already registered on this
558      *             Lane
559      */
560     public final int addGTU(final LaneBasedGTU gtu, final double fractionalPosition) throws NetworkException
561     {
562         // figure out the rank for the new GTU
563         int index;
564         for (index = 0; index < this.gtuList.size(); index++)
565         {
566             LaneBasedGTU otherGTU = this.gtuList.get(index);
567             if (gtu == otherGTU)
568             {
569                 throw new NetworkException("GTU " + gtu + " already registered on Lane " + this
570                     + " [registered lanes: " + gtu.positions(gtu.getFront()).keySet() + "] locations: "
571                     + gtu.positions(gtu.getFront()).values() + " time: "
572                     + gtu.getSimulator().getSimulatorTime().getTime());
573             }
574             if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
575             {
576                 break;
577             }
578         }
579         this.gtuList.add(index, gtu);
580         return index;
581     }
582 
583     /**
584      * Add a LaneBasedGTU to the list of this Lane.
585      * @param gtu LaneBasedGTU; the GTU to add
586      * @param longitudinalPosition DoubleScalar.Rel&lt;LengthUnit&gt;; the longitudinal position that the newly added GTU will
587      *            have on this Lane
588      * @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
589      *         a lane change operation)
590      * @throws NetworkException when longitudinalPosition is negative or exceeds the length of this Lane
591      */
592     public final int addGTU(final LaneBasedGTU gtu, final Length.Rel longitudinalPosition) throws NetworkException
593     {
594         return addGTU(gtu, longitudinalPosition.getSI() / getLength().getSI());
595     }
596 
597     /**
598      * Remove a GTU from the GTU list of this lane.
599      * @param gtu the GTU to remove.
600      */
601     public final void removeGTU(final LaneBasedGTU gtu)
602     {
603         this.gtuList.remove(gtu);
604     }
605 
606     /**
607      * @param position the front position after which the relative position of a GTU will be searched.
608      * @param relativePosition the relative position of the GTU we are looking for.
609      * @param when the time for which to evaluate the positions.
610      * @return the first GTU after a position on this lane, or null if no GTU could be found.
611      * @throws NetworkException when there is a problem with the position of the GTUs on the lane.
612      */
613     public final LaneBasedGTU getGtuAfter(final Length.Rel position, final RelativePosition.TYPE relativePosition,
614         final Time.Abs when) throws NetworkException
615     {
616         for (LaneBasedGTU gtu : this.gtuList)
617         {
618             if (relativePosition.equals(RelativePosition.FRONT))
619             {
620                 if (gtu.position(this, gtu.getFront(), when).gt(position))
621                 {
622                     return gtu;
623                 }
624             }
625             else if (relativePosition.equals(RelativePosition.REAR))
626             {
627                 if (gtu.position(this, gtu.getRear(), when).ge(position))// PK was >; not >=
628                 {
629                     return gtu;
630                 }
631             }
632             else
633             {
634                 throw new NetworkException("Can only use Lane.getGtuAfter(...) method with FRONT and REAR positions");
635             }
636         }
637         return null;
638     }
639 
640     /**
641      * @param position the front position before which the relative position of a GTU will be searched.
642      * @param relativePosition the relative position of the GTU we are looking for.
643      * @param when the time for which to evaluate the positions.
644      * @return the first GTU before a position on this lane, or null if no GTU could be found.
645      * @throws NetworkException when there is a problem with the position of the GTUs on the lane.
646      */
647     public final LaneBasedGTU getGtuBefore(final Length.Rel position, final RelativePosition.TYPE relativePosition,
648         final Time.Abs when) throws NetworkException
649     {
650         for (int i = this.gtuList.size() - 1; i >= 0; i--)
651         {
652             LaneBasedGTU gtu = this.gtuList.get(i);
653             if (relativePosition.equals(RelativePosition.FRONT))
654             {
655                 if (gtu.position(this, gtu.getFront(), when).getSI() < position.getSI())
656                 {
657                     return gtu;
658                 }
659             }
660             else if (relativePosition.equals(RelativePosition.REAR))
661             {
662                 if (gtu.position(this, gtu.getRear(), when).getSI() < position.getSI())
663                 {
664                     return gtu;
665                 }
666             }
667             else
668             {
669                 throw new NetworkException("Can only use Lane.getGtuBefore(...) method with FRONT and REAR positions");
670             }
671         }
672         return null;
673     }
674 
675     /**
676      * Are two cross section elements laterally well enough aligned to be longitudinally connected?
677      * @param incomingCSE CrossSectionElement; the cross section element where the end position is considered
678      * @param outgoingCSE CrossSectionElement; the cross section element where the begin position is considered
679      * @param margin DoubleScalar.Rel&lt;LengthUnit&gt;; the maximum accepted alignment error
680      * @return boolean; true if the two cross section elements are well enough aligned to be connected
681      */
682     private boolean laterallyCloseEnough(final CrossSectionElement incomingCSE, final CrossSectionElement outgoingCSE,
683         final Length.Rel margin)
684     {
685         return Math.abs(incomingCSE.getDesignLineOffsetAtEnd().getSI() //
686             - outgoingCSE.getDesignLineOffsetAtBegin().getSI()) <= margin.getSI();
687     }
688 
689     /*
690      * TODO only center position? Or also width? What is a good cutoff? Base on average width of the GTU type that can drive on
691      * 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
692      * acceptable.
693      */
694     /** Lateral alignment margin for longitudinally connected Lanes. */
695     static final Length.Rel LATERAL_MARGIN = new Length.Rel(0.5, METER);
696 
697     /**
698      * The next lane(s) are cached, as it is too expensive to make the calculation every time. There are several possibilities:
699      * returning an empty set when the lane stops and there is no longitudinal transfer method to a next lane. Returning a set
700      * with just one lane if the lateral position of the next lane matches the lateral position of this lane (based on an
701      * overlap of the lateral positions of the two joining lanes of more than a certain percentage). Multiple lanes in case the
702      * Node where the underlying Link for this Lane has multiple outgoing Links, and there are multiple lanes that match the
703      * lateral position of this lane.<br>
704      * The next lanes can differ per GTU type. For instance, a lane where cars and buses are allowed can have a next lane where
705      * only buses are allowed, forcing the cars to leave that lane.
706      * @param gtuType the GTU type for which we return the next lanes.
707      * @return set of Lanes following this lane for the given GTU type.
708      */
709     public final Set<Lane> nextLanes(final GTUType gtuType)
710     {
711         if (this.nextLanes == null)
712         {
713             this.nextLanes = new LinkedHashMap<>(1);
714         }
715         if (!this.nextLanes.containsKey(gtuType))
716         {
717             Set<Lane> laneSet = new LinkedHashSet<>(1);
718             this.nextLanes.put(gtuType, laneSet);
719             // Construct (and cache) the result.
720             for (Link link : getParentLink().getEndNode().getLinksOut())
721             {
722                 if (link instanceof CrossSectionLink)
723                 {
724                     for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
725                     {
726                         if (cse instanceof Lane && laterallyCloseEnough(this, cse, LATERAL_MARGIN))
727                         {
728                             laneSet.add((Lane) cse);
729                         }
730                     }
731                 }
732             }
733         }
734         return this.nextLanes.get(gtuType);
735     }
736 
737     /**
738      * The previous lane(s) are cached, as it is too expensive to make the calculation every time. There are several
739      * possibilities: returning an empty set when the lane starts and there is no longitudinal transfer method from a previous
740      * lane. Returning a set with just one lane if the lateral position of the previous lane matches the lateral position of
741      * this lane (based on an overlap of the lateral positions of the two joining lanes of more than a certain percentage).
742      * Multiple lanes in case the Node where the underlying Link for this Lane has multiple incoming Links, and there are
743      * multiple lanes that match the lateral position of this lane. <br>
744      * The previous lanes can differ per GTU type. For instance, a lane where cars and buses are allowed can be preceded by a
745      * lane where only buses are allowed.
746      * @param gtuType the GTU type for which we return the next lanes.
747      * @return set of Lanes following this lane for the given GTU type.
748      */
749     public final Set<Lane> prevLanes(final GTUType gtuType)
750     {
751         if (this.prevLanes == null)
752         {
753             this.prevLanes = new LinkedHashMap<>(1);
754         }
755         if (!this.prevLanes.containsKey(gtuType))
756         {
757             Set<Lane> laneSet = new LinkedHashSet<>(1);
758             this.prevLanes.put(gtuType, laneSet);
759             // Construct (and cache) the result.
760             for (Link link : getParentLink().getStartNode().getLinksIn())
761             {
762                 if (link instanceof CrossSectionLink)
763                 {
764                     for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
765                     {
766                         if (cse instanceof Lane && laterallyCloseEnough(cse, this, LATERAL_MARGIN))
767                         {
768                             laneSet.add((Lane) cse);
769                         }
770                     }
771                 }
772             }
773         }
774         return this.prevLanes.get(gtuType);
775     }
776 
777     /**
778      * 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
779      * if no lane could be found. The method takes the LongitidinalDirectionality of the lane into account. In other words, if
780      * we drive FORWARD and look for a lane on the LEFT, and there is a lane but the Directionality of that lane is not FORWARD
781      * or BOTH, it will not be included.<br>
782      * A lane is called adjacent to another lane if the lateral edges are not more than a delta distance apart. This means that
783      * a lane that <i>overlaps</i> with another lane is <b>not</b> returned as an adjacent lane. <br>
784      * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction. <br>
785      * @param lateralDirection LEFT or RIGHT.
786      * @param gtuType the type of GTU for which to return the adjacent lanes.
787      * @return the set of lanes that are accessible, or null if there is no lane that is accessible with a matching driving
788      *         direction.
789      */
790     public final Set<Lane> accessibleAdjacentLanes(final LateralDirectionality lateralDirection, final GTUType gtuType)
791     {
792         Set<Lane> candidates = new LinkedHashSet<>(1);
793         for (Lane lane : neighbors(lateralDirection, gtuType))
794         {
795             if (lane.getDirectionality(gtuType).equals(LongitudinalDirectionality.BOTH)
796                 || lane.getDirectionality(gtuType).equals(this.getDirectionality(gtuType)))
797             {
798                 candidates.add(lane);
799             }
800         }
801         return candidates;
802     }
803 
804     /**
805      * Register a LaneBasedGTUSampler on this Lane.
806      * @param sampler LaneBasedGTUSampler; the sampler to register
807      */
808     public final void addSampler(final LaneBasedGTUSampler sampler)
809     {
810         this.samplers.add(sampler);
811     }
812 
813     /**
814      * Unregister a LaneBasedGTUSampler from this Lane.
815      * @param sampler LaneBasedGTUSampler; the sampler to unregister
816      */
817     public final void removeSampler(final LaneBasedGTUSampler sampler)
818     {
819         this.samplers.remove(sampler);
820     }
821 
822     /**
823      * Add the movement of a GTU to all graphs that sample this Lane.
824      * @param gtu AbstractLaneBasedGTU; the GTU to sample
825      * @throws NetworkException on network inconsistency
826      */
827     public final void sample(final AbstractLaneBasedGTU gtu) throws NetworkException
828     {
829         // FIXME: Hack; do not sample dummy vehicle at lane drop
830         if (gtu.getNextEvaluationTime().getSI() == Double.MAX_VALUE)
831         {
832             return;
833         }
834         for (LaneBasedGTUSampler sampler : this.samplers)
835         {
836             sampler.addData(gtu, this);
837         }
838     }
839 
840     /**
841      * @param gtuType the GTU type to provide the speed limit for
842      * @return speedLimit.
843      */
844     public final Speed.Abs getSpeedLimit(final GTUType gtuType)
845     {
846         if (this.speedLimitMap.containsKey(gtuType))
847         {
848             return this.speedLimitMap.get(gtuType);
849         }
850         if (this.speedLimitMap.containsKey(GTUType.ALL))
851         {
852             return this.speedLimitMap.get(GTUType.ALL);
853         }
854         return new Speed.Abs(0.0, METER_PER_SECOND); // XXX is this what we want, or should we throw exception?
855     }
856 
857     /**
858      * @param gtuType the GTU type to provide the speed limit for
859      * @param speedLimit the speed limit for this gtu type
860      */
861     public final void setSpeedLimit(final GTUType gtuType, final Speed.Abs speedLimit)
862     {
863         this.speedLimitMap.put(gtuType, speedLimit);
864     }
865 
866     /**
867      * @return laneType.
868      */
869     public final LaneType getLaneType()
870     {
871         return this.laneType;
872     }
873 
874     /**
875      * @param gtuType the GTU type to provide the directionality for
876      * @return directionality.
877      */
878     public final LongitudinalDirectionality getDirectionality(final GTUType gtuType)
879     {
880         if (this.directionalityMap.containsKey(gtuType))
881         {
882             return this.directionalityMap.get(gtuType);
883         }
884         if (this.directionalityMap.containsKey(GTUType.ALL))
885         {
886             return this.directionalityMap.get(GTUType.ALL);
887         }
888         return LongitudinalDirectionality.NONE;
889     }
890 
891     /**
892      * @return gtuList.
893      */
894     public final List<LaneBasedGTU> getGtuList()
895     {
896         return this.gtuList;
897     }
898 
899     /** {@inheritDoc} */
900     @Override
901     @SuppressWarnings("checkstyle:designforextension")
902     protected double getZ()
903     {
904         return 0.0;
905     }
906 
907     /**
908      * @return overtakingConditions
909      */
910     public final OvertakingConditions getOvertakingConditions()
911     {
912         return this.overtakingConditions;
913     }
914 
915     /** {@inheritDoc} */
916     public final String toString()
917     {
918         CrossSectionLink link = getParentLink();
919         return String.format("Lane %s of %s", getId(), link.toString());
920     }
921 
922     /** {@inheritDoc} */
923     @SuppressWarnings("checkstyle:designforextension")
924     @Override
925     public int hashCode()
926     {
927         final int prime = 31;
928         int result = super.hashCode();
929         result = prime * result + ((this.laneType == null) ? 0 : this.laneType.hashCode());
930         return result;
931     }
932 
933     /** {@inheritDoc} */
934     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
935     @Override
936     public boolean equals(final Object obj)
937     {
938         if (this == obj)
939             return true;
940         if (!super.equals(obj))
941             return false;
942         if (getClass() != obj.getClass())
943             return false;
944         Lane other = (Lane) obj;
945         if (this.laneType == null)
946         {
947             if (other.laneType != null)
948                 return false;
949         }
950         else if (!this.laneType.equals(other.laneType))
951             return false;
952         return true;
953     }
954 
955     /**
956      * The combination of GTUType and Sensor in one record.
957      * <p>
958      * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
959      * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
960      * <p>
961      * $LastChangedDate: 2015-09-24 14:17:07 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1407 $, by $Author: averbraeck $,
962      * initial version Aug 28, 2015 <br>
963      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
964      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
965      */
966     private class GTUTypeSensor implements Serializable
967     {
968         /** */
969         private static final long serialVersionUID = 20150828L;
970 
971         /** the GTU type that triggers this sensor; GTUType.ALL if all GTU types trigger the sensor. */
972         private final GTUType gtuType;
973 
974         /** the sensor that is triggers by the gtuType. */
975         private final Sensor sensor;
976 
977         /**
978          * @param gtuType the GTU type that triggers this sensor; GTUType.ALL if all GTU types trigger the sensor
979          * @param sensor the sensor that is triggers by the gtuType
980          */
981         public GTUTypeSensor(final GTUType gtuType, final Sensor sensor)
982         {
983             this.gtuType = gtuType;
984             this.sensor = sensor;
985         }
986 
987         /**
988          * @return gtuType
989          */
990         public final GTUType getGtuType()
991         {
992             return this.gtuType;
993         }
994 
995         /**
996          * @return sensor
997          */
998         public final Sensor getSensor()
999         {
1000             return this.sensor;
1001         }
1002     }
1003 }