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