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 <i>at least</i>
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 }