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