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