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