View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical;
2   
3   import java.util.Collections;
4   import java.util.Iterator;
5   import java.util.Set;
6   
7   import org.djunits.value.vdouble.scalar.Length;
8   import org.opentrafficsim.base.parameters.ParameterException;
9   import org.opentrafficsim.base.parameters.ParameterTypes;
10  import org.opentrafficsim.core.gtu.GtuException;
11  import org.opentrafficsim.core.gtu.plan.tactical.TacticalPlanner;
12  import org.opentrafficsim.core.network.LateralDirectionality;
13  import org.opentrafficsim.core.network.Link;
14  import org.opentrafficsim.core.network.Node;
15  import org.opentrafficsim.core.network.route.Route;
16  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
17  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
18  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneBasedOperationalPlan;
19  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
20  import org.opentrafficsim.road.network.lane.Lane;
21  
22  /**
23   * <p>
24   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
25   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
26   * </p>
27   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
28   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
29   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
30   */
31  public interface LaneBasedTacticalPlanner extends TacticalPlanner<LaneBasedGtu, LanePerception>
32  {
33      /**
34       * Returns the car-following model.
35       * @return car following model
36       */
37      CarFollowingModel getCarFollowingModel();
38  
39      /**
40       * Selects a lane from a possible set. This set contains all viable lanes in to which a lanes splits.
41       * @param from Lane; lane we come from
42       * @param lanes Set&lt;Lane&gt;; set of lanes possible
43       * @return Lane; preferred lane
44       * @throws ParameterException in case of a missing parameter
45       */
46      default Lane chooseLaneAtSplit(final Lane from, final Set<Lane> lanes) throws ParameterException
47      {
48          if (getGtu().getOperationalPlan() instanceof LaneBasedOperationalPlan
49                  && ((LaneBasedOperationalPlan) getGtu().getOperationalPlan()).isDeviative())
50          {
51              // take the lane adjacent to lane we are registered on, if any
52              LateralDirectionality forceSide = LateralDirectionality.NONE;
53              try
54              {
55                  Set<Lane> leftLanes = from.accessibleAdjacentLanesPhysical(LateralDirectionality.LEFT, getGtu().getType());
56                  if (!Collections.disjoint(getGtu().positions(getGtu().getReference()).keySet(), leftLanes))
57                  {
58                      forceSide = LateralDirectionality.LEFT;
59                  }
60                  else
61                  {
62                      Set<Lane> rightLanes =
63                              from.accessibleAdjacentLanesPhysical(LateralDirectionality.RIGHT, getGtu().getType());
64                      if (!Collections.disjoint(getGtu().positions(getGtu().getReference()).keySet(), rightLanes))
65                      {
66                          forceSide = LateralDirectionality.RIGHT;
67                      }
68                  }
69              }
70              catch (GtuException exception)
71              {
72                  throw new RuntimeException("Exception obtaining reference position.", exception);
73              }
74              if (!forceSide.isNone())
75              {
76                  if (lanes.isEmpty())
77                  {
78                      // A sink should delete the GTU, or a lane change should end, before reaching the end of the lane
79                      return null;
80                  }
81                  else
82                  {
83                      Iterator<Lane> iter = lanes.iterator();
84                      Lane next = iter.next();
85                      while (iter.hasNext())
86                      {
87                          Lane candidate = iter.next();
88                          next = LaneBasedTacticalPlanner.mostOnSide(next, candidate, forceSide);
89                      }
90                      return next;
91                  }
92              }
93          }
94          Route route = getGtu().getStrategicalPlanner().getRoute();
95          if (route == null)
96          {
97              // select right-most lane
98              Lane rightMost = null;
99              for (Lane lane : lanes)
100             {
101                 rightMost = rightMost == null ? lane : mostOnSide(rightMost, lane, LateralDirectionality.RIGHT);
102             }
103             return rightMost;
104         }
105         Length maxDistance = Length.NEGATIVE_INFINITY;
106         Lane best = null;
107         for (Lane lane : lanes)
108         {
109             Lane next = getGtu().getNextLaneForRoute(lane);
110             if (next != null)
111             {
112                 Length okDistance = okDistance(next, lane.getLength(), route,
113                         getGtu().getParameters().getParameter(ParameterTypes.PERCEPTION));
114                 if (maxDistance.eq(okDistance))
115                 {
116                     best = mostOnSide(best, lane, LateralDirectionality.RIGHT);
117                 }
118                 else if (okDistance.gt(maxDistance))
119                 {
120                     maxDistance = okDistance;
121                     best = lane;
122                 }
123             }
124         }
125         return best;
126     }
127 
128     /**
129      * Helper method for default chooseLaneAtSplit implementation that returns the distance from this lane onwards where the
130      * route can be followed.
131      * @param lane Lane; lane and direction
132      * @param distance Length; distance so far
133      * @param route Route; route
134      * @param maxDistance Length; max search distance
135      * @return Length; distance from this lane onwards where the route can be followed
136      */
137     // TODO private when we use java 9
138     default Length okDistance(final Lane lane, final Length distance, final Route route, final Length maxDistance)
139     {
140         if (distance.gt(maxDistance))
141         {
142             return maxDistance;
143         }
144         Lane next = getGtu().getNextLaneForRoute(lane);
145         if (next == null)
146         {
147             Node endNode = lane.getLink().getEndNode();
148             Set<Link> links = endNode.getLinks().toSet();
149             links.remove(lane.getLink());
150             if (route.contains(endNode) && (links.isEmpty() || links.iterator().next().isConnector()))
151             {
152                 // dead-end link, must be destination
153                 return maxDistance;
154             }
155             // there is no next lane on the route, return the distance to the end of this lane
156             return distance.plus(lane.getLength());
157         }
158         return okDistance(next, distance.plus(lane.getLength()), route, maxDistance);
159     }
160 
161     /**
162      * Returns the right-most of two lanes.
163      * @param lane1 Lane; lane 1
164      * @param lane2 Lane; lane 2
165      * @param lat LateralDirectionality; lateral side
166      * @return Lane; right-most of two lanes
167      */
168     static Lane mostOnSide(final Lane lane1, final Lane lane2, final LateralDirectionality lat)
169     {
170         Length offset1 = lane1.getOffsetAtBegin().plus(lane1.getOffsetAtEnd());
171         Length offset2 = lane2.getOffsetAtBegin().plus(lane2.getOffsetAtEnd());
172         if (lat.isLeft())
173         {
174             return offset1.gt(offset2) ? lane1 : lane2;
175         }
176         return offset1.gt(offset2) ? lane2 : lane1;
177     }
178 
179 }