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