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