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