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