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