AbstractLaneBasedTacticalPlanner.java

  1. package org.opentrafficsim.road.gtu.lane.tactical;

  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.Iterator;
  5. import java.util.LinkedHashSet;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Set;

  9. import org.djunits.value.vdouble.scalar.Length;
  10. import org.opentrafficsim.base.OTSClassUtil;
  11. import org.opentrafficsim.base.parameters.ParameterTypeClass;
  12. import org.opentrafficsim.base.parameters.ParameterTypeDuration;
  13. import org.opentrafficsim.base.parameters.ParameterTypeLength;
  14. import org.opentrafficsim.base.parameters.ParameterTypes;
  15. import org.opentrafficsim.core.geometry.OTSGeometryException;
  16. import org.opentrafficsim.core.geometry.OTSLine3D;
  17. import org.opentrafficsim.core.gtu.GTUDirectionality;
  18. import org.opentrafficsim.core.gtu.GTUException;
  19. import org.opentrafficsim.core.network.LateralDirectionality;
  20. import org.opentrafficsim.core.network.Link;
  21. import org.opentrafficsim.core.network.LinkDirection;
  22. import org.opentrafficsim.core.network.NetworkException;
  23. import org.opentrafficsim.core.network.Node;
  24. import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
  25. import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
  26. import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
  27. import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRS;
  28. import org.opentrafficsim.road.network.lane.CrossSectionElement;
  29. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  30. import org.opentrafficsim.road.network.lane.DirectedLanePosition;
  31. import org.opentrafficsim.road.network.lane.Lane;
  32. import org.opentrafficsim.road.network.lane.LaneDirection;

  33. /**
  34.  * A lane-based tactical planner generates an operational plan for the lane-based GTU. It can ask the strategic planner for
  35.  * assistance on the route to take when the network splits. This abstract class contains a number of helper methods that make it
  36.  * easy to implement a tactical planner.
  37.  * <p>
  38.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  39.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  40.  * </p>
  41.  * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  42.  * initial version Nov 25, 2015 <br>
  43.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  44.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  45.  */
  46. public abstract class AbstractLaneBasedTacticalPlanner implements LaneBasedTacticalPlanner, Serializable
  47. {

  48.     /** Tactical planner parameter. */
  49.     public static final ParameterTypeClass<LaneBasedTacticalPlanner> TACTICAL_PLANNER;

  50.     static
  51.     {
  52.         Class<LaneBasedTacticalPlanner> type = LaneBasedTacticalPlanner.class;
  53.         TACTICAL_PLANNER = new ParameterTypeClass<>("tactical planner", "Tactical planner class.",
  54.                 OTSClassUtil.getTypedClass(type), LMRS.class);
  55.     }

  56.     /** */
  57.     private static final long serialVersionUID = 20151125L;

  58.     /** Look ahead parameter type. */
  59.     protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;

  60.     /** Time step parameter type. */
  61.     protected static final ParameterTypeDuration DT = ParameterTypes.DT;

  62.     /** The car-following model. */
  63.     private CarFollowingModel carFollowingModel;

  64.     /** The perception. */
  65.     private final LanePerception lanePerception;

  66.     /** GTU. */
  67.     private final LaneBasedGTU gtu;

  68.     /**
  69.      * Instantiates a tactical planner.
  70.      * @param carFollowingModel CarFollowingModel; car-following model
  71.      * @param gtu LaneBasedGTU; GTU
  72.      * @param lanePerception LanePerception; perception
  73.      */
  74.     public AbstractLaneBasedTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu,
  75.             final LanePerception lanePerception)
  76.     {
  77.         setCarFollowingModel(carFollowingModel);
  78.         this.gtu = gtu;
  79.         this.lanePerception = lanePerception;
  80.     }

  81.     /** {@inheritDoc} */
  82.     @Override
  83.     public final LaneBasedGTU getGtu()
  84.     {
  85.         return this.gtu;
  86.     }

  87.     /**
  88.      * Build a list of lanes forward, with a maximum headway relative to the reference point of the GTU.
  89.      * @param gtu LaneBasedGTU; the GTU for which to calculate the lane list
  90.      * @param maxHeadway Length; the maximum length for which lanes should be returned
  91.      * @return LanePathInfo; an instance that provides the following information for an operational plan: the lanes to follow,
  92.      *         and the path to follow when staying on the same lane.
  93.      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
  94.      * @throws NetworkException when the strategic planner is not able to return a next node in the route
  95.      */
  96.     public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway)
  97.             throws GTUException, NetworkException
  98.     {
  99.         DirectedLanePosition dlp = gtu.getReferencePosition();
  100.         return buildLanePathInfo(gtu, maxHeadway, dlp.getLane(), dlp.getPosition(), dlp.getGtuDirection());
  101.     }

  102.     /**
  103.      * Build a list of lanes forward, with a maximum headway relative to the reference point of the GTU.
  104.      * @param gtu LaneBasedGTU; the GTU for which to calculate the lane list
  105.      * @param maxHeadway Length; the maximum length for which lanes should be returned
  106.      * @param startLane Lane; the lane in which the path starts
  107.      * @param position Length; the position on the start lane
  108.      * @param startDirectionality GTUDirectionality; the driving direction on the start lane
  109.      * @return LanePathInfo; an instance that provides the following information for an operational plan: the lanes to follow,
  110.      *         and the path to follow when staying on the same lane.
  111.      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
  112.      * @throws NetworkException when the strategic planner is not able to return a next node in the route
  113.      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
  114.      * @throws NetworkException when the strategic planner is not able to return a next node in the route
  115.      */
  116.     public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
  117.             final Length position, final GTUDirectionality startDirectionality) throws GTUException, NetworkException
  118.     {
  119.         List<LaneDirection> laneListForward = new ArrayList<>();
  120.         Lane lane = startLane;
  121.         GTUDirectionality lastGtuDir = startDirectionality;
  122.         Length startPosition = position;
  123.         Lane lastLane = lane;
  124.         laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
  125.         Length distanceToEndOfLane;
  126.         OTSLine3D path;
  127.         try
  128.         {
  129.             if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
  130.             {
  131.                 distanceToEndOfLane = lane.getLength().minus(position);
  132.                 path = lane.getCenterLine().extract(position, lane.getLength());
  133.             }
  134.             else
  135.             {
  136.                 distanceToEndOfLane = position;
  137.                 path = lane.getCenterLine().extract(Length.ZERO, position).reverse();
  138.             }
  139.         }
  140.         catch (OTSGeometryException exception)
  141.         {
  142.             // System.err.println(gtu + ": " + exception.getMessage());
  143.             // System.err.println(lane + ", len=" + lane.getLength());
  144.             // System.err.println(position);
  145.             // throw new GTUException(exception);

  146.             // section on current lane too short, floating point operations cause only a single point at the end of the lane
  147.             path = null;
  148.             distanceToEndOfLane = Length.ZERO;
  149.             laneListForward.clear();
  150.             startPosition = Length.ZERO;
  151.         }

  152.         while (distanceToEndOfLane.lt(maxHeadway))
  153.         {
  154.             Map<Lane, GTUDirectionality> lanes = lastGtuDir.equals(GTUDirectionality.DIR_PLUS)
  155.                     ? lane.nextLanes(gtu.getGTUType()) : lane.prevLanes(gtu.getGTUType());
  156.             if (lanes.size() == 0)
  157.             {
  158.                 // Dead end. Return with the list as is.
  159.                 return new LanePathInfo(path, laneListForward, startPosition);
  160.             }
  161.             else if (lanes.size() == 1)
  162.             {
  163.                 // Ask the strategical planner what the next link should be (if known), because the strategical planner knows
  164.                 // best!
  165.                 LinkDirection ld = null;
  166.                 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), lastGtuDir, gtu.getGTUType());
  167.                 lane = lanes.keySet().iterator().next();
  168.                 if (ld != null && !lane.getParentLink().equals(ld.getLink()))
  169.                 {
  170.                     // Lane not on route anymore. return with the list as is.
  171.                     return new LanePathInfo(path, laneListForward, startPosition);
  172.                 }
  173.             }
  174.             else
  175.             {
  176.                 // Multiple next lanes; ask the strategical planner where to go.
  177.                 // Note: this is not necessarily a split; it could e.g. be a bike path on a road
  178.                 LinkDirection ld;
  179.                 try
  180.                 {
  181.                     ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), /* gtu.getLanes().get(lane), */
  182.                             lastGtuDir, gtu.getGTUType());
  183.                 }
  184.                 catch (NetworkException ne)
  185.                 {
  186.                     // no route found.
  187.                     // return the data structure up to this point...
  188.                     return new LanePathInfo(path, laneListForward, startPosition);
  189.                 }
  190.                 Link nextLink = ld.getLink();
  191.                 Lane newLane = null;
  192.                 for (Lane nextLane : lanes.keySet())
  193.                 {
  194.                     if (nextLane.getParentLink().equals(nextLink))
  195.                     {
  196.                         newLane = nextLane;
  197.                         break;
  198.                     }
  199.                 }
  200.                 if (newLane == null)
  201.                 {
  202.                     // we cannot reach the next node on this lane -- we have to make a lane change!
  203.                     // return the data structure up to this point...
  204.                     return new LanePathInfo(path, laneListForward, startPosition);
  205.                 }
  206.                 // otherwise: continue!
  207.                 lane = newLane;
  208.             }

  209.             // determine direction for the path
  210.             try
  211.             {
  212.                 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
  213.                 {
  214.                     if (lastLane.getParentLink().getEndNode().equals(lane.getParentLink().getStartNode()))
  215.                     {
  216.                         // -----> O ----->, GTU moves ---->
  217.                         path = concatenateNull(path, lane.getCenterLine());
  218.                         // path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
  219.                         lastGtuDir = GTUDirectionality.DIR_PLUS;
  220.                     }
  221.                     else
  222.                     {
  223.                         // -----> O <-----, GTU moves ---->
  224.                         path = concatenateNull(path, lane.getCenterLine().reverse());
  225.                         // path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
  226.                         lastGtuDir = GTUDirectionality.DIR_MINUS;
  227.                     }
  228.                 }
  229.                 else
  230.                 {
  231.                     if (lastLane.getParentLink().getStartNode().equals(lane.getParentLink().getStartNode()))
  232.                     {
  233.                         // <----- O ----->, GTU moves ---->
  234.                         path = concatenateNull(path, lane.getCenterLine());
  235.                         // path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
  236.                         lastGtuDir = GTUDirectionality.DIR_PLUS;
  237.                     }
  238.                     else
  239.                     {
  240.                         // <----- O <-----, GTU moves ---->
  241.                         path = concatenateNull(path, lane.getCenterLine().reverse());
  242.                         // path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
  243.                         lastGtuDir = GTUDirectionality.DIR_MINUS;
  244.                     }
  245.                 }
  246.                 lastLane = lane;
  247.             }
  248.             catch (OTSGeometryException exception)
  249.             {
  250.                 throw new GTUException(exception);
  251.             }

  252.             laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
  253.             distanceToEndOfLane = distanceToEndOfLane.plus(lastLane.getLength());

  254.         }
  255.         return new LanePathInfo(path, laneListForward, startPosition);
  256.     }

  257.     /**
  258.      * Concatenate two paths, where the first may be {@code null}.
  259.      * @param path OTSLine3D; path, may be {@code null}
  260.      * @param centerLine OTSLine3D; center line of lane to add
  261.      * @return concatenated line
  262.      * @throws OTSGeometryException when lines are degenerate or too distant
  263.      */
  264.     public static OTSLine3D concatenateNull(final OTSLine3D path, final OTSLine3D centerLine) throws OTSGeometryException
  265.     {
  266.         if (path == null)
  267.         {
  268.             return centerLine;
  269.         }
  270.         return OTSLine3D.concatenate(Lane.MARGIN.si, path, centerLine);
  271.     }

  272.     /**
  273.      * Calculate the next location where the network splits, with a maximum headway relative to the reference point of the GTU.
  274.      * Note: a lane drop is also considered a split (!).
  275.      * @param gtu LaneBasedGTU; the GTU for which to calculate the lane list
  276.      * @param maxHeadway Length; the maximum length for which lanes should be returned
  277.      * @return NextSplitInfo; an instance that provides the following information for an operational plan: whether the network
  278.      *         splits, the node where it splits, and the current lanes that lead to the right node after the split node.
  279.      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
  280.      * @throws NetworkException when the strategic planner is not able to return a next node in the route
  281.      */
  282.     public static NextSplitInfo determineNextSplit(final LaneBasedGTU gtu, final Length maxHeadway)
  283.             throws GTUException, NetworkException
  284.     {
  285.         Node nextSplitNode = null;
  286.         Set<Lane> correctCurrentLanes = new LinkedHashSet<>();
  287.         DirectedLanePosition dlp = gtu.getReferencePosition();
  288.         Lane referenceLane = dlp.getLane();
  289.         double refFrac = dlp.getPosition().si / referenceLane.getLength().si;
  290.         Link lastLink = referenceLane.getParentLink();
  291.         GTUDirectionality lastGtuDir = dlp.getGtuDirection();
  292.         GTUDirectionality referenceLaneDirectionality = lastGtuDir;
  293.         Length lengthForward;
  294.         Length position = dlp.getPosition();
  295.         Node lastNode;
  296.         if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
  297.         {
  298.             lengthForward = referenceLane.getLength().minus(position);
  299.             lastNode = referenceLane.getParentLink().getEndNode();
  300.         }
  301.         else
  302.         {
  303.             lengthForward = gtu.position(referenceLane, gtu.getReference());
  304.             lastNode = referenceLane.getParentLink().getStartNode();
  305.         }

  306.         // see if we have a split within maxHeadway distance
  307.         while (lengthForward.lt(maxHeadway) && nextSplitNode == null)
  308.         {
  309.             // calculate the number of "outgoing" links
  310.             Set<Link> links = lastNode.getLinks().toSet(); // safe copy
  311.             Iterator<Link> linkIterator = links.iterator();
  312.             while (linkIterator.hasNext())
  313.             {
  314.                 Link link = linkIterator.next();
  315.                 GTUDirectionality drivingDirection =
  316.                         lastNode.equals(link.getStartNode()) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
  317.                 if (!link.getDirectionality(gtu.getGTUType()).getDirectionalities().contains(drivingDirection))
  318.                 {
  319.                     linkIterator.remove();
  320.                 }
  321.                 // if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType())
  322.                 // || (link.getDirectionality(gtu.getGTUType()).isForward() && link.getEndNode().equals(lastNode))
  323.                 // || (link.getDirectionality(gtu.getGTUType()).isBackward() && link.getStartNode().equals(lastNode)))
  324.                 // {
  325.                 // linkIterator.remove();
  326.                 // }
  327.             }

  328.             // calculate the number of incoming and outgoing lanes on the link
  329.             boolean laneChange = false;
  330.             if (links.size() == 1)
  331.             {
  332.                 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
  333.                 {
  334.                     if (cse instanceof Lane && lastGtuDir.isPlus())
  335.                     {
  336.                         Lane lane = (Lane) cse;
  337.                         if (lane.nextLanes(gtu.getGTUType()).size() == 0)
  338.                         {
  339.                             laneChange = true;
  340.                         }
  341.                     }
  342.                     if (cse instanceof Lane && lastGtuDir.isMinus())
  343.                     {
  344.                         Lane lane = (Lane) cse;
  345.                         if (lane.prevLanes(gtu.getGTUType()).size() == 0)
  346.                         {
  347.                             laneChange = true;
  348.                         }
  349.                     }
  350.                 }
  351.             }

  352.             // see if we have a lane drop
  353.             if (laneChange)
  354.             {
  355.                 nextSplitNode = lastNode;
  356.                 // which lane(s) we are registered on and adjacent lanes link to a lane
  357.                 // that does not drop?
  358.                 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
  359.                 {
  360.                     if (cse instanceof Lane)
  361.                     {
  362.                         Lane l = (Lane) cse;
  363.                         // if (noLaneDrop(gtu, maxHeadway, l, position, referenceLaneDirectionality))
  364.                         if (noLaneDrop(gtu, maxHeadway, l, l.getLength().times(refFrac), referenceLaneDirectionality))
  365.                         {
  366.                             correctCurrentLanes.add(l);
  367.                         }
  368.                     }
  369.                 }
  370.                 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
  371.             }

  372.             // see if we have a split
  373.             if (links.size() > 1)
  374.             {
  375.                 nextSplitNode = lastNode;
  376.                 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType());
  377.                 // which lane(s) we are registered on and adjacent lanes link to a lane
  378.                 // that is on the route at the next split?
  379.                 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
  380.                 {
  381.                     if (cse instanceof Lane)
  382.                     {
  383.                         Lane l = (Lane) cse;
  384.                         // if (connectsToPath(gtu, maxHeadway, l, position, referenceLaneDirectionality, ld.getLink()))
  385.                         if (connectsToPath(gtu, maxHeadway, l, l.getLength().times(refFrac), referenceLaneDirectionality,
  386.                                 ld.getLink()))
  387.                         {
  388.                             correctCurrentLanes.add(l);
  389.                         }
  390.                     }
  391.                 }
  392.                 if (correctCurrentLanes.size() > 0)
  393.                 {
  394.                     return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
  395.                 }
  396.                 // split, but no lane on current link to right direction
  397.                 Set<Lane> correctLanes = new LinkedHashSet<>();
  398.                 Set<Lane> wrongLanes = new LinkedHashSet<>();
  399.                 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
  400.                 {
  401.                     if (cse instanceof Lane)
  402.                     {
  403.                         Lane l = (Lane) cse;
  404.                         if (connectsToPath(gtu, maxHeadway.plus(l.getLength()), l, Length.ZERO, lastGtuDir, ld.getLink()))
  405.                         {
  406.                             correctLanes.add(l);
  407.                         }
  408.                         else
  409.                         {
  410.                             wrongLanes.add(l);
  411.                         }
  412.                     }
  413.                 }
  414.                 for (Lane wrongLane : wrongLanes)
  415.                 {
  416.                     for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtu.getGTUType(),
  417.                             referenceLaneDirectionality))
  418.                     {
  419.                         if (correctLanes.contains(adjLane))
  420.                         {
  421.                             return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.LEFT);
  422.                         }
  423.                     }
  424.                     for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtu.getGTUType(),
  425.                             referenceLaneDirectionality))
  426.                     {
  427.                         if (correctLanes.contains(adjLane))
  428.                         {
  429.                             return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.RIGHT);
  430.                         }
  431.                     }
  432.                 }
  433.                 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, null);
  434.             }

  435.             if (links.size() == 0)
  436.             {
  437.                 return new NextSplitInfo(null, correctCurrentLanes);
  438.             }

  439.             // just one link
  440.             Link link = links.iterator().next();

  441.             // determine direction for the path
  442.             if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
  443.             {
  444.                 if (lastLink.getEndNode().equals(link.getStartNode()))
  445.                 {
  446.                     // -----> O ----->, GTU moves ---->
  447.                     lastGtuDir = GTUDirectionality.DIR_PLUS;
  448.                     lastNode = link.getEndNode();
  449.                 }
  450.                 else
  451.                 {
  452.                     // -----> O <-----, GTU moves ---->
  453.                     lastGtuDir = GTUDirectionality.DIR_MINUS;
  454.                     lastNode = link.getEndNode();
  455.                 }
  456.             }
  457.             else
  458.             {
  459.                 if (lastLink.getStartNode().equals(link.getStartNode()))
  460.                 {
  461.                     // <----- O ----->, GTU moves ---->
  462.                     lastNode = link.getStartNode();
  463.                     lastGtuDir = GTUDirectionality.DIR_PLUS;
  464.                 }
  465.                 else
  466.                 {
  467.                     // <----- O <-----, GTU moves ---->
  468.                     lastNode = link.getStartNode();
  469.                     lastGtuDir = GTUDirectionality.DIR_MINUS;
  470.                 }
  471.             }
  472.             lastLink = links.iterator().next();
  473.             lengthForward = lengthForward.plus(lastLink.getLength());
  474.         }

  475.         return new NextSplitInfo(null, correctCurrentLanes);
  476.     }

  477.     /**
  478.      * Determine whether the lane is directly connected to our route, in other words: if we would (continue to) drive on the
  479.      * given lane, can we take the right branch at the nextSplitNode without switching lanes?
  480.      * @param gtu LaneBasedGTU; the GTU for which we have to determine the lane suitability
  481.      * @param maxHeadway Length; the maximum length for use in the calculation
  482.      * @param startLane Lane; the first lane in the list
  483.      * @param startLanePosition Length; the position on the start lane
  484.      * @param startDirectionality GTUDirectionality; the driving direction on the start lane
  485.      * @param linkAfterSplit Link; the link after the split to which we should connect
  486.      * @return boolean; true if the lane (XXXXX which lane?) is connected to our path
  487.      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
  488.      * @throws NetworkException when the strategic planner is not able to return a next node in the route
  489.      */
  490.     protected static boolean connectsToPath(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
  491.             final Length startLanePosition, final GTUDirectionality startDirectionality, final Link linkAfterSplit)
  492.             throws GTUException, NetworkException
  493.     {
  494.         List<LaneDirection> laneDirections =
  495.                 buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality).getLaneDirectionList();
  496.         for (LaneDirection laneDirection : laneDirections)
  497.         {
  498.             if (laneDirection.getLane().getParentLink().equals(linkAfterSplit))
  499.             {
  500.                 return true;
  501.             }
  502.         }
  503.         return false;
  504.     }

  505.     /**
  506.      * Determine whether the lane does not drop, in other words: if we would (continue to) drive on the given lane, can we
  507.      * continue to drive at the nextSplitNode without switching lanes?
  508.      * @param gtu LaneBasedGTU; the GTU for which we have to determine the lane suitability
  509.      * @param maxHeadway Length; the maximum length for use in the calculation
  510.      * @param startLane Lane; the first lane in the list
  511.      * @param startLanePosition Length; the position on the start lane
  512.      * @param startDirectionality GTUDirectionality; the driving direction on the start lane
  513.      * @return boolean; true if the lane (XXX which lane?) is connected to our path
  514.      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
  515.      * @throws NetworkException when the strategic planner is not able to return a next node in the route
  516.      */
  517.     protected static boolean noLaneDrop(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
  518.             final Length startLanePosition, final GTUDirectionality startDirectionality) throws GTUException, NetworkException
  519.     {
  520.         LanePathInfo lpi = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality);
  521.         if (lpi.getPath().getLength().lt(maxHeadway))
  522.         {
  523.             return false;
  524.         }
  525.         return true;
  526.     }

  527.     /**
  528.      * Make a list of links on which to drive next, with a maximum headway relative to the reference point of the GTU.
  529.      * @param gtu LaneBasedGTU; the GTU for which to calculate the link list
  530.      * @param maxHeadway Length; the maximum length for which links should be returned
  531.      * @return List&lt;LinkDirection&gt;; a list of links on which to drive next
  532.      * @throws GTUException when the vehicle is not on one of the lanes on which it is registered
  533.      * @throws NetworkException when the strategic planner is not able to return a next node in the route
  534.      */
  535.     protected static List<LinkDirection> buildLinkListForward(final LaneBasedGTU gtu, final Length maxHeadway)
  536.             throws GTUException, NetworkException
  537.     {
  538.         List<LinkDirection> linkList = new ArrayList<>();
  539.         DirectedLanePosition dlp = gtu.getReferencePosition();
  540.         Lane referenceLane = dlp.getLane();
  541.         Link lastLink = referenceLane.getParentLink();
  542.         GTUDirectionality lastGtuDir = dlp.getGtuDirection();
  543.         linkList.add(new LinkDirection(lastLink, lastGtuDir));
  544.         Length lengthForward;
  545.         Length position = dlp.getPosition();
  546.         Node lastNode;
  547.         if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
  548.         {
  549.             lengthForward = referenceLane.getLength().minus(position);
  550.             lastNode = referenceLane.getParentLink().getEndNode();
  551.         }
  552.         else
  553.         {
  554.             lengthForward = gtu.position(referenceLane, gtu.getReference());
  555.             lastNode = referenceLane.getParentLink().getStartNode();
  556.         }

  557.         // see if we have a split within maxHeadway distance
  558.         while (lengthForward.lt(maxHeadway))
  559.         {
  560.             // calculate the number of "outgoing" links
  561.             Set<Link> links = lastNode.getLinks().toSet(); // is a safe copy
  562.             Iterator<Link> linkIterator = links.iterator();
  563.             while (linkIterator.hasNext())
  564.             {
  565.                 Link link = linkIterator.next();
  566.                 GTUDirectionality drivingDirection =
  567.                         lastNode.equals(link.getStartNode()) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
  568.                 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType(), drivingDirection))
  569.                 {
  570.                     linkIterator.remove();
  571.                 }
  572.             }

  573.             if (links.size() == 0)
  574.             {
  575.                 return linkList; // the path stops here...
  576.             }

  577.             Link link;
  578.             if (links.size() > 1)
  579.             {
  580.                 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(lastLink, lastGtuDir, gtu.getGTUType());
  581.                 link = ld.getLink();
  582.             }
  583.             else
  584.             {
  585.                 link = links.iterator().next();
  586.             }

  587.             // determine direction for the path
  588.             if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
  589.             {
  590.                 if (lastLink.getEndNode().equals(link.getStartNode()))
  591.                 {
  592.                     // -----> O ----->, GTU moves ---->
  593.                     lastGtuDir = GTUDirectionality.DIR_PLUS;
  594.                     lastNode = lastLink.getEndNode();
  595.                 }
  596.                 else
  597.                 {
  598.                     // -----> O <-----, GTU moves ---->
  599.                     lastGtuDir = GTUDirectionality.DIR_MINUS;
  600.                     lastNode = lastLink.getEndNode();
  601.                 }
  602.             }
  603.             else
  604.             {
  605.                 if (lastLink.getStartNode().equals(link.getStartNode()))
  606.                 {
  607.                     // <----- O ----->, GTU moves ---->
  608.                     lastNode = lastLink.getStartNode();
  609.                     lastGtuDir = GTUDirectionality.DIR_PLUS;
  610.                 }
  611.                 else
  612.                 {
  613.                     // <----- O <-----, GTU moves ---->
  614.                     lastNode = lastLink.getStartNode();
  615.                     lastGtuDir = GTUDirectionality.DIR_MINUS;
  616.                 }
  617.             }
  618.             lastLink = link;
  619.             linkList.add(new LinkDirection(lastLink, lastGtuDir));
  620.             lengthForward = lengthForward.plus(lastLink.getLength());
  621.         }
  622.         return linkList;
  623.     }

  624.     /** {@inheritDoc} */
  625.     @Override
  626.     public final CarFollowingModel getCarFollowingModel()
  627.     {
  628.         return this.carFollowingModel;
  629.     }

  630.     /**
  631.      * Sets the car-following model.
  632.      * @param carFollowingModel CarFollowingModel; Car-following model to set.
  633.      */
  634.     public final void setCarFollowingModel(final CarFollowingModel carFollowingModel)
  635.     {
  636.         this.carFollowingModel = carFollowingModel;
  637.     }

  638.     /** {@inheritDoc} */
  639.     @Override
  640.     public final LanePerception getPerception()
  641.     {
  642.         return this.lanePerception;
  643.     }

  644. }