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