1 package org.opentrafficsim.demo.carFollowing;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Dimension;
6 import java.awt.Frame;
7 import java.awt.GridBagConstraints;
8 import java.awt.GridBagLayout;
9 import java.awt.Insets;
10 import java.awt.Toolkit;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.awt.event.ItemEvent;
14 import java.awt.event.ItemListener;
15 import java.beans.PropertyChangeEvent;
16 import java.beans.PropertyChangeListener;
17 import java.util.ArrayList;
18 import java.util.List;
19
20 import javax.naming.NamingException;
21 import javax.swing.BoxLayout;
22 import javax.swing.ButtonGroup;
23 import javax.swing.JButton;
24 import javax.swing.JCheckBox;
25 import javax.swing.JComboBox;
26 import javax.swing.JFrame;
27 import javax.swing.JLabel;
28 import javax.swing.JPanel;
29 import javax.swing.JRadioButton;
30 import javax.swing.JScrollBar;
31 import javax.swing.JScrollPane;
32 import javax.swing.JSlider;
33 import javax.swing.JTextField;
34 import javax.swing.ScrollPaneConstants;
35 import javax.swing.SwingConstants;
36 import javax.swing.SwingUtilities;
37 import javax.swing.event.ChangeEvent;
38 import javax.swing.event.ChangeListener;
39 import javax.swing.event.DocumentEvent;
40 import javax.swing.event.DocumentListener;
41
42 import org.djunits.locale.DefaultLocale;
43 import org.djunits.unit.UNITS;
44 import org.djunits.value.vdouble.scalar.Acceleration;
45 import org.djunits.value.vdouble.scalar.Duration;
46 import org.djunits.value.vdouble.scalar.Length;
47 import org.djunits.value.vdouble.scalar.Time;
48 import org.opentrafficsim.base.modelproperties.BooleanProperty;
49 import org.opentrafficsim.base.modelproperties.CompoundProperty;
50 import org.opentrafficsim.base.modelproperties.ContinuousProperty;
51 import org.opentrafficsim.base.modelproperties.IntegerProperty;
52 import org.opentrafficsim.base.modelproperties.ProbabilityDistributionProperty;
53 import org.opentrafficsim.base.modelproperties.Property;
54 import org.opentrafficsim.base.modelproperties.PropertyException;
55 import org.opentrafficsim.base.modelproperties.SelectionProperty;
56 import org.opentrafficsim.base.modelproperties.StringProperty;
57 import org.opentrafficsim.core.network.NetworkException;
58 import org.opentrafficsim.demo.trafficcontrol.TrafCODDemo2;
59 import org.opentrafficsim.gui.LabeledPanel;
60 import org.opentrafficsim.gui.ProbabilityDistributionEditor;
61 import org.opentrafficsim.gui.SimulatorFrame;
62 import org.opentrafficsim.road.modelproperties.IDMPropertySet;
63 import org.opentrafficsim.simulationengine.OTSSimulationException;
64 import org.opentrafficsim.simulationengine.WrappableAnimation;
65
66 import nl.tudelft.simulation.dsol.SimRuntimeException;
67
68
69
70
71
72
73
74
75
76
77
78 public class SuperDemo implements UNITS
79 {
80
81 private JPanel propertyPanel;
82
83
84 @SuppressWarnings("checkstyle:visibilitymodifier")
85 protected JPanel simulationSelection;
86
87
88 @SuppressWarnings("checkstyle:visibilitymodifier")
89 protected List<Property<?>> activeProperties = null;
90
91
92 private LabeledPanel descriptionPanel;
93
94
95
96
97
98 public static void main(final String[] args)
99 {
100 SwingUtilities.invokeLater(new Runnable()
101 {
102 @Override
103 public void run()
104 {
105 try
106 {
107 JFrame frame = new SimulatorFrame("Open Traffic Simulator Demonstrations", new SuperDemo().buildGUI());
108 frame.setExtendedState(frame.getExtendedState() & ~Frame.MAXIMIZED_BOTH);
109
110
111
112 Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(frame.getGraphicsConfiguration());
113 int taskHeight = screenInsets.bottom;
114 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
115 frame.setSize(d.width / 2, d.height - taskHeight);
116 frame.setLocation(0, 0);
117 }
118 catch (PropertyException exception)
119 {
120 exception.printStackTrace();
121 }
122 }
123 });
124 }
125
126
127
128
129
130
131 public final JPanel buildGUI() throws PropertyException
132 {
133 final JPanel mainPanel = new JPanel(new BorderLayout());
134
135 mainPanel.setPreferredSize(new Dimension(800, 800));
136 final ArrayList<WrappableAnimation> demonstrations = new ArrayList<>();
137 demonstrations.add(new Straight());
138 demonstrations.add(new SequentialLanes());
139 demonstrations.add(new CircularLane());
140 demonstrations.add(new CircularRoad());
141 demonstrations.add(new XMLNetworks());
142 demonstrations.add(new XMLNetworks2());
143 demonstrations.add(new XMLSampler());
144 demonstrations.add(new OpenStreetMap());
145 demonstrations.add(new CrossingTrafficLights());
146 demonstrations.add(new TrafCODDemo2());
147
148 this.simulationSelection = new LabeledPanel("Network");
149 this.simulationSelection.setLayout(new GridBagLayout());
150 GridBagConstraints gbcSimulation = new GridBagConstraints();
151 gbcSimulation.gridx = 0;
152 gbcSimulation.gridy = -1;
153 gbcSimulation.anchor = GridBagConstraints.LINE_START;
154 gbcSimulation.weighty = 0;
155 gbcSimulation.fill = GridBagConstraints.HORIZONTAL;
156 final JPanel left = new JPanel(new GridBagLayout());
157 GridBagConstraints gbcLeft = new GridBagConstraints();
158 gbcLeft.gridx = 0;
159 gbcLeft.gridy = 0;
160 gbcLeft.anchor = GridBagConstraints.LINE_START;
161 gbcLeft.weighty = 0;
162 gbcLeft.fill = GridBagConstraints.HORIZONTAL;
163 final JLabel description = new JLabel("Please select a demonstration from the buttons on the left");
164 final JPanel centerPanel = new JPanel(new BorderLayout());
165 this.propertyPanel = new JPanel();
166 this.propertyPanel.setLayout(new BoxLayout(this.propertyPanel, BoxLayout.Y_AXIS));
167 rebuildPropertyPanel(new ArrayList<Property<?>>());
168 mainPanel.add(centerPanel, BorderLayout.CENTER);
169 this.descriptionPanel = new LabeledPanel("Description");
170 this.descriptionPanel.setLayout(new BorderLayout());
171 this.descriptionPanel.add(description);
172 centerPanel.add(this.descriptionPanel, BorderLayout.CENTER);
173 final JButton startButton = new JButton("Start simulation");
174 ButtonGroup buttonGroup = new ButtonGroup();
175 for (final WrappableAnimation demo : demonstrations)
176 {
177 CleverRadioButton button = new CleverRadioButton(demo);
178
179 button.addActionListener(new ActionListener()
180 {
181 @Override
182 public void actionPerformed(final ActionEvent e)
183 {
184
185 description.setText(demo.description());
186 startButton.setEnabled(true);
187 startButton.setVisible(true);
188 rebuildPropertyPanel(demo.getProperties());
189 }
190
191 });
192 buttonGroup.add(button);
193 gbcSimulation.gridy++;
194 this.simulationSelection.add(button, gbcSimulation);
195 }
196 JScrollPane scrollPane = new JScrollPane(left);
197 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
198 scrollPane.getVerticalScrollBar().setUnitIncrement(10);
199 mainPanel.add(scrollPane, BorderLayout.LINE_START);
200 startButton.setEnabled(false);
201 startButton.setVisible(false);
202 startButton.addActionListener(new ActionListener()
203 {
204 @Override
205 public void actionPerformed(final ActionEvent e)
206 {
207 System.out.println("Starting simulation");
208 WrappableAnimation simulation = null;
209 for (Component c : SuperDemo.this.simulationSelection.getComponents())
210 {
211 if (c instanceof CleverRadioButton)
212 {
213 CleverRadioButton crb = (CleverRadioButton) c;
214 if (crb.isSelected())
215 {
216 simulation = crb.getAnimation();
217 }
218 }
219 }
220
221 if (null == simulation)
222 {
223 throw new Error("Cannot find a selected button");
224 }
225
226 try
227 {
228 System.out.println("Active properties: " + SuperDemo.this.activeProperties);
229 simulation.buildAnimator(Time.ZERO, Duration.ZERO, new Duration(3600.0, SECOND),
230 SuperDemo.this.activeProperties, null, false);
231 }
232 catch (SimRuntimeException | NetworkException | NamingException | OTSSimulationException
233 | PropertyException exception)
234 {
235 exception.printStackTrace();
236 }
237 }
238 });
239 gbcLeft.gridy++;
240 left.add(this.propertyPanel, gbcLeft);
241 gbcLeft.gridy++;
242 gbcLeft.weighty = 1;
243 JPanel filler = new JPanel();
244 filler.setMinimumSize(new Dimension(400, 0));
245 filler.setPreferredSize(new Dimension(400, 0));
246 left.add(filler, gbcLeft);
247 gbcLeft.weighty = 0;
248 gbcLeft.anchor = GridBagConstraints.SOUTH;
249 left.add(startButton, gbcLeft);
250 JPanel rightFiller = new JPanel();
251 rightFiller.setPreferredSize(new Dimension((int) new JScrollBar().getPreferredSize().getWidth(), 0));
252 gbcLeft.gridx = 2;
253 left.add(rightFiller, gbcLeft);
254 return mainPanel;
255 }
256
257
258
259
260
261 final void rebuildPropertyPanel(final List<Property<?>> properties)
262 {
263 this.propertyPanel.removeAll();
264 try
265 {
266 CompoundProperty simulationSettings =
267 new CompoundProperty("SimulationSettings", "Simulation settings",
268 "Select the simulation network and traffic composition", null, false, 0);
269
270
271
272
273 BooleanProperty dummy = new BooleanProperty("Dummy", "Dummy", "Dummy", false, false, 0);
274 simulationSettings.add(dummy);
275 if (properties.size() > 0)
276 {
277 while (true)
278 {
279 boolean movedAny = false;
280
281 for (Property<?> ap : properties)
282 {
283 if (ap.getDisplayPriority() < 100)
284 {
285
286 simulationSettings.add(ap);
287 properties.remove(ap);
288 movedAny = true;
289 break;
290 }
291 }
292 if (!movedAny)
293 {
294 break;
295 }
296 }
297 simulationSettings.add(new ProbabilityDistributionProperty("TrafficComposition", "Traffic composition",
298 "<html>Mix of passenger cars and trucks</html>", new String[] { "passenger car", "truck" },
299 new Double[] { 0.8, 0.2 }, false, 5));
300 CompoundProperty modelSelection =
301 new CompoundProperty("ModelSelection", "Model selection", "Modeling specific settings", null, false,
302 300);
303 modelSelection.add(new SelectionProperty("SimulationScale", "Simulation scale",
304 "Level of detail of the simulation", new String[] { "Micro", "Macro", "Meta" }, 0, true, 0));
305 modelSelection.add(new SelectionProperty("CarFollowingModel", "Car following model",
306 "<html>The car following model determines "
307 + "the acceleration that a vehicle will make taking into account "
308 + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
309 + "curvature of the road) capabilities of the vehicle and personality "
310 + "of the driver.</html>", new String[] { "IDM", "IDM+" }, 1, false, 1));
311 modelSelection.add(IDMPropertySet.makeIDMPropertySet("IDMCar", "Car",
312 new Acceleration(1.56, METER_PER_SECOND_2), new Acceleration(2.09, METER_PER_SECOND_2),
313 new Length(3.0, METER), new Duration(1.2, SECOND), 2));
314 modelSelection.add(IDMPropertySet.makeIDMPropertySet("IDMTruck", "Truck", new Acceleration(0.75,
315 METER_PER_SECOND_2), new Acceleration(1.25, METER_PER_SECOND_2), new Length(3.0, METER), new Duration(
316 1.2, SECOND), 3));
317 properties.add(properties.size() > 0 ? 1 : 0, modelSelection);
318 }
319 properties.add(0, simulationSettings);
320 boolean fixedDummy = false;
321 for (Property<?> p : new CompoundProperty("", "", "", properties, false, 0).displayOrderedValue())
322 {
323 JPanel propertySubPanel = makePropertyEditor(p);
324 if (!fixedDummy)
325 {
326
327 JPanel subPanel = (JPanel) propertySubPanel.getComponent(0);
328 subPanel.removeAll();
329 subPanel.add(this.simulationSelection);
330 fixedDummy = true;
331 }
332 this.propertyPanel.add(propertySubPanel);
333 }
334 simulationSettings.remove(dummy);
335 SuperDemo.this.activeProperties = properties;
336 }
337 catch (PropertyException exception)
338 {
339 exception.printStackTrace();
340 }
341 }
342
343
344
345
346
347
348 @SuppressWarnings("checkstyle:methodlength")
349 final JPanel makePropertyEditor(final Property<?> ap)
350 {
351 JPanel result;
352 if (ap instanceof SelectionProperty)
353 {
354 result = new JPanel();
355 result.setLayout(new BorderLayout());
356 final SelectionProperty sp = (SelectionProperty) ap;
357 final JComboBox<String> comboBox = new JComboBox<String>(sp.getOptionNames());
358 comboBox.setSelectedItem(sp.getValue());
359 comboBox.setToolTipText(sp.getDescription());
360 comboBox.addItemListener(new ItemListener()
361 {
362 @Override
363 public void itemStateChanged(final ItemEvent itemEvent)
364 {
365 if (itemEvent.getStateChange() == ItemEvent.SELECTED)
366 {
367 String itemText = (String) itemEvent.getItem();
368 try
369 {
370 sp.setValue(itemText);
371 }
372 catch (PropertyException exception)
373 {
374 exception.printStackTrace();
375 }
376 }
377 }
378 });
379 if (ap.isReadOnly())
380 {
381 comboBox.removeItemListener(comboBox.getItemListeners()[0]);
382 comboBox.addActionListener(new ActionListener()
383 {
384
385 @Override
386 public void actionPerformed(final ActionEvent actionEvent)
387 {
388 if (comboBox.getSelectedIndex() != 0)
389 {
390 comboBox.setSelectedIndex(0);
391 }
392 }
393 });
394 }
395 result.setToolTipText(sp.getDescription());
396 result.add(new JLabel(ap.getShortName() + ": "), BorderLayout.LINE_START);
397 result.add(comboBox, BorderLayout.CENTER);
398 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) comboBox.getPreferredSize().getHeight()));
399 }
400 else if (ap instanceof ProbabilityDistributionProperty)
401 {
402 result = new LabeledPanel(ap.getShortName());
403 result.setLayout(new BorderLayout());
404 final ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
405 final ProbabilityDistributionEditor pdpe = new ProbabilityDistributionEditor(pdp.getElementNames(), pdp.getValue());
406 pdpe.addPropertyChangeListener(new PropertyChangeListener()
407 {
408 @Override
409 public void propertyChange(final PropertyChangeEvent arg0)
410 {
411 try
412 {
413 pdp.setValue(pdpe.getProbabilities());
414 }
415 catch (PropertyException exception)
416 {
417 exception.printStackTrace();
418 }
419 }
420
421 });
422 result.add(pdpe, BorderLayout.LINE_END);
423 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) new JLabel("ABC").getPreferredSize().getHeight()));
424 result.setToolTipText(pdp.getDescription());
425 }
426 else if (ap instanceof IntegerProperty)
427 {
428 final IntegerProperty ip = (IntegerProperty) ap;
429 result = new LabeledPanel(ap.getShortName());
430 result.setLayout(new BorderLayout());
431 final JSlider slider = new JSlider();
432 slider.setMaximum(ip.getMaximumValue());
433 slider.setMinimum(ip.getMinimumValue());
434 slider.setValue(ip.getValue());
435 slider.setPaintTicks(true);
436 final JLabel currentValue =
437 new JLabel(String.format(DefaultLocale.getLocale(), ip.getFormatString(), ip.getValue()),
438 SwingConstants.RIGHT);
439 slider.addChangeListener(new ChangeListener()
440 {
441 @Override
442 public void stateChanged(final ChangeEvent changeEvent)
443 {
444 int value = slider.getValue();
445 currentValue.setText(String.format(DefaultLocale.getLocale(), ip.getFormatString(), value));
446 if (slider.getValueIsAdjusting())
447 {
448 return;
449 }
450 try
451 {
452 ip.setValue(value);
453 }
454 catch (PropertyException exception)
455 {
456 exception.printStackTrace();
457 }
458 }
459 });
460 result.setToolTipText(ap.getDescription());
461 result.add(slider, BorderLayout.CENTER);
462 result.add(currentValue, BorderLayout.SOUTH);
463 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) slider.getPreferredSize().getHeight()));
464 }
465 else if (ap instanceof ContinuousProperty)
466 {
467 final ContinuousProperty cp = (ContinuousProperty) ap;
468 result = new LabeledPanel(ap.getShortName());
469 result.setLayout(new BorderLayout());
470 final JSlider slider = new JSlider();
471 final int useSteps = 1000;
472 slider.setMaximum(useSteps);
473 slider.setMinimum(0);
474 slider.setValue((int) (useSteps * (cp.getValue() - cp.getMinimumValue()) / (cp.getMaximumValue() - cp
475 .getMinimumValue())));
476 final JLabel currentValue =
477 new JLabel(String.format(DefaultLocale.getLocale(), cp.getFormatString(), cp.getValue()),
478 SwingConstants.RIGHT);
479 slider.addChangeListener(new ChangeListener()
480 {
481 @Override
482 public void stateChanged(final ChangeEvent changeEvent)
483 {
484 double value =
485 slider.getValue() * (cp.getMaximumValue() - cp.getMinimumValue()) / useSteps + cp.getMinimumValue();
486 currentValue.setText(String.format(DefaultLocale.getLocale(), cp.getFormatString(), value));
487 if (slider.getValueIsAdjusting())
488 {
489 return;
490 }
491 try
492 {
493 cp.setValue(value);
494 }
495 catch (PropertyException exception)
496 {
497 exception.printStackTrace();
498 }
499 }
500 });
501 result.setToolTipText(ap.getDescription());
502 result.add(slider, BorderLayout.CENTER);
503 result.add(currentValue, BorderLayout.SOUTH);
504 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) slider.getPreferredSize().getHeight() * 4));
505 }
506 else if (ap instanceof BooleanProperty)
507 {
508 final BooleanProperty bp = (BooleanProperty) ap;
509 result = new JPanel(new BorderLayout());
510 final JCheckBox checkBox = new JCheckBox(bp.getShortName(), bp.getValue());
511 checkBox.setToolTipText(bp.getDescription());
512 checkBox.setEnabled(!bp.isReadOnly());
513 checkBox.addChangeListener(new ChangeListener()
514 {
515 @Override
516 public void stateChanged(final ChangeEvent arg0)
517 {
518 try
519 {
520 bp.setValue(checkBox.isSelected());
521 }
522 catch (PropertyException exception)
523 {
524 exception.printStackTrace();
525 }
526 }
527 });
528 JPanel filler = new JPanel();
529 filler.setPreferredSize(new Dimension(0, (int) checkBox.getPreferredSize().getHeight()));
530 result.add(checkBox, BorderLayout.CENTER);
531 result.add(filler, BorderLayout.LINE_END);
532 }
533 else if (ap instanceof CompoundProperty)
534 {
535 CompoundProperty cp = (CompoundProperty) ap;
536 result = new LabeledPanel(ap.getShortName());
537 result.setLayout(new BoxLayout(result, BoxLayout.Y_AXIS));
538 for (Property<?> subProperty : cp.displayOrderedValue())
539 {
540 result.add(makePropertyEditor(subProperty));
541 }
542 }
543 else if (ap instanceof StringProperty)
544 {
545 StringProperty sp = (StringProperty) ap;
546 result = new LabeledPanel(sp.getShortName());
547 result.setLayout(new BorderLayout());
548 JTextField textField = new JTextField(sp.getValue(), 30);
549
550 textField.getDocument().addDocumentListener(new DocumentListener()
551 {
552
553 @Override
554 public void insertUpdate(final DocumentEvent e)
555 {
556 try
557 {
558 sp.setValue(textField.getText());
559 }
560 catch (PropertyException exception)
561 {
562 exception.printStackTrace();
563 }
564 }
565
566 @Override
567 public void removeUpdate(final DocumentEvent e)
568 {
569 try
570 {
571 sp.setValue(textField.getText());
572 }
573 catch (PropertyException exception)
574 {
575 exception.printStackTrace();
576 }
577 }
578
579 @Override
580 public void changedUpdate(final DocumentEvent e)
581 {
582 try
583 {
584 sp.setValue(textField.getText());
585 }
586 catch (PropertyException exception)
587 {
588 exception.printStackTrace();
589 }
590 }
591
592 });
593 result.add(textField, BorderLayout.CENTER);
594 }
595 else
596 {
597 throw new Error("Unhandled property: " + ap.getDescription());
598 }
599 return result;
600 }
601
602 }
603
604
605 class CleverRadioButton extends JRadioButton
606 {
607
608 private static final long serialVersionUID = 20141217L;
609
610
611 private final WrappableAnimation animation;
612
613
614
615
616
617
618 CleverRadioButton(final WrappableAnimation animation)
619 {
620 super(animation.shortName());
621 this.animation = animation;
622 }
623
624
625
626
627
628 public final WrappableAnimation getAnimation()
629 {
630 return this.animation;
631 }
632 }