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