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