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