1 package org.opentrafficsim.demo.fd;
2
3 import java.awt.Color;
4 import java.awt.Component;
5 import java.awt.Container;
6 import java.awt.Dimension;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.lang.reflect.Field;
10 import java.util.ArrayList;
11 import java.util.Dictionary;
12 import java.util.Hashtable;
13 import java.util.LinkedHashMap;
14 import java.util.LinkedHashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import javax.swing.Box;
20 import javax.swing.BoxLayout;
21 import javax.swing.JButton;
22 import javax.swing.JComboBox;
23 import javax.swing.JLabel;
24 import javax.swing.JPanel;
25 import javax.swing.JPopupMenu;
26 import javax.swing.JSlider;
27 import javax.swing.border.EmptyBorder;
28 import javax.swing.event.ChangeEvent;
29 import javax.swing.event.ChangeListener;
30
31 import org.djunits.unit.FrequencyUnit;
32 import org.djunits.unit.SpeedUnit;
33 import org.djunits.value.vdouble.scalar.Acceleration;
34 import org.djunits.value.vdouble.scalar.Direction;
35 import org.djunits.value.vdouble.scalar.Duration;
36 import org.djunits.value.vdouble.scalar.Frequency;
37 import org.djunits.value.vdouble.scalar.Length;
38 import org.djunits.value.vdouble.scalar.Speed;
39 import org.djutils.cli.CliUtil;
40 import org.djutils.draw.point.Point2d;
41 import org.djutils.exceptions.Try;
42 import org.djutils.means.HarmonicMean;
43 import org.opentrafficsim.animation.GraphLaneUtil;
44 import org.opentrafficsim.base.parameters.ParameterException;
45 import org.opentrafficsim.base.parameters.ParameterTypes;
46 import org.opentrafficsim.base.parameters.Parameters;
47 import org.opentrafficsim.core.definitions.Defaults;
48 import org.opentrafficsim.core.definitions.DefaultsNl;
49 import org.opentrafficsim.core.distributions.Generator;
50 import org.opentrafficsim.core.distributions.ProbabilityException;
51 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
52 import org.opentrafficsim.core.gtu.Gtu;
53 import org.opentrafficsim.core.gtu.GtuErrorHandler;
54 import org.opentrafficsim.core.gtu.GtuException;
55 import org.opentrafficsim.core.gtu.GtuType;
56 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
57 import org.opentrafficsim.core.idgenerator.IdGenerator;
58 import org.opentrafficsim.core.network.Link;
59 import org.opentrafficsim.core.network.LinkPosition;
60 import org.opentrafficsim.core.network.LinkType;
61 import org.opentrafficsim.core.network.NetworkException;
62 import org.opentrafficsim.core.network.Node;
63 import org.opentrafficsim.core.parameters.ParameterFactory;
64 import org.opentrafficsim.draw.graphs.FundamentalDiagram;
65 import org.opentrafficsim.draw.graphs.FundamentalDiagram.FdLine;
66 import org.opentrafficsim.draw.graphs.FundamentalDiagram.FdSource;
67 import org.opentrafficsim.draw.graphs.FundamentalDiagram.Quantity;
68 import org.opentrafficsim.draw.graphs.GraphCrossSection;
69 import org.opentrafficsim.draw.graphs.GraphPath;
70 import org.opentrafficsim.draw.graphs.PlotScheduler;
71 import org.opentrafficsim.draw.graphs.TrajectoryPlot;
72 import org.opentrafficsim.road.definitions.DefaultsRoadNl;
73 import org.opentrafficsim.road.gtu.generator.CfBaRoomChecker;
74 import org.opentrafficsim.road.gtu.generator.GeneratorPositions;
75 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.LaneBias;
76 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.LaneBiases;
77 import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator;
78 import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator.RoomChecker;
79 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
80 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator;
81 import org.opentrafficsim.road.gtu.lane.VehicleModel;
82 import org.opentrafficsim.road.gtu.lane.perception.PerceptionFactory;
83 import org.opentrafficsim.road.gtu.lane.perception.categories.DirectInfrastructurePerception;
84 import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
85 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory;
86 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModelFactory;
87 import org.opentrafficsim.road.gtu.lane.tactical.following.IdmPlus;
88 import org.opentrafficsim.road.gtu.lane.tactical.following.IdmPlusFactory;
89 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.DefaultLmrsPerceptionFactory;
90 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs;
91 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory;
92 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlannerFactory;
93 import org.opentrafficsim.road.network.RoadNetwork;
94 import org.opentrafficsim.road.network.factory.LaneFactory;
95 import org.opentrafficsim.road.network.lane.CrossSectionLink;
96 import org.opentrafficsim.road.network.lane.Lane;
97 import org.opentrafficsim.road.network.lane.LanePosition;
98 import org.opentrafficsim.road.network.lane.LaneType;
99 import org.opentrafficsim.road.network.lane.Stripe.Type;
100 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
101 import org.opentrafficsim.road.network.lane.object.detector.SinkDetector;
102 import org.opentrafficsim.road.network.sampling.LaneDataRoad;
103 import org.opentrafficsim.road.network.sampling.RoadSampler;
104 import org.opentrafficsim.swing.graphs.OtsPlotScheduler;
105 import org.opentrafficsim.swing.graphs.SwingFundamentalDiagram;
106 import org.opentrafficsim.swing.graphs.SwingTrajectoryPlot;
107 import org.opentrafficsim.swing.gui.OtsAnimationPanel;
108 import org.opentrafficsim.swing.gui.OtsAnimationPanel.DemoPanelPosition;
109 import org.opentrafficsim.swing.script.AbstractSimulationScript;
110
111 import nl.tudelft.simulation.dsol.swing.gui.TablePanel;
112 import nl.tudelft.simulation.jstats.distributions.DistNormal;
113 import nl.tudelft.simulation.jstats.streams.StreamInterface;
114
115
116
117
118
119
120
121
122
123
124
125 public class FundamentalDiagramDemo extends AbstractSimulationScript
126 {
127
128
129 private static final long serialVersionUID = 20200509L;
130
131
132 private Frequency demand = new Frequency(3500.0, FrequencyUnit.PER_HOUR);
133
134
135 private double truckFraction = 0.05;
136
137
138 private Speed speedLimit = new Speed(120.0, SpeedUnit.KM_PER_HOUR);
139
140
141 private Duration tMin = Duration.instantiateSI(0.56);
142
143
144 private Duration tMax = Duration.instantiateSI(1.2);
145
146
147 private JPanel splitPanel;
148
149
150 private TablePanel graphPanel;
151
152
153 private RoadSampler sampler;
154
155
156 private String absoluteCrossSection1 = "1.50";
157
158
159 private String absoluteCrossSection2 = "None";
160
161
162 private String absoluteCrossSection3 = "None";
163
164
165 @SuppressWarnings("synthetic-access")
166 private DynamicFdLine fdLine = new DynamicFdLine();
167
168
169 private Set<FundamentalDiagram> funamentalDiagrams = new LinkedHashSet<>();
170
171
172 private Map<String, FdSource> fdSourceMap = new LinkedHashMap<>();
173
174
175 private Container trajectoryPanel;
176
177
178 private PlotScheduler scheduler;
179
180
181
182
183 public FundamentalDiagramDemo()
184 {
185 super("FD Demo", "Fundamental diagram demo");
186 }
187
188
189
190
191
192 public static void main(final String[] args)
193 {
194 FundamentalDiagramDemo demo = new FundamentalDiagramDemo();
195 try
196 {
197 CliUtil.changeOptionDefault(demo, "simulationTime", "360000s");
198 CliUtil.execute(demo, args);
199 demo.start();
200 }
201 catch (Exception exception)
202 {
203 exception.printStackTrace();
204 }
205 }
206
207
208 @Override
209 protected RoadNetwork setupSimulation(final OtsSimulatorInterface sim) throws Exception
210 {
211
212 RoadNetwork network = new RoadNetwork("FD demo network", sim);
213 GtuType car = DefaultsNl.CAR;
214 GtuType truck = DefaultsNl.TRUCK;
215 GtuType.registerTemplateSupplier(car, Defaults.NL);
216 GtuType.registerTemplateSupplier(truck, Defaults.NL);
217
218 Node nodeA = new Node(network, "Origin", new Point2d(0.0, 0.0), Direction.ZERO);
219 Node nodeB = new Node(network, "Lane-drop", new Point2d(1500.0, 0.0), Direction.ZERO);
220 Node nodeC = new Node(network, "Destination", new Point2d(2500.0, 0.0), Direction.ZERO);
221
222 LinkType linkType = DefaultsNl.FREEWAY;
223 LaneKeepingPolicy policy = LaneKeepingPolicy.KEEPRIGHT;
224 Length laneWidth = Length.instantiateSI(3.5);
225 LaneType laneType = DefaultsRoadNl.FREEWAY;
226 Speed speedLim = new Speed(120.0, SpeedUnit.KM_PER_HOUR);
227
228 List<Lane> lanesAB = new LaneFactory(network, nodeA, nodeB, linkType, sim, policy, DefaultsNl.VEHICLE)
229 .leftToRight(3.0, laneWidth, laneType, speedLim).addLanes(Type.DASHED, Type.DASHED).getLanes();
230 List<Lane> lanesBC = new LaneFactory(network, nodeB, nodeC, linkType, sim, policy, DefaultsNl.VEHICLE)
231 .leftToRight(2.0, laneWidth, laneType, speedLim).addLanes(Type.DASHED).getLanes();
232
233
234
235 StreamInterface stream = sim.getModel().getStream("generation");
236 Generator<Duration> interarrivelTimeGenerator = new Generator<Duration>()
237 {
238 @Override
239 public Duration draw() throws ProbabilityException, ParameterException
240 {
241 @SuppressWarnings("synthetic-access")
242 double mean = 1.0 / FundamentalDiagramDemo.this.demand.si;
243 return Duration.instantiateSI(-mean * Math.log(stream.nextDouble()));
244 }
245 };
246
247 CarFollowingModelFactory<IdmPlus> carFollowingModelFactory = new IdmPlusFactory(stream);
248 PerceptionFactory perceptionFactory = new DefaultLmrsPerceptionFactory();
249 LaneBasedTacticalPlannerFactory<Lmrs> tacticalPlannerFactory =
250 new LmrsFactory(carFollowingModelFactory, perceptionFactory);
251 DistNormal fSpeed = new DistNormal(stream, 123.7 / 120.0, 12.0 / 120.0);
252 ParameterFactory parametersFactory = new ParameterFactory()
253 {
254 @SuppressWarnings("synthetic-access")
255 @Override
256 public void setValues(final Parameters parameters, final GtuType gtuType) throws ParameterException
257 {
258 if (gtuType.equals(truck))
259 {
260 parameters.setParameter(ParameterTypes.A, Acceleration.instantiateSI(0.4));
261 }
262 else
263 {
264 parameters.setParameter(ParameterTypes.A, Acceleration.instantiateSI(2.0));
265 }
266 parameters.setParameter(ParameterTypes.FSPEED, fSpeed.draw());
267 parameters.setParameter(ParameterTypes.TMIN, FundamentalDiagramDemo.this.tMin);
268 parameters.setParameter(ParameterTypes.TMAX, FundamentalDiagramDemo.this.tMax);
269 }
270 };
271 LaneBasedStrategicalRoutePlannerFactory laneBasedStrategicalPlannerFactory =
272 new LaneBasedStrategicalRoutePlannerFactory(tacticalPlannerFactory, parametersFactory);
273 LaneBasedGtuCharacteristicsGenerator laneBasedGtuCharacteristicsGenerator = new LaneBasedGtuCharacteristicsGenerator()
274 {
275 @Override
276 public LaneBasedGtuCharacteristics draw() throws ProbabilityException, ParameterException, GtuException
277 {
278 @SuppressWarnings("synthetic-access")
279 GtuType gtuType = stream.nextDouble() > FundamentalDiagramDemo.this.truckFraction ? car : truck;
280 return new LaneBasedGtuCharacteristics(GtuType.defaultCharacteristics(gtuType, network, stream),
281 laneBasedStrategicalPlannerFactory, null, nodeA, nodeC, VehicleModel.MINMAX);
282 }
283 };
284
285 Set<LanePosition> initialPosition = new LinkedHashSet<>();
286 for (Lane lane : lanesAB)
287 {
288 initialPosition.add(new LanePosition(lane, Length.ZERO));
289 }
290 LaneBiases biases = new LaneBiases();
291 biases.addBias(car, LaneBias.bySpeed(new Speed(130.0, SpeedUnit.KM_PER_HOUR), new Speed(70.0, SpeedUnit.KM_PER_HOUR)));
292 biases.addBias(truck, LaneBias.TRUCK_RIGHT);
293 GeneratorPositions generatorPositions = GeneratorPositions.create(initialPosition, stream, biases);
294
295 RoomChecker roomChecker = new CfBaRoomChecker();
296
297 IdGenerator idGenerator = new IdGenerator("");
298
299 LaneBasedGtuGenerator generator = new LaneBasedGtuGenerator("generator", interarrivelTimeGenerator,
300 laneBasedGtuCharacteristicsGenerator, generatorPositions, network, sim, roomChecker, idGenerator);
301 generator.setErrorHandler(GtuErrorHandler.DELETE);
302 generator.setInstantaneousLaneChange(true);
303 generator.setNoLaneChangeDistance(Length.instantiateSI(100.0));
304
305
306 for (Lane lane : lanesBC)
307 {
308 new SinkDetector(lane, lane.getLength(), sim, DefaultsRoadNl.ROAD_USERS);
309 }
310
311 return network;
312 }
313
314
315 @Override
316 protected void setupDemo(final OtsAnimationPanel animationPanel, final RoadNetwork net)
317 {
318 this.fdLine.update();
319
320
321 animationPanel.createDemoPanel(DemoPanelPosition.BOTTOM);
322 animationPanel.getDemoPanel().setPreferredSize(new Dimension(1000, 500));
323 this.splitPanel = new JPanel();
324 JPanel controlPanel = new JPanel();
325 this.splitPanel.setLayout(new BoxLayout(this.splitPanel, BoxLayout.X_AXIS));
326 this.splitPanel.add(controlPanel);
327 animationPanel.getDemoPanel().add(this.splitPanel);
328
329
330 controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
331 controlPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
332 controlPanel.setPreferredSize(new Dimension(250, 500));
333 Dimension controlSize = new Dimension(250, 0);
334 int strutSize = 20;
335
336 JLabel crossSectionLabel = new JLabel("<html>Cross-section location [km]</html>");
337 crossSectionLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
338 crossSectionLabel.setMinimumSize(controlSize);
339 controlPanel.add(crossSectionLabel);
340 List<String> list = new ArrayList<>();
341 for (int i = 250; i <= 2250; i += 250)
342 {
343 list.add(String.format("%.2f", i / 1000.0));
344 }
345 JComboBox<String> crossSectionMenu = new JComboBox<String>(list.toArray(new String[0]));
346 Dimension crossSectionMenuSize = new Dimension(250, 25);
347 crossSectionMenu.setMinimumSize(crossSectionMenuSize);
348 crossSectionMenu.setMaximumSize(crossSectionMenuSize);
349 crossSectionMenu.setSelectedIndex(5);
350 crossSectionMenu.addActionListener(new ActionListener()
351 {
352 @SuppressWarnings({"synthetic-access", "unchecked"})
353 @Override
354 public void actionPerformed(final ActionEvent e)
355 {
356 FundamentalDiagramDemo.this.absoluteCrossSection1 =
357 ((String) ((JComboBox<String>) e.getSource()).getSelectedItem()).replace(",", ".");
358 createFundamentalDiagramsForCrossSections();
359 }
360 });
361 controlPanel.add(crossSectionMenu);
362
363 list = new ArrayList<>();
364 list.add("None");
365 for (int i = 250; i <= 2250; i += 250)
366 {
367 list.add(String.format("%.2f", i / 1000.0));
368 }
369 crossSectionMenu = new JComboBox<String>(list.toArray(new String[0]));
370 crossSectionMenu.setMinimumSize(crossSectionMenuSize);
371 crossSectionMenu.setMaximumSize(crossSectionMenuSize);
372 crossSectionMenu.setSelectedIndex(0);
373 crossSectionMenu.addActionListener(new ActionListener()
374 {
375 @SuppressWarnings({"synthetic-access", "unchecked"})
376 @Override
377 public void actionPerformed(final ActionEvent e)
378 {
379 FundamentalDiagramDemo.this.absoluteCrossSection2 =
380 ((String) ((JComboBox<String>) e.getSource()).getSelectedItem()).replace(",", ".");
381 createFundamentalDiagramsForCrossSections();
382 }
383 });
384 controlPanel.add(crossSectionMenu);
385
386 crossSectionMenu = new JComboBox<String>(list.toArray(new String[0]));
387 crossSectionMenu.setMinimumSize(crossSectionMenuSize);
388 crossSectionMenu.setMaximumSize(crossSectionMenuSize);
389 crossSectionMenu.setSelectedIndex(0);
390 crossSectionMenu.addActionListener(new ActionListener()
391 {
392 @SuppressWarnings({"synthetic-access", "unchecked"})
393 @Override
394 public void actionPerformed(final ActionEvent e)
395 {
396 FundamentalDiagramDemo.this.absoluteCrossSection3 =
397 ((String) ((JComboBox<String>) e.getSource()).getSelectedItem()).replace(",", ".");
398 createFundamentalDiagramsForCrossSections();
399 }
400 });
401 controlPanel.add(crossSectionMenu);
402
403 controlPanel.add(Box.createVerticalStrut(strutSize));
404
405 JButton reset = new JButton("Clear data & graphs");
406 reset.setAlignmentX(Component.CENTER_ALIGNMENT);
407 reset.addActionListener(new ActionListener()
408 {
409 @SuppressWarnings("synthetic-access")
410 @Override
411 public void actionPerformed(final ActionEvent e)
412 {
413 clearDataAndGraphs();
414 }
415 });
416 controlPanel.add(reset);
417
418 controlPanel.add(Box.createVerticalStrut(strutSize));
419
420 JLabel demandLabel = new JLabel("<html>Demand [veh/h]</html>");
421 demandLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
422 demandLabel.setPreferredSize(controlSize);
423 controlPanel.add(demandLabel);
424 JSlider demandSlider = new JSlider(500, 5000, 3500);
425 demandSlider.setPreferredSize(controlSize);
426 demandSlider.setSnapToTicks(true);
427 demandSlider.setMinorTickSpacing(250);
428 demandSlider.setMajorTickSpacing(1000);
429 demandSlider.setPaintTicks(true);
430 demandSlider.setPaintLabels(true);
431 demandSlider.setToolTipText("<html>Demand [veh/h]</html>");
432 demandSlider.addChangeListener(new ChangeListener()
433 {
434 @SuppressWarnings("synthetic-access")
435 @Override
436 public void stateChanged(final ChangeEvent e)
437 {
438 double value = ((JSlider) e.getSource()).getValue();
439 FundamentalDiagramDemo.this.demand = new Frequency(value, FrequencyUnit.PER_HOUR);
440 }
441 });
442 controlPanel.add(demandSlider);
443
444 controlPanel.add(Box.createVerticalStrut(strutSize));
445
446 JLabel truckLabel = new JLabel("<html>Truck percentage [%]</html>");
447 truckLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
448 truckLabel.setPreferredSize(controlSize);
449 controlPanel.add(truckLabel);
450 JSlider truckSlider = new JSlider(0, 30, 5);
451 truckSlider.setPreferredSize(controlSize);
452 truckSlider.setSnapToTicks(true);
453 truckSlider.setMinorTickSpacing(5);
454 truckSlider.setMajorTickSpacing(10);
455 truckSlider.setPaintTicks(true);
456 truckSlider.setPaintLabels(true);
457 truckSlider.setToolTipText("<html>Truck percentage [%]</html>");
458 truckSlider.addChangeListener(new ChangeListener()
459 {
460 @SuppressWarnings("synthetic-access")
461 @Override
462 public void stateChanged(final ChangeEvent e)
463 {
464 double value = ((JSlider) e.getSource()).getValue() / 100.0;
465 FundamentalDiagramDemo.this.truckFraction = value;
466 FundamentalDiagramDemo.this.fdLine.update();
467 notifyPlotsChanged();
468 }
469 });
470 controlPanel.add(truckSlider);
471
472 controlPanel.add(Box.createVerticalStrut(strutSize));
473
474 JLabel tLabel = new JLabel("<html>Max. headway [s]</html>");
475 tLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
476 tLabel.setPreferredSize(controlSize);
477 controlPanel.add(tLabel);
478 JSlider tSlider = new JSlider(10, 20, 12);
479 Dictionary<Integer, JLabel> labels = new Hashtable<>();
480 for (int i = 10; i <= 20; i += 2)
481 {
482 labels.put(i, new JLabel(String.format("%.1f", i / 10.0)));
483 }
484 tSlider.setLabelTable(labels);
485 tSlider.setPreferredSize(controlSize);
486 tSlider.setSnapToTicks(true);
487 tSlider.setMinorTickSpacing(1);
488 tSlider.setMajorTickSpacing(2);
489 tSlider.setPaintTicks(true);
490 tSlider.setPaintLabels(true);
491 tSlider.setToolTipText("<html>Max. headway [s]</html>");
492 tSlider.addChangeListener(new ChangeListener()
493 {
494 @SuppressWarnings("synthetic-access")
495 @Override
496 public void stateChanged(final ChangeEvent e)
497 {
498 double value = ((JSlider) e.getSource()).getValue() / 10.0;
499 FundamentalDiagramDemo.this.tMin = Duration.instantiateSI((0.56 / 1.2) * value);
500 FundamentalDiagramDemo.this.tMax = Duration.instantiateSI(value);
501 FundamentalDiagramDemo.this.fdLine.update();
502 notifyPlotsChanged();
503 for (Gtu gtu : getNetwork().getGTUs())
504 {
505 try
506 {
507 gtu.getParameters().setParameter(ParameterTypes.TMIN, FundamentalDiagramDemo.this.tMin);
508 gtu.getParameters().setParameter(ParameterTypes.TMAX, FundamentalDiagramDemo.this.tMax);
509 }
510 catch (ParameterException exception)
511 {
512 System.err.println("Unable to set headway parameter.");
513 }
514 }
515 }
516 });
517 controlPanel.add(tSlider);
518
519 controlPanel.add(Box.createVerticalStrut(strutSize));
520
521 JLabel vLabel = new JLabel("<html>Speed limit [km/h]</html>");
522 vLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
523 vLabel.setPreferredSize(controlSize);
524 controlPanel.add(vLabel);
525 JSlider vSlider = new JSlider(80, 130, 120);
526 vSlider.setPreferredSize(controlSize);
527 vSlider.setSnapToTicks(true);
528 vSlider.setMinorTickSpacing(10);
529 vSlider.setMajorTickSpacing(10);
530 vSlider.setPaintTicks(true);
531 vSlider.setPaintLabels(true);
532 vSlider.setToolTipText("<html>Speed limit [km/h]</html>");
533 vSlider.addChangeListener(new ChangeListener()
534 {
535 @SuppressWarnings("synthetic-access")
536 @Override
537 public void stateChanged(final ChangeEvent e)
538 {
539 FundamentalDiagramDemo.this.speedLimit = new Speed(((JSlider) e.getSource()).getValue(), SpeedUnit.KM_PER_HOUR);
540 FundamentalDiagramDemo.this.fdLine.update();
541 notifyPlotsChanged();
542 for (Link link : getNetwork().getLinkMap().values())
543 {
544 for (Lane lane : ((CrossSectionLink) link).getLanes())
545 {
546 lane.setSpeedLimit(DefaultsNl.VEHICLE, FundamentalDiagramDemo.this.speedLimit);
547 }
548 }
549 for (Gtu gtu : getNetwork().getGTUs())
550 {
551 DirectInfrastructurePerception infra;
552 try
553 {
554 infra = (DirectInfrastructurePerception) gtu.getTacticalPlanner().getPerception()
555 .getPerceptionCategory(InfrastructurePerception.class);
556
557 Field field = DirectInfrastructurePerception.class.getDeclaredField("root");
558 field.setAccessible(true);
559 field.set(infra, null);
560 }
561 catch (OperationalPlanException | NoSuchFieldException | SecurityException | IllegalArgumentException
562 | IllegalAccessException exception)
563 {
564 System.err.println("Unable to update perceived speed limit.");
565 }
566 }
567 }
568 });
569 controlPanel.add(vSlider);
570
571
572 this.scheduler = new OtsPlotScheduler(getSimulator());
573 clearDataAndGraphs();
574 }
575
576
577
578
579 void notifyPlotsChanged()
580 {
581 for (FundamentalDiagram diagram : this.funamentalDiagrams)
582 {
583 diagram.notifyPlotChange();
584 }
585 }
586
587
588
589
590 private void clearDataAndGraphs()
591 {
592
593 boolean wasRunning = getSimulator().isStartingOrRunning();
594 if (wasRunning)
595 {
596 getSimulator().stop();
597 }
598
599
600 this.sampler = new RoadSampler(getNetwork());
601
602
603 for (int i = 250; i <= 2250; i += 250)
604 {
605 List<String> names = new ArrayList<>();
606 names.add("Left");
607 names.add("Right");
608 Length lanePosition;
609 String linkId;
610 if (i >= 1500.0)
611 {
612 lanePosition = Length.instantiateSI(i - 1500.0);
613 linkId = "Lane-dropDestination";
614 }
615 else
616 {
617 names.add(1, "Middle");
618 lanePosition = Length.instantiateSI(i);
619 linkId = "OriginLane-drop";
620 }
621 LinkPosition linkPosition = new LinkPosition(getNetwork().getLink(linkId), lanePosition);
622 GraphCrossSection<LaneDataRoad> crossSection;
623 try
624 {
625 crossSection = GraphLaneUtil.createCrossSection(names, linkPosition);
626 }
627 catch (NetworkException exception)
628 {
629 throw new RuntimeException("Unable to create cross section.", exception);
630 }
631 Duration aggregationTime = Duration.instantiateSI(30.0);
632 FdSource source = FundamentalDiagram.sourceFromSampler(this.sampler, crossSection, true, aggregationTime, false);
633 this.fdSourceMap.put(String.format("%.2f", i / 1000.0).replace(",", "."), source);
634 }
635
636
637 List<String> names = new ArrayList<>();
638 names.add("Left lane");
639 names.add("Middle lane");
640 names.add("Right lane");
641 List<Lane> firstLanes = new ArrayList<>();
642 for (Lane lane : ((CrossSectionLink) getNetwork().getLink("OriginLane-drop")).getLanes())
643 {
644 firstLanes.add(lane);
645 }
646 GraphPath<LaneDataRoad> path = Try.assign(() -> GraphLaneUtil.createPath(names, firstLanes), "");
647 TrajectoryPlot trajectoryPlot = new TrajectoryPlot("Trajectories", Duration.instantiateSI(5.0), this.scheduler,
648 this.sampler.getSamplerData(), path);
649 trajectoryPlot.updateFixedDomainRange(true);
650 SwingTrajectoryPlot swingTrajectoryPlot = new SwingTrajectoryPlot(trajectoryPlot)
651 {
652
653 private static final long serialVersionUID = 20200516L;
654
655
656 @Override
657 protected void addPopUpMenuItems(final JPopupMenu popupMenu)
658 {
659
660 }
661 };
662 this.trajectoryPanel = swingTrajectoryPlot.getContentPane();
663
664
665 if (!getSimulator().isStartingOrRunning() && wasRunning)
666 {
667 getSimulator().start();
668 }
669
670
671 createFundamentalDiagramsForCrossSections();
672 }
673
674
675
676
677 private void createFundamentalDiagramsForCrossSections()
678 {
679
680 boolean wasRunning = getSimulator().isStartingOrRunning();
681 if (wasRunning)
682 {
683 getSimulator().stop();
684 }
685
686
687 Color color = null;
688 if (this.graphPanel != null)
689 {
690 color = this.graphPanel.getBackground();
691
692 this.splitPanel.remove(this.graphPanel);
693 }
694
695 this.graphPanel = new TablePanel(2, 2);
696 this.graphPanel.setBorder(new EmptyBorder(0, 0, 20, 0));
697 if (color != null)
698 {
699 this.graphPanel.setBackground(color);
700 }
701 this.splitPanel.add(this.graphPanel);
702
703
704 FdSource source;
705 if (this.absoluteCrossSection2.equals("None") && this.absoluteCrossSection3.equals("None"))
706 {
707 source = this.fdSourceMap.get(this.absoluteCrossSection1);
708 if (source == null)
709 {
710 source = this.fdSourceMap.get(this.absoluteCrossSection1);
711 }
712 source.clearFundamentalDiagrams();
713 }
714 else
715 {
716 Map<String, FdSource> sources = new LinkedHashMap<>();
717 sources.put(this.absoluteCrossSection1 + "km", this.fdSourceMap.get(this.absoluteCrossSection1));
718 if (!this.absoluteCrossSection2.equals("None"))
719 {
720 sources.put(this.absoluteCrossSection2 + "km", this.fdSourceMap.get(this.absoluteCrossSection2));
721 }
722 if (!this.absoluteCrossSection3.equals("None"))
723 {
724 sources.put(this.absoluteCrossSection3 + "km", this.fdSourceMap.get(this.absoluteCrossSection3));
725 }
726 for (FdSource subSource : sources.values())
727 {
728 subSource.clearFundamentalDiagrams();
729 }
730 source = FundamentalDiagram.combinedSource(sources);
731 }
732
733
734 source.setAggregateName(this.absoluteCrossSection1);
735
736
737 FundamentalDiagram fdPlota =
738 new FundamentalDiagram("Density-speed", Quantity.DENSITY, Quantity.SPEED, this.scheduler, source, this.fdLine);
739 FundamentalDiagram fdPlotb =
740 new FundamentalDiagram("Density-flow", Quantity.DENSITY, Quantity.FLOW, this.scheduler, source, this.fdLine);
741 FundamentalDiagram fdPlotc =
742 new FundamentalDiagram("Flow-speed", Quantity.FLOW, Quantity.SPEED, this.scheduler, source, this.fdLine);
743
744
745 source.recalculate(getSimulator().getSimulatorAbsTime());
746
747
748 this.funamentalDiagrams.clear();
749 this.funamentalDiagrams.add(fdPlota);
750 this.funamentalDiagrams.add(fdPlotb);
751 this.funamentalDiagrams.add(fdPlotc);
752
753
754 Container fda = new SwingFundamentalDiagramNoControl(fdPlota).getContentPane();
755 Container fdb = new SwingFundamentalDiagramNoControl(fdPlotb).getContentPane();
756 Container fdc = new SwingFundamentalDiagramNoControl(fdPlotc).getContentPane();
757 Dimension preferredGraphSize = new Dimension(375, 230);
758 fda.setPreferredSize(preferredGraphSize);
759 fdb.setPreferredSize(preferredGraphSize);
760 fdc.setPreferredSize(preferredGraphSize);
761 this.graphPanel.setCell(fda, 0, 0);
762 this.graphPanel.setCell(fdb, 0, 1);
763 this.graphPanel.setCell(fdc, 1, 0);
764
765
766 this.trajectoryPanel.setPreferredSize(preferredGraphSize);
767 this.graphPanel.setCell(this.trajectoryPanel, 1, 1);
768
769
770 if (color != null)
771 {
772 fda.setBackground(color);
773 fdb.setBackground(color);
774 fdc.setBackground(color);
775 this.trajectoryPanel.setBackground(color);
776 }
777
778
779 fda.getParent().getParent().validate();
780
781
782 if (!getSimulator().isStartingOrRunning() && wasRunning)
783 {
784 getSimulator().start();
785 }
786 }
787
788
789
790
791 private class SwingFundamentalDiagramNoControl extends SwingFundamentalDiagram
792 {
793
794 private static final long serialVersionUID = 20200516L;
795
796
797
798
799 SwingFundamentalDiagramNoControl(final FundamentalDiagram plot)
800 {
801 super(plot);
802 }
803
804
805 @Override
806 protected void addPopUpMenuItems(final JPopupMenu popupMenu)
807 {
808
809 }
810 }
811
812
813
814
815 private class DynamicFdLine implements FdLine
816 {
817
818 private Map<Quantity, double[]> map = new LinkedHashMap<>();
819
820
821 @Override
822 public double[] getValues(final Quantity quantity)
823 {
824 return this.map.get(quantity);
825 }
826
827
828 @Override
829 public String getName()
830 {
831 return "Theoretical";
832 }
833
834
835
836
837 @SuppressWarnings("synthetic-access")
838 public void update()
839 {
840
841 HarmonicMean<Speed, Double> meanSpeed = new HarmonicMean<>();
842 Speed carSpeed = FundamentalDiagramDemo.this.speedLimit.times(123.7 / 120.0);
843 meanSpeed.add(carSpeed, 1.0 - FundamentalDiagramDemo.this.truckFraction);
844 Speed truckSpeed = Speed.min(carSpeed, new Speed(85.0, SpeedUnit.KM_PER_HOUR));
845 meanSpeed.add(truckSpeed, FundamentalDiagramDemo.this.truckFraction);
846
847
848 double meanLength =
849 4.19 * (1.0 - FundamentalDiagramDemo.this.truckFraction) + 12.0 * FundamentalDiagramDemo.this.truckFraction;
850
851
852 double vMax = meanSpeed.getMean();
853 double kCrit = 1000.0 / (vMax * FundamentalDiagramDemo.this.tMax.si + meanLength + 3.0);
854 vMax = vMax * 3.6;
855 double qMax = vMax * kCrit;
856 int kJam = (int) (1000.0 / (meanLength + 3.0));
857
858
859 double[] k = new double[kJam * 10 + 1];
860 double[] q = new double[kJam * 10 + 1];
861 double[] v = new double[kJam * 10 + 1];
862 for (int kk = 0; kk <= kJam * 10; kk++)
863 {
864 double kVal = kk / 10.0;
865 k[kk] = kVal;
866 if (kVal > kCrit)
867 {
868
869 q[kk] = qMax * (1.0 - (kVal - kCrit) / (kJam - kCrit));
870 v[kk] = q[kk] / k[kk];
871 }
872 else
873 {
874
875 v[kk] = vMax;
876 q[kk] = k[kk] * v[kk];
877 }
878 }
879
880
881 this.map.put(Quantity.DENSITY, k);
882 this.map.put(Quantity.FLOW, q);
883 this.map.put(Quantity.SPEED, v);
884 }
885 }
886
887 }