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