View Javadoc
1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.ArrayList;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.unit.DurationUnit;
13  import org.djunits.unit.LengthUnit;
14  import org.djunits.unit.SpeedUnit;
15  import org.djunits.value.vdouble.scalar.Acceleration;
16  import org.djunits.value.vdouble.scalar.Duration;
17  import org.djunits.value.vdouble.scalar.Length;
18  import org.djunits.value.vdouble.scalar.Speed;
19  import org.djunits.value.vdouble.scalar.Time;
20  import org.djutils.draw.point.Point3d;
21  import org.djutils.event.EventProducer;
22  import org.opentrafficsim.core.distributions.Generator;
23  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
24  import org.opentrafficsim.core.geometry.Bounds;
25  import org.opentrafficsim.core.geometry.DirectedPoint;
26  import org.opentrafficsim.core.geometry.OTSGeometryException;
27  import org.opentrafficsim.core.gtu.GTUDirectionality;
28  import org.opentrafficsim.core.gtu.GTUException;
29  import org.opentrafficsim.core.gtu.GTUType;
30  import org.opentrafficsim.core.gtu.RelativePosition;
31  import org.opentrafficsim.core.network.Network;
32  import org.opentrafficsim.core.network.NetworkException;
33  import org.opentrafficsim.core.network.route.Route;
34  import org.opentrafficsim.core.units.distributions.ContinuousDistDoubleScalar;
35  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
36  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
37  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU.LaneBasedIndividualCarBuilder;
38  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
39  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayDistance;
40  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
41  import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
42  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
43  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
44  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
45  import org.opentrafficsim.road.network.OTSRoadNetwork;
46  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
47  import org.opentrafficsim.road.network.lane.Lane;
48  
49  import nl.tudelft.simulation.dsol.SimRuntimeException;
50  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
51  
52  /**
53   * Common code for LaneBasedGTU generators that may have to postpone putting a GTU on the road due to congestion growing into
54   * the generator. <br>
55   * Generally, these generators will discover that there is not enough room AFTER having decided what kind (particular length) of
56   * GTU will be constructed next. When this happens, the generator must remember the properties of the GTU, but postpone actual
57   * generation until there is enough room.
58   * <p>
59   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
60   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
61   * <p>
62   * @version $Revision: 1401 $, $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, by $Author: averbraeck $,
63   *          initial version Feb 2, 2015 <br>
64   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
65   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
66   */
67  public abstract class AbstractGTUGeneratorOld extends EventProducer implements Serializable, GtuGeneratorQueue
68  {
69      /** */
70      private static final long serialVersionUID = 20150202L;
71  
72      /** The generator name. Will be used for generated GTUs as Name:# where # is the id of the GTU when ID is a String. */
73      private final String name;
74  
75      /** The type of GTU to generate. */
76      private final GTUType gtuType;
77  
78      /** The GTU class to instantiate. */
79      private final Class<?> gtuClass;
80  
81      /** Distribution of the initial speed of the GTU. */
82      private final ContinuousDistDoubleScalar.Rel<Speed, SpeedUnit> initialSpeedDist;
83  
84      /** Distribution of the interarrival time. */
85      private final ContinuousDistDoubleScalar.Rel<Duration, DurationUnit> interarrivelTimeDist;
86  
87      /** Generated number of GTUs. */
88      private long generatedGTUs = 0;
89  
90      /** Maximum number of GTUs to generate. */
91      private final long maxGTUs;
92  
93      /** Start time of generation (delayed start). */
94      private final Time startTime;
95  
96      /** End time of generation. */
97      private final Time endTime;
98  
99      /** Lane to generate the GTU on -- at the end for now. */
100     private final Lane lane;
101 
102     /** Position on the lane, relative to the design line of the link. */
103     private final Length position;
104 
105     /** The direction in which the GTU has to be generated; DIR_PLUS or DIR_MINUS. */
106     private final GTUDirectionality direction;
107 
108     /** The lane-based strategical planner factory to use. */
109     private final LaneBasedStrategicalPlannerFactory<? extends LaneBasedStrategicalPlanner> strategicalPlannerFactory;
110 
111     /** Route generator. */
112     private final Generator<Route> routeGenerator;
113 
114     /** The network. */
115     private final OTSRoadNetwork network;
116 
117     /** Car builder list. */
118     private List<LaneBasedIndividualCarBuilder> carBuilderList = new ArrayList<>();
119 
120     /** Number of generated GTUs. */
121     @SuppressWarnings("checkstyle:visibilitymodifier")
122     protected long numberGTUs = 0;
123 
124     /** Bounds for animation. */
125     private final Bounds bounds;
126 
127     /**
128      * @param name String; the name of the generator
129      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator to schedule the start of the generation
130      * @param gtuType GTUType; the type of GTU to generate
131      * @param gtuClass Class&lt;?&gt;; the GTU class to instantiate
132      * @param initialSpeedDist ContinuousDistDoubleScalar.Rel&lt;Speed,SpeedUnit&gt;; distribution of the initial speed of the
133      *            GTU
134      * @param interarrivelTimeDist ContinuousDistDoubleScalar.Rel&lt;Duration,DurationUnit&gt;; distribution of the interarrival
135      *            time
136      * @param maxGTUs long; maximum number of GTUs to generate
137      * @param startTime Time; start time of generation (delayed start)
138      * @param endTime Time; end time of generation
139      * @param lane Lane; the lane to generate the GTU on
140      * @param position Length; position on the lane, relative to the design line of the link
141      * @param direction GTUDirectionality; the direction on the lane in which the GTU has to be generated (DIR_PLUS, or
142      *            DIR_MINUS)
143      * @param strategicalPlannerFactory LaneBasedStrategicalPlannerFactory&lt;? extends LaneBasedStrategicalPlanner&gt;; the
144      *            lane-based strategical planner factory to use
145      * @param routeGenerator Generator&lt;Route&gt;; route generator
146      * @param network OTSRoadNetwork; the network to register the generated GTUs into
147      * @throws SimRuntimeException when simulation scheduling fails
148      */
149     @SuppressWarnings("checkstyle:parameternumber")
150     public AbstractGTUGeneratorOld(final String name, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
151             final GTUType gtuType, final Class<?> gtuClass,
152             final ContinuousDistDoubleScalar.Rel<Speed, SpeedUnit> initialSpeedDist,
153             final ContinuousDistDoubleScalar.Rel<Duration, DurationUnit> interarrivelTimeDist, final long maxGTUs,
154             final Time startTime, final Time endTime, final Lane lane, final Length position, final GTUDirectionality direction,
155             final LaneBasedStrategicalPlannerFactory<? extends LaneBasedStrategicalPlanner> strategicalPlannerFactory,
156             final Generator<Route> routeGenerator, final OTSRoadNetwork network) throws SimRuntimeException
157     {
158         this.name = name;
159         this.gtuType = gtuType;
160         this.gtuClass = gtuClass;
161         this.initialSpeedDist = initialSpeedDist;
162         this.interarrivelTimeDist = interarrivelTimeDist;
163         this.maxGTUs = maxGTUs;
164         this.startTime = startTime;
165         this.endTime = endTime;
166         this.lane = lane;
167         this.position = position;
168         this.direction = direction;
169         this.strategicalPlannerFactory = strategicalPlannerFactory;
170         this.routeGenerator = routeGenerator;
171         this.network = network;
172         DirectedPoint p;
173         p = this.getLocation();
174         this.bounds = new Bounds(new Point3d(p.x - 1, p.y - 1, 0.0), new Point3d(p.x + 1, p.y + 1, 0.0));
175         simulator.scheduleEventAbs(startTime, this, this, "generate", null);
176 
177         // notify the potential animation of the existence of a GTUGenerator
178         fireTimedEvent(Network.GENERATOR_ADD_EVENT, name, simulator.getSimulatorTime());
179         fireTimedEvent(Network.ANIMATION_GENERATOR_ADD_EVENT, this, simulator.getSimulatorTime());
180     }
181 
182     /**
183      * Generate a GTU.
184      * @throws Exception when something in the generation fails.
185      */
186     protected final void generate() throws Exception
187     {
188         // check if we are after the end time
189         if (getSimulator().getSimulatorTime().gt(this.endTime))
190         {
191             return;
192         }
193 
194         // check if we have generated sufficient GTUs
195         if (this.generatedGTUs >= this.maxGTUs)
196         {
197             return;
198         }
199 
200         // create a unique id
201         this.numberGTUs++;
202         String id = this.name + ":" + this.numberGTUs;
203 
204         // create the GTU
205         if (LaneBasedIndividualGTU.class.isAssignableFrom(getGtuClass()))
206         {
207             LaneBasedIndividualCarBuilder carBuilder = new LaneBasedIndividualCarBuilder();
208             carBuilder.setId(id);
209             carBuilder.setGtuType(getGtuType());
210             Length carLength = getLengthDist().draw();
211             carBuilder.setLength(carLength);
212             carBuilder.setFront(carLength.times(0.75));
213             carBuilder.setWidth(getWidthDist().draw());
214             carBuilder.setMaximumSpeed(getMaximumSpeedDist().draw());
215             carBuilder.setInitialSpeed(getInitialSpeedDist().draw());
216             carBuilder.setSimulator(getSimulator());
217             Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
218             initialLongitudinalPositions.add(new DirectedLanePosition(this.lane, this.position, this.direction));
219             carBuilder.setInitialLongitudinalPositions(initialLongitudinalPositions);
220             carBuilder.setNetwork(this.network);
221             carBuilder.setMaximumAcceleration(Acceleration.instantiateSI(3.0));
222             carBuilder.setMaximumDeceleration(Acceleration.instantiateSI(-8.0));
223             this.generatedGTUs++;
224 
225             if (enoughSpace(carBuilder))
226             {
227                 carBuilder.build(this.strategicalPlannerFactory, this.routeGenerator.draw(), null, null);
228             }
229             else
230             {
231                 // put the car in the queue and take it from there -- if the headway is enough, build the car.
232                 this.carBuilderList.add(carBuilder);
233                 // System.out.println("GTUGenerator - backlog = " + this.carBuilderList.size());
234                 if (this.carBuilderList.size() == 1)
235                 {
236                     // first entry in list - start the watch thread
237                     getSimulator().scheduleEventRel(new Duration(0.1, DurationUnit.SECOND), this, this, "checkCarBuilderList",
238                             null);
239                 }
240             }
241         }
242         else
243         {
244             throw new GTUException("GTU class " + getGtuClass().getName() + ": cannot instantiate, no builder.");
245         }
246 
247         // reschedule next arrival
248         Time nextTime = getSimulator().getSimulatorTime().plus(this.interarrivelTimeDist.draw());
249         if (nextTime.le(this.endTime))
250         {
251             getSimulator().scheduleEventAbs(nextTime, this, this, "generate", null);
252         }
253     }
254 
255     /**
256      * Check if the car to be built is not overlapping with another GTU on the same lane, and if it has enough headway to be
257      * generated safely.
258      * @param carBuilder LaneBasedIndividualCarBuilder; the car to be generated
259      * @return true if car can be safely built, false otherwise.
260      * @throws NetworkException when the speed limit of the lane is not known
261      * @throws GTUException if GTU does not have a position on the lane where it is registered
262      */
263     protected final boolean enoughSpace(final LaneBasedIndividualCarBuilder carBuilder) throws NetworkException, GTUException
264     {
265         DirectedLanePosition directedLanePosition = carBuilder.getInitialLongitudinalPositions().iterator().next();
266         Lane generatorLane = directedLanePosition.getLane();
267         double genPosSI = directedLanePosition.getPosition().getSI();
268         // GTUDirectionality direction = directedLanePosition.getGtuDirection();
269         // XXX different from this.direction?
270         double lengthSI = generatorLane.getLength().getSI();
271         double frontNew = (genPosSI + carBuilder.getLength().getSI()) / lengthSI;
272         double rearNew = genPosSI / lengthSI;
273 
274         // test for overlap with other GTUs
275         for (LaneBasedGTU gtu : generatorLane.getGtuList())
276         {
277             double frontGTU = gtu.fractionalPosition(generatorLane, gtu.getFront());
278             double rearGTU = gtu.fractionalPosition(generatorLane, gtu.getRear());
279             if ((frontNew >= rearGTU && frontNew <= frontGTU) || (rearNew >= rearGTU && rearNew <= frontGTU)
280                     || (frontGTU >= rearNew && frontGTU <= frontNew) || (rearGTU >= rearNew && rearGTU <= frontNew))
281             {
282                 // System.out.println(getSimulator().getSimulatorTime() + ", generator overlap with GTU " + gtu);
283                 return false;
284             }
285         }
286 
287         // test for sufficient headway
288         GTUFollowingModelOld followingModel = new IDMPlusOld();
289         // carBuilder.getStrategicalPlanner().getBehavioralCharacteristics().getGTUFollowingModel();
290 
291         Headway headway = headway(new Length(250.0, LengthUnit.METER), generatorLane);
292         Length minimumHeadway = new Length(0.0, LengthUnit.METER);
293         if (headway.getObjectType().isGtu())
294         {
295             minimumHeadway = followingModel.minimumHeadway(carBuilder.getInitialSpeed(), headway.getSpeed(),
296                     new Length(1.0, LengthUnit.CENTIMETER), new Length(250.0, LengthUnit.METER),
297                     generatorLane.getSpeedLimit(carBuilder.getGtuType()), carBuilder.getMaximumSpeed());
298             // WS: changed mininumHeadway to headway.getDistance()
299             double acc = followingModel.computeAcceleration(carBuilder.getInitialSpeed(), carBuilder.getMaximumSpeed(),
300                     headway.getSpeed(), headway.getDistance(), carBuilder.getMaximumSpeed()).getSI();
301             if (acc < 0)
302             {
303                 // System.err.println(getSimulator().getSimulatorTime() + ", generator headway for GTU " + headway.getId()
304                 // + ", distance " + headway.getDistance().si + " m, max " + minimumHeadway + ", has to brake with a="
305                 // + acc + " m/s^2");
306                 return false;
307             }
308         }
309 
310         // System.out.println(getSimulator().getSimulatorTime() + ", generator headway for GTU " + headwayGTU.getOtherGTU()
311         // + ", distance " + headwayGTU.getDistance().si + " m, max " + minimumHeadway);
312         return headway.getDistance().ge(minimumHeadway);
313     }
314 
315     /**
316      * Calculate the minimum headway, possibly on subsequent lanes, in DIR_PLUS direction.
317      * @param theLane Lane; the lane where we are looking right now
318      * @param lanePositionSI double; from which position on this lane do we start measuring? This is the current position of the
319      *            GTU when we measure in the lane where the original GTU is positioned, and 0.0 for each subsequent lane
320      * @param cumDistanceSI double; the distance we have already covered searching on previous lanes
321      * @param maxDistanceSI the maximum distance to look for in SI units; stays the same in subsequent calls
322      * @param when Time; the current or future time for which to calculate the headway
323      * @return the headway in SI units when we have found the GTU, or a null GTU with a distance of Double.MAX_VALUE meters when
324      *         no other GTU could not be found within maxDistanceSI meters
325      * @throws GTUException when there is a problem with the geometry of the network
326      */
327     private Headway headwayRecursiveForwardSI(final Lane theLane, final double lanePositionSI, final double cumDistanceSI,
328             final double maxDistanceSI, final Time when) throws GTUException
329     {
330         // TODO: THIS METHOD IS ALSO IN PERCEPTION -- DON'T DUPLICATE; ALSO, THIS VERSION IS WRONG.
331         LaneBasedGTU otherGTU = theLane.getGtuAhead(new Length(lanePositionSI, LengthUnit.METER), GTUDirectionality.DIR_PLUS,
332                 RelativePosition.REAR, when);
333         if (otherGTU != null)
334         {
335             double distanceM = cumDistanceSI + otherGTU.position(theLane, otherGTU.getRear(), when).getSI() - lanePositionSI;
336             if (distanceM > 0 && distanceM <= maxDistanceSI)
337             {
338                 return new HeadwayGTUSimple(otherGTU.getId(), otherGTU.getGTUType(), new Length(distanceM, LengthUnit.SI),
339                         otherGTU.getLength(), otherGTU.getWidth(), otherGTU.getSpeed(), otherGTU.getAcceleration(), null);
340             }
341             return new HeadwayDistance(Double.MAX_VALUE);
342         }
343 
344         // Continue search on successor lanes.
345         if (cumDistanceSI + theLane.getLength().getSI() - lanePositionSI < maxDistanceSI)
346         {
347             // is there a successor link?
348             if (theLane.nextLanes(this.gtuType).size() > 0)
349             {
350                 Headway foundMaxGTUDistanceSI = new HeadwayDistance(Double.MAX_VALUE);
351                 for (Lane nextLane : theLane.nextLanes(this.gtuType).keySet())
352                 {
353                     // TODO Only follow links on the Route if there is a "real" Route
354                     // if (routeNavigator.getRoute() == null || routeNavigator.getRoute().size() == 0 /* XXXXX STUB dummy route
355                     // */
356                     // || routeNavigator.getRoute().containsLink((Link) theLane.getParentLink()))
357                     {
358                         double traveledDistanceSI = cumDistanceSI + theLane.getLength().getSI() - lanePositionSI;
359                         Headway closest = headwayRecursiveForwardSI(nextLane, 0.0, traveledDistanceSI, maxDistanceSI, when);
360                         if (closest.getDistance().si < maxDistanceSI
361                                 && closest.getDistance().si < foundMaxGTUDistanceSI.getDistance().si)
362                         {
363                             foundMaxGTUDistanceSI = closest;
364                         }
365                     }
366                 }
367                 return foundMaxGTUDistanceSI;
368             }
369         }
370 
371         // No other GTU was not on one of the current lanes or their successors.
372         return new HeadwayDistance(Double.MAX_VALUE);
373     }
374 
375     /**
376      * Calculate the minimum headway, possibly on subsequent lanes, in DIR_MINUS direction.
377      * @param theLane Lane; the lane where we are looking right now
378      * @param lanePositionSI double; from which position on this lane do we start measuring? This is the current position of the
379      *            GTU when we measure in the lane where the original GTU is positioned, and 0.0 for each subsequent lane
380      * @param cumDistanceSI double; the distance we have already covered searching on previous lanes
381      * @param maxDistanceSI the maximum distance to look for in SI units; stays the same in subsequent calls
382      * @param when Time; the current or future time for which to calculate the headway
383      * @return the headway in SI units when we have found the GTU, or a null GTU with a distance of Double.MAX_VALUE meters when
384      *         no other GTU could not be found within maxDistanceSI meters
385      * @throws GTUException when there is a problem with the geometry of the network
386      */
387     private Headway headwayRecursiveBackwardSI(final Lane theLane, final double lanePositionSI, final double cumDistanceSI,
388             final double maxDistanceSI, final Time when) throws GTUException
389     {
390         // TODO: THIS METHOD IS ALSO IN PERCEPTION -- DON'T DUPLICATE; ALSO, THIS VERSION IS WRONG.
391         LaneBasedGTU otherGTU = theLane.getGtuBehind(new Length(lanePositionSI, LengthUnit.METER), GTUDirectionality.DIR_PLUS,
392                 RelativePosition.FRONT, when);
393         if (otherGTU != null)
394         {
395             double distanceM = cumDistanceSI + otherGTU.position(theLane, otherGTU.getFront(), when).getSI() - lanePositionSI;
396             if (distanceM > 0 && distanceM <= maxDistanceSI)
397             {
398                 return new HeadwayGTUSimple(otherGTU.getId(), otherGTU.getGTUType(), new Length(distanceM, LengthUnit.SI),
399                         otherGTU.getLength(), otherGTU.getWidth(), otherGTU.getSpeed(), otherGTU.getAcceleration(), null);
400             }
401             return new HeadwayDistance(Double.MAX_VALUE);
402         }
403 
404         // Continue search on all predecessor lanes.
405         if (cumDistanceSI + theLane.getLength().getSI() - lanePositionSI < maxDistanceSI)
406         {
407             // is there a predecessor link?
408             if (theLane.prevLanes(this.gtuType).size() > 0)
409             {
410                 Headway foundMaxGTUDistanceSI = new HeadwayDistance(Double.MAX_VALUE);
411                 for (Lane prevLane : theLane.prevLanes(this.gtuType).keySet())
412                 {
413                     // TODO Only follow links on the Route if there is a "real" Route
414                     // if (routeNavigator.getRoute() == null || routeNavigator.getRoute().size() == 0 /* XXXXX STUB dummy route
415                     // */
416                     // || routeNavigator.getRoute().containsLink((Link) theLane.getParentLink()))
417                     {
418                         double traveledDistanceSI = cumDistanceSI + theLane.getLength().getSI() - lanePositionSI;
419                         Headway closest = headwayRecursiveBackwardSI(prevLane, 0.0, traveledDistanceSI, maxDistanceSI, when);
420                         if (closest.getDistance().si < maxDistanceSI
421                                 && closest.getDistance().si < foundMaxGTUDistanceSI.getDistance().si)
422                         {
423                             foundMaxGTUDistanceSI = closest;
424                         }
425                     }
426                 }
427                 return foundMaxGTUDistanceSI;
428             }
429         }
430 
431         // No other GTU was not on one of the current lanes or their successors.
432         return new HeadwayDistance(Double.MAX_VALUE);
433     }
434 
435     /**
436      * Find the first GTU starting on the specified lane following the specified route.
437      * @param maxDistanceSI double; the maximum distance to look for in SI units
438      * @param generatorLane Lane; the lane on which the the search for a leader starts
439      * @return the nearest GTU and the net headway to this GTU in SI units when we have found the GTU, or a null GTU with a
440      *         distance of Double.MAX_VALUE meters when no other GTU could not be found within maxDistanceSI meters
441      * @throws GTUException when there is a problem with the geometry of the network
442      */
443     private Headway headwayGTUSIForward(final double maxDistanceSI, final Lane generatorLane) throws GTUException
444     {
445         Time when = getSimulator().getSimulatorTime();
446         Headway foundMaxGTUDistanceSI = new HeadwayDistance(Double.MAX_VALUE);
447         // search for the closest GTU on all current lanes we are registered on.
448         Headway closest;
449         if (this.direction.equals(GTUDirectionality.DIR_PLUS))
450         {
451             closest = headwayRecursiveForwardSI(this.lane, 0.0, 0.0, maxDistanceSI, when);
452         }
453         else
454         {
455             closest = headwayRecursiveBackwardSI(this.lane, generatorLane.getLength().getSI(), 0.0, maxDistanceSI, when);
456         }
457         if (closest.getDistance().si < maxDistanceSI && closest.getDistance().si < foundMaxGTUDistanceSI.getDistance().si)
458         {
459             foundMaxGTUDistanceSI = closest;
460         }
461         return foundMaxGTUDistanceSI;
462     }
463 
464     /**
465      * Check the available headway for GTU that is about to be constructed.
466      * @param maxDistance Length; the maximum distance to look for a leader
467      * @param generatorLane Lane; the lane on which the GTU is generated
468      * @return HeadwayGTU; the available headway and the GTU at that headway
469      * @throws GTUException on network inconsistency
470      */
471     public final Headway headway(final Length maxDistance, final Lane generatorLane) throws GTUException
472     {
473         return headwayGTUSIForward(maxDistance.getSI(), generatorLane);
474     }
475 
476     /**
477      * Check if car can be generated.
478      * @throws Exception on any problem
479      */
480     protected final void checkCarBuilderList() throws Exception
481     {
482         if (!this.carBuilderList.isEmpty())
483         {
484             LaneBasedIndividualCarBuilder carBuilder = this.carBuilderList.get(0);
485             if (enoughSpace(carBuilder))
486             {
487                 this.carBuilderList.remove(0);
488                 carBuilder.build(this.strategicalPlannerFactory, this.routeGenerator.draw(), null, null);
489             }
490         }
491 
492         // only reschedule if list not empty
493         if (!this.carBuilderList.isEmpty())
494         {
495             getSimulator().scheduleEventRel(new Duration(0.1, DurationUnit.SECOND), this, this, "checkCarBuilderList", null);
496         }
497     }
498 
499     /** @return simulator. */
500     public abstract OTSSimulatorInterface getSimulator();
501 
502     /** @return lengthDist. */
503     public abstract ContinuousDistDoubleScalar.Rel<Length, LengthUnit> getLengthDist();
504 
505     /** @return widthDist. */
506     public abstract ContinuousDistDoubleScalar.Rel<Length, LengthUnit> getWidthDist();
507 
508     /** @return maximumSpeedDist. */
509     public abstract ContinuousDistDoubleScalar.Rel<Speed, SpeedUnit> getMaximumSpeedDist();
510 
511     /**
512      * @return name.
513      */
514     public final String getName()
515     {
516         return this.name;
517     }
518 
519     /**
520      * @return gtuType.
521      */
522     public final GTUType getGtuType()
523     {
524         return this.gtuType;
525     }
526 
527     /**
528      * @return gtuClass.
529      */
530     public final Class<?> getGtuClass()
531     {
532         return this.gtuClass;
533     }
534 
535     /**
536      * @return initialSpeedDist.
537      */
538     public final ContinuousDistDoubleScalar.Rel<Speed, SpeedUnit> getInitialSpeedDist()
539     {
540         return this.initialSpeedDist;
541     }
542 
543     /**
544      * @return interarrivelTimeDist.
545      */
546     public final ContinuousDistDoubleScalar.Rel<Duration, DurationUnit> getInterarrivelTimeDist()
547     {
548         return this.interarrivelTimeDist;
549     }
550 
551     /**
552      * @return maxGTUs.
553      */
554     public final long getMaxGTUs()
555     {
556         return this.maxGTUs;
557     }
558 
559     /**
560      * @return startTime.
561      */
562     public final Time getStartTime()
563     {
564         return this.startTime;
565     }
566 
567     /**
568      * @return endTime.
569      */
570     public final Time getEndTime()
571     {
572         return this.endTime;
573     }
574 
575     /**
576      * @return strategicalPlanner
577      */
578     public final LaneBasedStrategicalPlannerFactory<? extends LaneBasedStrategicalPlanner> getStrategicalPlannerFactory()
579     {
580         return this.strategicalPlannerFactory;
581     }
582 
583     /** {@inheritDoc} */
584     @Override
585     public DirectedPoint getLocation()
586     {
587         try
588         {
589             return this.lane.getCenterLine().getLocation(this.position);
590         }
591         catch (OTSGeometryException exception)
592         {
593             return this.lane.getLocation();
594         }
595     }
596 
597     /** {@inheritDoc} */
598     @Override
599     public Bounds getBounds() throws RemoteException
600     {
601         return this.bounds;
602     }
603 
604     /** {@inheritDoc} */
605     @Override
606     public Map<DirectedPoint, Integer> getQueueLengths()
607     {
608         Map<DirectedPoint, Integer> map = new LinkedHashMap<>();
609         map.put(getLocation(), this.carBuilderList.size());
610         return map;
611     }
612 
613 }