View Javadoc
1   package org.opentrafficsim.demo.strategies;
2   
3   import java.awt.Color;
4   import java.awt.Component;
5   import java.awt.Dimension;
6   import java.util.ArrayList;
7   import java.util.Hashtable;
8   import java.util.Iterator;
9   import java.util.List;
10  
11  import javax.naming.NamingException;
12  import javax.swing.Box;
13  import javax.swing.BoxLayout;
14  import javax.swing.JLabel;
15  import javax.swing.JSlider;
16  import javax.swing.SwingConstants;
17  import javax.swing.border.EmptyBorder;
18  import javax.swing.event.ChangeEvent;
19  import javax.swing.event.ChangeListener;
20  
21  import org.djunits.unit.DirectionUnit;
22  import org.djunits.unit.SpeedUnit;
23  import org.djunits.value.vdouble.scalar.Acceleration;
24  import org.djunits.value.vdouble.scalar.Direction;
25  import org.djunits.value.vdouble.scalar.Duration;
26  import org.djunits.value.vdouble.scalar.Length;
27  import org.djunits.value.vdouble.scalar.Speed;
28  import org.djutils.cli.CliException;
29  import org.djutils.cli.CliUtil;
30  import org.djutils.draw.curve.Arc2d;
31  import org.djutils.draw.curve.OffsetCurve2d;
32  import org.djutils.draw.point.DirectedPoint2d;
33  import org.djutils.draw.point.Point2d;
34  import org.djutils.event.Event;
35  import org.djutils.event.EventListener;
36  import org.djutils.exceptions.Try;
37  import org.opentrafficsim.animation.gtu.colorer.AccelerationGtuColorer;
38  import org.opentrafficsim.animation.gtu.colorer.DesiredHeadwayGtuColorer;
39  import org.opentrafficsim.animation.gtu.colorer.IncentiveGtuColorer;
40  import org.opentrafficsim.animation.gtu.colorer.SocialPressureGtuColorer;
41  import org.opentrafficsim.animation.gtu.colorer.SpeedGtuColorer;
42  import org.opentrafficsim.base.OtsRuntimeException;
43  import org.opentrafficsim.base.parameters.ParameterTypes;
44  import org.opentrafficsim.core.definitions.Defaults;
45  import org.opentrafficsim.core.definitions.DefaultsNl;
46  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
47  import org.opentrafficsim.core.gtu.Gtu;
48  import org.opentrafficsim.core.gtu.GtuCharacteristics;
49  import org.opentrafficsim.core.gtu.GtuException;
50  import org.opentrafficsim.core.gtu.GtuType;
51  import org.opentrafficsim.core.network.Link;
52  import org.opentrafficsim.core.network.NetworkException;
53  import org.opentrafficsim.core.network.Node;
54  import org.opentrafficsim.draw.colorer.FixedColorer;
55  import org.opentrafficsim.road.definitions.DefaultsRoadNl;
56  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
57  import org.opentrafficsim.road.gtu.lane.LaneBookkeeping;
58  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveSocioSpeed;
59  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs;
60  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory;
61  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory.Setting;
62  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
63  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
64  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
65  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlanner;
66  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlannerFactory;
67  import org.opentrafficsim.road.network.LaneKeepingPolicy;
68  import org.opentrafficsim.road.network.RoadNetwork;
69  import org.opentrafficsim.road.network.factory.LaneFactory;
70  import org.opentrafficsim.road.network.lane.CrossSectionLink;
71  import org.opentrafficsim.road.network.lane.Lane;
72  import org.opentrafficsim.road.network.lane.LanePosition;
73  import org.opentrafficsim.swing.gui.OtsAnimationPanel;
74  import org.opentrafficsim.swing.gui.OtsAnimationPanel.DemoPanelPosition;
75  import org.opentrafficsim.swing.script.AbstractSimulationScript;
76  
77  import nl.tudelft.simulation.dsol.SimRuntimeException;
78  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
79  import nl.tudelft.simulation.jstats.streams.StreamInterface;
80  import picocli.CommandLine.Option;
81  
82  /**
83   * Demo of lane change strategies.
84   * <p>
85   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
86   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
87   * </p>
88   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
89   */
90  public class StrategiesDemo extends AbstractSimulationScript
91  {
92      /** Factory. */
93      private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalRoutePlanner> factory;
94  
95      /** GTU id number. */
96      private int gtuIdNum = 0;
97  
98      /** Number of GTUs. */
99      private int gtuNum = 60;
100 
101     /** Random stream. */
102     private final StreamInterface stream = new MersenneTwister(1L);
103 
104     /** Queue of cumulative odometers. */
105     private final List<Double> queue = new ArrayList<>();
106 
107     /** Lane change listener. */
108     private KmplcListener kmplcListener;
109 
110     /** Truck fraction. */
111     private double truckFraction = 0.1;
112 
113     /** Next GTU type. */
114     private GtuType nextGtuType;
115 
116     /** Truck length. */
117     private Length truckLength;
118 
119     /** Truck mid. */
120     private Length truckMid;
121 
122     /** Car length. */
123     @Option(names = "--length", description = "Length", defaultValue = "2m")
124     private Length carLength;
125 
126     /** Car mid. */
127     private Length carMid;
128 
129     /**
130      * Constructor.
131      */
132     protected StrategiesDemo()
133     {
134         super("Strategies demo", "Demo of driving strategies in LMRS.");
135         setGtuColorers(List.of(new FixedColorer<>(Color.BLUE, "Blue"), new SpeedGtuColorer(), new AccelerationGtuColorer(),
136                 new SocialPressureGtuColorer(), new DesiredHeadwayGtuColorer(Duration.ofSI(0.5), Duration.ofSI(1.6)),
137                 new IncentiveGtuColorer(IncentiveSocioSpeed.class)));
138         try
139         {
140             CliUtil.changeOptionDefault(this, "simulationTime", "3600000s");
141         }
142         catch (NoSuchFieldException | IllegalStateException | IllegalArgumentException | CliException exception)
143         {
144             throw new OtsRuntimeException(exception);
145         }
146     }
147 
148     /**
149      * Main method.
150      * @param args arguments
151      */
152     public static void main(final String[] args)
153     {
154         StrategiesDemo demo = new StrategiesDemo();
155         CliUtil.execute(demo, args);
156         try
157         {
158             demo.start();
159         }
160         catch (Exception ex)
161         {
162             ex.printStackTrace();
163         }
164     }
165 
166     @Override
167     protected void setupDemo(final OtsAnimationPanel animation, final RoadNetwork network)
168     {
169         // demo panel
170         animation.createDemoPanel(DemoPanelPosition.RIGHT);
171         animation.getDemoPanel().setBorder(new EmptyBorder(10, 10, 10, 10));
172         animation.getDemoPanel().setLayout(new BoxLayout(animation.getDemoPanel(), BoxLayout.Y_AXIS));
173         animation.getDemoPanel().setPreferredSize(new Dimension(300, 300));
174 
175         // text
176         JLabel textLabel = new JLabel("<html><p align=\"justify\">"
177                 + "Adjust the sliders below to change the ego-speed sensitivity and socio-speed sensitivity of the drivers, "
178                 + "and observe how traffic is affected." + "</html>"); // Detailed instructions are in the attached read-me.
179         textLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
180         animation.getDemoPanel().add(textLabel);
181 
182         // spacer
183         animation.getDemoPanel().add(Box.createVerticalStrut(20));
184 
185         // number of vehicles
186         JLabel gtuLabel = new JLabel("<html>Number of vehicles</html>");
187         gtuLabel.setHorizontalAlignment(SwingConstants.CENTER);
188         gtuLabel.setPreferredSize(new Dimension(200, 0));
189         gtuLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
190         animation.getDemoPanel().add(gtuLabel);
191         JSlider gtuSlider = new JSlider(0, 120, this.gtuNum);
192         gtuSlider.setMinorTickSpacing(10);
193         gtuSlider.setMajorTickSpacing(30);
194         gtuSlider.setPaintTicks(true);
195         gtuSlider.setPaintLabels(true);
196         gtuSlider.setToolTipText("<html>Number of vehicles</html>");
197         gtuSlider.addChangeListener(new ChangeListener()
198         {
199             @SuppressWarnings("synthetic-access")
200             @Override
201             public void stateChanged(final ChangeEvent e)
202             {
203                 StrategiesDemo.this.gtuNum = ((JSlider) e.getSource()).getValue();
204                 if (!StrategiesDemo.this.getSimulator().isStartingOrRunning())
205                 {
206                     // StrategiesDemo.this.checkVehicleNumber();
207                     animation.getDemoPanel().getParent().repaint();
208                 }
209             }
210         });
211         animation.getDemoPanel().add(gtuSlider);
212 
213         // spacer
214         animation.getDemoPanel().add(Box.createVerticalStrut(20));
215 
216         // ego
217         JLabel egoLabel = new JLabel("<html>Ego-speed sensitivity<sup>-1</sup> [km/h]</html>");
218         egoLabel.setHorizontalAlignment(SwingConstants.CENTER);
219         egoLabel.setPreferredSize(new Dimension(200, 0));
220         egoLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
221         animation.getDemoPanel().add(egoLabel);
222         int egoSteps = 70;
223         JSlider egoSlider = new JSlider(0, egoSteps, egoSteps / 2);
224         egoSlider.setMinorTickSpacing(2);
225         egoSlider.setMajorTickSpacing(egoSteps / 5);
226         egoSlider.setPaintTicks(true);
227         egoSlider.setPaintLabels(true);
228         Hashtable<Integer, JLabel> egoTable = new Hashtable<>();
229         for (int i = 0; i <= egoSteps; i += egoSteps / 5)
230         {
231             egoTable.put(i, new JLabel(String.format("%d", i)));
232         }
233         egoSlider.setLabelTable(egoTable);
234         egoSlider.setToolTipText("<html>Ego-speed sensitivity as 1/<i>v<sub>gain</sub></i></html>");
235         egoSlider.addChangeListener(new ChangeListener()
236         {
237             @Override
238             public void stateChanged(final ChangeEvent e)
239             {
240                 double v = Math.max(((JSlider) e.getSource()).getValue(), 0.01);
241                 Speed vGain = new Speed(v, SpeedUnit.KM_PER_HOUR);
242                 for (Gtu gtu : getNetwork().getGTUs())
243                 {
244                     if (gtu.getType().isOfType(DefaultsNl.CAR))
245                     {
246                         Try.execute(() -> gtu.getParameters().setClaimedParameter(LmrsParameters.VGAIN, vGain, egoSlider),
247                                 "Exception while setting vGain");
248                     }
249                 }
250             }
251         });
252         animation.getDemoPanel().add(egoSlider);
253 
254         // spacer
255         animation.getDemoPanel().add(Box.createVerticalStrut(20));
256 
257         // socio
258         JLabel socioLabel = new JLabel("Socio-speed sensitivity [-]");
259         socioLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
260         animation.getDemoPanel().add(socioLabel);
261         int socioSteps = 20;
262         JSlider socioSlider = new JSlider(0, socioSteps, socioSteps / 2);
263         socioSlider.setMinorTickSpacing(1);
264         socioSlider.setMajorTickSpacing(socioSteps / 5);
265         socioSlider.setPaintTicks(true);
266         socioSlider.setPaintLabels(true);
267         Hashtable<Integer, JLabel> socioTable = new Hashtable<>();
268         for (int i = 0; i <= socioSteps; i += socioSteps / 5)
269         {
270             double val = (double) i / socioSteps;
271             socioTable.put(i, new JLabel(String.format("%.2f", val)));
272         }
273         socioSlider.setLabelTable(socioTable);
274         socioSlider.setToolTipText("Socio-speed sensitivity between 0 and 1");
275         socioSlider.addChangeListener(new ChangeListener()
276         {
277             @Override
278             public void stateChanged(final ChangeEvent e)
279             {
280                 JSlider slider = (JSlider) e.getSource();
281                 double sigma = ((double) slider.getValue()) / slider.getMaximum();
282                 for (Gtu gtu : getNetwork().getGTUs())
283                 {
284                     if (gtu.getType().isOfType(DefaultsNl.CAR))
285                     {
286                         Try.execute(() -> gtu.getParameters().setClaimedParameter(LmrsParameters.SOCIO, sigma, socioSlider),
287                                 "Exception while setting vGain");
288                     }
289                 }
290             }
291         });
292         animation.getDemoPanel().add(socioSlider);
293 
294         // spacer
295         animation.getDemoPanel().add(Box.createVerticalStrut(20));
296 
297         // km/lc
298         JLabel kmplcLabel = new JLabel("Km between lane changes (last 0): -");
299         this.kmplcListener = new KmplcListener(kmplcLabel, network);
300         for (Gtu gtu : network.getGTUs())
301         {
302             Try.execute(() -> gtu.addListener(this.kmplcListener, LaneBasedGtu.LANE_CHANGE_EVENT),
303                     "Exception while adding lane change listener");
304         }
305         kmplcLabel.setHorizontalAlignment(SwingConstants.LEFT);
306         kmplcLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
307         animation.getDemoPanel().add(kmplcLabel);
308     }
309 
310     /**
311      * Create or destroy vehicles.
312      */
313     @SuppressWarnings("unused")
314     private void checkVehicleNumber()
315     {
316         while (getNetwork().getGTUs().size() > this.gtuNum)
317         {
318             int n = getNetwork().getGTUs().size();
319             int i = n <= 1 ? 0 : StrategiesDemo.this.stream.nextInt(0, n - 1);
320             Iterator<Gtu> it = getNetwork().getGTUs().iterator();
321             Gtu gtu = it.next();
322             for (int j = 0; j < i; j++)
323             {
324                 gtu = it.next();
325             }
326             gtu.destroy();
327             this.queue.clear();
328         }
329         // if: add one vehicle per cycle, so we limit the disturbance
330         if (getNetwork().getGTUs().size() < this.gtuNum)
331         {
332             Lane lane = null;
333             Length pos = null;
334             Speed initialSpeed = null;
335             Length gap = Length.ZERO;
336             for (Link link : getNetwork().getLinkMap().values())
337             {
338                 for (Lane l : ((CrossSectionLink) link).getLanes())
339                 {
340                     if (l.numberOfGtus() == 0)
341                     {
342                         lane = l;
343                         pos = lane.getLength().times(0.5);
344                         gap = Length.POSITIVE_INFINITY;
345                         initialSpeed = Speed.ZERO;
346                     }
347                     for (int i = 0; i < l.numberOfGtus(); i++)
348                     {
349                         LaneBasedGtu gtu1 = l.getGtu(i);
350                         Length up = Try.assign(() -> gtu1.getPosition(l, gtu1.getFront()), "");
351                         LaneBasedGtu gtu2;
352                         Length down;
353                         if (i < l.numberOfGtus() - 1)
354                         {
355                             gtu2 = l.getGtu(i + 1);
356                             down = Try.assign(() -> gtu2.getPosition(l, gtu2.getRear()), "");
357                         }
358                         else
359                         {
360                             Lane nextLane = l.nextLanes(DefaultsNl.VEHICLE).iterator().next();
361                             if (nextLane.numberOfGtus() == 0)
362                             {
363                                 continue;
364                             }
365                             gtu2 = nextLane.getGtu(0);
366                             down = l.getLength().plus(Try.assign(() -> gtu2.getPosition(nextLane, gtu2.getRear()), ""));
367                         }
368                         Length tentativeGap = down.minus(up)
369                                 .minus(this.nextGtuType.isOfType(DefaultsNl.TRUCK) ? this.truckLength : this.carLength);
370                         if (tentativeGap.gt(gap))
371                         {
372                             // check reasonable gap (0.3s)
373                             Speed maxSpeed = Speed.max(gtu1.getSpeed(), gtu2.getSpeed());
374                             if (maxSpeed.eq0() || tentativeGap.divide(maxSpeed).si * .5 > .3)
375                             {
376                                 gap = tentativeGap;
377                                 initialSpeed = Speed.interpolate(gtu1.getSpeed(), gtu2.getSpeed(), 0.5);
378                                 pos = up.plus(tentativeGap.times(0.5))
379                                         .minus(this.nextGtuType.isOfType(DefaultsNl.TRUCK) ? this.truckMid : this.carMid);
380                                 if (pos.gt(l.getLength()))
381                                 {
382                                     pos = pos.minus(l.getLength());
383                                     lane = l.nextLanes(DefaultsNl.VEHICLE).iterator().next();
384                                 }
385                                 else
386                                 {
387                                     lane = l;
388                                 }
389                             }
390                         }
391                     }
392                 }
393             }
394             if (lane != null)
395             {
396                 try
397                 {
398                     createGtu(lane, pos, this.nextGtuType, initialSpeed, getNetwork());
399                 }
400                 catch (NamingException | GtuException | NetworkException | SimRuntimeException exception)
401                 {
402                     throw new OtsRuntimeException(exception);
403                 }
404                 this.nextGtuType = this.stream.nextDouble() < this.truckFraction ? DefaultsNl.TRUCK : DefaultsNl.CAR;
405                 this.queue.clear();
406             }
407         }
408         getSimulator().scheduleEventRel(Duration.ofSI(0.5), () -> checkVehicleNumber());
409     }
410 
411     /** Lane change listener. */
412     private class KmplcListener implements EventListener
413     {
414         /** Label to show statistic. */
415         private final JLabel label;
416 
417         /** Network. */
418         private final RoadNetwork network;
419 
420         /**
421          * Constructor.
422          * @param label label
423          * @param network network
424          */
425         @SuppressWarnings("synthetic-access")
426         KmplcListener(final JLabel label, final RoadNetwork network)
427         {
428             this.label = label;
429             this.network = network;
430             StrategiesDemo.this.queue.add(0.0);
431         }
432 
433         @SuppressWarnings("synthetic-access")
434         @Override
435         public void notify(final Event event)
436         {
437             if (event.getType().equals(LaneBasedGtu.LANE_CHANGE_EVENT))
438             {
439                 double cumul = 0.0;
440                 for (Gtu gtu : this.network.getGTUs())
441                 {
442                     cumul += gtu.getOdometer().si;
443                 }
444                 cumul /= 1000;
445                 StrategiesDemo.this.queue.add(cumul);
446                 while (StrategiesDemo.this.queue.size() > 51)
447                 {
448                     StrategiesDemo.this.queue.remove(0);
449                 }
450                 double val =
451                         (StrategiesDemo.this.queue.get(StrategiesDemo.this.queue.size() - 1) - StrategiesDemo.this.queue.get(0))
452                                 / (StrategiesDemo.this.queue.size() - 1.0);
453                 this.label.setText(
454                         String.format("Lane change rate (last %d): %.1f km/lc", StrategiesDemo.this.queue.size() - 1, val));
455             }
456         }
457     }
458 
459     @Override
460     protected RoadNetwork setupSimulation(final OtsSimulatorInterface sim) throws Exception
461     {
462         RoadNetwork network = new RoadNetwork("Strategies demo", getSimulator());
463 
464         GtuCharacteristics truck = Defaults.NL.apply(DefaultsNl.TRUCK, this.stream)
465                 .orElseThrow(() -> new GtuException("No characteristics for NL.TRUCK could be generated.")).get();
466         GtuCharacteristics car = Defaults.NL.apply(DefaultsNl.CAR, this.stream)
467                 .orElseThrow(() -> new GtuException("No characteristics for NL.CAR could be generated.")).get();
468         this.truckLength = truck.getLength();
469         this.truckMid = truck.getLength().times(0.5).minus(truck.getFront());
470         this.carLength = car.getLength();
471         this.carMid = car.getLength().times(0.5).minus(car.getFront());
472 
473         double radius = 150;
474         Speed speedLimit = new Speed(120.0, SpeedUnit.KM_PER_HOUR);
475         Node nodeA = new Node(network, "A", new Point2d(-radius, 0), new Direction(270, DirectionUnit.EAST_DEGREE));
476         Node nodeB = new Node(network, "B", new Point2d(radius, 0), new Direction(90, DirectionUnit.EAST_DEGREE));
477 
478         OffsetCurve2d half1 = new Arc2d(new DirectedPoint2d(radius, 0.0, Math.PI / 2), radius, true, Math.PI);
479         List<Lane> lanes1 = new LaneFactory(network, nodeB, nodeA, DefaultsNl.FREEWAY, sim, LaneKeepingPolicy.KEEPRIGHT,
480                 DefaultsNl.VEHICLE, half1).leftToRight(0.0, Length.ofSI(3.5), DefaultsRoadNl.FREEWAY, speedLimit)
481                         .addLanes(DefaultsRoadNl.DASHED).getLanes();
482         OffsetCurve2d half2 = new Arc2d(new DirectedPoint2d(-radius, 0.0, -Math.PI / 2), radius, true, Math.PI);
483         List<Lane> lanes2 = new LaneFactory(network, nodeA, nodeB, DefaultsNl.FREEWAY, sim, LaneKeepingPolicy.KEEPRIGHT,
484                 DefaultsNl.VEHICLE, half2).leftToRight(0.0, Length.ofSI(3.5), DefaultsRoadNl.FREEWAY, speedLimit)
485                         .addLanes(DefaultsRoadNl.DASHED).getLanes();
486 
487         LmrsFactory<Lmrs> lmrsFactory = new LmrsFactory<>(Lmrs::new).set(Setting.SOCIO_TAILGATING, true)
488                 .set(Setting.SOCIO_LANE_CHANGE, true).set(Setting.SOCIO_SPEED, true)
489                 .set(Setting.INCENTIVE_STAY_RIGHT, true, DefaultsNl.TRUCK).setStream(this.stream);
490         lmrsFactory.addParameter(DefaultsNl.CAR, LmrsParameters.SOCIO, 0.5);
491         lmrsFactory.addParameter(DefaultsNl.TRUCK, LmrsParameters.SOCIO, 1.0);
492         lmrsFactory.addParameter(DefaultsNl.CAR, LmrsParameters.VGAIN, new Speed(35.0, SpeedUnit.KM_PER_HOUR));
493         lmrsFactory.addParameter(DefaultsNl.TRUCK, LmrsParameters.VGAIN, new Speed(50.0, SpeedUnit.KM_PER_HOUR));
494         lmrsFactory.addParameter(ParameterTypes.TMAX, Duration.ofSI(1.6));
495         lmrsFactory.addParameter(DefaultsNl.TRUCK, ParameterTypes.A, Acceleration.ofSI(0.4));
496         lmrsFactory.addParameter(DefaultsNl.TRUCK, ParameterTypes.FSPEED, 1.0);
497         this.factory = new LaneBasedStrategicalRoutePlannerFactory(lmrsFactory, lmrsFactory);
498 
499         for (int i = 0; i < lanes1.size(); i++)
500         {
501             Length pos = Length.ofSI(10.0);
502             Length gap = lanes1.get(i).getLength().plus(lanes2.get(i).getLength()).divide(this.gtuNum / 2);
503             for (int j = 0; j < 2; j++)
504             {
505                 Lane lane = j == 0 ? lanes1.get(i) : lanes2.get(i);
506                 while (true)
507                 {
508                     GtuType gtuType;
509                     if (i == 0)
510                     {
511                         gtuType = DefaultsNl.CAR;
512                     }
513                     else
514                     {
515                         gtuType = this.stream.nextDouble() < 2 * this.truckFraction ? DefaultsNl.TRUCK : DefaultsNl.CAR;
516                     }
517 
518                     Speed initialSpeed = Speed.ZERO;
519 
520                     createGtu(lane, pos, gtuType, initialSpeed, network);
521 
522                     pos = pos.plus(gap);
523                     if (pos.si > lane.getLength().si)
524                     {
525                         pos = pos.minus(lane.getLength());
526                         break;
527                     }
528                 }
529             }
530         }
531 
532         this.nextGtuType = this.stream.nextDouble() < this.truckFraction ? DefaultsNl.TRUCK : DefaultsNl.CAR;
533         sim.scheduleEventNow(() -> checkVehicleNumber());
534 
535         return network;
536     }
537 
538     /**
539      * Creates a GTU.
540      * @param lane lane
541      * @param pos position
542      * @param gtuType GTU type
543      * @param initialSpeed initial speed
544      * @param net network
545      * @throws NamingException on exception
546      * @throws GtuException on exception
547      * @throws NetworkException on exception
548      * @throws SimRuntimeException on exception
549      */
550     public void createGtu(final Lane lane, final Length pos, final GtuType gtuType, final Speed initialSpeed,
551             final RoadNetwork net) throws NamingException, GtuException, NetworkException, SimRuntimeException
552     {
553         GtuCharacteristics gtuCharacteristics = Defaults.NL.apply(gtuType, this.stream)
554                 .orElseThrow(() -> new GtuException("No characteristics for GTU type " + gtuType + " could be generated."))
555                 .get();
556 
557         LaneBasedGtu gtu = new LaneBasedGtu("" + (++this.gtuIdNum), gtuType, gtuCharacteristics.getLength(),
558                 gtuCharacteristics.getWidth(), gtuCharacteristics.getMaximumSpeed(), gtuCharacteristics.getFront(), net);
559         gtu.setMaximumAcceleration(gtuCharacteristics.getMaximumAcceleration());
560         gtu.setMaximumDeceleration(gtuCharacteristics.getMaximumDeceleration());
561         gtu.setNoLaneChangeDistance(Length.ofSI(50));
562         gtu.setBookkeeping(LaneBookkeeping.INSTANT);
563 
564         // strategical planner
565         LaneBasedStrategicalPlanner strategicalPlanner = this.factory.create(gtu, null, null, null);
566         // this.factories.get(gtuType).create(gtu, null, null, null);
567 
568         // init
569         gtu.init(strategicalPlanner, new LanePosition(lane, pos).getLocation(), initialSpeed);
570 
571         if (this.kmplcListener != null)
572         {
573             Try.execute(() -> gtu.addListener(this.kmplcListener, LaneBasedGtu.LANE_CHANGE_EVENT),
574                     "Exception while adding lane change listener");
575         }
576     }
577 
578 }