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.HashSet;
9   import java.util.LinkedHashSet;
10  import java.util.List;
11  import java.util.Random;
12  import java.util.Set;
13  
14  import javax.naming.NamingException;
15  import javax.swing.JPanel;
16  import javax.swing.SwingUtilities;
17  
18  import org.djunits.unit.UNITS;
19  import org.djunits.value.vdouble.scalar.Acceleration;
20  import org.djunits.value.vdouble.scalar.Duration;
21  import org.djunits.value.vdouble.scalar.Length;
22  import org.djunits.value.vdouble.scalar.Speed;
23  import org.djunits.value.vdouble.scalar.Time;
24  import org.opentrafficsim.base.modelproperties.BooleanProperty;
25  import org.opentrafficsim.base.modelproperties.CompoundProperty;
26  import org.opentrafficsim.base.modelproperties.ContinuousProperty;
27  import org.opentrafficsim.base.modelproperties.IntegerProperty;
28  import org.opentrafficsim.base.modelproperties.ProbabilityDistributionProperty;
29  import org.opentrafficsim.base.modelproperties.Property;
30  import org.opentrafficsim.base.modelproperties.PropertyException;
31  import org.opentrafficsim.base.modelproperties.SelectionProperty;
32  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
33  import org.opentrafficsim.core.dsol.OTSModelInterface;
34  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
35  import org.opentrafficsim.core.geometry.OTSGeometryException;
36  import org.opentrafficsim.core.geometry.OTSPoint3D;
37  import org.opentrafficsim.core.gtu.GTUDirectionality;
38  import org.opentrafficsim.core.gtu.GTUException;
39  import org.opentrafficsim.core.gtu.GTUType;
40  import org.opentrafficsim.core.gtu.animation.GTUColorer;
41  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
42  import org.opentrafficsim.core.network.LongitudinalDirectionality;
43  import org.opentrafficsim.core.network.NetworkException;
44  import org.opentrafficsim.core.network.OTSNetwork;
45  import org.opentrafficsim.core.network.OTSNode;
46  import org.opentrafficsim.graphs.AccelerationContourPlot;
47  import org.opentrafficsim.graphs.ContourPlot;
48  import org.opentrafficsim.graphs.DensityContourPlot;
49  import org.opentrafficsim.graphs.FlowContourPlot;
50  import org.opentrafficsim.graphs.LaneBasedGTUSampler;
51  import org.opentrafficsim.graphs.SpeedContourPlot;
52  import org.opentrafficsim.graphs.TrajectoryPlot;
53  import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
54  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
55  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlannerFactory;
56  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
57  import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
58  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMOld;
59  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusFactory;
60  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
61  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.AbstractLaneChangeModel;
62  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
63  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
64  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRSFactory;
65  import org.opentrafficsim.road.gtu.lane.tactical.toledo.ToledoFactory;
66  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
67  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
68  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
69  import org.opentrafficsim.road.modelproperties.IDMPropertySet;
70  import org.opentrafficsim.road.network.factory.LaneFactory;
71  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
72  import org.opentrafficsim.road.network.lane.Lane;
73  import org.opentrafficsim.road.network.lane.LaneType;
74  import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
75  import org.opentrafficsim.simulationengine.OTSSimulationException;
76  import org.opentrafficsim.simulationengine.SimpleSimulatorInterface;
77  
78  import nl.tudelft.simulation.dsol.SimRuntimeException;
79  import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
80  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
81  
82  /**
83   * Circular road simulation demo.
84   * <p>
85   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
86   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
87   * <p>
88   * $LastChangedDate: 2016-11-02 11:22:48 +0100 (Wed, 02 Nov 2016) $, @version $Revision: 2441 $, by $Author: averbraeck $,
89   * initial version 21 nov. 2014 <br>
90   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
91   */
92  public class CircularRoad extends AbstractWrappableAnimation implements UNITS
93  {
94      /** */
95      private static final long serialVersionUID = 1L;
96  
97      /** The model. */
98      private RoadSimulationModel model;
99  
100     /**
101      * Create a CircularRoad simulation.
102      * @throws PropertyException when a property is not handled
103      */
104     public CircularRoad() throws PropertyException
105     {
106         this.properties.add(new SelectionProperty("LaneChanging", "Lane changing",
107                 "<html>The lane change strategies vary in politeness.<br>"
108                         + "Two types are implemented:<ul><li>Egoistic (looks only at personal gain).</li>"
109                         + "<li>Altruistic (assigns effect on new and current follower the same weight as "
110                         + "the personal gain).</html>",
111                 new String[] { "Egoistic", "Altruistic" }, 0, false, 500));
112         this.properties.add(new SelectionProperty("TacticalPlanner", "Tactical planner",
113                 "<html>The tactical planner determines if a lane change is desired and possible.</html>",
114                 new String[] { "MOBIL", "LMRS", "Toledo" }, 0, false, 600));
115         this.properties.add(new IntegerProperty("TrackLength", "Track length", "Circumference of the track", 2000, 500, 6000,
116                 "Track length %dm", false, 10));
117         this.properties.add(new ContinuousProperty("MeanDensity", "Mean density", "Number of vehicles per km", 40.0, 5.0, 45.0,
118                 "Density %.1f veh/km", false, 11));
119         this.properties.add(new ContinuousProperty("DensityVariability", "Density variability",
120                 "Variability of the number of vehicles per km", 0.0, 0.0, 1.0, "%.1f", false, 12));
121         List<Property<?>> outputProperties = new ArrayList<Property<?>>();
122         for (int lane = 1; lane <= 2; lane++)
123         {
124             String laneId = String.format("Lane %d ", lane);
125             outputProperties.add(new BooleanProperty(laneId + "Density", laneId + " Density", laneId + "Density contour plot",
126                     true, false, 0));
127             outputProperties
128                     .add(new BooleanProperty(laneId + "Flow", laneId + " Flow", laneId + "Flow contour plot", true, false, 1));
129             outputProperties.add(
130                     new BooleanProperty(laneId + "Speed", laneId + " Speed", laneId + "Speed contour plot", true, false, 2));
131             outputProperties.add(new BooleanProperty(laneId + "Acceleration", laneId + " Acceleration",
132                     laneId + "Acceleration contour plot", true, false, 3));
133             outputProperties.add(new BooleanProperty(laneId + "Fixed Sample Rate Trajectories", laneId + " FSR Trajectories",
134                     laneId + "Trajectory (time/distance) diagram", true, false, 4));
135             outputProperties.add(new BooleanProperty(laneId + "Variable Sample Rate Trajectories", laneId + " VSR Trajectories",
136                     laneId + "Trajectory (time/distance) diagram", true, false, 5));
137         }
138         this.properties.add(new CompoundProperty("OutputGraphs", "Output graphs", "Select the graphical output",
139                 outputProperties, true, 1000));
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public final void stopTimersThreads()
145     {
146         super.stopTimersThreads();
147         this.model = null;
148     }
149 
150     /**
151      * Main program.
152      * @param args String[]; the command line arguments (not used)
153      * @throws SimRuntimeException should never happen
154      */
155     public static void main(final String[] args) throws SimRuntimeException
156     {
157         SwingUtilities.invokeLater(new Runnable()
158         {
159             @Override
160             public void run()
161             {
162                 try
163                 {
164                     CircularRoad circularRoad = new CircularRoad();
165                     List<Property<?>> propertyList = circularRoad.getProperties();
166                     try
167                     {
168                         propertyList.add(new ProbabilityDistributionProperty("TrafficComposition", "Traffic composition",
169                                 "<html>Mix of passenger cars and trucks</html>", new String[] { "passenger car", "truck" },
170                                 new Double[] { 0.8, 0.2 }, false, 10));
171                     }
172                     catch (PropertyException exception)
173                     {
174                         exception.printStackTrace();
175                     }
176                     propertyList.add(new SelectionProperty("CarFollowingModel", "Car following model",
177                             "<html>The car following model determines "
178                                     + "the acceleration that a vehicle will make taking into account "
179                                     + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
180                                     + "curvature of the road) capabilities of the vehicle and personality "
181                                     + "of the driver.</html>",
182                             new String[] { "IDM", "IDM+" }, 1, false, 1));
183                     propertyList.add(IDMPropertySet.makeIDMPropertySet("IDMCar", "Car",
184                             new Acceleration(1.0, METER_PER_SECOND_2), new Acceleration(1.5, METER_PER_SECOND_2),
185                             new Length(2.0, METER), new Duration(1.0, SECOND), 2));
186                     propertyList.add(IDMPropertySet.makeIDMPropertySet("IDMTruck", "Truck",
187                             new Acceleration(0.5, METER_PER_SECOND_2), new Acceleration(1.25, METER_PER_SECOND_2),
188                             new Length(2.0, METER), new Duration(1.0, SECOND), 3));
189 
190                     circularRoad.buildAnimator(new Time(0.0, SECOND), new Duration(0.0, SECOND), new Duration(3600.0, SECOND),
191                             propertyList, null, true);
192                 }
193                 catch (SimRuntimeException | NamingException | OTSSimulationException | PropertyException exception)
194                 {
195                     exception.printStackTrace();
196                 }
197             }
198         });
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     protected final OTSModelInterface makeModel(final GTUColorer colorer)
204     {
205         this.model = new RoadSimulationModel(getSavedUserModifiedProperties(), colorer);
206         return this.model;
207     }
208 
209     /**
210      * @return the saved user properties for a next run
211      */
212     private List<Property<?>> getSavedUserModifiedProperties()
213     {
214         return this.savedUserModifiedProperties;
215     }
216 
217     /** {@inheritDoc} */
218     @Override
219     protected final Rectangle2D.Double makeAnimationRectangle()
220     {
221         return new Rectangle2D.Double(-350, -350, 700, 700);
222     }
223 
224     /** {@inheritDoc} */
225     @Override
226     protected final JPanel makeCharts(final SimpleSimulatorInterface simulator) throws OTSSimulationException, PropertyException
227     {
228         // Make the tab with the plots
229         Property<?> output = new CompoundProperty("", "", "", this.properties, false, 0).findByKey("OutputGraphs");
230         if (null == output)
231         {
232             throw new Error("Cannot find output properties");
233         }
234         ArrayList<BooleanProperty> graphs = new ArrayList<BooleanProperty>();
235         if (output instanceof CompoundProperty)
236         {
237             CompoundProperty outputProperties = (CompoundProperty) output;
238             for (Property<?> ap : outputProperties.getValue())
239             {
240                 if (ap instanceof BooleanProperty)
241                 {
242                     BooleanProperty bp = (BooleanProperty) ap;
243                     if (bp.getValue())
244                     {
245                         graphs.add(bp);
246                     }
247                 }
248             }
249         }
250         else
251         {
252             throw new Error("output properties should be compound");
253         }
254 
255         int graphCount = graphs.size();
256         int columns = (int) Math.ceil(Math.sqrt(graphCount));
257         int rows = 0 == columns ? 0 : (int) Math.ceil(graphCount * 1.0 / columns);
258         TablePanel charts = new TablePanel(columns, rows);
259 
260         for (int i = 0; i < graphCount; i++)
261         {
262             String graphName = graphs.get(i).getKey();
263             Container container = null;
264             LaneBasedGTUSampler graph;
265             int pos = graphName.indexOf(' ') + 1;
266             String laneNumberText = graphName.substring(pos, pos + 1);
267             int lane = Integer.parseInt(laneNumberText) - 1;
268 
269             if (graphName.contains("Trajectories"))
270             {
271                 Duration sampleInterval = graphName.contains("Variable Sample Rate") ? null : new Duration(0.2, SECOND);
272                 TrajectoryPlot tp = new TrajectoryPlot(graphName, sampleInterval, this.model.getPath(lane), simulator);
273                 tp.setTitle("Trajectory Graph");
274                 tp.setExtendedState(Frame.MAXIMIZED_BOTH);
275                 graph = tp;
276                 container = tp.getContentPane();
277             }
278             else
279             {
280                 ContourPlot cp;
281                 if (graphName.contains("Density"))
282                 {
283                     cp = new DensityContourPlot(graphName, this.model.getPath(lane));
284                     cp.setTitle("Density Contour Graph");
285                 }
286                 else if (graphName.contains("Speed"))
287                 {
288                     cp = new SpeedContourPlot(graphName, this.model.getPath(lane));
289                     cp.setTitle("Speed Contour Graph");
290                 }
291                 else if (graphName.contains("Flow"))
292                 {
293                     cp = new FlowContourPlot(graphName, this.model.getPath(lane));
294                     cp.setTitle("Flow Contour Graph");
295                 }
296                 else if (graphName.contains("Acceleration"))
297                 {
298                     cp = new AccelerationContourPlot(graphName, this.model.getPath(lane));
299                     cp.setTitle("Acceleration Contour Graph");
300                 }
301                 else
302                 {
303                     throw new Error("Unhandled type of contourplot: " + graphName);
304                 }
305                 graph = cp;
306                 container = cp.getContentPane();
307             }
308             // Add the container to the matrix
309             charts.setCell(container, i % columns, i / columns);
310             this.model.getPlots().add(graph);
311         }
312 
313         return charts;
314     }
315 
316     /** {@inheritDoc} */
317     @Override
318     public final String shortName()
319     {
320         return "Circular Road simulation";
321     }
322 
323     /** {@inheritDoc} */
324     @Override
325     public final String description()
326     {
327         return "<html><h1>Circular Road simulation</h1>" + "Vehicles are unequally distributed over a two lane ring road.<br>"
328                 + "When simulation starts, all vehicles begin driving, some lane changes will occurr and some "
329                 + "shockwaves should develop.<br>"
330                 + "Trajectories and contourplots are generated during the simulation for both lanes.</html>";
331     }
332 
333 }
334 
335 /**
336  * Simulate traffic on a circular, two-lane road.
337  * <p>
338  * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
339  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
340  * <p>
341  * $LastChangedDate: 2016-11-02 11:22:48 +0100 (Wed, 02 Nov 2016) $, @version $Revision: 2441 $, by $Author: averbraeck $,
342  * initial version 1 nov. 2014 <br>
343  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
344  */
345 class RoadSimulationModel implements OTSModelInterface, UNITS
346 {
347     /** */
348     private static final long serialVersionUID = 20141121L;
349 
350     /** The simulator. */
351     private OTSDEVSSimulatorInterface simulator;
352 
353     /** Number of cars created. */
354     private int carsCreated = 0;
355 
356     /** The car following model, e.g. IDM Plus for cars. */
357     private GTUFollowingModelOld carFollowingModelCars;
358 
359     /** The car following model, e.g. IDM Plus for trucks. */
360     private GTUFollowingModelOld carFollowingModelTrucks;
361 
362     /** The probability that the next generated GTU is a passenger car. */
363     private double carProbability;
364 
365     /** The lane change model. */
366     private AbstractLaneChangeModel laneChangeModel;
367 
368     /** Minimum distance. */
369     private Length minimumDistance = new Length(0, METER);
370 
371     /** The speed limit. */
372     private Speed speedLimit = new Speed(100, KM_PER_HOUR);
373 
374     /** The plots. */
375     private List<LaneBasedGTUSampler> plots = new ArrayList<>();
376 
377     /** User settable properties. */
378     private List<Property<?>> properties = null;
379 
380     /** The sequence of Lanes that all vehicles will follow. */
381     private List<List<Lane>> paths = new ArrayList<>();
382 
383     /** The random number generator used to decide what kind of GTU to generate. */
384     private Random randomGenerator = new Random(12345);
385 
386     /** The GTUColorer for the generated vehicles. */
387     private final GTUColorer gtuColorer;
388 
389     /** Strategical planner generator for cars. */
390     private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalPlanner> strategicalPlannerGeneratorCars = null;
391 
392     /** Strategical planner generator for cars. */
393     private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalPlanner> strategicalPlannerGeneratorTrucks = null;
394 
395     /** The OTSNetwork. */
396     private final OTSNetwork network = new OTSNetwork("network");
397 
398     /**
399      * @param properties ArrayList&lt;AbstractProperty&lt;?&gt;&gt;; the properties
400      * @param gtuColorer the default and initial GTUColorer, e.g. a DefaultSwitchableTUColorer
401      */
402     RoadSimulationModel(final List<Property<?>> properties, final GTUColorer gtuColorer)
403     {
404         this.properties = properties;
405         this.gtuColorer = gtuColorer;
406     }
407 
408     /**
409      * @param index int; the rank number of the path
410      * @return List&lt;Lane&gt;; the set of lanes for the specified index
411      */
412     public List<Lane> getPath(final int index)
413     {
414         return this.paths.get(index);
415     }
416 
417     /** {@inheritDoc} */
418     @Override
419     public void constructModel(final SimulatorInterface<Time, Duration, OTSSimTimeDouble> theSimulator)
420             throws SimRuntimeException, RemoteException
421     {
422         final int laneCount = 2;
423         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
424         {
425             this.paths.add(new ArrayList<Lane>());
426         }
427         this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
428         double radius = 6000 / 2 / Math.PI;
429         double headway = 40;
430         double headwayVariability = 0;
431         try
432         {
433 
434             // Get car-following model name
435             String carFollowingModelName = null;
436             CompoundProperty propertyContainer = new CompoundProperty("", "", "", this.properties, false, 0);
437             Property<?> cfmp = propertyContainer.findByKey("CarFollowingModel");
438             if (null == cfmp)
439             {
440                 throw new Error("Cannot find \"Car following model\" property");
441             }
442             if (cfmp instanceof SelectionProperty)
443             {
444                 carFollowingModelName = ((SelectionProperty) cfmp).getValue();
445             }
446             else
447             {
448                 throw new Error("\"Car following model\" property has wrong type");
449             }
450 
451             // Get car-following model parameter
452             for (Property<?> ap : new CompoundProperty("", "", "", this.properties, false, 0))
453             {
454                 if (ap instanceof CompoundProperty)
455                 {
456                     CompoundProperty cp = (CompoundProperty) ap;
457                     System.out.println("Checking compound property " + cp);
458                     if (ap.getKey().contains("IDM"))
459                     {
460                         System.out.println("Car following model name appears to be " + ap.getKey());
461                         Acceleration a = IDMPropertySet.getA(cp);
462                         Acceleration b = IDMPropertySet.getB(cp);
463                         Length s0 = IDMPropertySet.getS0(cp);
464                         Duration tSafe = IDMPropertySet.getTSafe(cp);
465                         GTUFollowingModelOld gtuFollowingModel = null;
466                         if (carFollowingModelName.equals("IDM"))
467                         {
468                             gtuFollowingModel = new IDMOld(a, b, s0, tSafe, 1.0);
469                         }
470                         else if (carFollowingModelName.equals("IDM+"))
471                         {
472                             gtuFollowingModel = new IDMPlusOld(a, b, s0, tSafe, 1.0);
473                         }
474                         else
475                         {
476                             throw new Error("Unknown gtu following model: " + carFollowingModelName);
477                         }
478                         if (ap.getKey().contains("Car"))
479                         {
480                             this.carFollowingModelCars = gtuFollowingModel;
481                         }
482                         else if (ap.getKey().contains("Truck"))
483                         {
484                             this.carFollowingModelTrucks = gtuFollowingModel;
485                         }
486                         else
487                         {
488                             throw new Error("Cannot determine gtu type for " + ap.getKey());
489                         }
490                     }
491                 }
492             }
493 
494             // Get lane change model
495             cfmp = propertyContainer.findByKey("LaneChanging");
496             if (null == cfmp)
497             {
498                 throw new Error("Cannot find \"Lane changing\" property");
499             }
500             if (cfmp instanceof SelectionProperty)
501             {
502                 String laneChangeModelName = ((SelectionProperty) cfmp).getValue();
503                 if ("Egoistic".equals(laneChangeModelName))
504                 {
505                     this.laneChangeModel = new Egoistic();
506                 }
507                 else if ("Altruistic".equals(laneChangeModelName))
508                 {
509                     this.laneChangeModel = new Altruistic();
510                 }
511                 else
512                 {
513                     throw new Error("Lane changing " + laneChangeModelName + " not implemented");
514                 }
515             }
516             else
517             {
518                 throw new Error("\"Lane changing\" property has wrong type");
519             }
520 
521             // Get remaining properties
522             for (Property<?> ap : new CompoundProperty("", "", "", this.properties, false, 0))
523             {
524                 if (ap instanceof SelectionProperty)
525                 {
526                     SelectionProperty sp = (SelectionProperty) ap;
527                     if ("TacticalPlanner".equals(sp.getKey()))
528                     {
529                         String tacticalPlannerName = sp.getValue();
530                         if ("MOBIL".equals(tacticalPlannerName))
531                         {
532                             this.strategicalPlannerGeneratorCars = new LaneBasedStrategicalRoutePlannerFactory(
533                                     new LaneBasedCFLCTacticalPlannerFactory(this.carFollowingModelCars, this.laneChangeModel));
534                             this.strategicalPlannerGeneratorTrucks =
535                                     new LaneBasedStrategicalRoutePlannerFactory(new LaneBasedCFLCTacticalPlannerFactory(
536                                             this.carFollowingModelTrucks, this.laneChangeModel));
537                         }
538                         else if ("LMRS".equals(tacticalPlannerName))
539                         {
540                             // provide default parameters with the car-following model
541                             BehavioralCharacteristics defaultBehavioralCFCharacteristics = new BehavioralCharacteristics();
542                             defaultBehavioralCFCharacteristics.setDefaultParameters(AbstractIDM.class);
543                             this.strategicalPlannerGeneratorCars = new LaneBasedStrategicalRoutePlannerFactory(
544                                     new LMRSFactory(new IDMPlusFactory(), defaultBehavioralCFCharacteristics));
545                             this.strategicalPlannerGeneratorTrucks = new LaneBasedStrategicalRoutePlannerFactory(
546                                     new LMRSFactory(new IDMPlusFactory(), defaultBehavioralCFCharacteristics));
547                         }
548                         else if ("Toledo".equals(tacticalPlannerName))
549                         {
550                             this.strategicalPlannerGeneratorCars =
551                                     new LaneBasedStrategicalRoutePlannerFactory(new ToledoFactory());
552                             this.strategicalPlannerGeneratorTrucks =
553                                     new LaneBasedStrategicalRoutePlannerFactory(new ToledoFactory());
554                         }
555                         else
556                         {
557                             throw new Error("Don't know how to create a " + tacticalPlannerName + " tactical planner");
558                         }
559                     }
560                 }
561                 else if (ap instanceof ProbabilityDistributionProperty)
562                 {
563                     ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
564                     if (ap.getKey().equals("TrafficComposition"))
565                     {
566                         this.carProbability = pdp.getValue()[0];
567                     }
568                 }
569                 else if (ap instanceof IntegerProperty)
570                 {
571                     IntegerProperty ip = (IntegerProperty) ap;
572                     if ("TrackLength".equals(ip.getKey()))
573                     {
574                         radius = ip.getValue() / 2 / Math.PI;
575                     }
576                 }
577                 else if (ap instanceof ContinuousProperty)
578                 {
579                     ContinuousProperty cp = (ContinuousProperty) ap;
580                     if (cp.getKey().equals("MeanDensity"))
581                     {
582                         headway = 1000 / cp.getValue();
583                     }
584                     if (cp.getKey().equals("DensityVariability"))
585                     {
586                         headwayVariability = cp.getValue();
587                     }
588                 }
589                 else if (ap instanceof CompoundProperty)
590                 {
591                     if (ap.getKey().equals("OutputGraphs"))
592                     {
593                         continue; // Output settings are handled elsewhere
594                     }
595                 }
596             }
597             GTUType gtuType = new GTUType("car");
598             Set<GTUType> compatibility = new HashSet<GTUType>();
599             compatibility.add(gtuType);
600             LaneType laneType = new LaneType("CarLane", compatibility);
601             OTSNode start = new OTSNode(this.network, "Start", new OTSPoint3D(radius, 0, 0));
602             OTSNode halfway = new OTSNode(this.network, "Halfway", new OTSPoint3D(-radius, 0, 0));
603 
604             OTSPoint3D[] coordsHalf1 = new OTSPoint3D[127];
605             for (int i = 0; i < coordsHalf1.length; i++)
606             {
607                 double angle = Math.PI * (1 + i) / (1 + coordsHalf1.length);
608                 coordsHalf1[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
609             }
610             Lane[] lanes1 = LaneFactory.makeMultiLane(this.network, "FirstHalf", start, halfway, coordsHalf1, laneCount,
611                     laneType, this.speedLimit, this.simulator, LongitudinalDirectionality.DIR_PLUS);
612             OTSPoint3D[] coordsHalf2 = new OTSPoint3D[127];
613             for (int i = 0; i < coordsHalf2.length; i++)
614             {
615                 double angle = Math.PI + Math.PI * (1 + i) / (1 + coordsHalf2.length);
616                 coordsHalf2[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
617             }
618             Lane[] lanes2 = LaneFactory.makeMultiLane(this.network, "SecondHalf", halfway, start, coordsHalf2, laneCount,
619                     laneType, this.speedLimit, this.simulator, LongitudinalDirectionality.DIR_PLUS);
620             for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
621             {
622                 this.paths.get(laneIndex).add(lanes1[laneIndex]);
623                 this.paths.get(laneIndex).add(lanes2[laneIndex]);
624             }
625             // Put the (not very evenly spaced) cars on the track
626             double variability = (headway - 20) * headwayVariability;
627             System.out.println("headway is " + headway + " variability limit is " + variability);
628             Random random = new Random(12345);
629             for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
630             {
631                 double lane1Length = lanes1[laneIndex].getLength().getSI();
632                 double trackLength = lane1Length + lanes2[laneIndex].getLength().getSI();
633                 for (double pos = 0; pos <= trackLength - headway - variability;)
634                 {
635                     Lane lane = pos >= lane1Length ? lanes2[laneIndex] : lanes1[laneIndex];
636                     // Actual headway is uniformly distributed around headway
637                     double laneRelativePos = pos > lane1Length ? pos - lane1Length : pos;
638                     double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
639                     // System.out.println(lane + ", len=" + lane.getLength() + ", pos=" + laneRelativePos);
640                     generateCar(new Length(laneRelativePos, METER), lane, gtuType);
641                     pos += actualHeadway;
642                 }
643             }
644             // Schedule regular updates of the graph
645             this.simulator.scheduleEventAbs(new Time(9.999, SECOND), this, this, "drawGraphs", null);
646         }
647         catch (SimRuntimeException | NamingException | NetworkException | GTUException | OTSGeometryException
648                 | PropertyException exception)
649         {
650             exception.printStackTrace();
651         }
652     }
653 
654     /**
655      * Notify the contour plots that the underlying data has changed.
656      */
657     protected final void drawGraphs()
658     {
659         for (LaneBasedGTUSampler plot : this.plots)
660         {
661             plot.reGraph();
662         }
663         // Re schedule this method
664         try
665         {
666             this.simulator.scheduleEventAbs(new Time(this.simulator.getSimulatorTime().get().getSI() + 10, SECOND), this, this,
667                     "drawGraphs", null);
668         }
669         catch (SimRuntimeException exception)
670         {
671             exception.printStackTrace();
672         }
673 
674     }
675 
676     /**
677      * Generate one car.
678      * @param initialPosition Length; the initial position of the new cars
679      * @param lane Lane; the lane on which the new cars are placed
680      * @param gtuType GTUType&lt;String&gt;; the type of the new cars
681      * @throws NamingException on ???
682      * @throws SimRuntimeException cannot happen
683      * @throws NetworkException on network inconsistency
684      * @throws GTUException when something goes wrong during construction of the car
685      * @throws OTSGeometryException when the initial position is outside the center line of the lane
686      */
687     protected final void generateCar(final Length initialPosition, final Lane lane, final GTUType gtuType)
688             throws NamingException, NetworkException, SimRuntimeException, GTUException, OTSGeometryException
689     {
690 
691         // GTU itself
692         boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
693         Length vehicleLength = new Length(generateTruck ? 15 : 4, METER);
694         LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU("" + (++this.carsCreated), gtuType, vehicleLength,
695                 new Length(1.8, METER), new Speed(200, KM_PER_HOUR), this.simulator, this.network);
696 
697         // strategical planner
698         LaneBasedStrategicalPlanner strategicalPlanner;
699         if (!generateTruck)
700         {
701             strategicalPlanner = this.strategicalPlannerGeneratorCars.create(gtu);
702         }
703         else
704         {
705             strategicalPlanner = this.strategicalPlannerGeneratorTrucks.create(gtu);
706         }
707 
708         // init
709         Set<DirectedLanePosition> initialPositions = new LinkedHashSet<>(1);
710         initialPositions.add(new DirectedLanePosition(lane, initialPosition, GTUDirectionality.DIR_PLUS));
711         Speed initialSpeed = new Speed(0, KM_PER_HOUR);
712         gtu.initWithAnimation(strategicalPlanner, initialPositions, initialSpeed, DefaultCarAnimation.class, this.gtuColorer);
713         // Check that the gtu is indeed at the expected position
714         /*-
715         Lane actualLane = gtu.getLanes().keySet().iterator().next();
716         if (actualLane != lane)
717         {
718             System.out.println("Newly created GTU already switched from lane" + lane + " to " + actualLane);
719         }
720         else
721         {
722             Length actualPosition = gtu.position(lane, gtu.getReference());
723             if (Math.abs(actualPosition.si - initialPosition.si) > 0.5)
724             {
725                 System.out.println("Newly created GTU jumped from " + initialPosition + " to " + actualPosition);
726             }
727             DirectedPoint actualLocation = gtu.getLocation();
728             DirectedPoint expectedLocation =
729                     lane.getCenterLine().extract(initialPosition, initialPosition.plus(new Length(0.0001, LengthUnit.METER)))
730                             .getLocation();
731             double distance = actualLocation.distance(expectedLocation);
732             if (distance > 0.5)
733             {
734                 System.out.println("Actual location of new GTU is " + actualLocation + ", expectedLocation is "
735                         + expectedLocation + " difference is " + distance + "m");
736             }
737         }
738          */
739     }
740 
741     /** {@inheritDoc} */
742     @Override
743     public SimulatorInterface<Time, Duration, OTSSimTimeDouble> getSimulator() throws RemoteException
744     {
745         return this.simulator;
746     }
747 
748     /**
749      * @return plots
750      */
751     public final List<LaneBasedGTUSampler> getPlots()
752     {
753         return this.plots;
754     }
755 
756     /**
757      * @return minimumDistance
758      */
759     public final Length getMinimumDistance()
760     {
761         return this.minimumDistance;
762     }
763 
764     /**
765      * Stop simulation and throw an Error.
766      * @param theSimulator OTSDEVSSimulatorInterface; the simulator
767      * @param errorMessage String; the error message
768      */
769     public void stopSimulator(final OTSDEVSSimulatorInterface theSimulator, final String errorMessage)
770     {
771         System.out.println("Error: " + errorMessage);
772         try
773         {
774             if (theSimulator.isRunning())
775             {
776                 theSimulator.stop();
777             }
778         }
779         catch (SimRuntimeException exception)
780         {
781             exception.printStackTrace();
782         }
783         throw new Error(errorMessage);
784     }
785 
786 }