1 package org.opentrafficsim.demo.carFollowing;
2
3 import java.awt.Container;
4 import java.awt.Frame;
5 import java.awt.geom.Rectangle2D;
6 import java.io.IOException;
7 import java.net.URL;
8 import java.rmi.RemoteException;
9 import java.util.ArrayList;
10 import java.util.Iterator;
11 import java.util.LinkedHashMap;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Random;
15
16 import javax.naming.NamingException;
17 import javax.swing.JComponent;
18 import javax.swing.JPanel;
19 import javax.swing.JScrollPane;
20 import javax.swing.SwingUtilities;
21
22 import nl.tudelft.simulation.dsol.SimRuntimeException;
23 import nl.tudelft.simulation.dsol.gui.swing.HTMLPanel;
24 import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
25 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
26
27 import org.djunits.unit.TimeUnit;
28 import org.djunits.value.vdouble.scalar.DoubleScalar;
29 import org.opentrafficsim.core.OTS_SCALAR;
30 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
31 import org.opentrafficsim.core.dsol.OTSModelInterface;
32 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
33 import org.opentrafficsim.core.geometry.OTSGeometryException;
34 import org.opentrafficsim.core.geometry.OTSPoint3D;
35 import org.opentrafficsim.core.gtu.GTUException;
36 import org.opentrafficsim.core.gtu.GTUType;
37 import org.opentrafficsim.core.gtu.animation.GTUColorer;
38 import org.opentrafficsim.core.network.LongitudinalDirectionality;
39 import org.opentrafficsim.core.network.NetworkException;
40 import org.opentrafficsim.core.network.OTSNode;
41 import org.opentrafficsim.core.network.route.CompleteRoute;
42 import org.opentrafficsim.graphs.AccelerationContourPlot;
43 import org.opentrafficsim.graphs.ContourPlot;
44 import org.opentrafficsim.graphs.DensityContourPlot;
45 import org.opentrafficsim.graphs.FlowContourPlot;
46 import org.opentrafficsim.graphs.LaneBasedGTUSampler;
47 import org.opentrafficsim.graphs.SpeedContourPlot;
48 import org.opentrafficsim.graphs.TrajectoryPlot;
49 import org.opentrafficsim.road.car.LaneBasedIndividualCar;
50 import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
51 import org.opentrafficsim.road.gtu.following.GTUFollowingModel;
52 import org.opentrafficsim.road.gtu.following.IDM;
53 import org.opentrafficsim.road.gtu.following.IDMPlus;
54 import org.opentrafficsim.road.gtu.lane.changing.AbstractLaneChangeModel;
55 import org.opentrafficsim.road.gtu.lane.changing.Egoistic;
56 import org.opentrafficsim.road.network.factory.LaneFactory;
57 import org.opentrafficsim.road.network.lane.CrossSectionLink;
58 import org.opentrafficsim.road.network.lane.Lane;
59 import org.opentrafficsim.road.network.lane.LaneType;
60 import org.opentrafficsim.road.network.lane.Sensor;
61 import org.opentrafficsim.road.network.lane.SinkSensor;
62 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
63 import org.opentrafficsim.road.network.route.CompleteLaneBasedRouteNavigator;
64 import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
65 import org.opentrafficsim.simulationengine.properties.AbstractProperty;
66 import org.opentrafficsim.simulationengine.properties.BooleanProperty;
67 import org.opentrafficsim.simulationengine.properties.CompoundProperty;
68 import org.opentrafficsim.simulationengine.properties.IDMPropertySet;
69 import org.opentrafficsim.simulationengine.properties.ProbabilityDistributionProperty;
70 import org.opentrafficsim.simulationengine.properties.PropertyException;
71 import org.opentrafficsim.simulationengine.properties.SelectionProperty;
72
73
74
75
76
77
78
79
80
81
82
83 public class Straight extends AbstractWrappableAnimation
84 {
85
86 private StraightModel model;
87
88
89 public Straight()
90 {
91 ArrayList<AbstractProperty<?>> outputProperties = new ArrayList<AbstractProperty<?>>();
92 outputProperties.add(new BooleanProperty("Density", "Density contour plot", true, false, 0));
93 outputProperties.add(new BooleanProperty("Flow", "Flow contour plot", true, false, 1));
94 outputProperties.add(new BooleanProperty("Speed", "Speed contour plot", true, false, 2));
95 outputProperties.add(new BooleanProperty("Acceleration", "Acceleration contour plot", true, false, 3));
96 outputProperties.add(new BooleanProperty("Trajectories", "Trajectory (time/distance) diagram", true, false, 4));
97 this.properties.add(new CompoundProperty("Output graphs", "Select the graphical output", outputProperties, true,
98 1000));
99 }
100
101
102 @Override
103 public final void stopTimersThreads()
104 {
105 super.stopTimersThreads();
106 this.model = null;
107 }
108
109
110
111
112
113
114 public static void main(final String[] args) throws SimRuntimeException
115 {
116 SwingUtilities.invokeLater(new Runnable()
117 {
118 @Override
119 public void run()
120 {
121 try
122 {
123 Straight straight = new Straight();
124 ArrayList<AbstractProperty<?>> localProperties = straight.getProperties();
125 try
126 {
127 localProperties.add(new ProbabilityDistributionProperty("Traffic composition",
128 "<html>Mix of passenger cars and trucks</html>", new String[]{"passenger car", "truck"},
129 new Double[]{0.8, 0.2}, false, 10));
130 }
131 catch (PropertyException exception)
132 {
133 exception.printStackTrace();
134 }
135 localProperties.add(new SelectionProperty("Car following model",
136 "<html>The car following model determines "
137 + "the acceleration that a vehicle will make taking into account "
138 + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
139 + "curvature of the road) capabilities of the vehicle and personality "
140 + "of the driver.</html>", new String[]{"IDM", "IDM+"}, 1, false, 1));
141 localProperties.add(IDMPropertySet.makeIDMPropertySet("Car", new Acceleration.Abs(1.0,
142 METER_PER_SECOND_2), new Acceleration.Abs(1.5, METER_PER_SECOND_2), new Length.Rel(2.0, METER),
143 new Time.Rel(1.0, SECOND), 2));
144 localProperties.add(IDMPropertySet.makeIDMPropertySet("Truck", new Acceleration.Abs(0.5,
145 METER_PER_SECOND_2), new Acceleration.Abs(1.25, METER_PER_SECOND_2), new Length.Rel(2.0, METER),
146 new Time.Rel(1.0, SECOND), 3));
147 straight.buildAnimator(new Time.Abs(0.0, SECOND), new Time.Rel(0.0, SECOND),
148 new Time.Rel(3600.0, SECOND), localProperties, null, true);
149 straight.panel.getTabbedPane().addTab("info", straight.makeInfoPane());
150 }
151 catch (SimRuntimeException | NamingException exception)
152 {
153 exception.printStackTrace();
154 }
155 }
156 });
157 }
158
159
160 @Override
161 protected final Rectangle2D.Double makeAnimationRectangle()
162 {
163 return new Rectangle2D.Double(0, -100, 5000, 200);
164 }
165
166
167 @Override
168 protected final OTSModelInterface makeModel(final GTUColorer colorer)
169 {
170 this.model = new StraightModel(this.savedUserModifiedProperties, colorer);
171 return this.model;
172 }
173
174
175
176
177 protected final JComponent makeInfoPane()
178 {
179
180 String helpSource = "/" + StraightModel.class.getPackage().getName().replace('.', '/') + "/IDMPlus.html";
181 URL page = StraightModel.class.getResource(helpSource);
182 if (page != null)
183 {
184 try
185 {
186 HTMLPanel htmlPanel = new HTMLPanel(page);
187 return new JScrollPane(htmlPanel);
188 }
189 catch (IOException exception)
190 {
191 exception.printStackTrace();
192 }
193 }
194 return new JPanel();
195 }
196
197
198 @Override
199 protected final JPanel makeCharts()
200 {
201
202
203 AbstractProperty<?> output =
204 new CompoundProperty("", "", this.properties, false, 0).findByShortName("Output graphs");
205 if (null == output)
206 {
207 throw new Error("Cannot find output properties");
208 }
209 ArrayList<BooleanProperty> graphs = new ArrayList<BooleanProperty>();
210 if (output instanceof CompoundProperty)
211 {
212 CompoundProperty outputProperties = (CompoundProperty) output;
213 for (AbstractProperty<?> ap : outputProperties.getValue())
214 {
215 if (ap instanceof BooleanProperty)
216 {
217 BooleanProperty bp = (BooleanProperty) ap;
218 if (bp.getValue())
219 {
220 graphs.add(bp);
221 }
222 }
223 }
224 }
225 else
226 {
227 throw new Error("output properties should be compound");
228 }
229 int graphCount = graphs.size();
230 int columns = (int) Math.ceil(Math.sqrt(graphCount));
231 int rows = 0 == columns ? 0 : (int) Math.ceil(graphCount * 1.0 / columns);
232 TablePanel charts = new TablePanel(columns, rows);
233
234 for (int i = 0; i < graphCount; i++)
235 {
236 String graphName = graphs.get(i).getShortName();
237 Container container = null;
238 LaneBasedGTUSampler graph;
239 if (graphName.contains("Trajectories"))
240 {
241 List<Lane> path = new ArrayList<Lane>();
242 path.add(this.model.getLane());
243 TrajectoryPlot tp = new TrajectoryPlot("TrajectoryPlot", new Time.Rel(0.5, SECOND), path);
244 tp.setTitle("Trajectory Graph");
245 tp.setExtendedState(Frame.MAXIMIZED_BOTH);
246 graph = tp;
247 container = tp.getContentPane();
248 }
249 else
250 {
251 ContourPlot cp;
252 if (graphName.contains("Density"))
253 {
254 cp = new DensityContourPlot("DensityPlot", this.model.getPath());
255 cp.setTitle("Density Contour Graph");
256 }
257 else if (graphName.contains("Speed"))
258 {
259 cp = new SpeedContourPlot("SpeedPlot", this.model.getPath());
260 cp.setTitle("Speed Contour Graph");
261 }
262 else if (graphName.contains("Flow"))
263 {
264 cp = new FlowContourPlot("FlowPlot", this.model.getPath());
265 cp.setTitle("Flow Contour Graph");
266 }
267 else if (graphName.contains("Acceleration"))
268 {
269 cp = new AccelerationContourPlot("AccelerationPlot", this.model.getPath());
270 cp.setTitle("Acceleration Contour Graph");
271 }
272 else
273 {
274 throw new Error("Unhandled type of contourplot: " + graphName);
275 }
276 graph = cp;
277 container = cp.getContentPane();
278 }
279
280 charts.setCell(container, i % columns, i / columns);
281 this.model.getPlots().add(graph);
282 }
283 return charts;
284 }
285
286
287 @Override
288 public final String shortName()
289 {
290 return "Straight lane";
291 }
292
293
294 @Override
295 public final String description()
296 {
297 return "<html><h1>Simulation of a straight one-lane road with opening bridge</H1>"
298 + "Simulation of a single lane road of 5 km length. Vehicles are generated at a constant rate of "
299 + "1500 veh/hour. At time 300s a blockade is inserted at position 4km; this blockade is removed at "
300 + "time 420s. This blockade simulates a bridge opening.<br>"
301 + "The blockade causes a traffic jam that slowly dissolves after the blockade is removed.<br />"
302 + "Selected trajectory and contour plots are generated during the simulation.</html>";
303 }
304
305 }
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328 class StraightModel implements OTSModelInterface, OTS_SCALAR
329 {
330
331 private static final long serialVersionUID = 20140815L;
332
333
334 private OTSDEVSSimulatorInterface simulator;
335
336
337 private Time.Rel headway;
338
339
340 private int carsCreated = 0;
341
342
343 private GTUType gtuType = GTUType.makeGTUType("Car");
344
345
346 private GTUFollowingModel carFollowingModelCars;
347
348
349 private GTUFollowingModel carFollowingModelTrucks;
350
351
352 private double carProbability;
353
354
355 private AbstractLaneChangeModel laneChangeModel = new Egoistic();
356
357
358 private LaneBasedIndividualCar block = null;
359
360
361 private Length.Rel minimumDistance = new Length.Rel(0, METER);
362
363
364 private Length.Rel maximumDistance = new Length.Rel(5000, METER);
365
366
367 private Lane lane;
368
369
370 private ArrayList<LaneBasedGTUSampler> plots = new ArrayList<LaneBasedGTUSampler>();
371
372
373 private ArrayList<AbstractProperty<?>> properties = null;
374
375
376 private Random randomGenerator = new Random(12345);
377
378
379 private final GTUColorer gtuColorer;
380
381
382
383
384
385 public StraightModel(final ArrayList<AbstractProperty<?>> properties, final GTUColorer gtuColorer)
386 {
387 this.properties = properties;
388 this.gtuColorer = gtuColorer;
389 }
390
391
392 private List<Lane> path = new ArrayList<Lane>();
393
394
395 private Speed.Abs speedLimit = new Speed.Abs(100, KM_PER_HOUR);
396
397
398
399
400 public List<Lane> getPath()
401 {
402 return new ArrayList<Lane>(this.path);
403 }
404
405
406 @Override
407 public final void constructModel(
408 final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> theSimulator)
409 throws SimRuntimeException, RemoteException
410 {
411 this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
412 OTSNode from = new OTSNode("From", new OTSPoint3D(getMinimumDistance().getSI(), 0, 0));
413 OTSNode to = new OTSNode("To", new OTSPoint3D(getMaximumDistance().getSI(), 0, 0));
414 OTSNode end = new OTSNode("End", new OTSPoint3D(getMaximumDistance().getSI() + 50.0, 0, 0));
415 try
416 {
417 LaneType laneType = new LaneType("CarLane");
418 laneType.addCompatibility(this.gtuType);
419 this.lane = LaneFactory.makeLane("Lane", from, to, null, laneType, this.speedLimit, this.simulator);
420 this.path.add(this.lane);
421 CrossSectionLink endLink = LaneFactory.makeLink("endLink", to, end, null);
422
423 Lane sinkLane =
424 new Lane(endLink, "sinkLane", this.lane.getLateralCenterPosition(1.0), this.lane
425 .getLateralCenterPosition(1.0), this.lane.getWidth(1.0), this.lane.getWidth(1.0), laneType,
426 LongitudinalDirectionality.FORWARD, this.speedLimit, new OvertakingConditions.None());
427 Sensor sensor = new SinkSensor(sinkLane, new Length.Rel(10.0, METER), this.simulator);
428 sinkLane.addSensor(sensor, GTUType.ALL);
429 String carFollowingModelName = null;
430 CompoundProperty propertyContainer = new CompoundProperty("", "", this.properties, false, 0);
431 AbstractProperty<?> cfmp = propertyContainer.findByShortName("Car following model");
432 if (null == cfmp)
433 {
434 throw new Error("Cannot find \"Car following model\" property");
435 }
436 if (cfmp instanceof SelectionProperty)
437 {
438 carFollowingModelName = ((SelectionProperty) cfmp).getValue();
439 }
440 else
441 {
442 throw new Error("\"Car following model\" property has wrong type");
443 }
444 Iterator<AbstractProperty<ArrayList<AbstractProperty<?>>>> iterator =
445 new CompoundProperty("", "", this.properties, false, 0).iterator();
446 while (iterator.hasNext())
447 {
448 AbstractProperty<?> ap = iterator.next();
449 if (ap instanceof SelectionProperty)
450 {
451 SelectionProperty sp = (SelectionProperty) ap;
452 if ("Car following model".equals(sp.getShortName()))
453 {
454 carFollowingModelName = sp.getValue();
455 }
456 }
457 else if (ap instanceof ProbabilityDistributionProperty)
458 {
459 ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
460 String modelName = ap.getShortName();
461 if (modelName.equals("Traffic composition"))
462 {
463 this.carProbability = pdp.getValue()[0];
464 }
465 }
466 else if (ap instanceof CompoundProperty)
467 {
468 CompoundProperty cp = (CompoundProperty) ap;
469 if (ap.getShortName().equals("Output graphs"))
470 {
471 continue;
472 }
473 if (ap.getShortName().contains("IDM"))
474 {
475 Acceleration.Abs a = IDMPropertySet.getA(cp);
476 Acceleration.Abs b = IDMPropertySet.getB(cp);
477 Length.Rel s0 = IDMPropertySet.getS0(cp);
478 Time.Rel tSafe = IDMPropertySet.getTSafe(cp);
479 GTUFollowingModel gtuFollowingModel = null;
480 if (carFollowingModelName.equals("IDM"))
481 {
482 gtuFollowingModel = new IDM(a, b, s0, tSafe, 1.0);
483 }
484 else if (carFollowingModelName.equals("IDM+"))
485 {
486 gtuFollowingModel = new IDMPlus(a, b, s0, tSafe, 1.0);
487 }
488 else
489 {
490 throw new Error("Unknown gtu following model: " + carFollowingModelName);
491 }
492 if (ap.getShortName().contains(" Car "))
493 {
494 this.carFollowingModelCars = gtuFollowingModel;
495 }
496 else if (ap.getShortName().contains(" Truck "))
497 {
498 this.carFollowingModelTrucks = gtuFollowingModel;
499 }
500 else
501 {
502 throw new Error("Cannot determine gtu type for " + ap.getShortName());
503 }
504
505
506
507
508
509 }
510 }
511 }
512
513
514 this.headway = new Time.Rel(3600.0 / 1500.0, SECOND);
515
516 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(0.0, SECOND), this, this, "generateCar", null);
517
518 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(300, SECOND), this, this, "createBlock", null);
519
520 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(420, SECOND), this, this, "removeBlock", null);
521
522 for (int t = 1; t <= 1800; t++)
523 {
524 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(t - 0.001, SECOND), this, this, "drawGraphs",
525 null);
526 }
527 }
528 catch (SimRuntimeException | NamingException | NetworkException | OTSGeometryException exception)
529 {
530 exception.printStackTrace();
531 }
532 }
533
534
535
536
537 protected final void drawGraphs()
538 {
539 for (LaneBasedGTUSampler plot : this.plots)
540 {
541 plot.reGraph();
542 }
543 }
544
545
546
547
548
549 protected final void createBlock() throws RemoteException
550 {
551 Length.Rel initialPosition = new Length.Rel(4000, METER);
552 Map<Lane, Length.Rel> initialPositions = new LinkedHashMap<Lane, Length.Rel>();
553 initialPositions.put(this.lane, initialPosition);
554 try
555 {
556 this.block =
557 new LaneBasedIndividualCar("999999", this.gtuType, this.carFollowingModelCars, this.laneChangeModel,
558 initialPositions, new Speed.Abs(0, KM_PER_HOUR), new Length.Rel(4, METER), new Length.Rel(1.8, METER),
559 new Speed.Abs(0, KM_PER_HOUR), new CompleteLaneBasedRouteNavigator(new CompleteRoute("")),
560 this.simulator, DefaultCarAnimation.class, this.gtuColorer);
561 }
562 catch (SimRuntimeException | NamingException | NetworkException | GTUException exception)
563 {
564 exception.printStackTrace();
565 }
566 }
567
568
569
570
571 protected final void removeBlock()
572 {
573 this.block.destroy();
574 this.block = null;
575 }
576
577
578
579
580 protected final void generateCar()
581 {
582 boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
583 Length.Rel initialPosition = new Length.Rel(0, METER);
584 Speed.Abs initialSpeed = new Speed.Abs(100, KM_PER_HOUR);
585 Map<Lane, Length.Rel> initialPositions = new LinkedHashMap<Lane, Length.Rel>();
586 initialPositions.put(this.lane, initialPosition);
587 try
588 {
589 Length.Rel vehicleLength = new Length.Rel(generateTruck ? 15 : 4, METER);
590 GTUFollowingModel gtuFollowingModel = generateTruck ? this.carFollowingModelTrucks : this.carFollowingModelCars;
591 if (null == gtuFollowingModel)
592 {
593 throw new Error("gtuFollowingModel is null");
594 }
595 new LaneBasedIndividualCar("" + (++this.carsCreated), this.gtuType, gtuFollowingModel, this.laneChangeModel,
596 initialPositions, initialSpeed, vehicleLength, new Length.Rel(1.8, METER), new Speed.Abs(200, KM_PER_HOUR),
597 new CompleteLaneBasedRouteNavigator(new CompleteRoute("")), this.simulator, DefaultCarAnimation.class,
598 this.gtuColorer);
599 this.simulator.scheduleEventRel(this.headway, this, this, "generateCar", null);
600 }
601 catch (SimRuntimeException | NamingException | NetworkException | GTUException exception)
602 {
603 exception.printStackTrace();
604 }
605 }
606
607
608 @Override
609 public final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> getSimulator()
610 throws RemoteException
611 {
612 return this.simulator;
613 }
614
615
616
617
618 public final ArrayList<LaneBasedGTUSampler> getPlots()
619 {
620 return this.plots;
621 }
622
623
624
625
626 public final Length.Rel getMinimumDistance()
627 {
628 return this.minimumDistance;
629 }
630
631
632
633
634 public final Length.Rel getMaximumDistance()
635 {
636 return this.maximumDistance;
637 }
638
639
640
641
642 public Lane getLane()
643 {
644 return this.lane;
645 }
646
647 }