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