1   package nl.tudelft.simulation.dsol.web.animation.D2;
2   
3   import java.awt.Color;
4   import java.awt.Dimension;
5   import java.awt.geom.Rectangle2D;
6   import java.rmi.RemoteException;
7   import java.util.ArrayList;
8   import java.util.LinkedHashMap;
9   import java.util.LinkedHashSet;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Set;
13  import java.util.SortedSet;
14  import java.util.TreeSet;
15  
16  import javax.media.j3d.BoundingBox;
17  import javax.naming.Binding;
18  import javax.naming.NamingEnumeration;
19  import javax.naming.event.EventContext;
20  import javax.naming.event.NamespaceChangeListener;
21  import javax.naming.event.NamingEvent;
22  import javax.naming.event.NamingExceptionEvent;
23  import javax.vecmath.Point3d;
24  import javax.vecmath.Point4i;
25  
26  import org.opentrafficsim.core.animation.gtu.colorer.GTUColorer;
27  
28  import nl.javel.gisbeans.map.MapInterface;
29  import nl.tudelft.simulation.dsol.animation.Locatable;
30  import nl.tudelft.simulation.dsol.animation.D2.GisRenderable2D;
31  import nl.tudelft.simulation.dsol.animation.D2.Renderable2DComparator;
32  import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
33  import nl.tudelft.simulation.dsol.logger.SimLogger;
34  import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
35  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
36  import nl.tudelft.simulation.dsol.web.animation.HTMLGraphics2D;
37  import nl.tudelft.simulation.event.EventInterface;
38  import nl.tudelft.simulation.event.EventListenerInterface;
39  import nl.tudelft.simulation.language.d3.DirectedPoint;
40  import nl.tudelft.simulation.naming.context.ContextUtil;
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  public class HTMLAnimationPanel extends HTMLGridPanel implements EventListenerInterface, NamespaceChangeListener
57  {
58      
59      private SortedSet<Renderable2DInterface<? extends Locatable>> elements =
60              new TreeSet<Renderable2DInterface<? extends Locatable>>(new Renderable2DComparator());
61  
62      
63      private Map<Class<? extends Locatable>, Boolean> visibilityMap = new LinkedHashMap<>();
64  
65      
66      private Set<Class<? extends Locatable>> hiddenClasses = new LinkedHashSet<>();
67  
68      
69      private Set<Class<? extends Locatable>> shownClasses = new LinkedHashSet<>();
70  
71      
72      private SimulatorInterface<?, ?, ?> simulator;
73  
74      
75      private EventContext context = null;
76  
77      
78      private Point4i dragLine = new Point4i();
79  
80      
81      private boolean dragLineEnabled = false;
82  
83      
84      private List<Renderable2DInterface<? extends Locatable>> elementList = new ArrayList<>();
85  
86      
87      private boolean dirtyElements = false;
88  
89      
90      private Map<String, Class<? extends Locatable>> toggleLocatableMap = new LinkedHashMap<>();
91  
92      
93      private Map<Class<? extends Locatable>, ToggleButtonInfo> toggleButtonMap = new LinkedHashMap<>();
94  
95      
96      private Map<String, MapInterface> toggleGISMap = new LinkedHashMap<>();
97  
98      
99      private Map<String, ToggleButtonInfo> toggleGISButtonMap = new LinkedHashMap<>();
100 
101     
102     private List<ToggleButtonInfo> toggleButtons = new ArrayList<>();
103 
104     
105     private GTUColorer gtuColorer = null;
106 
107     
108 
109 
110 
111 
112 
113 
114     public HTMLAnimationPanel(final Rectangle2D extent, final Dimension size, final SimulatorInterface<?, ?, ?> simulator)
115             throws RemoteException
116     {
117         super(extent, size);
118         super.showGrid = true;
119         this.simulator = simulator;
120         simulator.addListener(this, SimulatorInterface.START_REPLICATION_EVENT);
121     }
122 
123     
124     @Override
125     public void paintComponent(final HTMLGraphics2D g2)
126     {
127         
128         super.paintComponent(g2);
129 
130         
131         if (this.dirtyElements)
132         {
133             synchronized (this.elementList)
134             {
135                 this.elementList.clear();
136                 this.elementList.addAll(this.elements);
137                 this.dirtyElements = false;
138             }
139         }
140 
141         
142         for (Renderable2DInterface<? extends Locatable> element : this.elementList)
143         {
144             if (isShowElement(element))
145             {
146                 element.paint(g2, this.getExtent(), this.getSize(), this);
147             }
148         }
149 
150         
151         if (this.dragLineEnabled)
152         {
153             g2.setColor(Color.BLACK);
154             g2.drawLine(this.dragLine.w, this.dragLine.x, this.dragLine.y, this.dragLine.z);
155             this.dragLineEnabled = false;
156         }
157     }
158 
159     
160 
161 
162 
163 
164     public boolean isShowElement(final Renderable2DInterface<? extends Locatable> element)
165     {
166         return isShowClass(element.getSource().getClass());
167     }
168 
169     
170 
171 
172 
173 
174 
175     public boolean isShowClass(final Class<? extends Locatable> locatableClass)
176     {
177         if (this.hiddenClasses.contains(locatableClass))
178         {
179             return false;
180         }
181         else
182         {
183             boolean show = true;
184             if (!this.shownClasses.contains(locatableClass))
185             {
186                 for (Class<? extends Locatable> lc : this.visibilityMap.keySet())
187                 {
188                     if (lc.isAssignableFrom(locatableClass))
189                     {
190                         if (!this.visibilityMap.get(lc))
191                         {
192                             show = false;
193                         }
194                     }
195                 }
196                 
197                 if (show)
198                 {
199                     this.shownClasses.add(locatableClass);
200                 }
201                 else
202                 {
203                     this.hiddenClasses.add(locatableClass);
204                 }
205             }
206             return show;
207         }
208     }
209 
210     
211     @Override
212     public void notify(final EventInterface event) throws RemoteException
213     {
214         if (event.getSource() instanceof AnimatorInterface
215                 && event.getType().equals(SimulatorInterface.START_REPLICATION_EVENT))
216         {
217             synchronized (this.elementList)
218             {
219                 this.elements.clear();
220                 try
221                 {
222                     if (this.context != null)
223                     {
224                         this.context.removeNamingListener(this);
225                     }
226 
227                     this.context =
228                             (EventContext) ContextUtil.lookup(this.simulator.getReplication().getContext(), "/animation/2D");
229                     this.context.addNamingListener("", EventContext.SUBTREE_SCOPE, this);
230                     NamingEnumeration<Binding> list = this.context.listBindings("");
231                     while (list.hasMore())
232                     {
233                         Binding binding = list.next();
234                         this.objectAdded(new NamingEvent(this.context, -1, binding, binding, null));
235                     }
236                     this.repaint();
237                 }
238                 catch (Exception exception)
239                 {
240                     SimLogger.always().warn(exception, "notify");
241                 }
242             }
243         }
244     }
245 
246     
247     @Override
248     public void objectAdded(final NamingEvent namingEvent)
249     {
250         @SuppressWarnings("unchecked")
251         Renderable2DInterface<? extends Locatable> element =
252                 (Renderable2DInterface<? extends Locatable>) namingEvent.getNewBinding().getObject();
253         synchronized (this.elementList)
254         {
255             this.elements.add(element);
256             this.dirtyElements = true;
257         }
258     }
259 
260     
261     @Override
262     public void objectRemoved(final NamingEvent namingEvent)
263     {
264         @SuppressWarnings("unchecked")
265         Renderable2DInterface<? extends Locatable> element =
266                 (Renderable2DInterface<? extends Locatable>) namingEvent.getOldBinding().getObject();
267         synchronized (this.elementList)
268         {
269             this.elements.remove(element);
270             this.dirtyElements = true;
271         }
272     }
273 
274     
275     @Override
276     public void objectRenamed(final NamingEvent namingEvent)
277     {
278         this.objectRemoved(namingEvent);
279         this.objectAdded(namingEvent);
280     }
281 
282     
283     @Override
284     public void namingExceptionThrown(final NamingExceptionEvent namingEvent)
285     {
286         SimLogger.always().warn(namingEvent.getException(), "namingExceptionThrown");
287     }
288 
289     
290 
291 
292 
293     public synchronized Rectangle2D fullExtent()
294     {
295         double minX = Double.MAX_VALUE;
296         double maxX = -Double.MAX_VALUE;
297         double minY = Double.MAX_VALUE;
298         double maxY = -Double.MAX_VALUE;
299         Point3d p3dL = new Point3d();
300         Point3d p3dU = new Point3d();
301         try
302         {
303             for (Renderable2DInterface<? extends Locatable> renderable : this.elementList)
304             {
305                 DirectedPoint l = renderable.getSource().getLocation();
306                 BoundingBox b = new BoundingBox(renderable.getSource().getBounds());
307                 b.getLower(p3dL);
308                 b.getUpper(p3dU);
309                 minX = Math.min(minX, l.x + Math.min(p3dL.x, p3dU.x));
310                 minY = Math.min(minY, l.y + Math.min(p3dL.y, p3dU.y));
311                 maxX = Math.max(maxX, l.x + Math.max(p3dL.x, p3dU.x));
312                 maxY = Math.max(maxY, l.y + Math.max(p3dL.y, p3dU.y));
313             }
314         }
315         catch (Exception e)
316         {
317             
318         }
319 
320         minX = minX - 0.05 * Math.abs(minX);
321         minY = minY - 0.05 * Math.abs(minY);
322         maxX = maxX + 0.05 * Math.abs(maxX);
323         maxY = maxY + 0.05 * Math.abs(maxY);
324 
325         return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
326     }
327 
328     
329 
330 
331     public synchronized void zoomAll()
332     {
333         this.extent = Renderable2DInterface.Util.computeVisibleExtent(fullExtent(), this.getSize());
334         this.repaint();
335     }
336 
337     
338 
339 
340 
341     public void showClass(final Class<? extends Locatable> locatableClass)
342     {
343         this.visibilityMap.put(locatableClass, true);
344         this.shownClasses.clear();
345         this.hiddenClasses.clear();
346         this.repaint();
347     }
348 
349     
350 
351 
352 
353     public void hideClass(final Class<? extends Locatable> locatableClass)
354     {
355         this.visibilityMap.put(locatableClass, false);
356         this.shownClasses.clear();
357         this.hiddenClasses.clear();
358         this.repaint();
359     }
360 
361     
362 
363 
364 
365 
366     public void toggleClass(final Class<? extends Locatable> locatableClass)
367     {
368         if (!this.visibilityMap.containsKey(locatableClass))
369         {
370             showClass(locatableClass);
371         }
372         this.visibilityMap.put(locatableClass, !this.visibilityMap.get(locatableClass));
373         this.shownClasses.clear();
374         this.hiddenClasses.clear();
375         this.repaint();
376     }
377 
378     
379 
380 
381     public final SortedSet<Renderable2DInterface<? extends Locatable>> getElements()
382     {
383         return this.elements;
384     }
385 
386     
387 
388 
389     public final Point4i getDragLine()
390     {
391         return this.dragLine;
392     }
393 
394     
395 
396 
397     public final boolean isDragLineEnabled()
398     {
399         return this.dragLineEnabled;
400     }
401 
402     
403 
404 
405     public final void setDragLineEnabled(final boolean dragLineEnabled)
406     {
407         this.dragLineEnabled = dragLineEnabled;
408     }
409 
410     
411     
412     
413 
414     
415 
416 
417 
418 
419 
420 
421     public final void addToggleAnimationButtonText(final String name, final Class<? extends Locatable> locatableClass,
422             final String toolTipText, final boolean initiallyVisible)
423     {
424         ToggleButtonInfo.LocatableClass buttonInfo =
425                 new ToggleButtonInfo.LocatableClass(name, locatableClass, toolTipText, initiallyVisible);
426         if (initiallyVisible)
427         {
428             showClass(locatableClass);
429         }
430         else
431         {
432             hideClass(locatableClass);
433         }
434         this.toggleButtons.add(buttonInfo);
435         this.toggleLocatableMap.put(name, locatableClass);
436         this.toggleButtonMap.put(locatableClass, buttonInfo);
437     }
438 
439     
440 
441 
442 
443     public final void showClass(final String name)
444     {
445         showClass(this.toggleLocatableMap.get(name));
446     }
447     
448     
449 
450 
451 
452     public final void hideClass(final String name)
453     {
454         hideClass(this.toggleLocatableMap.get(name));
455     }
456     
457     
458 
459 
460 
461     public final void addToggleText(final String text)
462     {
463         this.toggleButtons.add(new ToggleButtonInfo.Text(text, true));
464     }
465 
466     
467 
468 
469 
470 
471 
472     public final void addAllToggleGISButtonText(final String header, final GisRenderable2D gisMap, final String toolTipText)
473     {
474         addToggleText(" ");
475         addToggleText(header);
476         try
477         {
478             for (String layerName : gisMap.getMap().getLayerMap().keySet())
479             {
480                 addToggleGISButtonText(layerName, layerName, gisMap, toolTipText);
481             }
482         }
483         catch (RemoteException exception)
484         {
485             exception.printStackTrace();
486         }
487     }
488 
489     
490 
491 
492 
493 
494 
495 
496     public final void addToggleGISButtonText(final String layerName, final String displayName, final GisRenderable2D gisMap,
497             final String toolTipText)
498     {
499         ToggleButtonInfo.Gis buttonInfo = new ToggleButtonInfo.Gis(displayName, layerName, toolTipText, true);
500         this.toggleButtons.add(buttonInfo);
501         this.toggleGISMap.put(layerName, gisMap.getMap());
502         this.toggleGISButtonMap.put(layerName, buttonInfo);
503     }
504 
505     
506 
507 
508 
509     public final void showGISLayer(final String layerName)
510     {
511         MapInterface gisMap = this.toggleGISMap.get(layerName);
512         if (gisMap != null)
513         {
514             try
515             {
516                 gisMap.showLayer(layerName);
517                 this.toggleGISButtonMap.get(layerName).setVisible(true);
518             }
519             catch (RemoteException exception)
520             {
521                 exception.printStackTrace();
522             }
523         }
524     }
525 
526     
527 
528 
529 
530     public final void hideGISLayer(final String layerName)
531     {
532         MapInterface gisMap = this.toggleGISMap.get(layerName);
533         if (gisMap != null)
534         {
535             try
536             {
537                 gisMap.hideLayer(layerName);
538                 this.toggleGISButtonMap.get(layerName).setVisible(false);
539             }
540             catch (RemoteException exception)
541             {
542                 exception.printStackTrace();
543             }
544         }
545     }
546 
547     
548 
549 
550 
551     public final void toggleGISLayer(final String layerName)
552     {
553         MapInterface gisMap = this.toggleGISMap.get(layerName);
554         if (gisMap != null)
555         {
556             try
557             {
558                 if (gisMap.getVisibleLayers().contains(gisMap.getLayerMap().get(layerName)))
559                 {
560                     gisMap.hideLayer(layerName);
561                     this.toggleGISButtonMap.get(layerName).setVisible(false);
562                 }
563                 else
564                 {
565                     gisMap.showLayer(layerName);
566                     this.toggleGISButtonMap.get(layerName).setVisible(true);
567                 }
568             }
569             catch (RemoteException exception)
570             {
571                 exception.printStackTrace();
572             }
573         }
574     }
575 
576     
577 
578 
579     public final List<ToggleButtonInfo> getToggleButtons()
580     {
581         return this.toggleButtons;
582     }
583 
584 }