View Javadoc
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.core.dsol.OTSDEVSSimulatorInterface;
23  import org.opentrafficsim.core.dsol.OTSModelInterface;
24  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
25  import org.opentrafficsim.core.gtu.GTUType;
26  import org.opentrafficsim.core.gtu.following.GTUFollowingModel;
27  import org.opentrafficsim.core.gtu.following.GTUFollowingModel.GTUFollowingModelResult;
28  import org.opentrafficsim.core.gtu.following.IDM;
29  import org.opentrafficsim.core.gtu.following.IDMPlus;
30  import org.opentrafficsim.core.network.NetworkException;
31  import org.opentrafficsim.core.network.factory.LaneFactory;
32  import org.opentrafficsim.core.network.factory.Node;
33  import org.opentrafficsim.core.network.lane.CrossSectionLink;
34  import org.opentrafficsim.core.network.lane.Lane;
35  import org.opentrafficsim.core.network.lane.LaneType;
36  import org.opentrafficsim.core.unit.AccelerationUnit;
37  import org.opentrafficsim.core.unit.LengthUnit;
38  import org.opentrafficsim.core.unit.SpeedUnit;
39  import org.opentrafficsim.core.unit.TimeUnit;
40  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
41  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Abs;
42  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Rel;
43  import org.opentrafficsim.graphs.AccelerationContourPlot;
44  import org.opentrafficsim.graphs.ContourPlot;
45  import org.opentrafficsim.graphs.DensityContourPlot;
46  import org.opentrafficsim.graphs.FlowContourPlot;
47  import org.opentrafficsim.graphs.LaneBasedGTUSampler;
48  import org.opentrafficsim.graphs.SpeedContourPlot;
49  import org.opentrafficsim.graphs.TrajectoryPlot;
50  import org.opentrafficsim.simulationengine.AbstractProperty;
51  import org.opentrafficsim.simulationengine.BooleanProperty;
52  import org.opentrafficsim.simulationengine.CompoundProperty;
53  import org.opentrafficsim.simulationengine.ContinuousProperty;
54  import org.opentrafficsim.simulationengine.ControlPanel;
55  import org.opentrafficsim.simulationengine.IDMPropertySet;
56  import org.opentrafficsim.simulationengine.IntegerProperty;
57  import org.opentrafficsim.simulationengine.ProbabilityDistributionProperty;
58  import org.opentrafficsim.simulationengine.PropertyException;
59  import org.opentrafficsim.simulationengine.SelectionProperty;
60  import org.opentrafficsim.simulationengine.SimpleSimulator;
61  import org.opentrafficsim.simulationengine.SimulatorFrame;
62  import org.opentrafficsim.simulationengine.WrappableSimulation;
63  
64  import com.vividsolutions.jts.geom.Coordinate;
65  
66  /**
67   * Circular lane simulation demo.
68   * <p>
69   * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
70   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
71   * <p>
72   * @version 21 nov. 2014 <br>
73   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
74   */
75  public class CircularLane implements WrappableSimulation
76  {
77      /** The properties exhibited by this simulation. */
78      private ArrayList<AbstractProperty<?>> properties = new ArrayList<AbstractProperty<?>>();
79  
80      /** Create a CircularLane simulation. */
81      public CircularLane()
82      {
83          this.properties.add(new IntegerProperty("Track length", "Circumference of the track", 2000, 500, 6000,
84              "Track length %dm", false, 10));
85          this.properties.add(new ContinuousProperty("Mean density", "Number of vehicles per km", 40.0, 5.0, 45.0,
86              "Density %.1f veh/km", false, 11));
87          this.properties.add(new ContinuousProperty("Density variability", "Variability of the number of vehicles per km",
88              0.0, 0.0, 1.0, "%.1f", false, 12));
89          ArrayList<AbstractProperty<?>> outputProperties = new ArrayList<AbstractProperty<?>>();
90          outputProperties.add(new BooleanProperty("Density", "Density contour plot", true, false, 0));
91          outputProperties.add(new BooleanProperty("Flow", "Flow contour plot", true, false, 1));
92          outputProperties.add(new BooleanProperty("Speed", "Speed contour plot", true, false, 2));
93          outputProperties.add(new BooleanProperty("Acceleration", "Acceleration contour plot", true, false, 3));
94          outputProperties.add(new BooleanProperty("Trajectories", "Trajectory (time/distance) diagram", true, false, 4));
95          this.properties.add(new CompoundProperty("Output", "Select the graphical output", outputProperties, true, 1000));
96      }
97  
98      /**
99       * Main program.
100      * @param args String[]; the command line arguments (not used)
101      * @throws SimRuntimeException
102      * @throws RemoteException
103      */
104     public static void main(final String[] args) throws RemoteException, SimRuntimeException
105     {
106         SwingUtilities.invokeLater(new Runnable()
107         {
108             @Override
109             public void run()
110             {
111                 try
112                 {
113                     CircularLane circularLane = new CircularLane();
114                     ArrayList<AbstractProperty<?>> properties = circularLane.getProperties();
115                     try
116                     {
117                         properties.add(new ProbabilityDistributionProperty("Traffic composition",
118                             "<html>Mix of passenger cars and trucks</html>", new String[] {"passenger car", "truck"},
119                             new Double[] {0.8, 0.2}, false, 10));
120                     }
121                     catch (PropertyException exception)
122                     {
123                         exception.printStackTrace();
124                     }
125                     properties.add(new SelectionProperty("Car following model", "<html>The car following model determines "
126                         + "the acceleration that a vehicle will make taking into account "
127                         + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
128                         + "curvature of the road) capabilities of the vehicle and personality " + "of the driver.</html>",
129                         new String[] {"IDM", "IDM+"}, 1, false, 1));
130                     properties.add(IDMPropertySet.makeIDMPropertySet("Car", new DoubleScalar.Abs<AccelerationUnit>(1.0,
131                         AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Abs<AccelerationUnit>(1.5,
132                         AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<LengthUnit>(2.0, LengthUnit.METER),
133                         new DoubleScalar.Rel<TimeUnit>(1.0, TimeUnit.SECOND), 2));
134                     properties.add(IDMPropertySet.makeIDMPropertySet("Truck", new DoubleScalar.Abs<AccelerationUnit>(0.5,
135                         AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Abs<AccelerationUnit>(1.25,
136                         AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<LengthUnit>(2.0, LengthUnit.METER),
137                         new DoubleScalar.Rel<TimeUnit>(1.0, TimeUnit.SECOND), 3));
138                     new SimulatorFrame("Circular Lane animation", circularLane.buildSimulator(properties).getPanel());
139                 }
140                 catch (RemoteException | SimRuntimeException exception)
141                 {
142                     exception.printStackTrace();
143                 }
144             }
145         });
146     }
147 
148     /**
149      * Create the simulation.
150      * @return SimpleSimulator; the simulation
151      * @throws RemoteException on communications failure
152      * @throws SimRuntimeException on ???
153      */
154     public SimpleSimulator buildSimulator(ArrayList<AbstractProperty<?>> userModifiedProperties) throws RemoteException,
155         SimRuntimeException
156     {
157         LaneSimulationModel model = new LaneSimulationModel(userModifiedProperties);
158         SimpleSimulator result =
159             new SimpleSimulator(new OTSSimTimeDouble(new DoubleScalar.Abs<TimeUnit>(0.0, TimeUnit.SECOND)),
160                 new DoubleScalar.Rel<TimeUnit>(0.0, TimeUnit.SECOND),
161                 new DoubleScalar.Rel<TimeUnit>(3600.0, TimeUnit.SECOND), model, new Rectangle2D.Double(-1000, -1000, 2000,
162                     2000));
163         new ControlPanel(result);
164 
165         // Make the tab with the plots
166         AbstractProperty<?> output =
167             new CompoundProperty("", "", userModifiedProperties, false, 0).findByShortName("Output");
168         if (null == output)
169         {
170             throw new Error("Cannot find output properties");
171         }
172         ArrayList<BooleanProperty> graphs = new ArrayList<BooleanProperty>();
173         if (output instanceof CompoundProperty)
174         {
175             CompoundProperty outputProperties = (CompoundProperty) output;
176             for (AbstractProperty<?> ap : outputProperties.getValue())
177             {
178                 if (ap instanceof BooleanProperty)
179                 {
180                     BooleanProperty bp = (BooleanProperty) ap;
181                     if (bp.getValue())
182                     {
183                         graphs.add(bp);
184                     }
185                 }
186             }
187         }
188         else
189         {
190             throw new Error("output properties should be compound");
191         }
192         int graphCount = graphs.size();
193         int columns = (int) Math.ceil(Math.sqrt(graphCount));
194         int rows = 0 == columns ? 0 : (int) Math.ceil(graphCount * 1.0 / columns);
195         TablePanel charts = new TablePanel(columns, rows);
196         result.getPanel().getTabbedPane().addTab("statistics", charts);
197 
198         for (int i = 0; i < graphCount; i++)
199         {
200             String graphName = graphs.get(i).getShortName();
201             Container container = null;
202             LaneBasedGTUSampler graph;
203             if (graphName.contains("Trajectories"))
204             {
205                 TrajectoryPlot tp =
206                     new TrajectoryPlot("TrajectoryPlot", new DoubleScalar.Rel<TimeUnit>(0.5, TimeUnit.SECOND), model
207                         .getMinimumDistance(), model.lane1.getLength());
208                 tp.setTitle("Trajectory Graph");
209                 tp.setExtendedState(Frame.MAXIMIZED_BOTH);
210                 graph = tp;
211                 container = tp.getContentPane();
212             }
213             else
214             {
215                 ContourPlot cp;
216                 if (graphName.contains("Density"))
217                 {
218                     cp = new DensityContourPlot("DensityPlot", model.getMinimumDistance(), model.lane1.getLength());
219                     cp.setTitle("Density Contour Graph");
220                 }
221                 else if (graphName.contains("Speed"))
222                 {
223                     cp = new SpeedContourPlot("SpeedPlot", model.getMinimumDistance(), model.lane1.getLength());
224                     cp.setTitle("Speed Contour Graph");
225                 }
226                 else if (graphName.contains("Flow"))
227                 {
228                     cp = new FlowContourPlot("FlowPlot", model.getMinimumDistance(), model.lane1.getLength());
229                     cp.setTitle("Flow Contour Graph");
230                 }
231                 else if (graphName.contains("Acceleration"))
232                 {
233                     cp =
234                         new AccelerationContourPlot("AccelerationPlot", model.getMinimumDistance(), model.lane1.getLength());
235                     cp.setTitle("Acceleration Contour Graph");
236                 }
237                 else
238                 {
239                     throw new Error("Unhandled type of contourplot: " + graphName);
240                 }
241                 graph = cp;
242                 container = cp.getContentPane();
243             }
244             // Add the container to the matrix
245             charts.setCell(container, i % columns, i / columns);
246             model.getPlots().add(graph);
247         }
248         return result;
249     }
250 
251     /** {@inheritDoc} */
252     @Override
253     public String shortName()
254     {
255         return "Circular Lane simulation";
256     }
257 
258     /** {@inheritDoc} */
259     @Override
260     public String description()
261     {
262         return "<html><h1>Circular Lane simulation</h1>"
263             + "Vehicles are unequally distributed over a one lane ring road.<br />"
264             + "When simulation starts, all vehicles begin driving and some shockwaves may develop (depending on "
265             + "the selected track length and car following parameters).<br />"
266             + "Selected trajectory and contour plots are generated during the simulation.</html>";
267     }
268 
269     /** {@inheritDoc} */
270     @Override
271     public ArrayList<AbstractProperty<?>> getProperties()
272     {
273         return new ArrayList<AbstractProperty<?>>(this.properties);
274     }
275 
276 }
277 
278 /**
279  * Simulate traffic on a circular, one-lane road.
280  * <p>
281  * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
282  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
283  * <p>
284  * @version 21 nov. 2014 <br>
285  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
286  */
287 class LaneSimulationModel implements OTSModelInterface
288 {
289     /** */
290     private static final long serialVersionUID = 20141121L;
291 
292     /** the simulator. */
293     private OTSDEVSSimulatorInterface simulator;
294 
295     /** number of cars created. */
296     private int carsCreated = 0;
297 
298     /** the car following model, e.g. IDM Plus for cars. */
299     protected GTUFollowingModel carFollowingModelCars;
300 
301     /** the car following model, e.g. IDM Plus for trucks. */
302     protected GTUFollowingModel carFollowingModelTrucks;
303 
304     /** The probability that the next generated GTU is a passenger car. */
305     double carProbability;
306 
307     /** cars in the model. */
308     ArrayList<Car<Integer>> cars = new ArrayList<Car<Integer>>();
309 
310     /** minimum distance. */
311     private DoubleScalar.Rel<LengthUnit> minimumDistance = new DoubleScalar.Rel<LengthUnit>(0, LengthUnit.METER);
312 
313     /** The Lanes that contains the simulated Cars. */
314     Lane lane1, lane2;
315 
316     /** the speed limit. */
317     DoubleScalar.Abs<SpeedUnit> speedLimit = new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR);
318 
319     /** the contour plots. */
320     private ArrayList<LaneBasedGTUSampler> contourPlots = new ArrayList<LaneBasedGTUSampler>();
321 
322     /** the trajectory plot. */
323     private ArrayList<TrajectoryPlot> trajectoryPlots = new ArrayList<TrajectoryPlot>();
324 
325     /** User settable properties */
326     ArrayList<AbstractProperty<?>> properties = null;
327 
328     /** The random number generator used to decide what kind of GTU to generate. */
329     Random randomGenerator = new Random(12345);
330 
331     /**
332      * @param properties
333      */
334     public LaneSimulationModel(ArrayList<AbstractProperty<?>> properties)
335     {
336         this.properties = properties;
337     }
338 
339     /** {@inheritDoc} */
340     @Override
341     public void constructModel(SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> theSimulator)
342         throws SimRuntimeException, RemoteException
343     {
344         this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
345         double radius = 2000 / 2 / Math.PI;
346         double headway = 40;
347         double headwayVariability = 0;
348         try
349         {
350             String carFollowingModelName = null;
351             CompoundProperty propertyContainer = new CompoundProperty("", "", this.properties, false, 0);
352             AbstractProperty<?> cfmp = propertyContainer.findByShortName("Car following model");
353             if (null == cfmp)
354             {
355                 throw new Error("Cannot find \"Car following model\" property");
356             }
357             if (cfmp instanceof SelectionProperty)
358             {
359                 carFollowingModelName = ((SelectionProperty) cfmp).getValue();
360             }
361             else
362             {
363                 throw new Error("\"Car following model\" property has wrong type");
364             }
365             Iterator<AbstractProperty<ArrayList<AbstractProperty<?>>>> iterator =
366                 new CompoundProperty("", "", this.properties, false, 0).iterator();
367             while (iterator.hasNext())
368             {
369                 AbstractProperty<?> ap = iterator.next();
370                 // System.out.println("Handling property " + ap.getShortName());
371                 if (ap instanceof SelectionProperty)
372                 {
373                     SelectionProperty sp = (SelectionProperty) ap;
374                     if ("Car following model".equals(sp.getShortName()))
375                     {
376                         carFollowingModelName = sp.getValue();
377                     }
378                 }
379                 else if (ap instanceof ProbabilityDistributionProperty)
380                 {
381                     ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
382                     String modelName = ap.getShortName();
383                     if (modelName.equals("Traffic composition"))
384                     {
385                         this.carProbability = pdp.getValue()[0];
386                     }
387                 }
388                 else if (ap instanceof IntegerProperty)
389                 {
390                     IntegerProperty ip = (IntegerProperty) ap;
391                     if ("Track length".equals(ip.getShortName()))
392                     {
393                         radius = ip.getValue() / 2 / Math.PI;
394                     }
395                 }
396                 else if (ap instanceof ContinuousProperty)
397                 {
398                     ContinuousProperty cp = (ContinuousProperty) ap;
399                     if (cp.getShortName().equals("Mean density"))
400                     {
401                         headway = 1000 / cp.getValue();
402                     }
403                     if (cp.getShortName().equals("Density variability"))
404                     {
405                         headwayVariability = cp.getValue();
406                     }
407                 }
408                 else if (ap instanceof CompoundProperty)
409                 {
410                     CompoundProperty cp = (CompoundProperty) ap;
411                     if (ap.getShortName().equals("Output"))
412                     {
413                         continue; // Output settings are handled elsewhere
414                     }
415                     if (ap.getShortName().contains("IDM"))
416                     {
417                         // System.out.println("Car following model name appears to be " + ap.getShortName());
418                         DoubleScalar.Abs<AccelerationUnit> a = IDMPropertySet.getA(cp);
419                         DoubleScalar.Abs<AccelerationUnit> b = IDMPropertySet.getB(cp);
420                         DoubleScalar.Rel<LengthUnit> s0 = IDMPropertySet.getS0(cp);
421                         DoubleScalar.Rel<TimeUnit> tSafe = IDMPropertySet.getTSafe(cp);
422                         GTUFollowingModel gtuFollowingModel = null;
423                         if (carFollowingModelName.equals("IDM"))
424                         {
425                             gtuFollowingModel = new IDM(a, b, s0, tSafe, 1.0);
426                         }
427                         else if (carFollowingModelName.equals("IDM+"))
428                         {
429                             gtuFollowingModel = new IDMPlus(a, b, s0, tSafe, 1.0);
430                         }
431                         else
432                         {
433                             throw new Error("Unknown gtu following model: " + carFollowingModelName);
434                         }
435                         if (ap.getShortName().contains(" Car "))
436                         {
437                             this.carFollowingModelCars = gtuFollowingModel;
438                         }
439                         else if (ap.getShortName().contains(" Truck "))
440                         {
441                             this.carFollowingModelTrucks = gtuFollowingModel;
442                         }
443                         else
444                         {
445                             throw new Error("Cannot determine gtu type for " + ap.getShortName());
446                         }
447                     }
448                     /*
449                      * System.out.println("Created " + carFollowingModelName + " for " + p.getShortName());
450                      * System.out.println("a: " + a); System.out.println("b: " + b); System.out.println("s0: " + s0);
451                      * System.out.println("tSafe: " + tSafe);
452                      */
453                 }
454             }
455 
456             Node start = new Node("Start", new Coordinate(radius, 0, 0));
457             Node halfway = new Node("Halfway", new Coordinate(-radius, 0, 0));
458             LaneType<String> laneType = new LaneType<String>("CarLane");
459 
460             Coordinate[] coordsHalf1 = new Coordinate[127];
461             for (int i = 0; i < coordsHalf1.length; i++)
462             {
463                 double angle = Math.PI * (1 + i) / (1 + coordsHalf1.length);
464                 coordsHalf1[i] = new Coordinate(radius * Math.cos(angle), radius * Math.sin(angle), 0);
465             }
466             this.lane1 = LaneFactory.makeMultiLane("Lane1", start, halfway, coordsHalf1, 1, laneType, this.simulator)[0];
467 
468             Coordinate[] coordsHalf2 = new Coordinate[127];
469             for (int i = 0; i < coordsHalf2.length; i++)
470             {
471                 double angle = Math.PI + Math.PI * (1 + i) / (1 + coordsHalf2.length);
472                 coordsHalf2[i] = new Coordinate(radius * Math.cos(angle), radius * Math.sin(angle), 0);
473             }
474             this.lane2 = LaneFactory.makeMultiLane("Lane2", halfway, start, coordsHalf2, 1, laneType, this.simulator)[0];
475 
476             CrossSectionLink link1 = this.lane1.getParentLink();
477             CrossSectionLink link2 = this.lane2.getParentLink();
478             this.lane1.getParentLink().getStartNode().addLinkIn(link2);
479             this.lane1.getParentLink().getEndNode().addLinkOut(link2);
480             this.lane2.getParentLink().getStartNode().addLinkIn(link1);
481             this.lane2.getParentLink().getEndNode().addLinkOut(link1);
482 
483             // Put the (not very evenly spaced) cars on track1
484             double trackLength = this.lane1.getLength().getSI();
485             double variability = (headway - 20) * headwayVariability;
486             System.out.println("headway is " + headway + " variability limit is " + variability);
487             Random random = new Random(12345);
488             for (double pos = 0; pos <= trackLength - headway - variability;)
489             {
490                 // Actual headway is uniformly distributed around headway
491                 double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
492                 generateCar(lane1, new DoubleScalar.Rel<LengthUnit>(pos, LengthUnit.METER));
493                 pos += actualHeadway;
494             }
495 
496             // Put the (not very evenly spaced) cars on track2
497             trackLength = this.lane2.getLength().getSI();
498             variability = (headway - 20) * headwayVariability;
499             System.out.println("headway is " + headway + " variability limit is " + variability);
500             random = new Random(54321);
501             for (double pos = 0; pos <= trackLength - headway - variability;)
502             {
503                 // Actual headway is uniformly distributed around headway
504                 double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
505                 generateCar(lane2, new DoubleScalar.Rel<LengthUnit>(pos, LengthUnit.METER));
506                 pos += actualHeadway;
507             }
508 
509             // Schedule regular updates of the graph
510             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(0.999, TimeUnit.SECOND), this, this,
511                 "drawGraphs", null);
512         }
513         catch (RemoteException | SimRuntimeException | NamingException | NetworkException exception)
514         {
515             exception.printStackTrace();
516         }
517     }
518 
519     /**
520      * Add one movement step of one Car to all plots.
521      * @param car Car
522      * @throws NetworkException when car not in lane
523      * @throws RemoteException on communications failure
524      */
525     protected final void addToPlots(final Car<?> car) throws NetworkException, RemoteException
526     {
527         for (LaneBasedGTUSampler contourPlot : this.contourPlots)
528         {
529             contourPlot.addData(car);
530         }
531         for (TrajectoryPlot trajectoryPlot : this.trajectoryPlots)
532         {
533             trajectoryPlot.addData(car);
534         }
535     }
536 
537     /**
538      * Notify the contour plots that the underlying data has changed.
539      */
540     protected final void drawGraphs()
541     {
542         for (LaneBasedGTUSampler contourPlot : this.contourPlots)
543         {
544             contourPlot.reGraph();
545         }
546         for (TrajectoryPlot trajectoryPlot : this.trajectoryPlots)
547         {
548             trajectoryPlot.reGraph();
549         }
550         // Re schedule this method
551         try
552         {
553             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(
554                 this.simulator.getSimulatorTime().get().getSI() + 1, TimeUnit.SECOND), this, this, "drawGraphs", null);
555         }
556         catch (RemoteException | SimRuntimeException exception)
557         {
558             exception.printStackTrace();
559         }
560 
561     }
562 
563     /**
564      * Generate cars at a fixed rate (implemented by re-scheduling this method).
565      */
566     protected final void generateCar(Lane lane, DoubleScalar.Rel<LengthUnit> initialPosition)
567     {
568         boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
569         DoubleScalar.Abs<SpeedUnit> initialSpeed = new DoubleScalar.Abs<SpeedUnit>(0, SpeedUnit.KM_PER_HOUR);
570         Map<Lane, DoubleScalar.Rel<LengthUnit>> initialPositions = new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
571         initialPositions.put(lane, initialPosition);
572         try
573         {
574             DoubleScalar.Rel<LengthUnit> vehicleLength =
575                 new DoubleScalar.Rel<LengthUnit>(generateTruck ? 15 : 4, LengthUnit.METER);
576             GTUFollowingModel gtuFollowingModel = generateTruck ? this.carFollowingModelTrucks : this.carFollowingModelCars;
577             if (null == gtuFollowingModel)
578             {
579                 throw new Error("gtuFollowingModel is null");
580             }
581             IDMCar car =
582                 new IDMCar(++this.carsCreated, null, this.simulator, gtuFollowingModel, vehicleLength, this.simulator
583                     .getSimulatorTime().get(), initialPositions, initialSpeed);
584             this.cars.add(car);
585         }
586         catch (RemoteException | NamingException | SimRuntimeException | NetworkException exception)
587         {
588             exception.printStackTrace();
589         }
590     }
591 
592     /** {@inheritDoc} */
593     @Override
594     public SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator() throws RemoteException
595     {
596         return null;
597     }
598 
599     /**
600      * @return contourPlots
601      */
602     public final ArrayList<LaneBasedGTUSampler> getPlots()
603     {
604         return this.contourPlots;
605     }
606 
607     /**
608      * @return trajectoryPlots
609      */
610     public final ArrayList<TrajectoryPlot> getTrajectoryPlots()
611     {
612         return this.trajectoryPlots;
613     }
614 
615     /**
616      * @return minimumDistance
617      */
618     public final DoubleScalar.Rel<LengthUnit> getMinimumDistance()
619     {
620         return this.minimumDistance;
621     }
622 
623     /** Inner class IDMCar. */
624     protected class IDMCar extends Car<Integer>
625     {
626         /** */
627         private static final long serialVersionUID = 20141030L;
628 
629         /**
630          * Create a new IDMCar.
631          * @param id integer; the id of the new IDMCar
632          * @param gtuType GTUType&lt;String&gt;; the type of the GTU
633          * @param simulator OTSDEVSSimulator; the simulator that runs the new IDMCar
634          * @param carFollowingModel CarFollowingModel; the car following model of the new IDMCar
635          * @param vehicleLength DoubleScalar.Rel&lt;LengthUnit&gt;; the length of the new IDMCar
636          * @param initialTime DoubleScalar.Abs&lt;TimeUnit&gt;; the time of first evaluation of the new IDMCar
637          * @param initialLongitudinalPositions Map&lt;Lane, DoubleScalar.Rel&lt;LengthUnit&gt;&gt;; the initial lane positions
638          *            of the new IDMCar
639          * @param initialSpeed DoubleScalar.Abs&lt;SpeedUnit&gt;; the initial speed of the new IDMCar
640          * @throws NamingException on ???
641          * @throws RemoteException on Communications failure
642          */
643         public IDMCar(final int id, GTUType<String> gtuType, final OTSDEVSSimulatorInterface simulator,
644             final GTUFollowingModel carFollowingModel, DoubleScalar.Rel<LengthUnit> vehicleLength,
645             final DoubleScalar.Abs<TimeUnit> initialTime,
646             final Map<Lane, DoubleScalar.Rel<LengthUnit>> initialLongitudinalPositions,
647             final DoubleScalar.Abs<SpeedUnit> initialSpeed) throws RemoteException, NamingException, SimRuntimeException,
648             NetworkException
649         {
650             super(id, gtuType, carFollowingModel, initialLongitudinalPositions, initialSpeed, vehicleLength,
651                 new DoubleScalar.Rel<LengthUnit>(1.8, LengthUnit.METER), new DoubleScalar.Abs<SpeedUnit>(200,
652                     SpeedUnit.KM_PER_HOUR), simulator);
653             try
654             {
655                 if (id >= 0)
656                 {
657                     simulator.scheduleEventAbs(simulator.getSimulatorTime(), this, this, "move", null);
658                 }
659             }
660             catch (SimRuntimeException exception)
661             {
662                 exception.printStackTrace();
663             }
664         }
665 
666         /**
667          * Determine the movement of this car.
668          * @throws RemoteException RemoteException
669          * @throws NamingException on ???
670          * @throws NetworkException on network inconsistency
671          * @throws SimRuntimeException on ???
672          */
673         protected final void move() throws RemoteException, NamingException, NetworkException, SimRuntimeException
674         {
675             // System.out.println("move " + getId());
676             if (this.getId() < 0)
677             {
678                 return;
679             }
680             Collection<Car<Integer>> leaders = new ArrayList<Car<Integer>>();
681             // FIXME there should be a much easier way to obtain the leader; we should not have to maintain our own
682             // list
683             int carIndex = LaneSimulationModel.this.cars.indexOf(this);
684             if (carIndex < LaneSimulationModel.this.cars.size() - 1)
685             {
686                 leaders.add(LaneSimulationModel.this.cars.get(carIndex + 1));
687             }
688             else
689             {
690                 leaders.add(LaneSimulationModel.this.cars.get(0));
691             }
692 
693             /*
694              * NOT NEEDED ANYMORE -- THE SENSORS SHOULD TAKE CARE OF THIS... // Horrible hack; wrap the position back to zero
695              * when vehicle exceeds length of the circuit if (position(LaneSimulationModel.this.lane, getFront()).getSI() >
696              * LaneSimulationModel.this.lane.getLength() .getSI()) { Map<Lane, DoubleScalar.Rel<LengthUnit>> map =
697              * this.positions(getFront()); for (Lane l : map.keySet()) { map.put(l, new
698              * DoubleScalar.Rel<LengthUnit>(map.get(l).getSI() % LaneSimulationModel.this.lane.getLength().getSI(),
699              * LengthUnit.METER)); } } // Even more horrible hack; create a fake leader for the vehicle closest to the wrap
700              * around point Car<Integer> leader = leaders.iterator().next(); // Figure out the headway if
701              * (leader.position(LaneSimulationModel.this.lane, leader.getRear()).getSI() < this.position(
702              * LaneSimulationModel.this.lane, this.getFront()).getSI()) { Map<Lane, DoubleScalar.Rel<LengthUnit>>
703              * initialPositions = new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>(); initialPositions.put(
704              * LaneSimulationModel.this.lane, new DoubleScalar.Rel<LengthUnit>(leader.position(LaneSimulationModel.this.lane,
705              * leader.getFront(), getNextEvaluationTime()).getSI() + LaneSimulationModel.this.lane.getLength().getSI(),
706              * LengthUnit.METER)); try { // HACK FIXME (negative length trick) IDMCar fakeLeader = new IDMCar(-99999, null,
707              * this.getSimulator(), this.getGTUFollowingModel(), new DoubleScalar.Rel<LengthUnit>(-leader.getLength().getSI(),
708              * LengthUnit.METER), this.getSimulator().getSimulatorTime().get(), initialPositions,
709              * leader.getLongitudinalVelocity()); leaders.add(fakeLeader); } catch (RemoteException exception) {
710              * exception.printStackTrace(); } }
711              */
712 
713             GTUFollowingModelResult cfmr =
714                 this.getGTUFollowingModel().computeAcceleration(this, leaders, LaneSimulationModel.this.speedLimit);
715             setState(cfmr);
716             // Add the movement of this Car to the contour plots
717             // XXX Plots go wrong because same car reappears.
718             // XXX therefore foolowing statement not included:
719             // XXX addToPlots(this);
720             // Schedule the next evaluation of this car
721             getSimulator().scheduleEventRel(new DoubleScalar.Rel<TimeUnit>(0.5, TimeUnit.SECOND), this, this, "move", null);
722         }
723     }
724 
725 }