View Javadoc
1   package org.opentrafficsim.animation;
2   
3   import java.awt.Color;
4   import java.rmi.RemoteException;
5   import java.util.Collections;
6   import java.util.LinkedHashMap;
7   import java.util.Map;
8   import java.util.Optional;
9   
10  import javax.naming.NamingException;
11  
12  import org.djutils.event.Event;
13  import org.djutils.event.EventListener;
14  import org.djutils.exceptions.Throw;
15  import org.opentrafficsim.animation.PerceptionAnimation.ChannelAttention;
16  import org.opentrafficsim.animation.data.AnimationBusStopData;
17  import org.opentrafficsim.animation.data.AnimationConflictData;
18  import org.opentrafficsim.animation.data.AnimationCrossSectionElementData;
19  import org.opentrafficsim.animation.data.AnimationGtuData;
20  import org.opentrafficsim.animation.data.AnimationGtuGeneratorPositionData;
21  import org.opentrafficsim.animation.data.AnimationLaneData;
22  import org.opentrafficsim.animation.data.AnimationLaneDetectorData;
23  import org.opentrafficsim.animation.data.AnimationLinkData;
24  import org.opentrafficsim.animation.data.AnimationNodeData;
25  import org.opentrafficsim.animation.data.AnimationPriorityData;
26  import org.opentrafficsim.animation.data.AnimationShoulderData;
27  import org.opentrafficsim.animation.data.AnimationSpeedSignData;
28  import org.opentrafficsim.animation.data.AnimationStripeData;
29  import org.opentrafficsim.animation.data.AnimationTrafficLightData;
30  import org.opentrafficsim.animation.data.AnimationTrafficLightDetectorData;
31  import org.opentrafficsim.animation.gtu.colorer.GtuColorerManager;
32  import org.opentrafficsim.base.logger.Logger;
33  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
34  import org.opentrafficsim.core.gtu.Gtu;
35  import org.opentrafficsim.core.gtu.GtuGenerator.GtuGeneratorPosition;
36  import org.opentrafficsim.core.gtu.GtuType;
37  import org.opentrafficsim.core.network.Link;
38  import org.opentrafficsim.core.network.Network;
39  import org.opentrafficsim.core.network.Node;
40  import org.opentrafficsim.core.object.LocatedObject;
41  import org.opentrafficsim.core.object.NonLocatedObject;
42  import org.opentrafficsim.draw.gtu.DefaultCarAnimation;
43  import org.opentrafficsim.draw.gtu.DefaultCarAnimation.GtuData;
44  import org.opentrafficsim.draw.gtu.DefaultCarAnimation.GtuData.GtuMarker;
45  import org.opentrafficsim.draw.network.LinkAnimation;
46  import org.opentrafficsim.draw.network.NodeAnimation;
47  import org.opentrafficsim.draw.road.BusStopAnimation;
48  import org.opentrafficsim.draw.road.BusStopAnimation.BusStopData;
49  import org.opentrafficsim.draw.road.ConflictAnimation;
50  import org.opentrafficsim.draw.road.ConflictAnimation.ConflictData;
51  import org.opentrafficsim.draw.road.CrossSectionElementAnimation;
52  import org.opentrafficsim.draw.road.GtuGeneratorPositionAnimation;
53  import org.opentrafficsim.draw.road.GtuGeneratorPositionAnimation.GtuGeneratorPositionData;
54  import org.opentrafficsim.draw.road.LaneAnimation;
55  import org.opentrafficsim.draw.road.LaneDetectorAnimation;
56  import org.opentrafficsim.draw.road.LaneDetectorAnimation.LaneDetectorData;
57  import org.opentrafficsim.draw.road.PriorityAnimation;
58  import org.opentrafficsim.draw.road.SpeedSignAnimation;
59  import org.opentrafficsim.draw.road.SpeedSignAnimation.SpeedSignData;
60  import org.opentrafficsim.draw.road.StripeAnimation;
61  import org.opentrafficsim.draw.road.TrafficLightAnimation;
62  import org.opentrafficsim.draw.road.TrafficLightAnimation.TrafficLightData;
63  import org.opentrafficsim.draw.road.TrafficLightDetectorAnimation;
64  import org.opentrafficsim.draw.road.TrafficLightDetectorAnimation.TrafficLightDetectorData;
65  import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator;
66  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
67  import org.opentrafficsim.road.network.lane.CrossSectionElement;
68  import org.opentrafficsim.road.network.lane.CrossSectionLink;
69  import org.opentrafficsim.road.network.lane.Lane;
70  import org.opentrafficsim.road.network.lane.Shoulder;
71  import org.opentrafficsim.road.network.lane.Stripe;
72  import org.opentrafficsim.road.network.lane.conflict.Conflict;
73  import org.opentrafficsim.road.network.lane.object.BusStop;
74  import org.opentrafficsim.road.network.lane.object.SpeedSign;
75  import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
76  import org.opentrafficsim.road.network.lane.object.detector.SinkDetector;
77  import org.opentrafficsim.road.network.lane.object.detector.TrafficLightDetector;
78  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
79  
80  import nl.tudelft.simulation.dsol.SimRuntimeException;
81  import nl.tudelft.simulation.dsol.animation.Locatable;
82  import nl.tudelft.simulation.dsol.animation.d2.Renderable2d;
83  import nl.tudelft.simulation.naming.context.Contextualized;
84  
85  /**
86   * DefaultAnimationFactory.
87   * <p>
88   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
89   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
90   * </p>
91   * @author <a href="https://github.com/averbraeck" target="_blank">Alexander Verbraeck</a>
92   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
93   */
94  public class DefaultAnimationFactory implements EventListener
95  {
96      /** The network. */
97      private final Network network;
98  
99      /** The simulator. */
100     private final OtsSimulatorInterface simulator;
101 
102     /** GTU colorer. */
103     private final GtuColorerManager gtuColorerManager;
104 
105     /** Rendered gtus. */
106     private Map<LaneBasedGtu, Renderable2d<GtuData>> animatedGTUs = Collections.synchronizedMap(new LinkedHashMap<>());
107 
108     /** Rendered gtus. */
109     private Map<LaneBasedGtu, Renderable2d<ChannelAttention>> animatedAttention =
110             Collections.synchronizedMap(new LinkedHashMap<>());
111 
112     /** Rendered located objects. */
113     private Map<Locatable, Renderable2d<?>> animatedLocatedObjects = Collections.synchronizedMap(new LinkedHashMap<>());
114 
115     /** Rendered non-located objects. */
116     private Map<NonLocatedObject, Renderable2d<?>> animatedNonLocatedObjects =
117             Collections.synchronizedMap(new LinkedHashMap<>());
118 
119     /** Map of GTU type markers. */
120     private Map<GtuType, GtuMarker> gtuMarkers = new LinkedHashMap<>();
121 
122     /**
123      * Creates animations for nodes, links and lanes. The class will subscribe to the network and listen to changes, so the
124      * adding and removing of GTUs and Objects is animated correctly.
125      * @param network the network
126      * @param gtuColorerManager GTU colorer manager
127      * @param animateNetwork whether to animate the current network objects
128      */
129     protected DefaultAnimationFactory(final Network network, final GtuColorerManager gtuColorerManager,
130             final boolean animateNetwork)
131     {
132         this.network = network;
133         this.simulator = network.getSimulator();
134         this.gtuColorerManager = gtuColorerManager;
135 
136         // subscribe to adding and removing events
137         network.addListener(this, Network.GTU_ADD_EVENT);
138         network.addListener(this, Network.GTU_REMOVE_EVENT);
139         network.addListener(this, Network.OBJECT_ADD_EVENT);
140         network.addListener(this, Network.OBJECT_REMOVE_EVENT);
141         network.addListener(this, Network.NONLOCATED_OBJECT_ADD_EVENT);
142         network.addListener(this, Network.NONLOCATED_OBJECT_REMOVE_EVENT);
143 
144         // model the current infrastructure
145         if (animateNetwork)
146         {
147             for (Node node : network.getNodeMap().values())
148             {
149                 new NodeAnimation(new AnimationNodeData(node), this.simulator);
150             }
151             for (Link link : network.getLinkMap().values())
152             {
153                 new LinkAnimation(new AnimationLinkData(link), this.simulator, 0.5f);
154                 if (link instanceof CrossSectionLink cLink)
155                 {
156                     for (CrossSectionElement element : cLink.getCrossSectionElementList())
157                     {
158                         if (element instanceof Shoulder shoulder)
159                         {
160                             new CrossSectionElementAnimation<>(new AnimationShoulderData(shoulder), this.simulator,
161                                     Color.DARK_GRAY);
162                         }
163                         else if (element instanceof Lane lane)
164                         {
165                             new LaneAnimation(new AnimationLaneData(lane), this.simulator, Color.GRAY.brighter());
166                         }
167                         else if (element instanceof Stripe stripe)
168                         {
169                             new StripeAnimation(new AnimationStripeData(stripe), this.simulator);
170                         }
171                         else
172                         {
173                             new CrossSectionElementAnimation<>(new AnimationCrossSectionElementData<>(element), this.simulator,
174                                     Color.DARK_GRAY);
175                         }
176                     }
177                     if (!cLink.getPriority().isNone())
178                     {
179                         new PriorityAnimation(new AnimationPriorityData(cLink), this.simulator);
180                     }
181                 }
182             }
183         }
184 
185         for (Gtu gtu : network.getGTUs())
186         {
187             GtuData gtuData = new AnimationGtuData(this.gtuColorerManager, (LaneBasedGtu) gtu,
188                     this.gtuMarkers.getOrDefault(gtu.getType(), GtuMarker.CIRCLE));
189             Renderable2d<GtuData> gtuAnimation = new DefaultCarAnimation(gtuData, this.simulator);
190             this.animatedGTUs.put((LaneBasedGtu) gtu, gtuAnimation);
191 
192             Renderable2d<ChannelAttention> attentionAnimation = new PerceptionAnimation((LaneBasedGtu) gtu);
193             this.animatedAttention.put((LaneBasedGtu) gtu, attentionAnimation);
194         }
195 
196         for (LocatedObject object : network.getObjectMap().values())
197         {
198             animateLocatedObject(object);
199         }
200 
201         for (NonLocatedObject object : network.getNonLocatedObjectMap().values())
202         {
203             animateNonLocatedObject(object);
204         }
205 
206     }
207 
208     /**
209      * Set marker on GTU type.
210      * @param gtuType GTU type
211      * @param marker marker
212      * @throws NullPointerException when marker is null
213      */
214     public void setGtuMarker(final GtuType gtuType, final GtuMarker marker)
215     {
216         Throw.whenNull(marker, "marker");
217         this.gtuMarkers.put(gtuType, marker);
218     }
219 
220     /**
221      * Creates animations for nodes, links, lanes and GTUs. This can be used if the network is not read from XML. The class will
222      * subscribe to the network and listen to changes, so the adding and removing of GTUs and Objects is animated correctly.
223      * @param network the network
224      * @param contextualized context provider
225      * @param gtuColorerManager GTU colorer manager
226      * @param markers GTU type markers
227      * @return the DefaultAnimationFactory
228      */
229     public static DefaultAnimationFactory animateNetwork(final Network network, final Contextualized contextualized,
230             final GtuColorerManager gtuColorerManager, final Map<GtuType, GtuMarker> markers)
231     {
232         DefaultAnimationFactory factory = new DefaultAnimationFactory(network, gtuColorerManager, true);
233         markers.forEach((type, marker) -> factory.setGtuMarker(type, marker));
234         return factory;
235     }
236 
237     /**
238      * Creates animations for nodes, links, lanes and GTUs. This can be used if the network is read from XML. The class will
239      * subscribe to the network and listen to changes, so the adding and removing of GTUs and Objects is animated correctly.
240      * @param network the network
241      * @param gtuColorerManager GTU colorer manager
242      * @param markers GTU type markers
243      * @return the DefaultAnimationFactory
244      */
245     public static DefaultAnimationFactory animateXmlNetwork(final Network network, final GtuColorerManager gtuColorerManager,
246             final Map<GtuType, GtuMarker> markers)
247     {
248         DefaultAnimationFactory factory = new DefaultAnimationFactory(network, gtuColorerManager, false);
249         markers.forEach((type, marker) -> factory.setGtuMarker(type, marker));
250         return factory;
251     }
252 
253     @Override
254     public void notify(final Event event)
255     {
256         try
257         {
258             if (event.getType().equals(Network.GTU_ADD_EVENT))
259             {
260                 // schedule the addition of the GTU to prevent it from not having an operational plan
261                 Optional<Gtu> gtu = this.network.getGTU((String) event.getContent());
262                 if (gtu.isPresent() && gtu.get() instanceof LaneBasedGtu laneBasedGtu)
263                 {
264                     this.simulator.scheduleEventNow(() -> animateGTU(laneBasedGtu));
265                 }
266             }
267             else if (event.getType().equals(Network.GTU_REMOVE_EVENT))
268             {
269                 LaneBasedGtu gtu = (LaneBasedGtu) this.network.getGTU((String) event.getContent()).get();
270                 if (this.animatedGTUs.containsKey(gtu))
271                 {
272                     this.animatedGTUs.get(gtu).destroy(gtu.getSimulator());
273                     this.animatedGTUs.remove(gtu);
274                 }
275                 if (this.animatedAttention.containsKey(gtu))
276                 {
277                     this.animatedAttention.get(gtu).destroy(gtu.getSimulator());
278                     this.animatedAttention.remove(gtu);
279                 }
280             }
281             else if (event.getType().equals(Network.OBJECT_ADD_EVENT))
282             {
283                 LocatedObject object = this.network.getObjectMap().get((String) event.getContent());
284                 animateLocatedObject(object);
285             }
286             else if (event.getType().equals(Network.OBJECT_REMOVE_EVENT))
287             {
288                 LocatedObject object = this.network.getObjectMap().get((String) event.getContent());
289                 // TODO: this.animatedObjects.get(object).destroy(object.getSimulator());
290                 // XXX: this is now a memory leak; we don't expect static animation objects to be removed during the run
291                 this.animatedLocatedObjects.remove(object);
292             }
293             else if (event.getType().equals(Network.NONLOCATED_OBJECT_ADD_EVENT))
294             {
295                 NonLocatedObject object = this.network.getNonLocatedObjectMap().get((String) event.getContent());
296                 animateNonLocatedObject(object);
297             }
298             else if (event.getType().equals(Network.NONLOCATED_OBJECT_REMOVE_EVENT))
299             {
300                 NonLocatedObject object = this.network.getNonLocatedObjectMap().get((String) event.getContent());
301                 // TODO: this.animatedObjects.get(object).destroy(object.getSimulator());
302                 // XXX: this is now a memory leak; we don't expect static animation objects to be removed during the run
303                 this.animatedNonLocatedObjects.remove(object);
304             }
305         }
306         catch (SimRuntimeException exception)
307         {
308             Logger.ots().error(exception, "Exception while updating network animation.");
309         }
310     }
311 
312     /**
313      * Draw the GTU (scheduled method).
314      * @param gtu the GTU to draw
315      */
316     protected void animateGTU(final LaneBasedGtu gtu)
317     {
318         GtuData gtuData = new AnimationGtuData(this.gtuColorerManager, gtu,
319                 this.gtuMarkers.getOrDefault(gtu.getType(), GtuMarker.CIRCLE));
320         Renderable2d<GtuData> gtuAnimation = new DefaultCarAnimation(gtuData, this.simulator);
321         this.animatedGTUs.put(gtu, gtuAnimation);
322 
323         Renderable2d<ChannelAttention> attentionAnimation = new PerceptionAnimation((LaneBasedGtu) gtu);
324         this.animatedAttention.put((LaneBasedGtu) gtu, attentionAnimation);
325     }
326 
327     /**
328      * Draw the static object.
329      * @param object the object to draw
330      */
331     protected void animateLocatedObject(final LocatedObject object)
332     {
333         try
334         {
335             if (object instanceof SinkDetector)
336             {
337                 SinkDetector detector = (SinkDetector) object;
338                 // Renderable2d<SinkSensor> objectAnimation = new SinkAnimation(detector, this.simulator);
339                 Renderable2d<LaneDetectorData> objectAnimation = LaneDetectorAnimation
340                         .ofGenericType(new AnimationLaneDetectorData(detector), this.simulator, Color.ORANGE);
341                 this.animatedLocatedObjects.put(object, objectAnimation);
342             }
343             else if (object instanceof TrafficLightDetector)
344             {
345                 TrafficLightDetector trafficLigthDetector = (TrafficLightDetector) object;
346                 Renderable2d<TrafficLightDetectorData> objectAnimation = new TrafficLightDetectorAnimation(
347                         new AnimationTrafficLightDetectorData(trafficLigthDetector), this.simulator);
348                 this.animatedLocatedObjects.put(object, objectAnimation);
349             }
350             else if (object instanceof TrafficLightDetector.StartEndDetector)
351             {
352                 // we do not draw these, as we draw the TrafficLightDetector
353                 return;
354             }
355             else if (object instanceof LaneDetector)
356             {
357                 LaneDetector detector = (LaneDetector) object;
358                 Renderable2d<LaneDetectorData> objectAnimation = LaneDetectorAnimation
359                         .ofGenericType(new AnimationLaneDetectorData(detector), this.simulator, Color.BLACK);
360                 this.animatedLocatedObjects.put(object, objectAnimation);
361             }
362             else if (object instanceof Conflict)
363             {
364                 Conflict conflict = (Conflict) object;
365                 Renderable2d<ConflictData> objectAnimation =
366                         new ConflictAnimation(new AnimationConflictData(conflict), this.simulator);
367                 this.animatedLocatedObjects.put(object, objectAnimation);
368             }
369             else if (object instanceof TrafficLight)
370             {
371                 TrafficLight trafficLight = (TrafficLight) object;
372                 Renderable2d<TrafficLightData> objectAnimation =
373                         new TrafficLightAnimation(new AnimationTrafficLightData(trafficLight), this.simulator);
374                 this.animatedLocatedObjects.put(object, objectAnimation);
375             }
376             else if (object instanceof SpeedSign)
377             {
378                 SpeedSign speedSign = (SpeedSign) object;
379                 Renderable2d<SpeedSignData> objectAnimation =
380                         new SpeedSignAnimation(new AnimationSpeedSignData(speedSign), this.simulator);
381                 this.animatedLocatedObjects.put(object, objectAnimation);
382             }
383             else if (object instanceof BusStop)
384             {
385                 BusStop busStop = (BusStop) object;
386                 Renderable2d<BusStopData> objectAnimation =
387                         new BusStopAnimation(new AnimationBusStopData(busStop), this.simulator);
388                 this.animatedLocatedObjects.put(object, objectAnimation);
389             }
390         }
391         catch (RemoteException | NamingException exception)
392         {
393             Logger.ots().error(exception, "Exception while drawing Object of class LocatedObject.");
394         }
395     }
396 
397     /**
398      * Draw non-located objects.
399      * @param object the object to draw.
400      */
401     protected void animateNonLocatedObject(final NonLocatedObject object)
402     {
403         if (object instanceof LaneBasedGtuGenerator)
404         {
405             LaneBasedGtuGenerator generator = (LaneBasedGtuGenerator) object;
406             for (GtuGeneratorPosition position : generator.getPositions())
407             {
408                 Renderable2d<GtuGeneratorPositionData> objectAnimation =
409                         new GtuGeneratorPositionAnimation(new AnimationGtuGeneratorPositionData(position), this.simulator);
410                 this.animatedLocatedObjects.put(position, objectAnimation);
411             }
412         }
413     }
414 
415 }