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-2020 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 <i>at
48 * least</i> 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<Segment>; 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.instantiateSI(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.instantiateSI(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 if (nextLane == null)
160 {
161 // projection error on dad-end lane, add the length of the lane
162 f = 1.0;
163 length += lane.coveredDistance(f).si;
164 }
165 else
166 {
167 try
168 {
169 // compose gap line
170 OTSPoint3D last = lane.getDirection().isPlus() ? lane.getLane().getCenterLine().getLast()
171 : lane.getLane().getCenterLine().get(0);
172 OTSPoint3D first = nextLane.getDirection().isPlus() ? nextLane.getLane().getCenterLine().get(0)
173 : nextLane.getLane().getCenterLine().getLast();
174 if (!(last).equals(first))
175 {
176 OTSLine3D gap = new OTSLine3D(last, first);
177 double fGap = gap.projectFractional(null, null, point.x, point.y, FractionalFallback.NaN);
178 if (!Double.isNaN(fGap))
179 {
180 f = (lane.getLength().si + fGap * gap.getLengthSI()) / lane.getLength().si;
181 }
182 else
183 {
184 // gap, but no successful projection, use next lane in next loop, increase length so far
185 length += lane.getLength().si;
186 lane = nextLane;
187 prevDir = nextDir;
188 }
189 }
190 else
191 {
192 // no gap, use next lane in next loop, increase length so far
193 length += lane.getLength().si;
194 lane = nextLane;
195 prevDir = nextDir;
196 }
197 }
198 catch (OTSGeometryException exception)
199 {
200 // should not occur, we use get(0) and getLast()
201 throw new RuntimeException("Unexpected exception while assessing if a GTU is between lanes.",
202 exception);
203 }
204 }
205 }
206 else
207 {
208 // projection is ok on lane, increase length so far
209 length += lane.coveredDistance(f).si;
210 }
211 }
212 // add length on lane where the reference position was projected to (or to it's consecutive gap between lanes)
213 return Length.instantiateSI(length);
214 }
215
216 /** {@inheritDoc} */
217 @Override
218 public final String toString()
219 {
220 return "LaneBasedOperationalPlan [deviative=" + this.deviative + "]";
221 }
222 }