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.rmi.RemoteException;
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.HashMap;
10 import java.util.Iterator;
11 import java.util.Map;
12 import java.util.Random;
13
14 import javax.naming.NamingException;
15 import javax.swing.SwingUtilities;
16
17 import nl.tudelft.simulation.dsol.SimRuntimeException;
18 import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
19 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
20
21 import org.opentrafficsim.car.Car;
22 import org.opentrafficsim.car.lanechanging.AbstractLaneChangeModel;
23 import org.opentrafficsim.car.lanechanging.Altruistic;
24 import org.opentrafficsim.car.lanechanging.Egoistic;
25 import org.opentrafficsim.car.lanechanging.LaneChangeModel;
26 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
27 import org.opentrafficsim.core.dsol.OTSModelInterface;
28 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
29 import org.opentrafficsim.core.gtu.GTUType;
30 import org.opentrafficsim.core.gtu.following.GTUFollowingModel;
31 import org.opentrafficsim.core.gtu.following.IDM;
32 import org.opentrafficsim.core.gtu.following.IDMPlus;
33 import org.opentrafficsim.core.gtu.lane.AbstractLaneBasedGTU;
34 import org.opentrafficsim.core.network.LateralDirectionality;
35 import org.opentrafficsim.core.network.NetworkException;
36 import org.opentrafficsim.core.network.factory.LaneFactory;
37 import org.opentrafficsim.core.network.factory.Node;
38 import org.opentrafficsim.core.network.lane.Lane;
39 import org.opentrafficsim.core.network.lane.LaneLocation;
40 import org.opentrafficsim.core.network.lane.LaneType;
41 import org.opentrafficsim.core.unit.AccelerationUnit;
42 import org.opentrafficsim.core.unit.LengthUnit;
43 import org.opentrafficsim.core.unit.SpeedUnit;
44 import org.opentrafficsim.core.unit.TimeUnit;
45 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
46 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Abs;
47 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Rel;
48 import org.opentrafficsim.graphs.AccelerationContourPlot;
49 import org.opentrafficsim.graphs.ContourPlot;
50 import org.opentrafficsim.graphs.DensityContourPlot;
51 import org.opentrafficsim.graphs.FlowContourPlot;
52 import org.opentrafficsim.graphs.LaneBasedGTUSampler;
53 import org.opentrafficsim.graphs.SpeedContourPlot;
54 import org.opentrafficsim.graphs.TrajectoryPlot;
55 import org.opentrafficsim.simulationengine.AbstractProperty;
56 import org.opentrafficsim.simulationengine.BooleanProperty;
57 import org.opentrafficsim.simulationengine.CompoundProperty;
58 import org.opentrafficsim.simulationengine.ContinuousProperty;
59 import org.opentrafficsim.simulationengine.ControlPanel;
60 import org.opentrafficsim.simulationengine.IDMPropertySet;
61 import org.opentrafficsim.simulationengine.IntegerProperty;
62 import org.opentrafficsim.simulationengine.ProbabilityDistributionProperty;
63 import org.opentrafficsim.simulationengine.PropertyException;
64 import org.opentrafficsim.simulationengine.SelectionProperty;
65 import org.opentrafficsim.simulationengine.SimpleSimulator;
66 import org.opentrafficsim.simulationengine.SimulatorFrame;
67 import org.opentrafficsim.simulationengine.WrappableSimulation;
68
69 import com.vividsolutions.jts.geom.Coordinate;
70
71
72
73
74
75
76
77
78
79
80 public class CircularRoad implements WrappableSimulation
81 {
82
83 private ArrayList<AbstractProperty<?>> properties = new ArrayList<AbstractProperty<?>>();
84
85
86 public CircularRoad()
87 {
88 this.properties.add(new SelectionProperty("Lane changing",
89 "<html>The lane change strategies vary in politeness.<br />"
90 + "Two types are implemented:<ul><li>Egoistic (looks only at personal gain).</li>"
91 + "<li>Altruistic (assigns effect on new and current follower the same weight as "
92 + "the personal gain).</html>", new String[] {"Egoistic", "Altruistic"}, 0, false, 500));
93 this.properties.add(new IntegerProperty("Track length", "Circumference of the track", 2000, 500, 6000,
94 "Track length %dm", false, 10));
95 this.properties.add(new ContinuousProperty("Mean density", "Number of vehicles per km", 40.0, 5.0, 45.0,
96 "Density %.1f veh/km", false, 11));
97 this.properties.add(new ContinuousProperty("Density variability", "Variability of the number of vehicles per km",
98 0.0, 0.0, 1.0, "%.1f", false, 12));
99 ArrayList<AbstractProperty<?>> outputProperties = new ArrayList<AbstractProperty<?>>();
100 for (int lane = 1; lane <= 2; lane++)
101 {
102 String laneId = String.format("Lane %d ", lane);
103 outputProperties.add(new BooleanProperty(laneId + "Density", laneId + "Density contour plot", true, false, 0));
104 outputProperties.add(new BooleanProperty(laneId + "Flow", laneId + "Flow contour plot", true, false, 1));
105 outputProperties.add(new BooleanProperty(laneId + "Speed", laneId + "Speed contour plot", true, false, 2));
106 outputProperties.add(new BooleanProperty(laneId + "Acceleration", laneId + "Acceleration contour plot", true,
107 false, 3));
108 outputProperties.add(new BooleanProperty(laneId + "Trajectories", laneId + "Trajectory (time/distance) diagram",
109 true, false, 4));
110 }
111 this.properties.add(new CompoundProperty("Output", "Select the graphical output", outputProperties, true, 1000));
112 }
113
114
115
116
117
118
119
120 public static void main(final String[] args) throws RemoteException, SimRuntimeException
121 {
122 SwingUtilities.invokeLater(new Runnable()
123 {
124 @Override
125 public void run()
126 {
127 try
128 {
129 CircularRoad circularRoad = new CircularRoad();
130 ArrayList<AbstractProperty<?>> properties = circularRoad.getProperties();
131 try
132 {
133 properties.add(new ProbabilityDistributionProperty("Traffic composition",
134 "<html>Mix of passenger cars and trucks</html>", new String[] {"passenger car", "truck"},
135 new Double[] {0.8, 0.2}, false, 10));
136 }
137 catch (PropertyException exception)
138 {
139 exception.printStackTrace();
140 }
141 properties.add(new SelectionProperty("Car following model", "<html>The car following model determines "
142 + "the acceleration that a vehicle will make taking into account "
143 + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
144 + "curvature of the road) capabilities of the vehicle and personality " + "of the driver.</html>",
145 new String[] {"IDM", "IDM+"}, 1, false, 1));
146 properties.add(IDMPropertySet.makeIDMPropertySet("Car", new DoubleScalar.Abs<AccelerationUnit>(1.0,
147 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Abs<AccelerationUnit>(1.5,
148 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<LengthUnit>(2.0, LengthUnit.METER),
149 new DoubleScalar.Rel<TimeUnit>(1.0, TimeUnit.SECOND), 2));
150 properties.add(IDMPropertySet.makeIDMPropertySet("Truck", new DoubleScalar.Abs<AccelerationUnit>(0.5,
151 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Abs<AccelerationUnit>(1.25,
152 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<LengthUnit>(2.0, LengthUnit.METER),
153 new DoubleScalar.Rel<TimeUnit>(1.0, TimeUnit.SECOND), 3));
154 new SimulatorFrame("Circular Road animation", circularRoad.buildSimulator(properties).getPanel());
155 }
156 catch (RemoteException | SimRuntimeException exception)
157 {
158 exception.printStackTrace();
159 }
160 }
161 });
162 }
163
164
165
166
167
168
169
170 public SimpleSimulator buildSimulator(ArrayList<AbstractProperty<?>> userModifiedProperties) throws RemoteException,
171 SimRuntimeException
172 {
173 RoadSimulationModel model = new RoadSimulationModel(userModifiedProperties);
174 final SimpleSimulator result =
175 new SimpleSimulator(new OTSSimTimeDouble(new DoubleScalar.Abs<TimeUnit>(0.0, TimeUnit.SECOND)),
176 new DoubleScalar.Rel<TimeUnit>(0.0, TimeUnit.SECOND),
177 new DoubleScalar.Rel<TimeUnit>(3600.0, TimeUnit.SECOND), model, new Rectangle2D.Double(-1000, -1000, 2000,
178 2000));
179 new ControlPanel(result);
180
181
182 AbstractProperty<?> output = new CompoundProperty("", "", this.properties, false, 0).findByShortName("Output");
183 if (null == output)
184 {
185 throw new Error("Cannot find output properties");
186 }
187 ArrayList<BooleanProperty> graphs = new ArrayList<BooleanProperty>();
188 if (output instanceof CompoundProperty)
189 {
190 CompoundProperty outputProperties = (CompoundProperty) output;
191 for (AbstractProperty<?> ap : outputProperties.getValue())
192 {
193 if (ap instanceof BooleanProperty)
194 {
195 BooleanProperty bp = (BooleanProperty) ap;
196 if (bp.getValue())
197 {
198 graphs.add(bp);
199 }
200 }
201 }
202 }
203 else
204 {
205 throw new Error("output properties should be compound");
206 }
207 int graphCount = graphs.size();
208 int columns = (int) Math.ceil(Math.sqrt(graphCount));
209 int rows = 0 == columns ? 0 : (int) Math.ceil(graphCount * 1.0 / columns);
210 TablePanel charts = new TablePanel(columns, rows);
211 result.getPanel().getTabbedPane().addTab("statistics", charts);
212
213 for (int i = 0; i < graphCount; i++)
214 {
215 String graphName = graphs.get(i).getShortName();
216 Container container = null;
217 LaneBasedGTUSampler graph;
218 int pos = graphName.indexOf(' ') + 1;
219 String laneNumberText = graphName.substring(pos, pos + 1);
220 int lane = Integer.parseInt(laneNumberText) - 1;
221
222 if (graphName.contains("Trajectories"))
223 {
224 TrajectoryPlot tp =
225 new TrajectoryPlot(graphName, new DoubleScalar.Rel<TimeUnit>(0.5, TimeUnit.SECOND), model
226 .getMinimumDistance(), model.lanes[lane].getLength());
227 tp.setTitle("Trajectory Graph");
228 tp.setExtendedState(Frame.MAXIMIZED_BOTH);
229 graph = tp;
230 container = tp.getContentPane();
231 }
232 else
233 {
234 ContourPlot cp;
235 if (graphName.contains("Density"))
236 {
237 cp = new DensityContourPlot(graphName, model.getMinimumDistance(), model.lanes[lane].getLength());
238 cp.setTitle("Density Contour Graph");
239 }
240 else if (graphName.contains("Speed"))
241 {
242 cp = new SpeedContourPlot(graphName, model.getMinimumDistance(), model.lanes[lane].getLength());
243 cp.setTitle("Speed Contour Graph");
244 }
245 else if (graphName.contains("Flow"))
246 {
247 cp = new FlowContourPlot(graphName, model.getMinimumDistance(), model.lanes[lane].getLength());
248 cp.setTitle("Flow Contour Graph");
249 }
250 else if (graphName.contains("Acceleration"))
251 {
252 cp = new AccelerationContourPlot(graphName, model.getMinimumDistance(), model.lanes[lane].getLength());
253 cp.setTitle("Acceleration Contour Graph");
254 }
255 else
256 {
257 throw new Error("Unhandled type of contourplot: " + graphName);
258 }
259 graph = cp;
260 container = cp.getContentPane();
261 }
262
263 charts.setCell(container, i % columns, i / columns);
264 model.getPlots().get(lane).add(graph);
265 }
266 return result;
267 }
268
269
270 @Override
271 public String shortName()
272 {
273 return "Circular Road simulation";
274 }
275
276
277 @Override
278 public String description()
279 {
280 return "<html><h1>Circular Road simulation</h1>"
281 + "Vehicles are unequally distributed over a two lane ring road.<br />"
282 + "When simulation starts, all vehicles begin driving, some lane changes will occurr and some "
283 + "shockwaves should develop.<br />"
284 + "Trajectories and contourplots are generated during the simulation for both lanes.</html>";
285 }
286
287
288 @Override
289 public ArrayList<AbstractProperty<?>> getProperties()
290 {
291 return new ArrayList<AbstractProperty<?>>(this.properties);
292 }
293
294 }
295
296
297
298
299
300
301
302
303
304
305 class RoadSimulationModel implements OTSModelInterface
306 {
307
308 private static final long serialVersionUID = 20141121L;
309
310
311 OTSDEVSSimulatorInterface simulator;
312
313
314 private int carsCreated = 0;
315
316
317 protected GTUFollowingModel carFollowingModelCars;
318
319
320 protected GTUFollowingModel carFollowingModelTrucks;
321
322
323 double carProbability;
324
325
326 protected AbstractLaneChangeModel laneChangeModel;
327
328
329 ArrayList<ArrayList<Car<Integer>>> cars;
330
331
332 private DoubleScalar.Rel<LengthUnit> minimumDistance = new DoubleScalar.Rel<LengthUnit>(0, LengthUnit.METER);
333
334
335 Lane[] lanes;
336
337
338 DoubleScalar.Abs<SpeedUnit> speedLimit = new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR);
339
340
341 private ArrayList<ArrayList<LaneBasedGTUSampler>> plots = new ArrayList<ArrayList<LaneBasedGTUSampler>>();
342
343
344 ArrayList<AbstractProperty<?>> properties = null;
345
346
347 Random randomGenerator = new Random(12345);
348
349
350
351
352 public RoadSimulationModel(ArrayList<AbstractProperty<?>> properties)
353 {
354 this.properties = properties;
355 }
356
357
358 @Override
359 public void constructModel(SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> theSimulator)
360 throws SimRuntimeException, RemoteException
361 {
362 final int laneCount = 2;
363 this.cars = new ArrayList<ArrayList<Car<Integer>>>(laneCount);
364 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
365 {
366 this.cars.add(new ArrayList<Car<Integer>>());
367 this.plots.add(new ArrayList<LaneBasedGTUSampler>());
368 }
369 this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
370 double radius = 6000 / 2 / Math.PI;
371 double headway = 40;
372 double headwayVariability = 0;
373 try
374 {
375 String carFollowingModelName = null;
376 CompoundProperty propertyContainer = new CompoundProperty("", "", this.properties, false, 0);
377 AbstractProperty<?> cfmp = propertyContainer.findByShortName("Car following model");
378 if (null == cfmp)
379 {
380 throw new Error("Cannot find \"Car following model\" property");
381 }
382 if (cfmp instanceof SelectionProperty)
383 {
384 carFollowingModelName = ((SelectionProperty) cfmp).getValue();
385 }
386 else
387 {
388 throw new Error("\"Car following model\" property has wrong type");
389 }
390 Iterator<AbstractProperty<ArrayList<AbstractProperty<?>>>> iterator =
391 new CompoundProperty("", "", this.properties, false, 0).iterator();
392 while (iterator.hasNext())
393 {
394 AbstractProperty<?> ap = iterator.next();
395 if (ap instanceof SelectionProperty)
396 {
397 SelectionProperty sp = (SelectionProperty) ap;
398 if ("Car following model".equals(sp.getShortName()))
399 {
400 carFollowingModelName = sp.getValue();
401 }
402 else if ("Lane changing".equals(sp.getShortName()))
403 {
404 String strategyName = sp.getValue();
405 if ("Egoistic".equals(strategyName))
406 {
407 this.laneChangeModel = new Egoistic();
408 }
409 else if ("Altruistic".equals(strategyName))
410 {
411 this.laneChangeModel = new Altruistic();
412 }
413 else
414 {
415 throw new Error("Lane changing " + strategyName + " not implemented");
416 }
417 }
418 }
419 else if (ap instanceof ProbabilityDistributionProperty)
420 {
421 ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
422 if (ap.getShortName().equals("Traffic composition"))
423 {
424 this.carProbability = pdp.getValue()[0];
425 }
426 }
427 else if (ap instanceof IntegerProperty)
428 {
429 IntegerProperty ip = (IntegerProperty) ap;
430 if ("Track length".equals(ip.getShortName()))
431 {
432 radius = ip.getValue() / 2 / Math.PI;
433 }
434 }
435 else if (ap instanceof ContinuousProperty)
436 {
437 ContinuousProperty cp = (ContinuousProperty) ap;
438 if (cp.getShortName().equals("Mean density"))
439 {
440 headway = 1000 / cp.getValue();
441 }
442 if (cp.getShortName().equals("Density variability"))
443 {
444 headwayVariability = cp.getValue();
445 }
446 }
447 else if (ap instanceof CompoundProperty)
448 {
449 CompoundProperty cp = (CompoundProperty) ap;
450 if (ap.getShortName().equals("Output"))
451 {
452 continue;
453 }
454 if (ap.getShortName().contains("IDM"))
455 {
456
457 DoubleScalar.Abs<AccelerationUnit> a = IDMPropertySet.getA(cp);
458 DoubleScalar.Abs<AccelerationUnit> b = IDMPropertySet.getB(cp);
459 DoubleScalar.Rel<LengthUnit> s0 = IDMPropertySet.getS0(cp);
460 DoubleScalar.Rel<TimeUnit> tSafe = IDMPropertySet.getTSafe(cp);
461 GTUFollowingModel gtuFollowingModel = null;
462 if (carFollowingModelName.equals("IDM"))
463 {
464 gtuFollowingModel = new IDM(a, b, s0, tSafe, 1.0);
465 }
466 else if (carFollowingModelName.equals("IDM+"))
467 {
468 gtuFollowingModel = new IDMPlus(a, b, s0, tSafe, 1.0);
469 }
470 else
471 {
472 throw new Error("Unknown gtu following model: " + carFollowingModelName);
473 }
474 if (ap.getShortName().contains(" Car "))
475 {
476 this.carFollowingModelCars = gtuFollowingModel;
477 }
478 else if (ap.getShortName().contains(" Truck "))
479 {
480 this.carFollowingModelTrucks = gtuFollowingModel;
481 }
482 else
483 {
484 throw new Error("Cannot determine gtu type for " + ap.getShortName());
485 }
486
487
488
489
490
491 }
492 }
493 }
494 GTUType<String> gtuType = new GTUType<String>("car");
495 LaneType<String> laneType = new LaneType<String>("CarLane");
496 laneType.addPermeability(gtuType);
497 Node startEnd = new Node("Start/End", new Coordinate(radius, 0, 0));
498 Coordinate[] intermediateCoordinates = new Coordinate[255];
499 for (int i = 0; i < intermediateCoordinates.length; i++)
500 {
501 double angle = 2 * Math.PI * (1 + i) / (1 + intermediateCoordinates.length);
502 intermediateCoordinates[i] = new Coordinate(radius * Math.cos(angle), radius * Math.sin(angle), 0);
503 }
504 this.lanes =
505 LaneFactory.makeMultiLane("Circular Link with " + laneCount + " lanes", startEnd, startEnd,
506 intermediateCoordinates, laneCount, laneType, this.simulator);
507
508 double variability = (headway - 20) * headwayVariability;
509 System.out.println("headway is " + headway + " variability limit is " + variability);
510 Random random = new Random(12345);
511 for (int laneIndex = 0; laneIndex < this.lanes.length; laneIndex++)
512 {
513 double trackLength = this.lanes[laneIndex].getLength().getSI();
514 for (double pos = 0; pos <= trackLength - headway - variability;)
515 {
516
517 double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
518 generateCar(new DoubleScalar.Rel<LengthUnit>(pos, LengthUnit.METER), laneIndex, gtuType);
519
520
521
522
523 pos += actualHeadway;
524 }
525 }
526
527 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(9.999, TimeUnit.SECOND), this, this,
528 "drawGraphs", null);
529 checkOrdering(RoadSimulationModel.this.cars.get(0));
530 checkOrdering(RoadSimulationModel.this.cars.get(1));
531 }
532 catch (RemoteException | SimRuntimeException | NamingException | NetworkException exception)
533 {
534 exception.printStackTrace();
535 }
536 }
537
538
539
540
541
542
543
544 protected final void addToPlots(final Car<?> car, int lane) throws NetworkException, RemoteException
545 {
546 for (LaneBasedGTUSampler plot : this.plots.get(lane))
547 {
548 plot.addData(car);
549 }
550 }
551
552
553
554
555 protected final void drawGraphs()
556 {
557 for (ArrayList<LaneBasedGTUSampler> lanePlots : this.plots)
558 {
559 for (LaneBasedGTUSampler plot : lanePlots)
560 {
561 plot.reGraph();
562 }
563 }
564
565 try
566 {
567 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(
568 this.simulator.getSimulatorTime().get().getSI() + 10, TimeUnit.SECOND), this, this, "drawGraphs", null);
569 }
570 catch (RemoteException | SimRuntimeException exception)
571 {
572 exception.printStackTrace();
573 }
574
575 }
576
577
578
579
580
581
582
583 protected final void generateCar(DoubleScalar.Rel<LengthUnit> initialPosition, int laneIndex, GTUType<String> gtuType)
584 throws NamingException, NetworkException, SimRuntimeException
585 {
586 boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
587 DoubleScalar.Abs<SpeedUnit> initialSpeed = new DoubleScalar.Abs<SpeedUnit>(0, SpeedUnit.KM_PER_HOUR);
588 Map<Lane, DoubleScalar.Rel<LengthUnit>> initialPositions = new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
589 initialPositions.put(this.lanes[laneIndex], initialPosition);
590 try
591 {
592 DoubleScalar.Rel<LengthUnit> vehicleLength =
593 new DoubleScalar.Rel<LengthUnit>(generateTruck ? 15 : 4, LengthUnit.METER);
594 IDMCar car =
595 new IDMCar(++this.carsCreated, gtuType, this.simulator, generateTruck ? this.carFollowingModelTrucks
596 : this.carFollowingModelCars, vehicleLength, this.simulator.getSimulatorTime().get(), initialPositions,
597 initialSpeed);
598 this.cars.get(laneIndex).add(car);
599 }
600 catch (RemoteException exception)
601 {
602 exception.printStackTrace();
603 }
604 }
605
606
607 @Override
608 public SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator() throws RemoteException
609 {
610 return null;
611 }
612
613
614
615
616 public final ArrayList<ArrayList<LaneBasedGTUSampler>> getPlots()
617 {
618 return this.plots;
619 }
620
621
622
623
624 public final DoubleScalar.Rel<LengthUnit> getMinimumDistance()
625 {
626 return this.minimumDistance;
627 }
628
629
630
631
632 protected class IDMCar extends Car<Integer>
633 {
634
635 private static final long serialVersionUID = 20141030L;
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653 public IDMCar(final int id, GTUType<String> gtuType, final OTSDEVSSimulatorInterface simulator,
654 final GTUFollowingModel carFollowingModel, DoubleScalar.Rel<LengthUnit> vehicleLength,
655 final DoubleScalar.Abs<TimeUnit> initialTime,
656 final Map<Lane, DoubleScalar.Rel<LengthUnit>> initialLongitudinalPositions,
657 final DoubleScalar.Abs<SpeedUnit> initialSpeed) throws RemoteException, NamingException, NetworkException,
658 SimRuntimeException
659 {
660 super(id, gtuType, carFollowingModel, initialLongitudinalPositions, initialSpeed, vehicleLength,
661 new DoubleScalar.Rel<LengthUnit>(1.8, LengthUnit.METER), new DoubleScalar.Abs<SpeedUnit>(200,
662 SpeedUnit.KM_PER_HOUR), simulator);
663 try
664 {
665 if (id >= 0)
666 {
667 simulator.scheduleEventAbs(simulator.getSimulatorTime(), this, this, "move", null);
668 }
669 }
670 catch (SimRuntimeException exception)
671 {
672 exception.printStackTrace();
673 }
674
675 }
676
677
678
679
680
681
682
683
684 protected final void move() throws RemoteException, NamingException, NetworkException, SimRuntimeException
685 {
686 wrap();
687
688 if (this.getId() < 0)
689 {
690 return;
691 }
692
693
694 Lane lane = positions(getFront()).keySet().iterator().next();
695 int laneIndex = -1;
696 for (int i = 0; i < RoadSimulationModel.this.lanes.length; i++)
697 {
698 if (lane == RoadSimulationModel.this.lanes[i])
699 {
700 laneIndex = i;
701 }
702 }
703 if (laneIndex < 0)
704 {
705 throw new Error("Cannot find lane of vehicle " + this);
706 }
707 DoubleScalar.Abs<TimeUnit> when = getSimulator().getSimulatorTime().get();
708 DoubleScalar.Rel<LengthUnit> longitudinalPosition = position(lane, getFront(), when);
709 double relativePosition = longitudinalPosition.getSI() / lane.getLength().getSI();
710 Collection<AbstractLaneBasedGTU<?>> sameLaneTraffic = carsInSpecifiedLane(laneIndex);
711 Collection<AbstractLaneBasedGTU<?>> leftLaneTraffic = carsInSpecifiedLane(laneIndex - 1);
712 Collection<AbstractLaneBasedGTU<?>> rightLaneTraffic = carsInSpecifiedLane(laneIndex + 1);
713 LaneChangeModel.LaneChangeModelResult lcmr =
714 RoadSimulationModel.this.laneChangeModel.computeLaneChangeAndAcceleration(this, sameLaneTraffic,
715 rightLaneTraffic, leftLaneTraffic, RoadSimulationModel.this.speedLimit,
716 new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
717 new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
718 new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
719
720 if (lcmr.getLaneChange() != null)
721 {
722
723
724 Map<Lane, Rel<LengthUnit>> longitudinalPositions = positions(getFront());
725 DoubleScalar.Rel<LengthUnit> oldPosition = longitudinalPositions.get(lane);
726 double oldRatio = oldPosition.getSI() / lane.getLength().getSI();
727
728 RoadSimulationModel.this.cars.get(laneIndex).remove(this);
729
730 laneIndex += lcmr.getLaneChange().equals(LateralDirectionality.LEFT) ? -1 : +1;
731 ArrayList<Car<Integer>> carsInLane = RoadSimulationModel.this.cars.get(laneIndex);
732 lane = RoadSimulationModel.this.lanes[laneIndex];
733 int pivot = pivot(relativePosition, carsInLane);
734
735
736 carsInLane.add(pivot, this);
737 longitudinalPositions.clear();
738
739
740
741 longitudinalPositions.put(lane, new DoubleScalar.Rel<LengthUnit>(oldRatio * lane.getLength().getSI(),
742 LengthUnit.METER));
743 checkOrdering(carsInLane);
744 }
745 setState(lcmr.getGfmr());
746 checkOrdering(RoadSimulationModel.this.cars.get(0));
747 checkOrdering(RoadSimulationModel.this.cars.get(1));
748
749 addToPlots(this, laneIndex);
750
751
752 getSimulator().scheduleEventRel(new DoubleScalar.Rel<TimeUnit>(0.5, TimeUnit.SECOND), this, this, "move", null);
753
754
755 }
756 }
757
758
759
760
761 public void wrap()
762 {
763 DoubleScalar.Abs<TimeUnit> when = null;
764 try
765 {
766 when = RoadSimulationModel.this.simulator.getSimulatorTime().get();
767 }
768 catch (RemoteException exception1)
769 {
770 exception1.printStackTrace();
771 }
772 for (int laneIndex = 0; laneIndex < RoadSimulationModel.this.cars.size(); laneIndex++)
773 {
774 Lane lane = RoadSimulationModel.this.lanes[laneIndex];
775 ArrayList<Car<Integer>> carsInLane = RoadSimulationModel.this.cars.get(laneIndex);
776 int vehicleIndex = carsInLane.size() - 1;
777 if (vehicleIndex < 0)
778 {
779 continue;
780 }
781 while (true)
782 {
783 Car<Integer> car = carsInLane.get(vehicleIndex);
784 LaneLocation ll = null;
785 try
786 {
787 ll = new LaneLocation(lane, car.position(lane, car.getFront(), when));
788 }
789 catch (NetworkException exception)
790 {
791 exception.printStackTrace();
792 }
793 if (ll.getFractionalLongitudinalPosition() >= 1)
794 {
795
796
797 try
798 {
799
800 Map<Lane, Rel<LengthUnit>> relativePositions = car.positions(car.getFront());
801 double relativePosition = relativePositions.get(lane).getSI() / lane.getLength().getSI();
802
803
804 relativePositions.clear();
805 relativePosition -= 1;
806 relativePositions.put(lane, new DoubleScalar.Rel<LengthUnit>(relativePosition, LengthUnit.METER));
807 carsInLane.remove(car);
808 carsInLane.add(0, car);
809 checkOrdering(carsInLane);
810 addToPlots(car, laneIndex);
811 }
812 catch (RemoteException | NetworkException exception)
813 {
814 exception.printStackTrace();
815 }
816 }
817 else
818 {
819 break;
820 }
821 }
822 }
823 }
824
825
826
827
828
829
830
831 public int pivot(double relativePosition, ArrayList<Car<Integer>> carsInLane)
832 {
833 if (carsInLane.size() == 0)
834 {
835 return 0;
836 }
837 try
838 {
839 DoubleScalar.Abs<TimeUnit> when = carsInLane.get(0).getSimulator().getSimulatorTime().get();
840 Lane lane = carsInLane.get(0).positions(carsInLane.get(0).getFront()).keySet().iterator().next();
841 double laneLength = lane.getLength().getSI();
842 int result;
843 for (result = 0; result < carsInLane.size(); result++)
844 {
845 Car<Integer> pivotCar = carsInLane.get(result);
846 double pivotRelativePosition = pivotCar.position(lane, pivotCar.getFront(), when).getSI() / laneLength;
847 if (pivotRelativePosition > relativePosition)
848 {
849 break;
850 }
851 }
852
853 return result;
854 }
855 catch (RemoteException | NetworkException exception)
856 {
857 exception.printStackTrace();
858 }
859 throw new Error("Oops");
860 }
861
862
863
864
865
866
867 @SuppressWarnings("unchecked")
868 Collection<AbstractLaneBasedGTU<?>> carsInSpecifiedLane(int laneIndex)
869 {
870 ArrayList<AbstractLaneBasedGTU<?>> result = new ArrayList<AbstractLaneBasedGTU<?>>();
871 if (laneIndex < 0 || laneIndex >= RoadSimulationModel.this.lanes.length)
872 {
873 return result;
874 }
875 Lane lane = RoadSimulationModel.this.lanes[laneIndex];
876 result.addAll(RoadSimulationModel.this.cars.get(laneIndex));
877 if (0 == result.size())
878 {
879 return result;
880 }
881 try
882 {
883 final double laneLength = lane.getLength().getSI();
884
885 Car<Integer> prototype = (Car<Integer>) result.get(result.size() - 1);
886 Map<Lane, DoubleScalar.Rel<LengthUnit>> initialPositions = new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
887 DoubleScalar.Abs<TimeUnit> when = RoadSimulationModel.this.simulator.getSimulatorTime().get();
888 double position = prototype.position(lane, prototype.getFront(), when).getSI();
889 if (position > 0)
890 {
891 position -= laneLength;
892 }
893 initialPositions.put(lane, new DoubleScalar.Rel<LengthUnit>(position, LengthUnit.METER));
894
895 IDMCar fakeFollower =
896 new IDMCar(-10000 - prototype.getId(), (GTUType<String>) prototype.getGTUType(), prototype.getSimulator(),
897 prototype.getGTUFollowingModel(), new DoubleScalar.Rel<LengthUnit>(-prototype.getLength().getSI(),
898 LengthUnit.METER), when, initialPositions, prototype.getLongitudinalVelocity());
899 result.add(0, fakeFollower);
900
901 prototype = (Car<Integer>) result.get(1);
902 position = prototype.position(lane, prototype.getFront(), when).getSI();
903 if (position < laneLength)
904 {
905 position += laneLength;
906 }
907 initialPositions = new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
908 initialPositions.put(lane, new DoubleScalar.Rel<LengthUnit>(position, LengthUnit.METER));
909
910 IDMCar fakeLeader =
911 new IDMCar(-20000 - prototype.getId(), (GTUType<String>) prototype.getGTUType(), prototype.getSimulator(),
912 prototype.getGTUFollowingModel(), new DoubleScalar.Rel<LengthUnit>(-prototype.getLength().getSI(),
913 LengthUnit.METER), when, initialPositions, prototype.getLongitudinalVelocity());
914 result.add(fakeLeader);
915 }
916 catch (RemoteException | NetworkException | NamingException | SimRuntimeException exception)
917 {
918 exception.printStackTrace();
919 }
920 return result;
921 }
922
923
924
925
926
927 public void checkOrdering(ArrayList<Car<Integer>> list)
928 {
929 if (list.size() == 0)
930 {
931 return;
932 }
933 try
934 {
935 Car<Integer> first = list.get(0);
936 Lane lane = first.positions(first.getFront()).keySet().iterator().next();
937 DoubleScalar.Abs<TimeUnit> when = first.getSimulator().getSimulatorTime().get();
938 double position = first.position(lane, first.getFront(), when).getSI();
939 for (int rank = 1; rank < list.size(); rank++)
940 {
941 Car<Integer> other = list.get(rank);
942 Lane otherLane = other.positions(other.getFront()).keySet().iterator().next();
943 if (lane != otherLane)
944 {
945 printList(this.cars.indexOf(list));
946 stopSimulator(first.getSimulator(), "cars are not all in the same lane");
947 }
948 double otherPosition = other.position(lane, other.getFront(), when).getSI();
949 if (otherPosition <= position)
950 {
951 printList(this.cars.indexOf(list));
952 stopSimulator(first.getSimulator(), "cars are not correctly ordered: " + first + " should be ahead of "
953 + other);
954 }
955 first = other;
956 position = otherPosition;
957 }
958 }
959 catch (RemoteException | NetworkException exception)
960 {
961 exception.printStackTrace();
962 }
963 }
964
965
966
967
968
969 public void printList(int laneIndex)
970 {
971 ArrayList<Car<Integer>> list = this.cars.get(laneIndex);
972 for (int rank = 0; rank < list.size(); rank++)
973 {
974 Car<Integer> car = list.get(rank);
975 try
976 {
977 double relativePosition =
978 car.position(this.lanes[laneIndex], car.getFront(), this.simulator.getSimulatorTime().get()).getSI()
979 / this.lanes[laneIndex].getLength().getSI();
980 System.out.println(String.format("lane %d rank %2d relpos %7.5f: %s", laneIndex, rank, relativePosition, car
981 .toString()));
982 }
983 catch (RemoteException | NetworkException exception)
984 {
985 exception.printStackTrace();
986 }
987 }
988 }
989
990
991
992
993
994
995 public void stopSimulator(OTSDEVSSimulatorInterface theSimulator, String errorMessage)
996 {
997 System.out.println("Error: " + errorMessage);
998 try
999 {
1000 if (theSimulator.isRunning())
1001 {
1002 theSimulator.stop();
1003 }
1004 }
1005 catch (RemoteException | SimRuntimeException exception)
1006 {
1007 exception.printStackTrace();
1008 }
1009 throw new Error(errorMessage);
1010 }
1011
1012 }