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