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