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 }