1 package nl.tudelft.simulation.dsol.web.animation.d2;
2
3 import java.awt.Color;
4 import java.awt.geom.AffineTransform;
5 import java.rmi.RemoteException;
6 import java.util.ArrayList;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedHashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Set;
12 import java.util.SortedSet;
13 import java.util.TreeSet;
14
15 import org.djutils.draw.bounds.Bounds;
16 import org.djutils.draw.bounds.Bounds2d;
17 import org.djutils.draw.point.Point;
18 import org.djutils.event.Event;
19 import org.djutils.event.EventListener;
20 import org.opentrafficsim.animation.gtu.colorer.GtuColorerManager;
21 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
22
23 import nl.tudelft.simulation.dsol.animation.Locatable;
24 import nl.tudelft.simulation.dsol.animation.d2.Renderable2dComparator;
25 import nl.tudelft.simulation.dsol.animation.d2.Renderable2dInterface;
26 import nl.tudelft.simulation.dsol.animation.gis.GisMapInterface;
27 import nl.tudelft.simulation.dsol.animation.gis.GisRenderable2d;
28 import nl.tudelft.simulation.dsol.experiment.Replication;
29 import nl.tudelft.simulation.dsol.web.animation.HtmlGraphics2d;
30 import nl.tudelft.simulation.naming.context.ContextInterface;
31 import nl.tudelft.simulation.naming.context.util.ContextUtil;
32
33
34
35
36
37
38
39
40
41
42 public class HtmlAnimationPanel extends HtmlGridPanel implements EventListener
43 {
44
45 private static final long serialVersionUID = 1L;
46
47
48 private SortedSet<Renderable2dInterface<? extends Locatable>> elements =
49 new TreeSet<Renderable2dInterface<? extends Locatable>>(new Renderable2dComparator());
50
51
52 private Map<Class<? extends Locatable>, Boolean> visibilityMap = new LinkedHashMap<>();
53
54
55 private Set<Class<? extends Locatable>> hiddenClasses = new LinkedHashSet<>();
56
57
58 private Set<Class<? extends Locatable>> shownClasses = new LinkedHashSet<>();
59
60
61 private OtsSimulatorInterface simulator;
62
63
64 private ContextInterface context = null;
65
66
67 private int[] dragLine = new int[4];
68
69
70 private boolean dragLineEnabled = false;
71
72
73 private List<Renderable2dInterface<? extends Locatable>> elementList = new ArrayList<>();
74
75
76 private boolean dirtyElements = false;
77
78
79 private Map<String, Class<? extends Locatable>> toggleLocatableMap = new LinkedHashMap<>();
80
81
82 private Map<Class<? extends Locatable>, ToggleButtonInfo> toggleButtonMap = new LinkedHashMap<>();
83
84
85 private Map<String, GisMapInterface> toggleGISMap = new LinkedHashMap<>();
86
87
88 private Map<String, ToggleButtonInfo> toggleGISButtonMap = new LinkedHashMap<>();
89
90
91 private List<ToggleButtonInfo> toggleButtons = new ArrayList<>();
92
93
94 private final GtuColorerManager gtuColorerManager = new GtuColorerManager(Color.WHITE);
95
96
97 public static final double EXTENT_MARGIN_FACTOR = 0.05;
98
99
100
101
102
103
104
105 public HtmlAnimationPanel(final Bounds2d homeExtent, final OtsSimulatorInterface simulator) throws RemoteException
106 {
107 super(homeExtent);
108 super.showGrid = true;
109 this.simulator = simulator;
110 simulator.addListener(this, Replication.START_REPLICATION_EVENT);
111 }
112
113
114
115
116
117 public GtuColorerManager getGtuColorerManager()
118 {
119 return this.gtuColorerManager;
120 }
121
122 @Override
123 public void paintComponent(final HtmlGraphics2d g2)
124 {
125
126 super.paintComponent(g2);
127
128
129 if (this.dirtyElements)
130 {
131 synchronized (this.elementList)
132 {
133 this.elementList.clear();
134 this.elementList.addAll(this.elements);
135 this.dirtyElements = false;
136 }
137 }
138
139
140 for (Renderable2dInterface<? extends Locatable> element : this.elementList)
141 {
142
143 if (element.getSource() == null)
144 {
145 objectRemoved(element);
146 }
147 else if (isShowElement(element))
148 {
149 AffineTransform at = (AffineTransform) g2.getTransform().clone();
150 element.paintComponent(g2, this.getExtent(), this.getSize(), this.renderableScale, this);
151 g2.setTransform(at);
152 }
153 }
154
155
156 if (this.dragLineEnabled)
157 {
158 g2.setColor(Color.BLACK);
159 g2.drawLine(this.dragLine[0], this.dragLine[1], this.dragLine[2], this.dragLine[3]);
160 this.dragLineEnabled = false;
161 }
162 }
163
164
165
166
167
168
169 public boolean isShowElement(final Renderable2dInterface<? extends Locatable> element)
170 {
171 return element.getSource() == null ? false : isShowClass(element.getSource().getClass());
172 }
173
174
175
176
177
178
179
180 public boolean isShowClass(final Class<? extends Locatable> locatableClass)
181 {
182 if (this.hiddenClasses.contains(locatableClass))
183 {
184 return false;
185 }
186 else
187 {
188 boolean show = true;
189 if (!this.shownClasses.contains(locatableClass))
190 {
191 for (Class<? extends Locatable> lc : this.visibilityMap.keySet())
192 {
193 if (lc.isAssignableFrom(locatableClass))
194 {
195 if (!this.visibilityMap.get(lc))
196 {
197 show = false;
198 }
199 }
200 }
201
202 if (show)
203 {
204 this.shownClasses.add(locatableClass);
205 }
206 else
207 {
208 this.hiddenClasses.add(locatableClass);
209 }
210 }
211 return show;
212 }
213 }
214
215 @SuppressWarnings("unchecked")
216 @Override
217 public void notify(final Event event) throws RemoteException
218 {
219 if (event.getType().equals(ContextInterface.OBJECT_ADDED_EVENT))
220 {
221 objectAdded((Renderable2dInterface<? extends Locatable>) ((Object[]) event.getContent())[2]);
222 }
223
224 else if (event.getType().equals(ContextInterface.OBJECT_REMOVED_EVENT))
225 {
226 objectRemoved((Renderable2dInterface<? extends Locatable>) ((Object[]) event.getContent())[2]);
227 }
228
229 else if
230 (event.getType().equals(Replication.START_REPLICATION_EVENT))
231 {
232 synchronized (this.elementList)
233 {
234 this.elements.clear();
235 try
236 {
237 if (this.context != null)
238 {
239 this.context.removeListener(this, ContextInterface.OBJECT_ADDED_EVENT);
240 this.context.removeListener(this, ContextInterface.OBJECT_REMOVED_EVENT);
241 }
242
243 this.context =
244 ContextUtil.lookupOrCreateSubContext(this.simulator.getReplication().getContext(), "animation/2D");
245 this.context.addListener(this, ContextInterface.OBJECT_ADDED_EVENT);
246 this.context.addListener(this, ContextInterface.OBJECT_REMOVED_EVENT);
247 for (Object element : this.context.values())
248 {
249 objectAdded((Renderable2dInterface<? extends Locatable>) element);
250 }
251 this.repaint();
252 }
253 catch (Exception exception)
254 {
255 this.simulator.getLogger().always().warn(exception, "notify");
256 }
257 }
258 }
259 }
260
261
262
263
264
265 public void objectAdded(final Renderable2dInterface<? extends Locatable> element)
266 {
267 synchronized (this.elementList)
268 {
269 this.elements.add(element);
270 this.dirtyElements = true;
271 }
272 }
273
274
275
276
277
278 public void objectRemoved(final Renderable2dInterface<? extends Locatable> element)
279 {
280 synchronized (this.elementList)
281 {
282 this.elements.remove(element);
283 this.dirtyElements = true;
284 }
285 }
286
287
288
289
290
291 public synchronized Bounds2d fullExtent()
292 {
293 double minX = Double.MAX_VALUE;
294 double maxX = -Double.MAX_VALUE;
295 double minY = Double.MAX_VALUE;
296 double maxY = -Double.MAX_VALUE;
297 try
298 {
299 for (Renderable2dInterface<? extends Locatable> renderable : this.elementList)
300 {
301 if (renderable.getSource() == null)
302 {
303 continue;
304 }
305 Point<?> l = renderable.getSource().getLocation();
306 if (l != null)
307 {
308 Bounds<?, ?, ?> b = renderable.getSource().getBounds();
309 minX = Math.min(minX, l.getX() + b.getMinX());
310 minY = Math.min(minY, l.getY() + b.getMinY());
311 maxX = Math.max(maxX, l.getX() + b.getMaxX());
312 maxY = Math.max(maxY, l.getY() + b.getMaxY());
313 }
314 }
315 }
316 catch (Exception e)
317 {
318
319 }
320
321 minX -= EXTENT_MARGIN_FACTOR * Math.abs(maxX - minX);
322 minY -= EXTENT_MARGIN_FACTOR * Math.abs(maxY - minY);
323 maxX += EXTENT_MARGIN_FACTOR * Math.abs(maxX - minX);
324 maxY += EXTENT_MARGIN_FACTOR * Math.abs(maxY - minY);
325
326 return new Bounds2d(minX, maxX, minY, maxY);
327 }
328
329
330
331
332 public synchronized void zoomAll()
333 {
334 setExtent(getRenderableScale().computeVisibleExtent(fullExtent(), this.getSize()));
335 this.repaint();
336 }
337
338
339
340
341
342 public void showClass(final Class<? extends Locatable> locatableClass)
343 {
344 this.visibilityMap.put(locatableClass, true);
345 this.shownClasses.clear();
346 this.hiddenClasses.clear();
347 this.repaint();
348 }
349
350
351
352
353
354 public void hideClass(final Class<? extends Locatable> locatableClass)
355 {
356 this.visibilityMap.put(locatableClass, false);
357 this.shownClasses.clear();
358 this.hiddenClasses.clear();
359 this.repaint();
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 int[] 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 GisMapInterface 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 GisMapInterface 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 GisMapInterface 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 }