View Javadoc
1   package org.opentrafficsim.road.gtu.lane.plan.operational;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import javax.management.RuntimeErrorException;
9   
10  import org.djunits.unit.AccelerationUnit;
11  import org.djunits.unit.LengthUnit;
12  import org.djunits.unit.SpeedUnit;
13  import org.djunits.unit.TimeUnit;
14  import org.djunits.value.ValueException;
15  import org.djunits.value.vdouble.scalar.Acceleration;
16  import org.djunits.value.vdouble.scalar.Duration;
17  import org.djunits.value.vdouble.scalar.Length;
18  import org.djunits.value.vdouble.scalar.Speed;
19  import org.djunits.value.vdouble.scalar.Time;
20  import org.opentrafficsim.core.geometry.OTSGeometryException;
21  import org.opentrafficsim.core.geometry.OTSLine3D;
22  import org.opentrafficsim.core.geometry.OTSPoint3D;
23  import org.opentrafficsim.core.gtu.GTUException;
24  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
25  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.SpeedSegment;
26  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
27  import org.opentrafficsim.core.math.Solver;
28  import org.opentrafficsim.core.network.LateralDirectionality;
29  import org.opentrafficsim.road.gtu.lane.AbstractLaneBasedGTU;
30  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
31  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
32  import org.opentrafficsim.road.network.lane.Lane;
33  
34  import nl.tudelft.simulation.language.Throw;
35  import nl.tudelft.simulation.language.d3.DirectedPoint;
36  
37  /**
38   * Builder for several often used operational plans, based on a list of lanes. E.g., decelerate to come to a full stop at the
39   * end of a shape; accelerate to reach a certain speed at the end of a curve; drive constant on a curve; decelerate or
40   * accelerate to reach a given end speed at the end of a curve, etc.<br>
41   * TODO driving with negative speeds (backward driving) is not yet supported.
42   * <p>
43   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
44   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
45   * </p>
46   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
47   * initial version Nov 15, 2015 <br>
48   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
49   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
50   */
51  public final class LaneOperationalPlanBuilder
52  {
53      /** Maximum acceleration for unbounded accelerations: 1E12 m/s2. */
54      private static final Acceleration MAX_ACCELERATION = new Acceleration(1E12, AccelerationUnit.SI);
55  
56      /** Maximum deceleration for unbounded accelerations: -1E12 m/s2. */
57      private static final Acceleration MAX_DECELERATION = new Acceleration(-1E12, AccelerationUnit.SI);
58  
59      /** Private constructor. */
60      private LaneOperationalPlanBuilder()
61      {
62          // class should not be instantiated
63      }
64  
65      /**
66       * Build a plan with a path and a given start speed to try to reach a provided end speed, exactly at the end of the curve.
67       * The acceleration (and deceleration) are capped by maxAcceleration and maxDeceleration. Therefore, there is no guarantee
68       * that the end speed is actually reached by this plan.
69       * @param gtu the GTU for debugging purposes
70       * @param lanes a list of connected Lanes to do the driving on
71       * @param firstLanePosition position on the first lane with the reference point of the GTU
72       * @param distance distance to drive for reaching the end speed
73       * @param startTime the current time or a time in the future when the plan should start
74       * @param startSpeed the speed at the start of the path
75       * @param endSpeed the required end speed
76       * @param maxAcceleration the maximum acceleration that can be applied, provided as a POSITIVE number
77       * @param maxDeceleration the maximum deceleration that can be applied, provided as a NEGATIVE number
78       * @return the operational plan to accomplish the given end speed
79       * @throws OperationalPlanException when the plan cannot be generated, e.g. because of a path that is too short
80       * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
81       *             constructed segment list differ more than a given threshold
82       * @throws OTSGeometryException in case the lanes are not connected or firstLanePosition is larger than the length of the
83       *             first lane
84       */
85      public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
86              final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
87              final Speed endSpeed, final Acceleration maxAcceleration, final Acceleration maxDeceleration)
88              throws OperationalPlanException, OTSGeometryException
89      {
90          OTSLine3D path = makePath(lanes, firstLanePosition, distance);
91          OperationalPlan.Segment segment;
92          if (startSpeed.eq(endSpeed))
93          {
94              segment = new SpeedSegment(distance.divideBy(startSpeed));
95          }
96          else
97          {
98              try
99              {
100                 // t = 2x / (vt + v0); a = (vt - v0) / t
101                 Duration duration = distance.multiplyBy(2.0).divideBy(endSpeed.plus(startSpeed));
102                 Acceleration acceleration = endSpeed.minus(startSpeed).divideBy(duration);
103                 if (acceleration.si < 0.0 && acceleration.lt(maxDeceleration))
104                 {
105                     acceleration = maxDeceleration;
106                     duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
107                             TimeUnit.SI);
108                 }
109                 if (acceleration.si > 0.0 && acceleration.gt(maxAcceleration))
110                 {
111                     acceleration = maxAcceleration;
112                     duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
113                             TimeUnit.SI);
114                 }
115                 segment = new OperationalPlan.AccelerationSegment(duration, acceleration);
116             }
117             catch (ValueException ve)
118             {
119                 throw new OperationalPlanException(ve);
120             }
121         }
122         ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
123         segmentList.add(segment);
124         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, lanes);
125     }
126 
127     /**
128      * Build a plan with a path and a given start speed to try to reach a provided end speed, exactly at the end of the curve.
129      * The acceleration (and deceleration) are capped by maxAcceleration and maxDeceleration. Therefore, there is no guarantee
130      * that the end speed is actually reached by this plan.
131      * @param lanes a list of connected Lanes to do the driving on
132      * @param firstLanePosition position on the first lane with the reference point of the GTU
133      * @param distance distance to drive for reaching the end speed
134      * @return the driving path as a line
135      * @throws OperationalPlanException when the length of the lanes is less than the distance when we start at the
136      *             firstLanePosition on the first lane, or when the lanes list contains no elements
137      * @throws OTSGeometryException in case the lanes are not connected or firstLanePosition is larger than the length of the
138      *             first lane
139      */
140     public static OTSLine3D makePath(final List<Lane> lanes, final Length firstLanePosition, final Length distance)
141             throws OperationalPlanException, OTSGeometryException
142     {
143         if (lanes.size() == 0)
144         {
145             throw new OperationalPlanException("LaneOperationalPlanBuilder.makePath got a lanes list with size = 0");
146         }
147         OTSLine3D path = lanes.get(0).getCenterLine().extract(firstLanePosition, lanes.get(0).getLength());
148         for (int i = 1; i < lanes.size(); i++)
149         {
150             path = OTSLine3D.concatenate(0.15, path, lanes.get(i).getCenterLine());
151         }
152         return path.extract(0.0, distance.si);
153     }
154 
155     /**
156      * Build a plan with a path and a given start speed to reach a provided end speed, exactly at the end of the curve.
157      * Acceleration and deceleration are virtually unbounded (1E12 m/s2) to reach the end speed (e.g., to come to a complete
158      * stop).
159      * @param gtu the GTU for debugging purposes
160      * @param lanes a list of connected Lanes to do the driving on
161      * @param firstLanePosition position on the first lane with the reference point of the GTU
162      * @param distance distance to drive for reaching the end speed
163      * @param startTime the current time or a time in the future when the plan should start
164      * @param startSpeed the speed at the start of the path
165      * @param endSpeed the required end speed
166      * @return the operational plan to accomplish the given end speed
167      * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
168      *             constructed segment list differ more than a given threshold
169      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
170      *             first lane
171      */
172     public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
173             final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
174             final Speed endSpeed) throws OperationalPlanException, OTSGeometryException
175     {
176         return buildGradualAccelerationPlan(gtu, lanes, firstLanePosition, distance, startTime, startSpeed, endSpeed,
177                 MAX_ACCELERATION, MAX_DECELERATION);
178     }
179 
180     /**
181      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
182      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
183      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
184      * before completing the path, a truncated path that ends where the GTU stops is used instead.
185      * @param gtu the GTU for debugging purposes
186      * @param lanes a list of connected Lanes to do the driving on
187      * @param firstLanePosition position on the first lane with the reference point of the GTU
188      * @param distance distance to drive for reaching the end speed
189      * @param startTime the current time or a time in the future when the plan should start
190      * @param startSpeed the speed at the start of the path
191      * @param endSpeed the required end speed
192      * @param acceleration the acceleration to use if endSpeed &gt; startSpeed, provided as a POSITIVE number
193      * @param deceleration the deceleration to use if endSpeed &lt; startSpeed, provided as a NEGATIVE number
194      * @return the operational plan to accomplish the given end speed
195      * @throws OperationalPlanException when the construction of the operational path fails
196      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
197      *             first lane
198      */
199     public static LaneBasedOperationalPlan buildMaximumAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
200             final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
201             final Speed endSpeed, final Acceleration acceleration, final Acceleration deceleration)
202             throws OperationalPlanException, OTSGeometryException
203     {
204         OTSLine3D path = makePath(lanes, firstLanePosition, distance);
205         ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
206         if (startSpeed.eq(endSpeed))
207         {
208             segmentList.add(new OperationalPlan.SpeedSegment(distance.divideBy(startSpeed)));
209         }
210         else
211         {
212             try
213             {
214                 if (endSpeed.gt(startSpeed))
215                 {
216                     Duration t = endSpeed.minus(startSpeed).divideBy(acceleration);
217                     Length x = startSpeed.multiplyBy(t).plus(acceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
218                     if (x.ge(distance))
219                     {
220                         // we cannot reach the end speed in the given distance with the given acceleration
221                         Duration duration = new Duration(
222                                 Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si), TimeUnit.SI);
223                         segmentList.add(new OperationalPlan.AccelerationSegment(duration, acceleration));
224                     }
225                     else
226                     {
227                         // we reach the (higher) end speed before the end of the segment. Make two segments.
228                         segmentList.add(new OperationalPlan.AccelerationSegment(t, acceleration));
229                         Duration duration = distance.minus(x).divideBy(endSpeed);
230                         segmentList.add(new OperationalPlan.SpeedSegment(duration));
231                     }
232                 }
233                 else
234                 {
235                     Duration t = endSpeed.minus(startSpeed).divideBy(deceleration);
236                     Length x = startSpeed.multiplyBy(t).plus(deceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
237                     if (x.ge(distance))
238                     {
239                         // we cannot reach the end speed in the given distance with the given deceleration
240                         Duration duration = new Duration(
241                                 Solver.firstSolutionAfter(0, deceleration.si / 2, startSpeed.si, -distance.si), TimeUnit.SI);
242                         segmentList.add(new OperationalPlan.AccelerationSegment(duration, deceleration));
243                     }
244                     else
245                     {
246                         if (endSpeed.si == 0.0)
247                         {
248                             // if endSpeed == 0, we cannot reach the end of the path. Therefore, build a partial path.
249                             OTSLine3D partialPath = path.truncate(x.si);
250                             segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
251                             return new LaneBasedOperationalPlan(gtu, partialPath, startTime, startSpeed, segmentList, lanes);
252                         }
253                         // we reach the (lower) end speed, larger than zero, before the end of the segment. Make two segments.
254                         segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
255                         Duration duration = distance.minus(x).divideBy(endSpeed);
256                         segmentList.add(new OperationalPlan.SpeedSegment(duration));
257                     }
258                 }
259             }
260             catch (ValueException ve)
261             {
262                 throw new OperationalPlanException(ve);
263             }
264 
265         }
266         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, lanes);
267     }
268 
269     /**
270      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
271      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
272      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
273      * before completing the path, a truncated path that ends where the GTU stops is used instead.
274      * @param gtu the GTU for debugging purposes
275      * @param lanes a list of connected Lanes to do the driving on
276      * @param firstLanePosition position on the first lane with the reference point of the GTU
277      * @param startTime the current time or a time in the future when the plan should start
278      * @param startSpeed the speed at the start of the path
279      * @param acceleration the acceleration to use
280      * @param timeStep time step for the plan
281      * @return the operational plan to accomplish the given end speed
282      * @throws OperationalPlanException when the construction of the operational path fails
283      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
284      *             first lane
285      */
286     public static LaneBasedOperationalPlan buildAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
287             final Length firstLanePosition, final Time startTime, final Speed startSpeed, final Acceleration acceleration,
288             final Duration timeStep) throws OperationalPlanException, OTSGeometryException
289     {
290 
291         if (startSpeed.eq(Speed.ZERO) && acceleration.le(Acceleration.ZERO))
292         {
293             // stand-still
294             try
295             {
296                 return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, lanes.get(0));
297             }
298             catch (RemoteException exception)
299             {
300                 exception.printStackTrace();
301                 throw new OperationalPlanException(exception);
302             }
303         }
304         Length distance;
305         ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
306         if (startSpeed.plus(acceleration.multiplyBy(timeStep)).lt(Speed.ZERO))
307         {
308             // will reach stand-still within time step
309             Duration brakingTime = startSpeed.divideBy(acceleration.multiplyBy(-1.0));
310             segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
311             segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
312             distance = new Length(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si,
313                     LengthUnit.SI);
314         }
315         else
316         {
317             segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
318             distance =
319                     new Length(startSpeed.si * timeStep.si + .5 * acceleration.si * timeStep.si * timeStep.si, LengthUnit.SI);
320         }
321         OTSLine3D path;
322         try
323         {
324             path = makePath(lanes, firstLanePosition, distance);
325         }
326         catch (Exception e)
327         {
328             path = makePath(lanes, firstLanePosition, distance);
329             throw new Error("Bad!");
330         }
331         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, lanes);
332     }
333 
334     /**
335      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
336      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
337      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
338      * before completing the path, a truncated path that ends where the GTU stops is used instead.
339      * @param gtu the GTU for debugging purposes
340      * @param fromLanes lanes where the GTU changes from
341      * @param laneChangeDirectionality direction of lane change
342      * @param startPosition current position
343      * @param startTime the current time or a time in the future when the plan should start
344      * @param startSpeed the speed at the start of the path
345      * @param acceleration the acceleration to use
346      * @param timeStep time step for the plan
347      * @param laneChange lane change status
348      * @return the operational plan to accomplish the given end speed
349      * @throws OperationalPlanException when the construction of the operational path fails
350      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
351      *             first lane
352      */
353     @SuppressWarnings("checkstyle:parameternumber")
354     public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGTU gtu, final List<Lane> fromLanes,
355             final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition, final Time startTime,
356             final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange)
357             throws OperationalPlanException, OTSGeometryException
358     {
359         Length fromLaneDistance =
360                 new Length(startSpeed.si * timeStep.si + .5 * acceleration.si * timeStep.si * timeStep.si, LengthUnit.SI);
361         // TODO also for other driving directions, additional arguments in projectFractional?
362         double fractionalLinkPositionFirst = fromLanes.get(0).getCenterLine().projectFractional(
363                 fromLanes.get(0).getParentLink().getStartNode().getDirection(),
364                 fromLanes.get(0).getParentLink().getEndNode().getDirection(), startPosition.x, startPosition.y);
365         Length fromLaneFirstPosition = fromLanes.get(0).position(fractionalLinkPositionFirst);
366         Length cumulDistance = fromLanes.get(0).getLength().minus(fromLaneFirstPosition);
367         int lastLaneIndex = 0;
368         while (cumulDistance.lt(fromLaneDistance))
369         {
370             lastLaneIndex++;
371             cumulDistance = cumulDistance.plus(fromLanes.get(lastLaneIndex).getLength());
372         }
373         double fractionalLinkPositionLast =
374                 fromLanes.get(lastLaneIndex).getLength().minus(cumulDistance.minus(fromLaneDistance)).si
375                         / fromLanes.get(lastLaneIndex).getLength().si;
376 
377         List<Lane> toLanes = new ArrayList<>();
378         for (Lane lane : fromLanes)
379         {
380             if (!lane.accessibleAdjacentLanes(laneChangeDirectionality, gtu.getGTUType()).isEmpty())
381             {
382                 toLanes.add(lane.accessibleAdjacentLanes(laneChangeDirectionality, gtu.getGTUType()).iterator().next());
383             }
384             else
385             {
386                 new Exception().printStackTrace();
387                 System.exit(-1);
388             }
389         }
390 
391         Length toLaneFirstPosition = toLanes.get(0).position(fractionalLinkPositionFirst);
392         Length fromLaneLastPosition = fromLanes.get(lastLaneIndex).position(fractionalLinkPositionLast);
393         Length toLaneLastPosition = toLanes.get(lastLaneIndex).position(fractionalLinkPositionLast);
394 
395         DirectedPoint fromFirst = fromLanes.get(0).getCenterLine().getLocation(fromLaneFirstPosition);
396         DirectedPoint toFirst = toLanes.get(0).getCenterLine().getLocation(toLaneFirstPosition);
397         DirectedPoint fromLast = fromLanes.get(lastLaneIndex).getCenterLine().getLocation(fromLaneLastPosition);
398         DirectedPoint toLast = toLanes.get(lastLaneIndex).getCenterLine().getLocation(toLaneLastPosition);
399 
400         double lastFraction = laneChange.updateAndGetFraction(timeStep, laneChangeDirectionality, gtu);
401         OTSPoint3D lastPoint = new OTSPoint3D(fromLast.x * (1 - lastFraction) + toLast.x * lastFraction,
402                 fromLast.y * (1 - lastFraction) + toLast.y * lastFraction,
403                 fromLast.z * (1 - lastFraction) + toLast.z * lastFraction);
404         OTSPoint3D firstPoint = new OTSPoint3D(startPosition);
405         OTSLine3D path = new OTSLine3D(firstPoint, lastPoint);
406 
407         double t = timeStep.si;
408         Acceleration a = new Acceleration((2.0 * (path.getLength().si - startSpeed.si * t)) / (t * t), AccelerationUnit.SI);
409         Speed endSpeed = startSpeed.plus(a.multiplyBy(timeStep));
410         ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
411         if (endSpeed.lt(Speed.ZERO))
412         {
413             Duration brakingTime = startSpeed.divideBy(acceleration.multiplyBy(-1.0));
414             segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
415             segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
416         }
417         else
418         {
419             segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
420         }
421         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, fromLanes);
422     }
423 
424     /**
425      * Lane change status across operational plans.
426      * <p>
427      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
428      * <br>
429      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
430      * <p>
431      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 26, 2016 <br>
432      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
433      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
434      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
435      */
436     public static class LaneChange implements Serializable
437     {
438 
439         /** */
440         private static final long serialVersionUID = 20160811L;
441 
442         /** Lane change progress. */
443         private Duration laneChangeProgress;
444 
445         /** Total lane change duration. */
446         private Duration laneChangeDuration;
447 
448         /** Whether the GTU is changing lane. */
449         private LateralDirectionality laneChangeDirectionality = null;
450 
451         /**
452          * Return whether the GTU is changing lane.
453          * @return whether the GTU is changing lane
454          */
455         public final boolean isChangingLane()
456         {
457             return this.laneChangeDirectionality != null;
458         }
459 
460         /**
461          * Return whether the GTU is changing left.
462          * @return whether the GTU is changing left
463          */
464         public final boolean isChangingLeft()
465         {
466             return LateralDirectionality.LEFT.equals(this.laneChangeDirectionality);
467         }
468 
469         /**
470          * Return whether the GTU is changing right.
471          * @return whether the GTU is changing right
472          */
473         public final boolean isChangingRight()
474         {
475             return LateralDirectionality.RIGHT.equals(this.laneChangeDirectionality);
476         }
477 
478         /**
479          * Target lane of lane change.
480          * @return target lane of lane change
481          * @throws OperationalPlanException If no lane change is being performed.
482          */
483         public final RelativeLane getTargetLane() throws OperationalPlanException
484         {
485             Throw.when(!isChangingLane(), OperationalPlanException.class,
486                     "Target lane is requested, but no lane change is being performed.");
487             return isChangingLeft() ? RelativeLane.LEFT : RelativeLane.RIGHT;
488         }
489 
490         /**
491          * Sets the duration for a lane change. May be set during a lane change. Whenever the lane change progress exceeds the
492          * lane change duration, the lane change is finalized in the next time step.
493          * @param laneChangeDuration total lane change duration
494          */
495         public final void setLaneChangeDuration(final Duration laneChangeDuration)
496         {
497             this.laneChangeDuration = laneChangeDuration;
498         }
499 
500         /**
501          * Update the lane change and return the lateral fraction for the end of the coming time step. This method is for use by
502          * the operational plan builder. It adds the time step duration to the lane change progress. Lane changes are
503          * automatically initiated and finalized.
504          * @param timeStep coming time step duration
505          * @param laneChangeDirection direction of lane change
506          * @param gtu GTU
507          * @return lateral fraction for the end of the coming time step
508          */
509         // TODO remove GTU argument (only needed for instantaneous lane change hack)
510         final double updateAndGetFraction(final Duration timeStep, final LateralDirectionality laneChangeDirection,
511                 final LaneBasedGTU gtu)
512         {
513             if (!isChangingLane())
514             {
515                 // initiate lane change
516                 this.laneChangeProgress = Duration.ZERO;
517                 this.laneChangeDirectionality = laneChangeDirection;
518                 try
519                 {
520                     ((AbstractLaneBasedGTU) gtu).initLaneChange(laneChangeDirection);
521                 }
522                 catch (GTUException exception)
523                 {
524                     throw new RuntimeException("Error during lane change initialization.", exception);
525                 }
526             }
527             // add current time step
528             this.laneChangeProgress = this.laneChangeProgress.plus(timeStep);
529             // get lateral fraction at end of current time step
530             double fraction = this.laneChangeProgress.divideBy(this.laneChangeDuration).si;
531             if (fraction >= 1.0)
532             {
533                 // TODO this elsewhere based on path
534                 if (fraction >= 1.0)
535                 {
536                     try
537                     {
538                         ((AbstractLaneBasedGTU) gtu).finalizeLaneChange(laneChangeDirection);
539                     }
540                     catch (GTUException exception)
541                     {
542                         throw new RuntimeException("Error during lane change finalization.", exception);
543                     }
544                 }
545                 // limit by 1 and finalize lane change
546                 this.laneChangeDirectionality = null;
547                 return 1.0;
548             }
549             return fraction;
550         }
551 
552         /** {@inheritDoc} */
553         public final String toString()
554         {
555             return "LaneChange [" + this.laneChangeProgress + " of " + this.laneChangeDuration + " to "
556                     + this.laneChangeDirectionality + "]";
557         }
558 
559     }
560 
561     /**
562      * Build a plan with a path and a given start speed to try to come to a stop with a given deceleration. If the GTU can stop
563      * before completing the given path, a truncated path that ends where the GTU stops is used instead. There is no guarantee
564      * that the OperationalPlan will lead to a complete stop.
565      * @param gtu the GTU for debugging purposes
566      * @param lanes a list of connected Lanes to do the driving on
567      * @param firstLanePosition position on the first lane with the reference point of the GTU
568      * @param distance distance to drive for reaching the end speed
569      * @param startTime the current time or a time in the future when the plan should start
570      * @param startSpeed the speed at the start of the path
571      * @param deceleration the deceleration to use if endSpeed &lt; startSpeed, provided as a NEGATIVE number
572      * @return the operational plan to accomplish the given end speed
573      * @throws OperationalPlanException when construction of the operational path fails
574      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
575      *             first lane
576      */
577     public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
578             final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
579             final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
580     {
581         return buildMaximumAccelerationPlan(gtu, lanes, firstLanePosition, distance, startTime, startSpeed,
582                 new Speed(0.0, SpeedUnit.SI), new Acceleration(1.0, AccelerationUnit.SI), deceleration);
583     }
584 
585     /*-
586     public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Time startTime,
587         final Acceleration startAcceleration, final Speed maxSpeed, final Duration duration,
588         final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, final CurvatureType curvatureType,
589         final double laneChangeProgress, final DirectedPoint endPoint) throws OperationalPlanException
590     {
591         return buildSpatialPlan(gtu, startTime, gtu.getLocation(), gtu.getSpeed(), startAcceleration, maxSpeed, duration,
592             fromLanes, toLanes, curvatureType, laneChangeProgress, endPoint);
593     }
594     
595     public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Time startTime,
596         final DirectedPoint startPoint, final Speed startSpeed, final Acceleration startAcceleration, final Speed maxSpeed,
597         final Duration duration, final List<LaneDirection> fromLanes, List<LaneDirection> toLanes,
598         final CurvatureType curvatureType, final double laneChangeProgress, final DirectedPoint endPoint)
599         throws OperationalPlanException
600     {
601     
602         // check
603         checkLaneDirections(fromLanes, toLanes);
604     
605         // get start fractional position on link
606         final CrossSectionLink startLink = fromLanes.get(0).getLane().getParentLink();
607         Direction start;
608         Direction end;
609         if (fromLanes.get(0).getDirection() == GTUDirectionality.DIR_PLUS)
610         {
611             start = startLink.getStartNode().getDirection();
612             end = startLink.getEndNode().getDirection();
613         }
614         else
615         {
616             start = startLink.getEndNode().getDirection();
617             end = startLink.getStartNode().getDirection();
618         }
619         double fStart = startLink.getDesignLine().projectFractional(start, end, startPoint.x, startPoint.y);
620     
621         // get end fractional position on link, and end link
622         double fEnd = 0;
623         CrossSectionLink endLink = null;
624         for (int i = 0; i < toLanes.size(); i++)
625         {
626             CrossSectionLink l = toLanes.get(i).getLane().getParentLink();
627             if (toLanes.get(i).getDirection() == GTUDirectionality.DIR_PLUS)
628             {
629                 start = l.getStartNode().getDirection();
630                 end = l.getEndNode().getDirection();
631             }
632             else
633             {
634                 start = l.getEndNode().getDirection();
635                 end = l.getStartNode().getDirection();
636             }
637             fEnd = l.getDesignLine().projectFractional(start, end, endPoint.x, endPoint.y);
638             if (fEnd > 0 && fEnd < 1)
639             {
640                 endLink = l;
641                 break;
642             }
643         }
644         Throw.when(endLink == null, OperationalPlanException.class, "End point cannot be projected to to-lanes.");
645     
646         // build from-line and to-line
647         OTSLine3D from = null;
648         OTSLine3D to = null;
649         for (int i = 0; i < toLanes.size(); i++)
650         {
651             CrossSectionLink l = toLanes.get(i).getLane().getParentLink();
652             try
653             {
654                 if (l == startLink)
655                 {
656                     from = fromLanes.get(i).getLane().getCenterLine().extractFractional(fStart, 1);
657                     to = toLanes.get(i).getLane().getCenterLine().extractFractional(fStart, 1);
658                 }
659                 else if (l == endLink)
660                 {
661                     from =
662                         OTSLine3D.concatenate(from, fromLanes.get(i).getLane().getCenterLine().extractFractional(0, fEnd));
663                     to = OTSLine3D.concatenate(to, toLanes.get(i).getLane().getCenterLine().extractFractional(0, fEnd));
664                     break;
665                 }
666                 from = OTSLine3D.concatenate(from, fromLanes.get(i).getLane().getCenterLine());
667                 to = OTSLine3D.concatenate(to, toLanes.get(i).getLane().getCenterLine());
668             }
669             catch (OTSGeometryException exception)
670             {
671                 throw new RuntimeException("Bug in buildSpatialPlan method.");
672             }
673         }
674     
675         // interpolate path
676         List<OTSPoint3D> line = new ArrayList<>();
677         line.add(new OTSPoint3D(startPoint.x, startPoint.y, startPoint.z));
678         if (curvatureType.equals(CurvatureType.LINEAR))
679         {
680             int n = (int) Math.ceil(32 * (1.0 - laneChangeProgress));
681             for (int i = 1; i < n; i++)
682             {
683                 double fraction = 1.0 * i / n;
684                 double f0 = laneChangeProgress + (1.0 - laneChangeProgress) * fraction;
685                 double f1 = 1.0 - f0;
686                 DirectedPoint p1;
687                 DirectedPoint p2;
688                 try
689                 {
690                     p1 = from.getLocationFraction(fraction);
691                     p2 = to.getLocationFraction(fraction);
692                 }
693                 catch (OTSGeometryException exception)
694                 {
695                     throw new RuntimeException("Bug in buildSpatialPlan method.");
696                 }
697                 line.add(new OTSPoint3D(p1.x * f1 + p2.x * f0, p1.y * f1 + p2.y * f0, p1.z * f1 + p2.z * f0));
698             }
699         }
700         OTSLine3D path;
701         try
702         {
703             path = new OTSLine3D(line);
704         }
705         catch (OTSGeometryException exception)
706         {
707             throw new RuntimeException("Bug in buildSpatialPlan method.");
708         }
709     
710         // acceleration segments
711         List<Segment> segmentList = new ArrayList<>();
712         Acceleration b = startAcceleration.multiplyBy(-1.0);
713         if (startSpeed.lt(b.multiplyBy(duration)))
714         {
715             // will reach zero speed within duration
716             Duration d = startSpeed.divideBy(b);
717             segmentList.add(new AccelerationSegment(d, startAcceleration)); // decelerate to zero
718             segmentList.add(new SpeedSegment(duration.minus(d))); // stay at zero for the remainder of duration
719         }
720         else
721         {
722             segmentList.add(new AccelerationSegment(duration, startAcceleration));
723         }
724     
725         return new OperationalPlan(gtu, path, startTime, startSpeed, segmentList);
726     }
727     
728     public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Acceleration startAcceleration,
729         final Speed maxSpeed, final List<LaneDirection> fromLanes, List<LaneDirection> toLanes,
730         final CurvatureType curvatureType, final Duration duration) throws OperationalPlanException
731     {
732         return buildSpatialPlan(gtu, gtu.getLocation(), gtu.getSpeed(), startAcceleration, maxSpeed, fromLanes, toLanes,
733             curvatureType, duration);
734     }
735     
736     public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final DirectedPoint startPoint,
737         final Speed startSpeed, final Acceleration startAcceleration, final Speed maxSpeed,
738         final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, final CurvatureType curvatureType,
739         final Duration duration) throws OperationalPlanException
740     {
741         checkLaneDirections(fromLanes, toLanes);
742     
743         return null;
744     }
745     
746     private final static void checkLaneDirections(final List<LaneDirection> fromLanes, List<LaneDirection> toLanes)
747         throws OperationalPlanException
748     {
749         Throw.when(fromLanes == null || toLanes == null, OperationalPlanException.class, "Lane lists may not be null.");
750         Throw.when(fromLanes.isEmpty(), OperationalPlanException.class, "Lane lists may not be empty.");
751         Throw.when(fromLanes.size() != toLanes.size(), OperationalPlanException.class,
752             "Set of from lanes has different length than set of to lanes.");
753         for (int i = 0; i < fromLanes.size(); i++)
754         {
755             Throw.when(!fromLanes.get(i).getLane().getParentLink().equals(toLanes.get(i).getLane().getParentLink()),
756                 OperationalPlanException.class,
757                 "A lane in the from-lanes list is not on the same link as the lane at equal index in the to-lanes list.");
758         }
759     }
760     
761     /**
762      * Defines curvature.
763      * <p>
764      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
765      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
766      * <p>
767      * @version $Revision$, $LastChangedDate$, by $Author$, initial version May 27, 2016 <br>
768      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
769      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
770      */
771     /*-
772     public enum CurvatureType
773     {
774         /** Linear lateral movement. */
775     /*-
776     LINEAR
777     {
778         public double[] getFractions(final double strartFraction)
779         {
780             return new double[1];
781         }
782     },
783     
784     /** */
785     /*-
786     SCURVE
787     {
788         public double[] getFractions(final double strartFraction)
789         {
790             return new double[1];
791         }
792     };
793     
794     public abstract double[] getFractions(double startFraction);
795     }
796      */
797 
798 }