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