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.EventInterface;
12  import org.djutils.event.EventListenerInterface;
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.GTUType;
19  import org.opentrafficsim.core.gtu.GtuGenerator;
20  import org.opentrafficsim.core.network.LateralDirectionality;
21  import org.opentrafficsim.core.network.Link;
22  import org.opentrafficsim.core.network.Network;
23  import org.opentrafficsim.core.network.Node;
24  import org.opentrafficsim.core.network.OTSNetwork;
25  import org.opentrafficsim.core.object.ObjectInterface;
26  import org.opentrafficsim.draw.core.OTSDrawingException;
27  import org.opentrafficsim.draw.gtu.DefaultCarAnimation;
28  import org.opentrafficsim.draw.network.LinkAnimation;
29  import org.opentrafficsim.draw.network.NodeAnimation;
30  import org.opentrafficsim.draw.road.BusStopAnimation;
31  import org.opentrafficsim.draw.road.ConflictAnimation;
32  import org.opentrafficsim.draw.road.LaneAnimation;
33  import org.opentrafficsim.draw.road.SensorAnimation;
34  import org.opentrafficsim.draw.road.ShoulderAnimation;
35  import org.opentrafficsim.draw.road.SpeedSignAnimation;
36  import org.opentrafficsim.draw.road.StripeAnimation;
37  import org.opentrafficsim.draw.road.StripeAnimation.TYPE;
38  import org.opentrafficsim.draw.road.TrafficLightAnimation;
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.Shoulder;
44  import org.opentrafficsim.road.network.lane.Stripe;
45  import org.opentrafficsim.road.network.lane.conflict.Conflict;
46  import org.opentrafficsim.road.network.lane.object.BusStop;
47  import org.opentrafficsim.road.network.lane.object.SpeedSign;
48  import org.opentrafficsim.road.network.lane.object.sensor.DestinationSensor;
49  import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
50  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
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.D2.Renderable2D;
55  
56  /**
57   * DefaultAnimationFactory.java. <br>
58   * <br>
59   * Copyright (c) 2003-2020 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
60   * for project information <a href="https://www.simulation.tudelft.nl/" target="_blank">www.simulation.tudelft.nl</a>. The
61   * source code and binary code of this software is proprietary information of Delft University of Technology.
62   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
63   */
64  public class DefaultAnimationFactory implements EventListenerInterface
65  {
66      /** the simulator. */
67      private final OTSSimulatorInterface simulator;
68  
69      /** GTU colorer. */
70      private final GTUColorer gtuColorer;
71  
72      /** rendered gtus. */
73      private Map<LaneBasedGTU, Renderable2D<LaneBasedGTU>> animatedGTUs = Collections.synchronizedMap(new LinkedHashMap<>());
74  
75      /** rendered static objects. */
76      public Map<ObjectInterface, Renderable2D<?>> animatedObjects = Collections.synchronizedMap(new LinkedHashMap<>());
77  
78      /**
79       * Creates animations for nodes, links and lanes. The class will subscribe to the network and listen to changes, so the
80       * adding and removing of GTUs and Objects is animated correctly.
81       * @param network OTSNetwork; the network
82       * @param simulator OTSSimulatorInterface; the simulator
83       * @param gtuColorer GTUColorer; GTU colorer
84       * @param animateNetwork boolean; whether to animate the current network objects
85       * @throws OTSDrawingException on drawing error
86       */
87      protected DefaultAnimationFactory(final OTSNetwork network, final OTSSimulatorInterface simulator,
88              final GTUColorer gtuColorer, final boolean animateNetwork) throws OTSDrawingException
89      {
90          this.simulator = simulator;
91          this.gtuColorer = gtuColorer;
92  
93          // subscribe to adding and removing events
94          network.addListener(this, Network.ANIMATION_GTU_ADD_EVENT);
95          network.addListener(this, Network.ANIMATION_GTU_REMOVE_EVENT);
96          network.addListener(this, Network.ANIMATION_OBJECT_ADD_EVENT);
97          network.addListener(this, Network.ANIMATION_OBJECT_REMOVE_EVENT);
98          network.addListener(this, Network.ANIMATION_GENERATOR_ADD_EVENT);
99          network.addListener(this, Network.ANIMATION_GENERATOR_REMOVE_EVENT);
100 
101         // model the current infrastructure
102         try
103         {
104             if (animateNetwork)
105             {
106                 for (Node node : network.getNodeMap().values())
107                 {
108                     new NodeAnimation(node, simulator);
109                 }
110                 for (Link link : network.getLinkMap().values())
111                 {
112                     new LinkAnimation(link, simulator, 0.5f);
113                     if (link instanceof CrossSectionLink)
114                     {
115                         for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
116                         {
117                             if (element instanceof Lane)
118                             {
119                                 new LaneAnimation((Lane) element, simulator, Color.GRAY.brighter());
120                             }
121                             else if (element instanceof Shoulder)
122                             {
123                                 new ShoulderAnimation((Shoulder) element, simulator, Color.DARK_GRAY);
124                             }
125                             else if (element instanceof Stripe)
126                             {
127                                 Stripe stripe = (Stripe) element;
128                                 TYPE type;
129                                 if (stripe.isPermeable(network.getGtuType(GTUType.DEFAULTS.CAR), LateralDirectionality.LEFT))
130                                 {
131                                     type = stripe.isPermeable(network.getGtuType(GTUType.DEFAULTS.CAR),
132                                             LateralDirectionality.RIGHT) ? TYPE.DASHED : TYPE.LEFTONLY;
133                                 }
134                                 else
135                                 {
136                                     type = stripe.isPermeable(network.getGtuType(GTUType.DEFAULTS.CAR),
137                                             LateralDirectionality.RIGHT) ? TYPE.RIGHTONLY : TYPE.SOLID;
138                                 }
139                                 new StripeAnimation((Stripe) element, simulator, type);
140                             }
141                         }
142                     }
143                 }
144 
145                 for (TrafficLight tl : network.getObjectMap(TrafficLight.class).values())
146                 {
147                     new TrafficLightAnimation(tl, simulator);
148                 }
149 
150             }
151 
152             for (GTU gtu : network.getGTUs())
153             {
154                 Renderable2D<LaneBasedGTU> gtuAnimation =
155                         new DefaultCarAnimation((LaneBasedGTU) gtu, simulator, this.gtuColorer);
156                 this.animatedGTUs.put((LaneBasedGTU) gtu, gtuAnimation);
157             }
158 
159             for (ObjectInterface object : network.getObjectMap().values())
160             {
161                 animateStaticObject(object);
162             }
163         }
164         catch (RemoteException | NamingException | OTSGeometryException exception)
165         {
166             throw new OTSDrawingException("Exception while creating network animation.", exception);
167         }
168 
169     }
170 
171     /**
172      * Creates animations for nodes, links, lanes and GTUs. This can be used if the network is not read from XML. The class will
173      * subscribe to the network and listen to changes, so the adding and removing of GTUs and Objects is animated correctly.
174      * @param network OTSNetwork; the network
175      * @param simulator OTSSimulatorInterface; the simulator
176      * @param gtuColorer GTUColorer; GTU colorer
177      * @return the DefaultAnimationFactory
178      * @throws OTSDrawingException on drawing error
179      */
180     public static DefaultAnimationFactory animateNetwork(final OTSNetwork network, final OTSSimulatorInterface simulator,
181             final GTUColorer gtuColorer) throws OTSDrawingException
182     {
183         return new DefaultAnimationFactory(network, simulator, gtuColorer, true);
184     }
185 
186     /**
187      * Creates animations for nodes, links, lanes and GTUs. This can be used if the network is read from XML. The class will
188      * subscribe to the network and listen to changes, so the adding and removing of GTUs and Objects is animated correctly.
189      * @param network OTSNetwork; the network
190      * @param simulator OTSSimulatorInterface; the simulator
191      * @param gtuColorer GTUColorer; GTU colorer
192      * @return the DefaultAnimationFactory
193      * @throws OTSDrawingException on drawing error
194      */
195     public static DefaultAnimationFactory animateXmlNetwork(final OTSNetwork network, final OTSSimulatorInterface simulator,
196             final GTUColorer gtuColorer) throws OTSDrawingException
197     {
198         return new DefaultAnimationFactory(network, simulator, gtuColorer, false);
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public void notify(final EventInterface event) throws RemoteException
204     {
205         try
206         {
207             if (event.getType().equals(Network.ANIMATION_GTU_ADD_EVENT))
208             {
209                 // schedule the addition of the GTU to prevent it from not having an operational plan
210                 LaneBasedGTU gtu = (LaneBasedGTU) event.getContent();
211                 this.simulator.scheduleEventNow(this, this, "animateGTU", new Object[] {gtu});
212             }
213             else if (event.getType().equals(Network.ANIMATION_GTU_REMOVE_EVENT))
214             {
215                 LaneBasedGTU gtu = (LaneBasedGTU) event.getContent();
216                 if (this.animatedGTUs.containsKey(gtu))
217                 {
218                     this.animatedGTUs.get(gtu).destroy();
219                     this.animatedGTUs.remove(gtu);
220                 }
221             }
222             else if (event.getType().equals(Network.ANIMATION_OBJECT_ADD_EVENT))
223             {
224                 ObjectInterface object = (ObjectInterface) event.getContent();
225                 animateStaticObject(object);
226             }
227             else if (event.getType().equals(Network.ANIMATION_OBJECT_REMOVE_EVENT))
228             {
229                 ObjectInterface object = (ObjectInterface) event.getContent();
230                 if (this.animatedObjects.containsKey(object))
231                 {
232                     this.animatedObjects.get(object).destroy();
233                     this.animatedObjects.remove(object);
234                 }
235             }
236             else if (event.getType().equals(Network.ANIMATION_GENERATOR_ADD_EVENT))
237             {
238                 GtuGenerator gtuGenerator = (GtuGenerator) event.getContent();
239                 animateGTUGenerator(gtuGenerator);
240             }
241             else if (event.getType().equals(Network.ANIMATION_GENERATOR_REMOVE_EVENT))
242             {
243                 // TODO: change the way generators are animated
244             }
245         }
246         catch (NamingException | SimRuntimeException exception)
247         {
248             CategoryLogger.always().error(exception, "Exception while updating network animation.");
249         }
250     }
251 
252     /**
253      * Draw the GTU (scheduled method).
254      * @param gtu LaneBasedGTU; the GTU to draw
255      */
256     protected void animateGTU(final LaneBasedGTU gtu)
257     {
258         try
259         {
260             Renderable2D<LaneBasedGTU> gtuAnimation = new DefaultCarAnimation(gtu, this.simulator, this.gtuColorer);
261             this.animatedGTUs.put(gtu, gtuAnimation);
262         }
263         catch (RemoteException | NamingException exception)
264         {
265             gtu.getSimulator().getLogger().always().error(exception, "Exception while drawing GTU.");
266         }
267     }
268 
269     /**
270      * Draw the static object.
271      * @param object ObjectInterface; the object to draw
272      */
273     protected void animateStaticObject(final ObjectInterface object)
274     {
275         try
276         {
277             if (object instanceof SinkSensor)
278             {
279                 SinkSensor sensor = (SinkSensor) object;
280                 // Renderable2D<SinkSensor> objectAnimation = new SinkAnimation(sensor, this.simulator);
281                 Renderable2D<SingleSensor> objectAnimation =
282                         new SensorAnimation(sensor, sensor.getLongitudinalPosition(), this.simulator, Color.YELLOW);
283                 this.animatedObjects.put(object, objectAnimation);
284             }
285             else if (object instanceof DestinationSensor)
286             {
287                 DestinationSensor sensor = (DestinationSensor) object;
288                 // Renderable2D<DestinationSensor> objectAnimation = new DestinationAnimation(sensor, this.simulator);
289                 Renderable2D<SingleSensor> objectAnimation =
290                         new SensorAnimation(sensor, sensor.getLongitudinalPosition(), this.simulator, Color.ORANGE);
291                 this.animatedObjects.put(object, objectAnimation);
292             }
293             else if (object instanceof SingleSensor)
294             {
295                 SingleSensor sensor = (SingleSensor) object;
296                 Renderable2D<SingleSensor> objectAnimation =
297                         new SensorAnimation(sensor, sensor.getLongitudinalPosition(), this.simulator, Color.GREEN);
298                 this.animatedObjects.put(object, objectAnimation);
299             }
300             else if (object instanceof Conflict)
301             {
302                 Conflict conflict = (Conflict) object;
303                 Renderable2D<Conflict> objectAnimation = new ConflictAnimation(conflict, this.simulator);
304                 this.animatedObjects.put(object, objectAnimation);
305             }
306             else if (object instanceof SpeedSign)
307             {
308                 SpeedSign speedSign = (SpeedSign) object;
309                 Renderable2D<SpeedSign> objectAnimation = new SpeedSignAnimation(speedSign, this.simulator);
310                 this.animatedObjects.put(object, objectAnimation);
311             }
312             else if (object instanceof BusStop)
313             {
314                 BusStop busStop = (BusStop) object;
315                 Renderable2D<BusStop> objectAnimation = new BusStopAnimation(busStop, this.simulator);
316                 this.animatedObjects.put(object, objectAnimation);
317             }
318         }
319         catch (RemoteException | NamingException exception)
320         {
321             CategoryLogger.always().error(exception, "Exception while drawing Object of class ObjectInterface.");
322         }
323     }
324 
325     /**
326      * Draw the GTUGenerator.
327      * @param gtuGenerator GtuGenerator; the GTUGenerator to draw
328      */
329     protected void animateGTUGenerator(final GtuGenerator gtuGenerator)
330     {
331         // TODO: default animation of GTU generator
332     }
333 
334 }