View Javadoc
1   package org.opentrafficsim.road.gtu.lane.plan.operational;
2   
3   import java.util.List;
4   
5   import org.djunits.value.vdouble.scalar.Direction;
6   import org.djunits.value.vdouble.scalar.Duration;
7   import org.djunits.value.vdouble.scalar.Length;
8   import org.djunits.value.vdouble.scalar.Speed;
9   import org.djunits.value.vdouble.scalar.Time;
10  import org.opentrafficsim.core.geometry.DirectedPoint;
11  import org.opentrafficsim.core.geometry.OtsGeometryException;
12  import org.opentrafficsim.core.geometry.OtsLine3d;
13  import org.opentrafficsim.core.geometry.OtsLine3d.FractionalFallback;
14  import org.opentrafficsim.core.geometry.OtsPoint3d;
15  import org.opentrafficsim.core.gtu.GtuException;
16  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
17  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
18  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
19  import org.opentrafficsim.road.network.lane.Lane;
20  import org.opentrafficsim.road.network.lane.LanePosition;
21  
22  /**
23   * An operational plan with some extra information about the lanes and lane changes so this information does not have to be
24   * recalculated multiple times. Furthermore, it is quite expensive to check whether a lane change is part of the oprtational
25   * plan based on geographical data.
26   * <p>
27   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
29   * </p>
30   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
32   */
33  public class LaneBasedOperationalPlan extends OperationalPlan
34  {
35      /** */
36      private static final long serialVersionUID = 20160120L;
37  
38      /** Deviative; meaning not along lane center lines. */
39      private final boolean deviative;
40  
41      /**
42       * Construct an operational plan with or without a lane change.
43       * @param gtu LaneBasedGtu; the GTU for debugging purposes
44       * @param path OtsLine3d; the path to follow from a certain time till a certain time. The path should have &lt;i&gt;at
45       *            least&lt;/i&gt; the length
46       * @param startTime Time; the absolute start time when we start executing the path
47       * @param startSpeed Speed; the GTU speed when we start executing the path
48       * @param operationalPlanSegmentList List&lt;Segment&gt;; the segments that make up the path with an acceleration, constant
49       *            speed or deceleration profile
50       * @param deviative boolean; whether the path is not along lane center lines
51       * @throws OperationalPlanException when the path is too short for the operation
52       */
53      @SuppressWarnings("checkstyle:parameternumber")
54      public LaneBasedOperationalPlan(final LaneBasedGtu gtu, final OtsLine3d path, final Time startTime, final Speed startSpeed,
55              final List<Segment> operationalPlanSegmentList, final boolean deviative) throws OperationalPlanException
56      {
57          super(gtu, path, startTime, startSpeed, operationalPlanSegmentList);
58          this.deviative = deviative;
59      }
60  
61      /**
62       * Build a plan where the GTU will wait for a certain time. Of course no lane change takes place.
63       * @param gtu LaneBasedGtu; the GTU for debugging purposes
64       * @param waitPoint DirectedPoint; the point at which the GTU will wait
65       * @param startTime Time; the current time or a time in the future when the plan should start
66       * @param duration Duration; the waiting time
67       * @param deviative boolean; whether the path is not along lane center lines
68       * @throws OperationalPlanException when construction of a waiting path fails
69       */
70      public LaneBasedOperationalPlan(final LaneBasedGtu gtu, final DirectedPoint waitPoint, final Time startTime,
71              final Duration duration, final boolean deviative) throws OperationalPlanException
72      {
73          super(gtu, waitPoint, startTime, duration);
74          this.deviative = deviative;
75      }
76  
77      /**
78       * Check if we deviate from the center line.
79       * @return whether this maneuver involves deviation from the center line.
80       */
81      public final boolean isDeviative()
82      {
83          return this.deviative;
84      }
85  
86      /**
87       * Returns the total length along the reference lane that the GTU travels. In case of a deviative plan this involves
88       * projection of the actual path to the lane center lines.
89       * @param gtu LaneBasedGtu; GTU
90       * @return Length; total length along the path
91       * @throws GtuException if the GTU has not reference position
92       */
93      public final Length getTotalLengthAlongLane(final LaneBasedGtu gtu) throws GtuException
94      {
95          if (!this.deviative)
96          {
97              // along the lane center lines
98              return getTotalLength();
99          }
100 
101         // let's project the end position of the plan
102         return getDistanceAlongLane(gtu, getEndLocation());
103     }
104 
105     /**
106      * Helper method to get rotation at start or end of lane.
107      * @param lane Lane; lane
108      * @param start boolean; start (or end)
109      * @return rotation at start or end of lane
110      */
111     private double getRotZAtFraction(final Lane lane, final boolean start)
112     {
113         double f = start ? 0.0 : 1.0;
114         try
115         {
116             return lane.getCenterLine().getLocationFraction(f).getRotZ();
117         }
118         catch (OtsGeometryException exception)
119         {
120             // should not occur, we use 0.0 and 1.0
121             throw new RuntimeException("Unexpected exception while assessing if a GTU is between lanes.", exception);
122         }
123     }
124 
125     /**
126      * Returns the distance along the reference lane that the GTU travels from the current location up to the point.
127      * @param gtu LaneBasedGtu; GTU
128      * @param point DirectedPoint; point where the GTU is or will be
129      * @return Length; total length along the path
130      * @throws GtuException if the GTU has not reference position
131      */
132     public final Length getDistanceAlongLane(final LaneBasedGtu gtu, final DirectedPoint point) throws GtuException
133     {
134 
135         // start lane center lines at current reference lane
136         LanePosition pos = gtu.getReferencePosition();
137         Lane lane = pos.getLane();
138 
139         // initialize loop data
140         double length = -lane.coveredDistance(pos.getPosition().si / pos.getLane().getLength().si).si;
141         double f = Double.NaN;
142         Direction prevDir = Direction.instantiateSI(getRotZAtFraction(lane, true));
143 
144         // move to next lane while projection fails
145         while (Double.isNaN(f))
146         {
147             Lane nextLane = gtu.getNextLaneForRoute(lane);
148             Direction nextDir = Direction.instantiateSI(nextLane == null ? getRotZAtFraction(lane, false)
149                     : .5 * getRotZAtFraction(lane, false) + .5 * getRotZAtFraction(nextLane, true));
150             f = lane.getCenterLine().projectFractional(prevDir, nextDir, point.x, point.y, FractionalFallback.NaN);
151 
152             // check if the GTU is adjacent to the bit between the lanes (if there is such a bit)
153             if (Double.isNaN(f))
154             {
155                 if (nextLane == null)
156                 {
157                     // projection error on dad-end lane, add the length of the lane
158                     f = 1.0;
159                     length += lane.coveredDistance(f).si;
160                 }
161                 else
162                 {
163                     try
164                     {
165                         // compose gap line
166                         OtsPoint3d last = lane.getCenterLine().getLast();
167                         OtsPoint3d first = nextLane.getCenterLine().get(0);
168                         if (!(last).equals(first))
169                         {
170                             OtsLine3d gap = new OtsLine3d(last, first);
171                             double fGap = gap.projectFractional(null, null, point.x, point.y, FractionalFallback.NaN);
172                             if (!Double.isNaN(fGap))
173                             {
174                                 f = (lane.getLength().si + fGap * gap.getLengthSI()) / lane.getLength().si;
175                             }
176                             else
177                             {
178                                 // gap, but no successful projection, use next lane in next loop, increase length so far
179                                 length += lane.getLength().si;
180                                 lane = nextLane;
181                                 prevDir = nextDir;
182                             }
183                         }
184                         else
185                         {
186                             // no gap, use next lane in next loop, increase length so far
187                             length += lane.getLength().si;
188                             lane = nextLane;
189                             prevDir = nextDir;
190                         }
191                     }
192                     catch (OtsGeometryException exception)
193                     {
194                         // should not occur, we use get(0) and getLast()
195                         throw new RuntimeException("Unexpected exception while assessing if a GTU is between lanes.",
196                                 exception);
197                     }
198                 }
199             }
200             else
201             {
202                 // projection is ok on lane, increase length so far
203                 length += lane.coveredDistance(f).si;
204             }
205         }
206         // add length on lane where the reference position was projected to (or to it's consecutive gap between lanes)
207         return Length.instantiateSI(length);
208     }
209 
210     /** {@inheritDoc} */
211     @Override
212     public final String toString()
213     {
214         return "LaneBasedOperationalPlan [deviative=" + this.deviative + "]";
215     }
216 }