View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.ArrayList;
6   import java.util.HashSet;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.value.vdouble.scalar.Length;
13  import org.djunits.value.vdouble.scalar.Time;
14  import org.opentrafficsim.core.geometry.OTSGeometryException;
15  import org.opentrafficsim.core.geometry.OTSLine3D;
16  import org.opentrafficsim.core.gtu.GTUDirectionality;
17  import org.opentrafficsim.core.gtu.GTUException;
18  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
19  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
20  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
21  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
22  import org.opentrafficsim.core.network.Link;
23  import org.opentrafficsim.core.network.LinkDirection;
24  import org.opentrafficsim.core.network.NetworkException;
25  import org.opentrafficsim.core.network.Node;
26  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
27  import org.opentrafficsim.road.gtu.lane.perception.CategorialLanePerception;
28  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
29  import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
30  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneBasedOperationalPlan;
31  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
32  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder.LaneChange;
33  import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
34  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
35  import org.opentrafficsim.road.network.lane.CrossSectionElement;
36  import org.opentrafficsim.road.network.lane.CrossSectionLink;
37  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
38  import org.opentrafficsim.road.network.lane.Lane;
39  import org.opentrafficsim.road.network.lane.LaneDirection;
40  
41  /**
42   * A lane-based tactical planner generates an operational plan for the lane-based GTU. It can ask the strategic planner for
43   * assistance on the route to take when the network splits. This abstract class contains a number of helper methods that make it
44   * easy to implement a tactical planner.
45   * <p>
46   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
47   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
48   * </p>
49   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
50   * initial version Nov 25, 2015 <br>
51   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
52   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
53   */
54  public abstract class AbstractLaneBasedTacticalPlanner implements LaneBasedTacticalPlanner, Serializable
55  {
56      /** */
57      private static final long serialVersionUID = 20151125L;
58  
59      /** The car-following model. */
60      private CarFollowingModel carFollowingModel;
61  
62      /** The perception. */
63      private final LanePerception lanePerception;
64  
65      /** GTU. */
66      private final LaneBasedGTU gtu;
67  
68      /**
69       * Instantiates a tactical planner.
70       * @param carFollowingModel car-following model
71       * @param gtu GTU
72       */
73      public AbstractLaneBasedTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu)
74      {
75          setCarFollowingModel(carFollowingModel);
76          this.gtu = gtu;
77          CategorialLanePerception perception = new CategorialLanePerception(gtu);
78          perception.addPerceptionCategory(new DefaultSimplePerception(perception));
79          // TODO Specific tactical planners should know which perception categories to use
80          // SSMDEMO below categories can be disabled
81          // perception.addPerceptionCategory(new InfrastructurePerception(perception));
82          // perception.addPerceptionCategory(new NeighborsPerception(perception));
83          // perception.addPerceptionCategory(new IntersectionPerception(perception));
84          // perception.addPerceptionCategory(new EgoPerception(perception));
85          this.lanePerception = perception;
86      }
87  
88      /** {@inheritDoc} */
89      @Override
90      public final LaneBasedGTU getGtu()
91      {
92          return this.gtu;
93      }
94  
95      /**
96       * Build a list of lanes forward, with a maximum headway relative to the reference point of the GTU.
97       * @param gtu LaneBasedGTU; the GTU for which to calculate the lane list
98       * @param maxHeadway Length; the maximum length for which lanes should be returned
99       * @return LanePathInfo; an instance that provides the following information for an operational plan: the lanes to follow,
100      *         and the path to follow when staying on the same lane.
101      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
102      * @throws NetworkException when the strategic planner is not able to return a next node in the route
103      */
104     public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway) throws GTUException,
105         NetworkException
106     {
107         DirectedLanePosition dlp = gtu.getReferencePosition();
108         return buildLanePathInfo(gtu, maxHeadway, dlp.getLane(), dlp.getPosition(), dlp.getGtuDirection());
109     }
110 
111     /**
112      * Build a list of lanes forward, with a maximum headway relative to the reference point of the GTU.
113      * @param gtu LaneBasedGTU; the GTU for which to calculate the lane list
114      * @param maxHeadway Length; the maximum length for which lanes should be returned
115      * @param startLane Lane; the lane in which the path starts
116      * @param position Length; the position on the start lane
117      * @param startDirectionality GTUDirectionality; the driving direction on the start lane
118      * @return LanePathInfo; an instance that provides the following information for an operational plan: the lanes to follow,
119      *         and the path to follow when staying on the same lane.
120      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
121      * @throws NetworkException when the strategic planner is not able to return a next node in the route
122      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
123      * @throws NetworkException when the strategic planner is not able to return a next node in the route
124      */
125     public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
126         final Length position, final GTUDirectionality startDirectionality) throws GTUException,
127         NetworkException
128     {
129         List<LaneDirection> laneListForward = new ArrayList<>();
130         Lane lane = startLane;
131         GTUDirectionality lastGtuDir = startDirectionality;
132         Length startPosition = position;
133         Lane lastLane = lane;
134         laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
135         Length distanceToEndOfLane;
136         OTSLine3D path;
137         try
138         {
139             if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
140             {
141                 distanceToEndOfLane = lane.getLength().minus(position);
142                 path = lane.getCenterLine().extract(position, lane.getLength());
143             }
144             else
145             {
146                 distanceToEndOfLane = position;
147                 path = lane.getCenterLine().extract(Length.ZERO, position).reverse();
148             }
149         }
150         catch (OTSGeometryException exception)
151         {
152             System.err.println(gtu + ": " + exception.getMessage());
153             System.err.println(lane + ", len=" + lane.getLength());
154             System.err.println(position);
155             throw new GTUException(exception);
156         }
157 
158         while (distanceToEndOfLane.lt(maxHeadway))
159         {
160             Map<Lane, GTUDirectionality> lanes =
161                 lastGtuDir.equals(GTUDirectionality.DIR_PLUS) ? lane.nextLanes(gtu.getGTUType()) : lane.prevLanes(gtu
162                     .getGTUType());
163             if (lanes.size() == 0)
164             {
165                 // Dead end. Return with the list as is.
166                 return new LanePathInfo(path, laneListForward, startPosition);
167             }
168             else if (lanes.size() == 1)
169             {
170                 // Ask the strategical planner what the next link should be (if known), because the strategical planner knows
171                 // best!
172                 LinkDirection ld = null;
173                 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), lastGtuDir, gtu.getGTUType());
174                 lane = lanes.keySet().iterator().next();
175                 if (ld != null && !lane.getParentLink().equals(ld.getLink()))
176                 {
177                     // Lane not on route anymore. return with the list as is.
178                     return new LanePathInfo(path, laneListForward, startPosition);
179                 }
180             }
181             else
182             {
183                 // Multiple next lanes; ask the strategical planner where to go.
184                 // Note: this is not necessarily a split; it could e.g. be a bike path on a road
185                 LinkDirection ld;
186                 try
187                 {
188                     ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), /* gtu.getLanes().get(lane), */
189                     lastGtuDir, gtu.getGTUType());
190                 }
191                 catch (NetworkException ne)
192                 {
193                     // no route found.
194                     // return the data structure up to this point...
195                     return new LanePathInfo(path, laneListForward, startPosition);
196                 }
197                 Link nextLink = ld.getLink();
198                 Lane newLane = null;
199                 for (Lane nextLane : lanes.keySet())
200                 {
201                     if (nextLane.getParentLink().equals(nextLink))
202                     {
203                         newLane = nextLane;
204                         break;
205                     }
206                 }
207                 if (newLane == null)
208                 {
209                     // we cannot reach the next node on this lane -- we have to make a lane change!
210                     // return the data structure up to this point...
211                     return new LanePathInfo(path, laneListForward, startPosition);
212                 }
213                 // otherwise: continue!
214                 lane = newLane;
215             }
216 
217             // determine direction for the path
218             try
219             {
220                 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
221                 {
222                     if (lastLane.getParentLink().getEndNode().equals(lane.getParentLink().getStartNode()))
223                     {
224                         // -----> O ----->, GTU moves ---->
225                         path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
226                         lastGtuDir = GTUDirectionality.DIR_PLUS;
227                     }
228                     else
229                     {
230                         // -----> O <-----, GTU moves ---->
231                         path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
232                         lastGtuDir = GTUDirectionality.DIR_MINUS;
233                     }
234                 }
235                 else
236                 {
237                     if (lastLane.getParentLink().getStartNode().equals(lane.getParentLink().getStartNode()))
238                     {
239                         // <----- O ----->, GTU moves ---->
240                         path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
241                         lastGtuDir = GTUDirectionality.DIR_PLUS;
242                     }
243                     else
244                     {
245                         // <----- O <-----, GTU moves ---->
246                         path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
247                         lastGtuDir = GTUDirectionality.DIR_MINUS;
248                     }
249                 }
250                 lastLane = lane;
251             }
252             catch (OTSGeometryException exception)
253             {
254                 throw new GTUException(exception);
255             }
256 
257             laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
258             distanceToEndOfLane = distanceToEndOfLane.plus(lastLane.getLength());
259 
260         }
261         return new LanePathInfo(path, laneListForward, startPosition);
262     }
263 
264     /**
265      * Calculate the next location where the network splits, with a maximum headway relative to the reference point of the GTU.
266      * Note: a lane drop is also considered a split (!).
267      * @param gtu LaneBasedGTU; the GTU for which to calculate the lane list
268      * @param maxHeadway Length; the maximum length for which lanes should be returned
269      * @return NextSplitInfo; an instance that provides the following information for an operational plan: whether the network
270      *         splits, the node where it splits, and the current lanes that lead to the right node after the split node.
271      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
272      * @throws NetworkException when the strategic planner is not able to return a next node in the route
273      */
274     public static NextSplitInfo determineNextSplit(final LaneBasedGTU gtu, final Length maxHeadway) throws GTUException,
275         NetworkException
276     {
277         Node nextSplitNode = null;
278         Set<Lane> correctCurrentLanes = new HashSet<>();
279         DirectedLanePosition dlp = gtu.getReferencePosition();
280         Lane referenceLane = dlp.getLane();
281         Link lastLink = referenceLane.getParentLink();
282         GTUDirectionality lastGtuDir = dlp.getGtuDirection();
283         GTUDirectionality referenceLaneDirectionality = lastGtuDir;
284         Length lengthForward;
285         Length position = dlp.getPosition();
286         Node lastNode;
287         if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
288         {
289             lengthForward = referenceLane.getLength().minus(position);
290             lastNode = referenceLane.getParentLink().getEndNode();
291         }
292         else
293         {
294             lengthForward = gtu.position(referenceLane, gtu.getReference());
295             lastNode = referenceLane.getParentLink().getStartNode();
296         }
297 
298         // see if we have a split within maxHeadway distance
299         while (lengthForward.lt(maxHeadway) && nextSplitNode == null)
300         {
301             // calculate the number of "outgoing" links
302             Set<Link> links = lastNode.getLinks().toSet(); // safe copy
303             Iterator<Link> linkIterator = links.iterator();
304             while (linkIterator.hasNext())
305             {
306                 Link link = linkIterator.next();
307                 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType()))
308                 {
309                     linkIterator.remove();
310                 }
311             }
312 
313             // calculate the number of incoming and outgoing lanes on the link
314             boolean laneChange = false;
315             if (links.size() == 1)
316             {
317                 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
318                 {
319                     if (cse instanceof Lane && lastGtuDir.isPlus())
320                     {
321                         Lane lane = (Lane) cse;
322                         if (lane.nextLanes(gtu.getGTUType()).size() == 0)
323                         {
324                             laneChange = true;
325                         }
326                     }
327                     if (cse instanceof Lane && lastGtuDir.isMinus())
328                     {
329                         Lane lane = (Lane) cse;
330                         if (lane.prevLanes(gtu.getGTUType()).size() == 0)
331                         {
332                             laneChange = true;
333                         }
334                     }
335                 }
336             }
337 
338             // see if we have a lane drop
339             if (laneChange)
340             {
341                 nextSplitNode = lastNode;
342                 // which lane(s) we are registered on and adjacent lanes link to a lane
343                 // that does not drop?
344                 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
345                 {
346                     if (cse instanceof Lane)
347                     {
348                         Lane l = (Lane) cse;
349                         if (noLaneDrop(gtu, maxHeadway, l, position, referenceLaneDirectionality))
350                         {
351                             correctCurrentLanes.add(l);
352                         }
353                     }
354                 }
355                 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
356             }
357 
358             // see if we have a split
359             if (links.size() > 1)
360             {
361                 nextSplitNode = lastNode;
362                 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType());
363                 // which lane(s) we are registered on and adjacent lanes link to a lane
364                 // that is on the route at the next split?
365                 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
366                 {
367                     if (cse instanceof Lane)
368                     {
369                         Lane l = (Lane) cse;
370                         if (connectsToPath(gtu, maxHeadway, l, position, referenceLaneDirectionality,
371                             ld.getLink()))
372                         {
373                             correctCurrentLanes.add(l);
374                         }
375                     }
376                 }
377                 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
378             }
379 
380             if (links.size() == 0)
381             {
382                 return new NextSplitInfo(null, correctCurrentLanes);
383             }
384 
385             // just one link
386             Link link = links.iterator().next();
387 
388             // determine direction for the path
389             if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
390             {
391                 if (lastLink.getEndNode().equals(link.getStartNode()))
392                 {
393                     // -----> O ----->, GTU moves ---->
394                     lastGtuDir = GTUDirectionality.DIR_PLUS;
395                     lastNode = lastLink.getEndNode();
396                 }
397                 else
398                 {
399                     // -----> O <-----, GTU moves ---->
400                     lastGtuDir = GTUDirectionality.DIR_MINUS;
401                     lastNode = lastLink.getEndNode();
402                 }
403             }
404             else
405             {
406                 if (lastLink.getStartNode().equals(link.getStartNode()))
407                 {
408                     // <----- O ----->, GTU moves ---->
409                     lastNode = lastLink.getStartNode();
410                     lastGtuDir = GTUDirectionality.DIR_PLUS;
411                 }
412                 else
413                 {
414                     // <----- O <-----, GTU moves ---->
415                     lastNode = lastLink.getStartNode();
416                     lastGtuDir = GTUDirectionality.DIR_MINUS;
417                 }
418             }
419             lastLink = links.iterator().next();
420             lengthForward = lengthForward.plus(lastLink.getLength());
421         }
422 
423         return new NextSplitInfo(null, correctCurrentLanes);
424     }
425 
426     /**
427      * Determine whether the lane is directly connected to our route, in other words: if we would (continue to) drive on the
428      * given lane, can we take the right branch at the nextSplitNode without switching lanes?
429      * @param gtu LaneBasedGTU; the GTU for which we have to determine the lane suitability
430      * @param maxHeadway Length; the maximum length for use in the calculation
431      * @param startLane Lane; the first lane in the list
432      * @param startLanePosition Length; the position on the start lane
433      * @param startDirectionality GTUDirectionality; the driving direction on the start lane
434      * @param linkAfterSplit Link; the link after the split to which we should connect
435      * @return boolean; true if the lane (XXXXX which lane?) is connected to our path
436      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
437      * @throws NetworkException when the strategic planner is not able to return a next node in the route
438      */
439     protected static boolean connectsToPath(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
440         final Length startLanePosition, final GTUDirectionality startDirectionality, final Link linkAfterSplit)
441         throws GTUException, NetworkException
442     {
443         List<LaneDirection> laneDirections =
444             buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality)
445                 .getLaneDirectionList();
446         for (LaneDirection laneDirection : laneDirections)
447         {
448             if (laneDirection.getLane().getParentLink().equals(linkAfterSplit))
449             {
450                 return true;
451             }
452         }
453         return false;
454     }
455 
456     /**
457      * Determine whether the lane does not drop, in other words: if we would (continue to) drive on the given lane, can we
458      * continue to drive at the nextSplitNode without switching lanes?
459      * @param gtu LaneBasedGTU; the GTU for which we have to determine the lane suitability
460      * @param maxHeadway Length; the maximum length for use in the calculation
461      * @param startLane Lane; the first lane in the list
462      * @param startLanePosition Length; the position on the start lane
463      * @param startDirectionality GTUDirectionality; the driving direction on the start lane
464      * @return boolean; true if the lane (XXXXX which lane?) is connected to our path
465      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
466      * @throws NetworkException when the strategic planner is not able to return a next node in the route
467      */
468     protected static boolean noLaneDrop(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
469         final Length startLanePosition, final GTUDirectionality startDirectionality) throws GTUException,
470         NetworkException
471     {
472         LanePathInfo lpi = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality);
473         if (lpi.getPath().getLength().lt(maxHeadway))
474         {
475             return false;
476         }
477         return true;
478     }
479 
480     /**
481      * Make a list of links on which to drive next, with a maximum headway relative to the reference point of the GTU.
482      * @param gtu LaneBasedGTU; the GTU for which to calculate the link list
483      * @param maxHeadway Length; the maximum length for which links should be returned
484      * @return List&lt;LinkDirection&gt;; a list of links on which to drive next
485      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
486      * @throws NetworkException when the strategic planner is not able to return a next node in the route
487      */
488     protected static List<LinkDirection> buildLinkListForward(final LaneBasedGTU gtu, final Length maxHeadway)
489         throws GTUException, NetworkException
490     {
491         List<LinkDirection> linkList = new ArrayList<>();
492         DirectedLanePosition dlp = gtu.getReferencePosition();
493         Lane referenceLane = dlp.getLane();
494         Link lastLink = referenceLane.getParentLink();
495         GTUDirectionality lastGtuDir = dlp.getGtuDirection();
496         linkList.add(new LinkDirection(lastLink, lastGtuDir));
497         Length lengthForward;
498         Length position = dlp.getPosition();
499         Node lastNode;
500         if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
501         {
502             lengthForward = referenceLane.getLength().minus(position);
503             lastNode = referenceLane.getParentLink().getEndNode();
504         }
505         else
506         {
507             lengthForward = gtu.position(referenceLane, gtu.getReference());
508             lastNode = referenceLane.getParentLink().getStartNode();
509         }
510 
511         // see if we have a split within maxHeadway distance
512         while (lengthForward.lt(maxHeadway))
513         {
514             // calculate the number of "outgoing" links
515             Set<Link> links = lastNode.getLinks().toSet(); // is a safe copy
516             Iterator<Link> linkIterator = links.iterator();
517             while (linkIterator.hasNext())
518             {
519                 Link link = linkIterator.next();
520                 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType()))
521                 {
522                     linkIterator.remove();
523                 }
524             }
525 
526             if (links.size() == 0)
527             {
528                 return linkList; // the path stops here...
529             }
530 
531             Link link;
532             if (links.size() > 1)
533             {
534                 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(lastLink, lastGtuDir, gtu.getGTUType());
535                 link = ld.getLink();
536             }
537             else
538             {
539                 link = links.iterator().next();
540             }
541 
542             // determine direction for the path
543             if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
544             {
545                 if (lastLink.getEndNode().equals(link.getStartNode()))
546                 {
547                     // -----> O ----->, GTU moves ---->
548                     lastGtuDir = GTUDirectionality.DIR_PLUS;
549                     lastNode = lastLink.getEndNode();
550                 }
551                 else
552                 {
553                     // -----> O <-----, GTU moves ---->
554                     lastGtuDir = GTUDirectionality.DIR_MINUS;
555                     lastNode = lastLink.getEndNode();
556                 }
557             }
558             else
559             {
560                 if (lastLink.getStartNode().equals(link.getStartNode()))
561                 {
562                     // <----- O ----->, GTU moves ---->
563                     lastNode = lastLink.getStartNode();
564                     lastGtuDir = GTUDirectionality.DIR_PLUS;
565                 }
566                 else
567                 {
568                     // <----- O <-----, GTU moves ---->
569                     lastNode = lastLink.getStartNode();
570                     lastGtuDir = GTUDirectionality.DIR_MINUS;
571                 }
572             }
573             lastLink = link;
574             linkList.add(new LinkDirection(lastLink, lastGtuDir));
575             lengthForward = lengthForward.plus(lastLink.getLength());
576         }
577         return linkList;
578     }
579 
580     /** {@inheritDoc} */
581     @Override
582     public final CarFollowingModel getCarFollowingModel()
583     {
584         return this.carFollowingModel;
585     }
586 
587     /**
588      * Sets the car-following model.
589      * @param carFollowingModel Car-following model to set.
590      */
591     public final void setCarFollowingModel(final CarFollowingModel carFollowingModel)
592     {
593         this.carFollowingModel = carFollowingModel;
594     }
595 
596     /** {@inheritDoc} */
597     @Override
598     public final LanePerception getPerception()
599     {
600         return this.lanePerception;
601     }
602 
603     /**
604      * Build an operational plan based on a simple operational plan and status info.
605      * @param gtu gtu
606      * @param startTime start time for plan
607      * @param bc behavioral characteristics
608      * @param simplePlan simple operational plan
609      * @param laneChange lane change status
610      * @return operational plan
611      * @throws ParameterException if parameter is not defined
612      * @throws GTUException gtu exception
613      * @throws NetworkException network exception
614      * @throws OperationalPlanException operational plan exeption
615      */
616     public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
617         final BehavioralCharacteristics bc, final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
618         throws ParameterException, GTUException, NetworkException, OperationalPlanException
619     {
620         Length forwardHeadway = bc.getParameter(ParameterTypes.LOOKAHEAD);
621         List<Lane> lanes = buildLanePathInfo(gtu, forwardHeadway).getLanes();
622         if (simplePlan.getLaneChangeDirection() == null)
623         {
624             Length firstLanePosition = gtu.getReferencePosition().getPosition();
625             try
626             {
627                 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, lanes, firstLanePosition, startTime, gtu
628                     .getSpeed(), simplePlan.getAcceleration(), bc.getParameter(ParameterTypes.DT));
629             }
630             catch (OTSGeometryException exception)
631             {
632                 throw new OperationalPlanException(exception);
633             }
634         }
635 
636         try
637         {
638             return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, lanes, simplePlan
639                 .getLaneChangeDirection(), gtu.getLocation(), startTime, gtu.getSpeed(), simplePlan.getAcceleration(), bc
640                 .getParameter(ParameterTypes.DT), laneChange);
641         }
642         catch (OTSGeometryException | RemoteException exception)
643         {
644             throw new OperationalPlanException(exception);
645         }
646     }
647 }