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