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.characteristics.LaneBasedGTUCharacteristics;
43  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
44  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
45  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
46  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
47  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
48  import org.opentrafficsim.road.network.OTSRoadNetwork;
49  import org.opentrafficsim.road.network.lane.CrossSectionLink;
50  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
51  import org.opentrafficsim.road.network.lane.Lane;
52  import org.opentrafficsim.road.network.lane.LaneDirection;
53  
54  import nl.tudelft.simulation.dsol.SimRuntimeException;
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-2020 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,
82              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)
147             throws SimRuntimeException, ProbabilityException, 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             // this.generatedGTUs++;
205             // XXX try a few times until a valid route has been found
206             // XXX see ODApplier.draw() : LaneBasedGTUCharacteristics
207             LaneBasedGTUCharacteristics characteristics = this.laneBasedGTUCharacteristicsGenerator.draw();
208             /*-
209             LaneBasedGTUCharacteristics characteristics = null;
210             for (int tryRoute = 0; tryRoute < 10; tryRoute++)
211             {
212                 characteristics = this.laneBasedGTUCharacteristicsGenerator.draw();
213                 if (characteristics.getRoute() != null)
214                 {
215                     break;
216                 }
217             }
218             */
219             // XXX this should actually have been tested in the draw() method as a valid route also links to the GTUType...
220             GTUType gtuType = characteristics.getGTUType();
221             // gather information on number of unplaced templates per lane, and per link, for the drawing of a new position
222             Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
223             for (CrossSectionLink link : this.unplacedTemplates.keySet())
224             {
225                 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
226                 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkTemplates =
227                         this.unplacedTemplates.get(link);
228                 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
229                 {
230                     linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
231                 }
232                 unplaced.put(link, linkMap);
233             }
234             // position draw
235             Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
236                     this.generatorPositions.speedLimit(gtuType), characteristics.getMaximumSpeed());
237             GeneratorLanePosition lanePosition =
238                     this.generatorPositions.draw(gtuType, unplaced, desiredSpeed, characteristics.getRoute());
239 
240             // skip if disabled at this lane-direction
241             Set<LaneDirection> lanes = new LinkedHashSet<>();
242             for (DirectedLanePosition pos : lanePosition.getPosition())
243             {
244                 lanes.add(pos.getLaneDirection());
245             }
246             if (Collections.disjoint(this.disabled, lanes))
247             {
248                 queueGtu(lanePosition, characteristics);
249             }
250 
251         }
252         Duration headway = this.interarrivelTimeGenerator.draw();
253         if (headway != null)
254         {
255             this.simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
256         }
257     }
258 
259     /**
260      * Check if the queue is non-empty and, if it is, try to place the GTUs in the queue on the road.
261      * @param position GeneratorLanePosition; position
262      * @throws SimRuntimeException should never happen
263      * @throws GTUException when something wrong in the definition of the GTU
264      * @throws OTSGeometryException when something is wrong in the definition of the GTU
265      * @throws NetworkException when something is wrong with the initial location of the GTU
266      * @throws NamingException ???
267      * @throws ProbabilityException pe
268      */
269     @SuppressWarnings("unused")
270     private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GTUException, NamingException,
271             NetworkException, OTSGeometryException, ProbabilityException
272     {
273         TimeStampedObject<LaneBasedGTUCharacteristics> timedCharacteristics;
274         Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue =
275                 this.unplacedTemplates.get(position.getLink()).get(position);
276 
277         synchronized (queue)
278         {
279             timedCharacteristics = queue.peek();
280         }
281         if (null == timedCharacteristics)
282         {
283             return; // Do not re-schedule this method
284         }
285 
286         LaneBasedGTUCharacteristics characteristics = timedCharacteristics.getObject();
287         SortedSet<HeadwayGTU> leaders = new TreeSet<>();
288         for (DirectedLanePosition dirPos : position.getPosition())
289         {
290             getFirstLeaders(dirPos.getLaneDirection(), dirPos.getPosition().neg().minus(characteristics.getFront()),
291                     dirPos.getPosition(), leaders);
292         }
293         Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.getTimestamp());
294         Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
295         if (placement.canPlace())
296         {
297             // There is enough room; remove the template from the queue and construct the new GTU
298             synchronized (queue)
299             {
300                 queue.remove();
301             }
302             placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
303             if (queue.size() > 0)
304             {
305                 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] {position});
306             }
307         }
308         else if (queue.size() > 0)
309         {
310             this.simulator.scheduleEventRel(this.reTryInterval, this, this, "tryToPlaceGTU", new Object[] {position});
311         }
312     }
313 
314     /**
315      * Adds a GTU to the generation queue. This method ignores whether vehicle generation is enabled at the location. This
316      * allows an external party to govern (over some time) what vehicles are generated.
317      * @param characteristics LaneBasedGTUCharacteristics; characteristics of GTU to add to the queue
318      * @param position Set&lt;LaneDirection&gt;; position to generate the GTU at
319      */
320     public final void queueGtu(final LaneBasedGTUCharacteristics characteristics, final Set<LaneDirection> position)
321     {
322         // first find the correct GeneratorLanePosition
323         GeneratorLanePosition genPosition = null;
324         Set<LaneDirection> genSet = new LinkedHashSet<>();
325         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
326         {
327             for (DirectedLanePosition dirPos : lanePosition.getPosition())
328             {
329                 genSet.add(dirPos.getLaneDirection());
330             }
331             if (genSet.equals(position))
332             {
333                 genPosition = lanePosition;
334                 break;
335             }
336             genSet.clear();
337         }
338         Throw.when(genPosition == null, IllegalStateException.class, "Position %s is not part of the generation.", position);
339         try
340         {
341             queueGtu(genPosition, characteristics);
342         }
343         catch (SimRuntimeException exception)
344         {
345             throw new RuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
346         }
347     }
348 
349     /**
350      * Places the characteristics in the queue pertaining to the position, and schedules a call to {@code tryToPlace} now if the
351      * queue length is 1.
352      * @param lanePosition GeneratorLanePosition; position to generate the GTU at
353      * @param characteristics LaneBasedGTUCharacteristics; characteristics of GTU to add to the queue
354      * @throws SimRuntimeException when an event is scheduled in the past
355      */
356     private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGTUCharacteristics characteristics)
357             throws SimRuntimeException
358     {
359         if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
360         {
361             this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
362         }
363         Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkMap =
364                 this.unplacedTemplates.get(lanePosition.getLink());
365         if (!linkMap.containsKey(lanePosition))
366         {
367             linkMap.put(lanePosition, new LinkedList<>());
368         }
369         Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue = linkMap.get(lanePosition);
370         queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorTime()));
371         if (queue.size() == 1)
372         {
373             this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] {lanePosition});
374         }
375     }
376 
377     /**
378      * Places a GTU, regardless of whether it has room. The user of this method should verify this is the case.
379      * @param characteristics LaneBasedGTUCharacteristics; characteristics
380      * @param position Set&lt;DirectedLanePosition&gt;; position
381      * @param speed Speed; speed
382      * @throws NamingException on exception
383      * @throws GTUException on exception
384      * @throws NetworkException on exception
385      * @throws SimRuntimeException on exception
386      * @throws OTSGeometryException on exception
387      */
388     public final void placeGtu(final LaneBasedGTUCharacteristics characteristics, final Set<DirectedLanePosition> position,
389             final Speed speed) throws NamingException, GTUException, NetworkException, SimRuntimeException, OTSGeometryException
390     {
391         String gtuId = this.idGenerator.nextId();
392         LaneBasedIndividualGTUasedIndividualGTU.html#LaneBasedIndividualGTU">LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU(gtuId, characteristics.getGTUType(),
393                 characteristics.getLength(), characteristics.getWidth(), characteristics.getMaximumSpeed(),
394                 characteristics.getFront(), this.simulator, this.network);
395         gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
396         gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
397         gtu.setVehicleModel(characteristics.getVehicleModel());
398         gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
399         gtu.setInstantaneousLaneChange(this.instantaneousLaneChange);
400         gtu.setErrorHandler(this.errorHandler);
401         gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
402                 characteristics.getOrigin(), characteristics.getDestination()), position, speed);
403         this.generatedGTUs++;
404         fireEvent(GTU_GENERATED_EVENT, gtu);
405     }
406 
407     /**
408      * 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.
409      * @param lane LaneDirection; lane to search on
410      * @param startDistance Length; distance from generator location (nose) to start of the lane
411      * @param beyond Length; location to search downstream of which is the generator position, or the start for downstream lanes
412      * @param set Set&lt;HeadwayGTU&gt;; set to add the GTU's to
413      * @throws GTUException if a GTU is incorrectly positioned on a lane
414      */
415     private void getFirstLeaders(final LaneDirection lane, final Length startDistance, final Length beyond,
416             final Set<HeadwayGTU> set) throws GTUException
417     {
418         LaneBasedGTU next = lane.getLane().getGtuAhead(beyond, lane.getDirection(), RelativePosition.FRONT,
419                 this.simulator.getSimulatorTime());
420         if (next != null)
421         {
422             Length headway;
423             if (lane.getDirection().isPlus())
424             {
425                 headway = startDistance.plus(next.position(lane.getLane(), next.getRear()));
426             }
427             else
428             {
429                 headway = startDistance.plus(lane.getLane().getLength().minus(next.position(lane.getLane(), next.getRear())));
430             }
431             if (headway.si < 300)
432             {
433                 set.add(new HeadwayGTUReal(next, headway, true));
434             }
435             return;
436         }
437         ImmutableMap<Lane, GTUDirectionality> downstreamLanes =
438                 lane.getLane().downstreamLanes(lane.getDirection(), this.network.getGtuType(GTUType.DEFAULTS.VEHICLE));
439         for (Lane downstreamLane : downstreamLanes.keySet())
440         {
441             Length startDistanceDownstream = startDistance.plus(lane.getLane().getLength());
442             if (startDistanceDownstream.si > 300)
443             {
444                 return;
445             }
446             GTUDirectionality dir = downstreamLanes.get(downstreamLane);
447             Length beyondDownstream = dir.isPlus() ? Length.ZERO : downstreamLane.getLength();
448             getFirstLeaders(new LaneDirection(downstreamLane, dir), startDistanceDownstream, beyondDownstream, set);
449         }
450     }
451 
452     /** {@inheritDoc} */
453     @Override
454     public final String toString()
455     {
456         return "LaneBasedGTUGenerator " + this.id + " on " + this.generatorPositions;
457     }
458 
459     /**
460      * @return generatedGTUs.
461      */
462     public final long getGeneratedGTUs()
463     {
464         return this.generatedGTUs;
465     }
466 
467     /**
468      * Retrieve the id of this LaneBasedGTUGenerator.
469      * @return String; the id of this LaneBasedGTUGenerator
470      */
471     @Override
472     public final String getId()
473     {
474         return this.id;
475     }
476 
477     /**
478      * Disable the vehicle generator during the specific time. Underlying processes such as drawing characteristics and headways
479      * are continued, but simply will not result in the queuing of the GTU.
480      * @param start Time; start time
481      * @param end Time; end time
482      * @param laneDirections Set&lt;LaneDirection&gt;; lanes to disable generation on
483      * @throws SimRuntimeException if time is incorrect
484      */
485     public void disable(final Time start, final Time end, final Set<LaneDirection> laneDirections) throws SimRuntimeException
486     {
487         Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
488         this.simulator.scheduleEventAbs(start, this, this, "disable", new Object[] {laneDirections});
489         this.simulator.scheduleEventAbs(end, this, this, "enable", new Object[0]);
490     }
491 
492     /**
493      * Disables the generator.
494      * @param laneDirections Set&lt;LaneDirection&gt;; lanes to disable generation on
495      */
496     @SuppressWarnings("unused")
497     private void disable(final Set<LaneDirection> laneDirections)
498     {
499         Throw.when(this.disabled != null && !this.disabled.isEmpty(), IllegalStateException.class,
500                 "Disabling a generator that is already disabled is not allowed.");
501         this.disabled = laneDirections;
502     }
503 
504     /**
505      * Enables the generator.
506      */
507     @SuppressWarnings("unused")
508     private void enable()
509     {
510         this.disabled = new LinkedHashSet<>();
511     }
512 
513     /**
514      * Interface for class that checks that there is sufficient room for a proposed new GTU and returns the maximum safe speed
515      * and position for the proposed new GTU.
516      */
517     public interface RoomChecker
518     {
519         /**
520          * Return the maximum safe speed and position for a new GTU with the specified characteristics. Returns
521          * {@code Placement.NO} if there is no safe speed and position. This method might be called with an empty leader set
522          * such that the desired speed can be implemented.
523          * @param leaders SortedSet&lt;HeadwayGTU&gt;; leaders, usually 1, possibly more after a branch
524          * @param characteristics LaneBasedGTUCharacteristics; characteristics of the proposed new GTU
525          * @param since Duration; time since the GTU wanted to arrive
526          * @param initialPosition Set&lt;DirectedLanePosition&gt;; initial position
527          * @return Speed; maximum safe speed, or null if a GTU with the specified characteristics cannot be placed at the
528          *         current time
529          * @throws NetworkException this method may throw a NetworkException if it encounters an error in the network structure
530          * @throws GTUException on parameter exception
531          */
532         Placement canPlace(SortedSet<HeadwayGTU> leaders, LaneBasedGTUCharacteristics characteristics, Duration since,
533                 Set<DirectedLanePosition> initialPosition) throws NetworkException, GTUException;
534     }
535 
536     /**
537      * Placement contains the information that a {@code RoomChecker} returns.
538      * <p>
539      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
540      * <br>
541      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
542      * <p>
543      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 jan. 2018 <br>
544      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
545      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
546      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
547      */
548     public static final class Placement
549     {
550 
551         /** Value if the GTU cannot be placed. */
552         public static final Placement NO = new Placement();
553 
554         /** Speed. */
555         private final Speed speed;
556 
557         /** Position. */
558         private final Set<DirectedLanePosition> position;
559 
560         /**
561          * Constructor for NO.
562          */
563         private Placement()
564         {
565             this.speed = null;
566             this.position = null;
567         }
568 
569         /**
570          * Constructor.
571          * @param speed Speed; speed
572          * @param position Set&lt;DirectedLanePosition&gt;; position
573          */
574         public Placement(final Speed speed, final Set<DirectedLanePosition> position)
575         {
576             Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
577             Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
578             this.speed = speed;
579             this.position = position;
580         }
581 
582         /**
583          * Returns whether the GTU can be placed.
584          * @return whether the GTU can be placed
585          */
586         public boolean canPlace()
587         {
588             return this.speed != null && this.position != null;
589         }
590 
591         /**
592          * Returns the speed.
593          * @return Speed; speed
594          */
595         public Speed getSpeed()
596         {
597             return this.speed;
598         }
599 
600         /**
601          * Returns the position.
602          * @return Set&lt;DirectedLanePosition&gt;; position
603          */
604         public Set<DirectedLanePosition> getPosition()
605         {
606             return this.position;
607         }
608 
609         /** {@inheritDoc} */
610         @Override
611         public String toString()
612         {
613             return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
614         }
615 
616     }
617 
618     /** {@inheritDoc} */
619     @Override
620     public DirectedPoint getLocation() throws RemoteException
621     {
622         return this.generatorPositions.getLocation();
623     }
624 
625     /** {@inheritDoc} */
626     @Override
627     public Bounds getBounds() throws RemoteException
628     {
629         return this.generatorPositions.getBounds();
630     }
631 
632     /** {@inheritDoc} */
633     @Override
634     public Map<DirectedPoint, Integer> getQueueLengths()
635     {
636         Map<DirectedPoint, Integer> result = new LinkedHashMap<>();
637         for (CrossSectionLink link : this.unplacedTemplates.keySet())
638         {
639             for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
640             {
641                 result.put(lanePosition.getPosition().iterator().next().getLocation(),
642                         this.unplacedTemplates.get(link).get(lanePosition).size());
643             }
644         }
645         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
646         {
647             DirectedPoint p = lanePosition.getPosition().iterator().next().getLocation();
648             if (!result.containsKey(p))
649             {
650                 result.put(p, 0);
651             }
652         }
653         return result;
654     }
655 
656     /** {@inheritDoc} */
657     @Override
658     public Serializable getSourceId()
659     {
660         return this.id;
661     }
662 
663 }