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.djutils.exceptions.Throw;
24  import org.opentrafficsim.base.Identifiable;
25  import org.opentrafficsim.base.TimeStampedObject;
26  import org.opentrafficsim.base.parameters.ParameterException;
27  import org.opentrafficsim.core.distributions.Generator;
28  import org.opentrafficsim.core.distributions.ProbabilityException;
29  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
30  import org.opentrafficsim.core.geometry.OTSGeometryException;
31  import org.opentrafficsim.core.gtu.GTUDirectionality;
32  import org.opentrafficsim.core.gtu.GTUException;
33  import org.opentrafficsim.core.gtu.GTUType;
34  import org.opentrafficsim.core.gtu.RelativePosition;
35  import org.opentrafficsim.core.idgenerator.IdGenerator;
36  import org.opentrafficsim.core.network.NetworkException;
37  import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
38  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
39  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
40  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
41  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
42  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
43  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
44  import org.opentrafficsim.road.network.OTSRoadNetwork;
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.event.EventProducer;
52  import nl.tudelft.simulation.event.EventType;
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-2019 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 extends EventProducer implements Serializable, Identifiable, GtuGeneratorQueue
69  {
70      /** */
71      private static final long serialVersionUID = 20160000L;
72  
73      /**
74       * Event of a generated GTU. Payload: LaneBasedIndividualGTU
75       */
76      public static final EventType GTU_GENERATED_EVENT = new EventType("GENERATOR.GTU_GENERATED");
77  
78      /** FIFO for templates that have not been generated yet due to insufficient room/headway, per position, and per link. */
79      private final Map<CrossSectionLink, Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>>> unplacedTemplates =
80              new LinkedHashMap<>();
81  
82      /** Name of the GTU generator. */
83      private final String id;
84  
85      /** Time distribution that determines the interval times between GTUs. */
86      private final Generator<Duration> interarrivelTimeGenerator;
87  
88      /** Generates most properties of the GTUs. */
89      private final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator;
90  
91      /** Total number of GTUs generated so far. */
92      private long generatedGTUs = 0;
93  
94      /** Retry interval for checking if a GTU can be placed. */
95      private Duration reTryInterval = new Duration(0.1, DurationUnit.SI);
96  
97      /** Location and initial direction provider for all generated GTUs. */
98      private final GeneratorPositions generatorPositions;
99  
100     /** Network. */
101     private final OTSRoadNetwork network;
102 
103     /** Simulator. */
104     private final OTSSimulatorInterface simulator;
105 
106     /** The way that this generator checks if it is safe to construct and place the next lane based GTU. */
107     private final RoomChecker roomChecker;
108 
109     /** ID generator. */
110     private final IdGenerator idGenerator;
111 
112     /** Initial distance over which lane changes shouldn't be performed. */
113     private Length noLaneChangeDistance = null;
114 
115     /** Vehicle generation is ignored on these lanes. */
116     private Set<LaneDirection> disabled = new LinkedHashSet<>();
117 
118     /**
119      * Construct a new lane base GTU generator.
120      * @param id String; name of the new GTU generator
121      * @param interarrivelTimeGenerator Generator&lt;Duration&gt;; generator for the interval times between GTUs
122      * @param laneBasedGTUCharacteristicsGenerator LaneBasedGTUCharacteristicsGenerator; generator of the characteristics of
123      *            each GTU
124      * @param generatorPositions GeneratorPositions; location and initial direction provider for all generated GTUs
125      * @param network OTSRoadNetwork; the OTS network that owns the generated GTUs
126      * @param simulator OTSSimulatorInterface; simulator
127      * @param roomChecker RoomChecker; the way that this generator checks that there is sufficient room to 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     @SuppressWarnings("parameternumber")
134     public LaneBasedGTUGenerator(final String id, final Generator<Duration> interarrivelTimeGenerator,
135             final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator,
136             final GeneratorPositions generatorPositions, final OTSRoadNetwork network, final OTSSimulatorInterface simulator,
137             final RoomChecker roomChecker, final IdGenerator idGenerator)
138             throws SimRuntimeException, ProbabilityException, ParameterException
139     {
140         this.id = id;
141         this.interarrivelTimeGenerator = interarrivelTimeGenerator;
142         this.laneBasedGTUCharacteristicsGenerator = laneBasedGTUCharacteristicsGenerator;
143         this.generatorPositions = generatorPositions;
144         this.network = network;
145         this.simulator = simulator;
146         this.roomChecker = roomChecker;
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 
199             // skip if disabled at this lane-direction
200             Set<LaneDirection> lanes = new LinkedHashSet<>();
201             for (DirectedLanePosition pos : lanePosition.getPosition())
202             {
203                 lanes.add(pos.getLaneDirection());
204             }
205             if (Collections.disjoint(this.disabled, lanes))
206             {
207                 queueGtu(lanePosition, characteristics);
208             }
209 
210         }
211         Duration headway = this.interarrivelTimeGenerator.draw();
212         if (headway != null)
213         {
214             this.simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
215         }
216     }
217 
218     /**
219      * Check if the queue is non-empty and, if it is, try to place the GTUs in the queue on the road.
220      * @param position GeneratorLanePosition; position
221      * @throws SimRuntimeException should never happen
222      * @throws GTUException when something wrong in the definition of the GTU
223      * @throws OTSGeometryException when something is wrong in the definition of the GTU
224      * @throws NetworkException when something is wrong with the initial location of the GTU
225      * @throws NamingException ???
226      * @throws ProbabilityException pe
227      */
228     @SuppressWarnings("unused")
229     private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GTUException, NamingException,
230             NetworkException, OTSGeometryException, ProbabilityException
231     {
232         TimeStampedObject<LaneBasedGTUCharacteristics> timedCharacteristics;
233         Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue =
234                 this.unplacedTemplates.get(position.getLink()).get(position);
235 
236         synchronized (queue)
237         {
238             timedCharacteristics = queue.peek();
239         }
240         if (null == timedCharacteristics)
241         {
242             return; // Do not re-schedule this method
243         }
244 
245         LaneBasedGTUCharacteristics characteristics = timedCharacteristics.getObject();
246         SortedSet<HeadwayGTU> leaders = new TreeSet<>();
247         for (DirectedLanePosition dirPos : position.getPosition())
248         {
249             getFirstLeaders(dirPos.getLaneDirection(), dirPos.getPosition().neg().minus(characteristics.getFront()),
250                     dirPos.getPosition(), leaders);
251         }
252         Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.getTimestamp());
253         Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
254         if (placement.canPlace())
255         {
256             // There is enough room; remove the template from the queue and construct the new GTU
257             synchronized (queue)
258             {
259                 queue.remove();
260             }
261             placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
262             if (queue.size() > 0)
263             {
264                 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { position });
265             }
266         }
267         else if (queue.size() > 0)
268         {
269             this.simulator.scheduleEventRel(this.reTryInterval, this, this, "tryToPlaceGTU", new Object[] { position });
270         }
271     }
272 
273     /**
274      * Adds a GTU to the generation queue. This method ignores whether vehicle generation is enabled at the location. This
275      * allows an external party to govern (over some time) what vehicles are generated.
276      * @param characteristics LaneBasedGTUCharacteristics; characteristics of GTU to add to the queue
277      * @param position Set&lt;LaneDirection&gt;; position to generate the GTU at
278      */
279     public final void queueGtu(final LaneBasedGTUCharacteristics characteristics, final Set<LaneDirection> position)
280     {
281         // first find the correct GeneratorLanePosition
282         GeneratorLanePosition genPosition = null;
283         Set<LaneDirection> genSet = new LinkedHashSet<>();
284         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
285         {
286             for (DirectedLanePosition dirPos : lanePosition.getPosition())
287             {
288                 genSet.add(dirPos.getLaneDirection());
289             }
290             if (genSet.equals(position))
291             {
292                 genPosition = lanePosition;
293                 break;
294             }
295             genSet.clear();
296         }
297         Throw.when(genPosition == null, IllegalStateException.class, "Position %s is not part of the generation.", position);
298         try
299         {
300             queueGtu(genPosition, characteristics);
301         }
302         catch (SimRuntimeException exception)
303         {
304             throw new RuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
305         }
306     }
307 
308     /**
309      * Places the characteristics in the queue pertaining to the position, and schedules a call to {@code tryToPlace} now if the
310      * queue length is 1.
311      * @param lanePosition GeneratorLanePosition; position to generate the GTU at
312      * @param characteristics LaneBasedGTUCharacteristics; characteristics of GTU to add to the queue
313      * @throws SimRuntimeException when an event is scheduled in the past
314      */
315     private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGTUCharacteristics characteristics)
316             throws SimRuntimeException
317     {
318         if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
319         {
320             this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
321         }
322         Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkMap =
323                 this.unplacedTemplates.get(lanePosition.getLink());
324         if (!linkMap.containsKey(lanePosition))
325         {
326             linkMap.put(lanePosition, new LinkedList<>());
327         }
328         Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue = linkMap.get(lanePosition);
329         queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorTime()));
330         if (queue.size() == 1)
331         {
332             this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { lanePosition });
333         }
334     }
335 
336     /**
337      * Places a GTU, regardless of whether it has room. The user of this method should verify this is the case.
338      * @param characteristics LaneBasedGTUCharacteristics; characteristics
339      * @param position Set&lt;DirectedLanePosition&gt;; position
340      * @param speed Speed; speed
341      * @throws NamingException on exception
342      * @throws GTUException on exception
343      * @throws NetworkException on exception
344      * @throws SimRuntimeException on exception
345      * @throws OTSGeometryException on exception
346      */
347     public final void placeGtu(final LaneBasedGTUCharacteristics characteristics, final Set<DirectedLanePosition> position,
348             final Speed speed) throws NamingException, GTUException, NetworkException, SimRuntimeException, OTSGeometryException
349     {
350         String gtuId = this.idGenerator.nextId();
351         LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU(gtuId, characteristics.getGTUType(),
352                 characteristics.getLength(), characteristics.getWidth(), characteristics.getMaximumSpeed(),
353                 characteristics.getFront(), this.simulator, this.network);
354         gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
355         gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
356         gtu.setVehicleModel(characteristics.getVehicleModel());
357         gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
358         gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
359                 characteristics.getOrigin(), characteristics.getDestination()), position, speed);
360         this.generatedGTUs++;
361         fireEvent(GTU_GENERATED_EVENT, gtu);
362     }
363 
364     /**
365      * 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.
366      * @param lane LaneDirection; lane to search on
367      * @param startDistance Length; distance from generator location (nose) to start of the lane
368      * @param beyond Length; location to search downstream of which is the generator position, or the start for downstream lanes
369      * @param set Set&lt;HeadwayGTU&gt;; set to add the GTU's to
370      * @throws GTUException if a GTU is incorrectly positioned on a lane
371      */
372     private void getFirstLeaders(final LaneDirection lane, final Length startDistance, final Length beyond,
373             final Set<HeadwayGTU> set) throws GTUException
374     {
375         LaneBasedGTU next = lane.getLane().getGtuAhead(beyond, lane.getDirection(), RelativePosition.FRONT,
376                 this.simulator.getSimulatorTime());
377         if (next != null)
378         {
379             Length headway;
380             if (lane.getDirection().isPlus())
381             {
382                 headway = startDistance.plus(next.position(lane.getLane(), next.getRear()));
383             }
384             else
385             {
386                 headway = startDistance.plus(lane.getLane().getLength().minus(next.position(lane.getLane(), next.getRear())));
387             }
388             if (headway.si < 300)
389             {
390                 set.add(new HeadwayGTUReal(next, headway, true));
391             }
392             return;
393         }
394         Map<Lane, GTUDirectionality> downstreamLanes =
395                 lane.getLane().downstreamLanes(lane.getDirection(), network.getGtuType(GTUType.DEFAULTS.VEHICLE));
396         for (Lane downstreamLane : downstreamLanes.keySet())
397         {
398             Length startDistanceDownstream = startDistance.plus(lane.getLane().getLength());
399             if (startDistanceDownstream.si > 300)
400             {
401                 return;
402             }
403             GTUDirectionality dir = downstreamLanes.get(downstreamLane);
404             Length beyondDownstream = dir.isPlus() ? Length.ZERO : downstreamLane.getLength();
405             getFirstLeaders(new LaneDirection(downstreamLane, dir), startDistanceDownstream, beyondDownstream, set);
406         }
407     }
408 
409     /** {@inheritDoc} */
410     @Override
411     public final String toString()
412     {
413         return "LaneBasedGTUGenerator " + this.id + " on " + this.generatorPositions;
414     }
415 
416     /**
417      * @return generatedGTUs.
418      */
419     public final long getGeneratedGTUs()
420     {
421         return this.generatedGTUs;
422     }
423 
424     /**
425      * Retrieve the id of this LaneBasedGTUGenerator.
426      * @return String; the id of this LaneBasedGTUGenerator
427      */
428     @Override
429     public final String getId()
430     {
431         return this.id;
432     }
433 
434     /**
435      * Disable the vehicle generator during the specific time. Underlying processes such as drawing characteristics and headways
436      * are continued, but simply will not result in the queuing of the GTU.
437      * @param start Time; start time
438      * @param end Time; end time
439      * @param laneDirections Set&lt;LaneDirection&gt;; lanes to disable generation on
440      * @throws SimRuntimeException if time is incorrect
441      */
442     public void disable(final Time start, final Time end, final Set<LaneDirection> laneDirections) throws SimRuntimeException
443     {
444         Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
445         this.simulator.scheduleEventAbs(start, this, this, "disable", new Object[] { laneDirections });
446         this.simulator.scheduleEventAbs(end, this, this, "enable", new Object[0]);
447     }
448 
449     /**
450      * Disables the generator.
451      * @param laneDirections Set&lt;LaneDirection&gt;; lanes to disable generation on
452      */
453     @SuppressWarnings("unused")
454     private void disable(final Set<LaneDirection> laneDirections)
455     {
456         Throw.when(this.disabled != null && !this.disabled.isEmpty(), IllegalStateException.class,
457                 "Disabling a generator that is already disabled is not allowed.");
458         this.disabled = laneDirections;
459     }
460 
461     /**
462      * Enables the generator.
463      */
464     @SuppressWarnings("unused")
465     private void enable()
466     {
467         this.disabled = new LinkedHashSet<>();
468     }
469 
470     /**
471      * Interface for class that checks that there is sufficient room for a proposed new GTU and returns the maximum safe speed
472      * and position for the proposed new GTU.
473      */
474     public interface RoomChecker
475     {
476         /**
477          * Return the maximum safe speed and position for a new GTU with the specified characteristics. Returns
478          * {@code Placement.NO} if there is no safe speed and position. This method might be called with an empty leader set
479          * such that the desired speed can be implemented.
480          * @param leaders SortedSet&lt;HeadwayGTU&gt;; leaders, usually 1, possibly more after a branch
481          * @param characteristics LaneBasedGTUCharacteristics; characteristics of the proposed new GTU
482          * @param since Duration; time since the GTU wanted to arrive
483          * @param initialPosition Set&lt;DirectedLanePosition&gt;; initial position
484          * @return Speed; maximum safe speed, or null if a GTU with the specified characteristics cannot be placed at the
485          *         current time
486          * @throws NetworkException this method may throw a NetworkException if it encounters an error in the network structure
487          * @throws GTUException on parameter exception
488          */
489         Placement canPlace(SortedSet<HeadwayGTU> leaders, LaneBasedGTUCharacteristics characteristics, Duration since,
490                 Set<DirectedLanePosition> initialPosition) throws NetworkException, GTUException;
491     }
492 
493     /**
494      * Placement contains the information that a {@code RoomChecker} returns.
495      * <p>
496      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
497      * <br>
498      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
499      * <p>
500      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 jan. 2018 <br>
501      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
502      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
503      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
504      */
505     public static final class Placement
506     {
507 
508         /** Value if the GTU cannot be placed. */
509         public static final Placement NO = new Placement();
510 
511         /** Speed. */
512         private final Speed speed;
513 
514         /** Position. */
515         private final Set<DirectedLanePosition> position;
516 
517         /**
518          * Constructor for NO.
519          */
520         private Placement()
521         {
522             this.speed = null;
523             this.position = null;
524         }
525 
526         /**
527          * Constructor.
528          * @param speed Speed; speed
529          * @param position Set&lt;DirectedLanePosition&gt;; position
530          */
531         public Placement(final Speed speed, final Set<DirectedLanePosition> position)
532         {
533             Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
534             Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
535             this.speed = speed;
536             this.position = position;
537         }
538 
539         /**
540          * Returns whether the GTU can be placed.
541          * @return whether the GTU can be placed
542          */
543         public boolean canPlace()
544         {
545             return this.speed != null && this.position != null;
546         }
547 
548         /**
549          * Returns the speed.
550          * @return Speed; speed
551          */
552         public Speed getSpeed()
553         {
554             return this.speed;
555         }
556 
557         /**
558          * Returns the position.
559          * @return Set&lt;DirectedLanePosition&gt;; position
560          */
561         public Set<DirectedLanePosition> getPosition()
562         {
563             return this.position;
564         }
565 
566         /** {@inheritDoc} */
567         @Override
568         public String toString()
569         {
570             return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
571         }
572 
573     }
574 
575     /** {@inheritDoc} */
576     @Override
577     public DirectedPoint getLocation() throws RemoteException
578     {
579         return this.generatorPositions.getLocation();
580     }
581 
582     /** {@inheritDoc} */
583     @Override
584     public Bounds getBounds() throws RemoteException
585     {
586         return this.generatorPositions.getBounds();
587     }
588 
589     /** {@inheritDoc} */
590     @Override
591     public Map<DirectedPoint, Integer> getQueueLengths()
592     {
593         Map<DirectedPoint, Integer> result = new LinkedHashMap<>();
594         for (CrossSectionLink link : this.unplacedTemplates.keySet())
595         {
596             for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
597             {
598                 result.put(lanePosition.getPosition().iterator().next().getLocation(),
599                         this.unplacedTemplates.get(link).get(lanePosition).size());
600             }
601         }
602         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
603         {
604             DirectedPoint p = lanePosition.getPosition().iterator().next().getLocation();
605             if (!result.containsKey(p))
606             {
607                 result.put(p, 0);
608             }
609         }
610         return result;
611     }
612 
613 }