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