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