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