View Javadoc
1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.Collections;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.LinkedList;
9   import java.util.Map;
10  import java.util.Queue;
11  import java.util.Set;
12  import java.util.SortedSet;
13  import java.util.TreeSet;
14  
15  import javax.media.j3d.Bounds;
16  import javax.naming.NamingException;
17  
18  import org.djunits.unit.DurationUnit;
19  import org.djunits.value.vdouble.scalar.Duration;
20  import org.djunits.value.vdouble.scalar.Length;
21  import org.djunits.value.vdouble.scalar.Speed;
22  import org.djunits.value.vdouble.scalar.Time;
23  import org.opentrafficsim.base.Identifiable;
24  import org.opentrafficsim.base.TimeStampedObject;
25  import org.opentrafficsim.base.parameters.ParameterException;
26  import org.opentrafficsim.core.distributions.Generator;
27  import org.opentrafficsim.core.distributions.ProbabilityException;
28  import org.opentrafficsim.core.geometry.OTSGeometryException;
29  import org.opentrafficsim.core.gtu.GTUDirectionality;
30  import org.opentrafficsim.core.gtu.GTUException;
31  import org.opentrafficsim.core.gtu.GTUType;
32  import org.opentrafficsim.core.gtu.RelativePosition;
33  import org.opentrafficsim.core.gtu.animation.GTUColorer;
34  import org.opentrafficsim.core.idgenerator.IdGenerator;
35  import org.opentrafficsim.core.network.NetworkException;
36  import org.opentrafficsim.core.network.OTSNetwork;
37  import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
38  import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
39  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
40  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
41  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
42  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
43  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
44  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
45  import org.opentrafficsim.road.network.lane.CrossSectionLink;
46  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
47  import org.opentrafficsim.road.network.lane.Lane;
48  import org.opentrafficsim.road.network.lane.LaneDirection;
49  
50  import nl.tudelft.simulation.dsol.SimRuntimeException;
51  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
52  import nl.tudelft.simulation.language.Throw;
53  import nl.tudelft.simulation.language.d3.DirectedPoint;
54  
55  /**
56   * Lane based GTU generator. This generator generates lane based GTUs using a LaneBasedTemplateGTUType. The template is used to
57   * generate a set of GTU characteristics at the times implied by the headway generator. These sets are queued until there is
58   * sufficient room to construct a GTU at the specified lane locations. The speed of a construction GTU may be reduced to ensure
59   * it does not run into its immediate leader GTU.
60   * <p>
61   * Copyright (c) 2013-2018 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/current/license.html">OpenTrafficSim License</a>.
63   * <p>
64   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Mar 8, 2016 <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 class LaneBasedGTUGenerator implements Serializable, Identifiable, GTUGenerator
69  {
70      /** */
71      private static final long serialVersionUID = 20160000L;
72  
73      /** FIFO for templates that have not been generated yet due to insufficient room/headway, per position, and per link. */
74      private final Map<CrossSectionLink, Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>>> unplacedTemplates =
75              new LinkedHashMap<>();
76  
77      /** Name of the GTU generator. */
78      private final String id;
79  
80      /** Time distribution that determines the interval times between GTUs. */
81      private final Generator<Duration> interarrivelTimeGenerator;
82  
83      /** Generates most properties of the GTUs. */
84      private final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator;
85  
86      /** Total number of GTUs generated so far. */
87      private long generatedGTUs = 0;
88  
89      /** Retry interval for checking if a GTU can be placed. */
90      private Duration reTryInterval = new Duration(0.1, DurationUnit.SI);
91  
92      /** Location and initial direction provider for all generated GTUs. */
93      private final GeneratorPositions generatorPositions;
94  
95      /** Network. */
96      private final OTSNetwork network;
97  
98      /** Simulator. */
99      private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
100 
101     /** The way that this generator checks if it is safe to construct and place the next lane based GTU. */
102     private final RoomChecker roomChecker;
103 
104     /** The GTU colorer that will be linked to each generated GTU. */
105     private final GTUColorer gtuColorer;
106 
107     /** ID generator. */
108     private final IdGenerator idGenerator;
109 
110     /** Initial distance over which lane changes shouldn't be performed. */
111     private Length noLaneChangeDistance = null;
112 
113     /** Vehicle generation is ignored on these lanes. */
114     private Set<LaneDirection> disabled = new LinkedHashSet<>();
115 
116     /**
117      * Construct a new lane base GTU generator.
118      * @param id String; name of the new GTU generator
119      * @param interarrivelTimeGenerator Generator&lt;Duration&gt;; generator for the interval times between GTUs
120      * @param gtuColorer GTUColorer; the GTU colorer that will be used by all generated GTUs
121      * @param laneBasedGTUCharacteristicsGenerator LaneBasedGTUCharacteristicsGenerator; generator of the characteristics of
122      *            each GTU
123      * @param generatorPositions GeneratorPositions; location and initial direction provider for all generated GTUs
124      * @param network OTSNetwork; the OTS network that owns the generated GTUs
125      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
126      * @param roomChecker LaneBasedGTUGenerator.RoomChecker; the way that this generator checks that there is sufficient room to
127      *            place a new GTU
128      * @param idGenerator IdGenerator; id generator
129      * @throws SimRuntimeException when <cite>startTime</cite> lies before the current simulation time
130      * @throws ProbabilityException pe
131      * @throws ParameterException if drawing from the interarrival generator fails
132      */
133     public LaneBasedGTUGenerator(final String id, final Generator<Duration> interarrivelTimeGenerator,
134             final GTUColorer gtuColorer, final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator,
135             final GeneratorPositions generatorPositions, final OTSNetwork network, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
136             final RoomChecker roomChecker, final IdGenerator idGenerator)
137             throws SimRuntimeException, ProbabilityException, ParameterException
138     {
139         this.id = id;
140         this.interarrivelTimeGenerator = interarrivelTimeGenerator;
141         this.laneBasedGTUCharacteristicsGenerator = laneBasedGTUCharacteristicsGenerator;
142         this.generatorPositions = generatorPositions;
143         this.network = network;
144         this.simulator = simulator;
145         this.roomChecker = roomChecker;
146         this.gtuColorer = gtuColorer;
147         this.idGenerator = idGenerator;
148         Duration headway = this.interarrivelTimeGenerator.draw();
149         if (headway != null) // otherwise no demand at all
150         {
151             simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
152         }
153     }
154 
155     /**
156      * Sets the initial distance over which lane changes shouldn't be performed.
157      * @param noLaneChangeDistance Length; initial distance over which lane changes shouldn't be performed
158      */
159     public void setNoLaneChangeDistance(final Length noLaneChangeDistance)
160     {
161         this.noLaneChangeDistance = noLaneChangeDistance;
162     }
163 
164     /**
165      * Generate the characteristics of the next GTU.
166      * @throws ProbabilityException when something is wrongly defined in the LaneBasedTemplateGTUType
167      * @throws SimRuntimeException when this method fails to re-schedule itself or the call to the method that tries to place a
168      *             GTU on the road
169      * @throws ParameterException in case of a parameter problem
170      * @throws GTUException if strategical planner cannot generate a plan
171      */
172     @SuppressWarnings("unused")
173     private void generateCharacteristics() throws ProbabilityException, SimRuntimeException, ParameterException, GTUException
174     {
175         synchronized (this.unplacedTemplates)
176         {
177             this.generatedGTUs++;
178             LaneBasedGTUCharacteristics characteristics = this.laneBasedGTUCharacteristicsGenerator.draw();
179             GTUType gtuType = characteristics.getGTUType();
180             // gather information on number of unplaced templates per lane, and per link, for the drawing of a new position
181             Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
182             for (CrossSectionLink link : this.unplacedTemplates.keySet())
183             {
184                 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
185                 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkTemplates =
186                         this.unplacedTemplates.get(link);
187                 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
188                 {
189                     linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
190                 }
191                 unplaced.put(link, linkMap);
192             }
193             // position draw
194             Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
195                     this.generatorPositions.speedLimit(gtuType), characteristics.getMaximumSpeed());
196             GeneratorLanePosition lanePosition =
197                     this.generatorPositions.draw(gtuType, unplaced, desiredSpeed, characteristics.getRoute());
198             // add template in the right map location
199             if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
200             {
201                 this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
202             }
203             Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkMap =
204                     this.unplacedTemplates.get(lanePosition.getLink());
205             if (!linkMap.containsKey(lanePosition))
206             {
207                 linkMap.put(lanePosition, new LinkedList<>());
208             }
209             Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue = linkMap.get(lanePosition);
210             queue.add(new TimeStampedObject<LaneBasedGTUCharacteristics>(characteristics,
211                     this.simulator.getSimulatorTime()));
212             if (queue.size() == 1)
213             {
214                 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { lanePosition });
215             }
216         }
217         Duration headway = this.interarrivelTimeGenerator.draw();
218         if (headway != null)
219         {
220             this.simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
221         }
222     }
223 
224     /**
225      * Check if the queue is non-empty and, if it is, try to place the GTUs in the queue on the road.
226      * @param position GeneratorLanePosition; position
227      * @throws SimRuntimeException should never happen
228      * @throws GTUException when something wrong in the definition of the GTU
229      * @throws OTSGeometryException when something is wrong in the definition of the GTU
230      * @throws NetworkException when something is wrong with the initial location of the GTU
231      * @throws NamingException ???
232      * @throws ProbabilityException pe
233      */
234     @SuppressWarnings("unused")
235     private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GTUException, NamingException,
236             NetworkException, OTSGeometryException, ProbabilityException
237     {
238         TimeStampedObject<LaneBasedGTUCharacteristics> timedCharacteristics;
239         Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue =
240                 this.unplacedTemplates.get(position.getLink()).get(position);
241         
242         // skip if disabled at this lane-direction
243         Set<LaneDirection> lanes = new LinkedHashSet<>();
244         for (DirectedLanePosition pos : position.getPosition())
245         {
246             lanes.add(pos.getLaneDirection());
247         }
248         if (!Collections.disjoint(this.disabled, lanes))
249         {
250             queue.remove();
251             return;
252         }
253         
254         synchronized (queue)
255         {
256             timedCharacteristics = queue.peek();
257         }
258         if (null == timedCharacteristics)
259         {
260             return; // Do not re-schedule this method
261         }
262 
263         LaneBasedGTUCharacteristics characteristics = timedCharacteristics.getObject();
264         SortedSet<HeadwayGTU> leaders = new TreeSet<>();
265         for (DirectedLanePosition dirPos : position.getPosition())
266         {
267             // TODO subtracting halve the vehicle length as a hack, reference position can be different
268             getFirstLeaders(dirPos.getLaneDirection(),
269                     dirPos.getPosition().neg().minus(characteristics.getLength().divideBy(2.0)), dirPos.getPosition(), leaders);
270         }
271         Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.getTimestamp());
272         Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
273         if (placement.canPlace())
274         {
275             // There is enough room; remove the template from the queue and construct the new GTU
276             synchronized (queue)
277             {
278                 queue.remove();
279             }
280             placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
281             if (queue.size() > 0)
282             {
283                 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { position });
284             }
285         }
286         else if (queue.size() > 0)
287         {
288             this.simulator.scheduleEventRel(this.reTryInterval, this, this, "tryToPlaceGTU", new Object[] { position });
289         }
290     }
291 
292     /**
293      * @param characteristics LaneBasedGTUCharacteristics; characteristics
294      * @param position Set&lt;DirectedLanePosition&gt;; position
295      * @param speed Speed; speed
296      * @throws NamingException on exception
297      * @throws GTUException on exception
298      * @throws NetworkException on exception
299      * @throws SimRuntimeException on exception
300      * @throws OTSGeometryException on exception
301      */
302     final void placeGtu(final LaneBasedGTUCharacteristics characteristics, final Set<DirectedLanePosition> position,
303             final Speed speed) throws NamingException, GTUException, NetworkException, SimRuntimeException, OTSGeometryException
304     {
305         String gtuId = this.idGenerator.nextId();
306         LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU(gtuId, characteristics.getGTUType(),
307                 characteristics.getLength(), characteristics.getWidth(), characteristics.getMaximumSpeed(),
308                 characteristics.getFront(), this.simulator, this.network);
309         gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
310         gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
311         gtu.setVehicleModel(characteristics.getVehicleModel());
312         gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
313         gtu.initWithAnimation(
314                 characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
315                         characteristics.getOrigin(), characteristics.getDestination()),
316                 position, speed, DefaultCarAnimation.class, this.gtuColorer);
317     }
318 
319     /**
320      * Adds the first GTU on the lane to the set, or any number or leaders on downstream lane(s) if there is no GTU on the lane.
321      * @param lane LaneDirection; lane to search on
322      * @param startDistance Length; distance from generator location (nose) to start of the lane
323      * @param beyond Length; location to search downstream of which is the generator position, or the start for downstream lanes
324      * @param set Set&lt;HeadwayGTU&gt;; set to add the GTU's to
325      * @throws GTUException if a GTU is incorrectly positioned on a lane
326      */
327     private void getFirstLeaders(final LaneDirection lane, final Length startDistance, final Length beyond,
328             final Set<HeadwayGTU> set) throws GTUException
329     {
330         LaneBasedGTU next = lane.getLane().getGtuAhead(beyond, lane.getDirection(), RelativePosition.FRONT,
331                 this.simulator.getSimulatorTime());
332         if (next != null)
333         {
334             Length headway;
335             if (lane.getDirection().isPlus())
336             {
337                 headway = startDistance.plus(next.position(lane.getLane(), next.getRear()));
338             }
339             else
340             {
341                 headway = startDistance.plus(lane.getLane().getLength().minus(next.position(lane.getLane(), next.getRear())));
342             }
343             if (headway.si < 300)
344             {
345                 set.add(new HeadwayGTUReal(next, headway, true));
346             }
347             return;
348         }
349         Map<Lane, GTUDirectionality> downstreamLanes = lane.getLane().downstreamLanes(lane.getDirection(), GTUType.VEHICLE);
350         for (Lane downstreamLane : downstreamLanes.keySet())
351         {
352             Length startDistanceDownstream = startDistance.plus(lane.getLane().getLength());
353             if (startDistanceDownstream.si > 300)
354             {
355                 return;
356             }
357             GTUDirectionality dir = downstreamLanes.get(downstreamLane);
358             Length beyondDownstream = dir.isPlus() ? Length.ZERO : downstreamLane.getLength();
359             getFirstLeaders(new LaneDirection(downstreamLane, dir), startDistanceDownstream, beyondDownstream, set);
360         }
361     }
362 
363     /** {@inheritDoc} */
364     @Override
365     public final String toString()
366     {
367         return "LaneBasedGTUGenerator " + this.id + " on " + this.generatorPositions;
368     }
369 
370     /**
371      * @return generatedGTUs.
372      */
373     public final long getGeneratedGTUs()
374     {
375         return this.generatedGTUs;
376     }
377 
378     /**
379      * @param generatedGTUs set generatedGTUs.
380      */
381     public final void setGeneratedGTUs(final long generatedGTUs)
382     {
383         this.generatedGTUs = generatedGTUs;
384     }
385 
386     /**
387      * Retrieve the id of this LaneBasedGTUGenerator.
388      * @return String; the id of this LaneBasedGTUGenerator
389      */
390     @Override
391     public final String getId()
392     {
393         return this.id;
394     }
395 
396     /**
397      * Retrieve the GTUColorer that this LaneBasedGTUGenerator assigns to all generated GTUs.
398      * @return GtuColorer; the GTUColorer that this LaneBasedGTUGenerator assigns to all generated GTUs
399      */
400     public final GTUColorer getGtuColorer()
401     {
402         return this.gtuColorer;
403     }
404 
405     /**
406      * Disable the vehicle generator during the specific time. Underlying processes such as drawing characteristics and headways
407      * are continued, but simply will not result in the actual generation of a GTU.
408      * @param start Time; start time
409      * @param end Time; end time
410      * @param laneDirections Set&lt;LaneDirection&gt;; lanes to disable generation on
411      * @throws SimRuntimeException if time is incorrect
412      */
413     public void disable(final Time start, final Time end, final Set<LaneDirection> laneDirections) throws SimRuntimeException
414     {
415         Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
416         this.simulator.scheduleEventAbs(start, this, this, "disable", new Object[] { laneDirections });
417         this.simulator.scheduleEventAbs(end, this, this, "enable", new Object[0]);
418     }
419 
420     /**
421      * Disables the generator.
422      * @param laneDirections Set&lt;LaneDirection&gt;; lanes to disable generation on
423      */
424     @SuppressWarnings("unused")
425     private void disable(final Set<LaneDirection> laneDirections)
426     {
427         this.disabled = laneDirections;
428     }
429 
430     /**
431      * Enables the generator.
432      */
433     @SuppressWarnings("unused")
434     private void enable()
435     {
436         this.disabled = new LinkedHashSet<>();
437     }
438 
439     /**
440      * Interface for class that checks that there is sufficient room for a proposed new GTU and returns the maximum safe speed
441      * and position for the proposed new GTU.
442      */
443     public interface RoomChecker
444     {
445         /**
446          * Return the maximum safe speed and position for a new GTU with the specified characteristics. Returns
447          * {@code Placement.NO} if there is no safe speed and position. This method might be called with an empty leader set
448          * such that the desired speed can be implemented.
449          * @param leaders SortedSet&lt;HeadwayGTU&gt;; leaders, usually 1, possibly more after a branch
450          * @param characteristics LaneBasedGTUCharacteristics; characteristics of the proposed new GTU
451          * @param since Duration; time since the GTU wanted to arrive
452          * @param initialPosition Set&lt;DirectedLanePosition&gt;; initial position
453          * @return Speed; maximum safe speed, or null if a GTU with the specified characteristics cannot be placed at the
454          *         current time
455          * @throws NetworkException this method may throw a NetworkException if it encounters an error in the network structure
456          * @throws GTUException on parameter exception
457          */
458         Placement canPlace(SortedSet<HeadwayGTU> leaders, LaneBasedGTUCharacteristics characteristics, Duration since,
459                 Set<DirectedLanePosition> initialPosition) throws NetworkException, GTUException;
460     }
461 
462     /**
463      * Placement contains the information that a {@code RoomChecker} returns.
464      * <p>
465      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
466      * <br>
467      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
468      * <p>
469      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 jan. 2018 <br>
470      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
471      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
472      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
473      */
474     public static final class Placement
475     {
476 
477         /** Value if the GTU cannot be placed. */
478         public static final Placement NO = new Placement();
479 
480         /** Speed. */
481         private final Speed speed;
482 
483         /** Position. */
484         private final Set<DirectedLanePosition> position;
485 
486         /**
487          * Constructor for NO.
488          */
489         private Placement()
490         {
491             this.speed = null;
492             this.position = null;
493         }
494 
495         /**
496          * Constructor.
497          * @param speed Speed; speed
498          * @param position Set&lt;DirectedLanePosition&gt;; position
499          */
500         public Placement(final Speed speed, final Set<DirectedLanePosition> position)
501         {
502             Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
503             Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
504             this.speed = speed;
505             this.position = position;
506         }
507 
508         /**
509          * Returns whether the GTU can be placed.
510          * @return whether the GTU can be placed
511          */
512         public boolean canPlace()
513         {
514             return this.speed != null && this.position != null;
515         }
516 
517         /**
518          * Returns the speed.
519          * @return Speed; speed
520          */
521         public Speed getSpeed()
522         {
523             return this.speed;
524         }
525 
526         /**
527          * Returns the position.
528          * @return Set&lt;DirectedLanePosition&gt;; position
529          */
530         public Set<DirectedLanePosition> getPosition()
531         {
532             return this.position;
533         }
534 
535         /** {@inheritDoc} */
536         @Override
537         public String toString()
538         {
539             return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
540         }
541 
542     }
543 
544     /** {@inheritDoc} */
545     @Override
546     public DirectedPoint getLocation() throws RemoteException
547     {
548         return this.generatorPositions.getLocation();
549     }
550 
551     /** {@inheritDoc} */
552     @Override
553     public Bounds getBounds() throws RemoteException
554     {
555         return this.generatorPositions.getBounds();
556     }
557 
558     /**
559      * Returns the number of vehicles in queue per included position for animation.
560      * @return number of vehicles in queue per included position for animation
561      */
562     public Map<DirectedPoint, Integer> getQueueLengths()
563     {
564         Map<DirectedPoint, Integer> result = new LinkedHashMap<>();
565         for (CrossSectionLink link : this.unplacedTemplates.keySet())
566         {
567             for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
568             {
569                 result.put(lanePosition.getPosition().iterator().next().getLocation(),
570                         this.unplacedTemplates.get(link).get(lanePosition).size());
571             }
572         }
573         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
574         {
575             DirectedPoint p = lanePosition.getPosition().iterator().next().getLocation();
576             if (!result.containsKey(p))
577             {
578                 result.put(p, 0);
579             }
580         }
581         return result;
582     }
583 
584 }