1 package org.opentrafficsim.road.gtu.lane.plan.operational;
2
3 import java.util.Iterator;
4 import java.util.List;
5 import java.util.Map;
6
7 import org.djunits.unit.LengthUnit;
8 import org.djunits.value.vdouble.scalar.Acceleration;
9 import org.djunits.value.vdouble.scalar.Duration;
10 import org.djunits.value.vdouble.scalar.Length;
11 import org.djunits.value.vdouble.scalar.Speed;
12 import org.djunits.value.vdouble.scalar.Time;
13 import org.djutils.draw.point.OrientedPoint2d;
14 import org.djutils.draw.point.Point2d;
15 import org.djutils.exceptions.Throw;
16 import org.djutils.logger.CategoryLogger;
17 import org.opentrafficsim.base.geometry.OtsLine2d;
18 import org.opentrafficsim.base.parameters.ParameterException;
19 import org.opentrafficsim.core.gtu.GtuException;
20 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
21 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
22 import org.opentrafficsim.core.gtu.plan.operational.Segment;
23 import org.opentrafficsim.core.gtu.plan.operational.Segments;
24 import org.opentrafficsim.core.network.LateralDirectionality;
25 import org.opentrafficsim.core.network.NetworkException;
26 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
27 import org.opentrafficsim.road.network.lane.Lane;
28 import org.opentrafficsim.road.network.lane.LanePosition;
29 import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
30 import org.opentrafficsim.road.network.lane.object.detector.SinkDetector;
31
32 import nl.tudelft.simulation.dsol.SimRuntimeException;
33 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public final class LaneOperationalPlanBuilder
48 {
49
50
51
52
53
54 private static final Length MINIMUM_CREDIBLE_PATH_LENGTH = new Length(0.001, LengthUnit.METER);
55
56
57 LaneOperationalPlanBuilder()
58 {
59
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public static LaneBasedOperationalPlan buildAccelerationPlan(final LaneBasedGtu gtu, final Time startTime,
77 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final boolean deviative)
78 throws OperationalPlanException
79 {
80 Segments segments = Segments.off(startSpeed, timeStep, acceleration);
81 Length distance = Length.ZERO;
82 for (Segment segment : segments.getSegments())
83 {
84 distance = distance.plus(segment.totalDistance());
85 }
86
87 if (startSpeed.si <= OperationalPlan.DRIFTING_SPEED_SI && acceleration.le(Acceleration.ZERO)
88 || distance.le(MINIMUM_CREDIBLE_PATH_LENGTH))
89 {
90 OrientedPoint2d point = gtu.getLocation();
91 Point2d p2 = new Point2d(point.x + Math.cos(point.getDirZ()), point.y + Math.sin(point.getDirZ()));
92 OtsLine2d path = new OtsLine2d(point, p2);
93 return new LaneBasedOperationalPlan(gtu, path, startTime, Segments.standStill(timeStep), deviative);
94 }
95
96 OtsLine2d path = createPathAlongCenterLine(gtu, distance);
97 return new LaneBasedOperationalPlan(gtu, path, startTime, segments, deviative);
98 }
99
100
101
102
103
104
105
106 public static OtsLine2d createPathAlongCenterLine(final LaneBasedGtu gtu, final Length distance)
107 {
108 OtsLine2d path = null;
109 try
110 {
111 LanePosition ref = gtu.getReferencePosition();
112 double f = ref.lane().fraction(ref.position());
113 if (f < 1.0)
114 {
115 if (f >= 0.0)
116 {
117 path = ref.lane().getCenterLine().extractFractional(f, 1.0);
118 }
119 else
120 {
121 path = ref.lane().getCenterLine().extractFractional(0.0, 1.0);
122 }
123 }
124 Lane prevFrom = null;
125 Lane from = ref.lane();
126 Length prevPos = null;
127 Length pos = ref.position();
128 int n = 1;
129 while (path == null || path.getLength() < distance.si + n * Lane.MARGIN.si)
130 {
131 n++;
132 prevFrom = from;
133 if (null == from)
134 {
135 CategoryLogger.always().warn("About to die: GTU {} has null from value", gtu.getId());
136 }
137 from = gtu.getNextLaneForRoute(from);
138
139
140
141
142
143 prevPos = pos;
144 pos = Length.ZERO;
145 if (from == null)
146 {
147
148 for (LaneDetector detector : prevFrom.getDetectors(prevPos, prevFrom.getLength(), gtu.getType()))
149 {
150 if (detector instanceof SinkDetector && ((SinkDetector) detector).willDestroy(gtu))
151 {
152
153 OrientedPoint2d end = path.getLocationExtendedSI(distance.si + n * Lane.MARGIN.si);
154 List<Point2d> points = path.getPointList();
155 points.add(end);
156 return new OtsLine2d(points);
157 }
158 }
159
160 for (LateralDirectionality lat : new LateralDirectionality[] {LateralDirectionality.LEFT,
161 LateralDirectionality.RIGHT})
162 {
163 Lane latLane = prevFrom.getAdjacentLane(lat, gtu.getType());
164 if (latLane != null && gtu.getNextLaneForRoute(latLane) != null)
165 {
166 gtu.changeLaneInstantaneously(lat);
167 CategoryLogger.always().warn("GTU {} on link {} is forced to change lane towards {}", gtu.getId(),
168 ref.lane().getLink().getId(), lat);
169 return createPathAlongCenterLine(gtu, distance);
170 }
171 }
172 CategoryLogger.always().error("GTU {} on link {} has nowhere to go and no sink detector either",
173 gtu.getId(), ref.lane().getLink().getId());
174 gtu.destroy();
175 return path;
176 }
177 if (path == null)
178 {
179 path = from.getCenterLine();
180 }
181 else
182 {
183 path = OtsLine2d.concatenate(Lane.MARGIN.si, path, from.getCenterLine());
184 }
185 }
186 }
187 catch (GtuException exception)
188 {
189 throw new RuntimeException("Error during creation of path.", exception);
190 }
191 return path;
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210 @SuppressWarnings("checkstyle:parameternumber")
211 public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGtu gtu,
212 final LateralDirectionality laneChangeDirectionality, final OrientedPoint2d startPosition, final Time startTime,
213 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange)
214 throws OperationalPlanException
215 {
216
217
218
219 LateralDirectionality direction = laneChange.isChangingLane() ? laneChange.getDirection() : laneChangeDirectionality;
220
221 Segments segments = Segments.off(startSpeed, timeStep, acceleration);
222 Length distance = Length.ZERO;
223 for (Segment segment : segments.getSegments())
224 {
225 distance = distance.plus(segment.totalDistance());
226 }
227
228 try
229 {
230
231 Map<Lane, Length> positions = gtu.positions(gtu.getReference());
232 LanePosition ref = gtu.getReferencePosition();
233 Iterator<Lane> iterator = ref.lane().accessibleAdjacentLanesPhysical(direction, gtu.getType()).iterator();
234 Lane adjLane = iterator.hasNext() ? iterator.next() : null;
235 LanePosition from = null;
236 if (laneChange.getDirection() == null || (adjLane != null && positions.containsKey(adjLane)))
237 {
238
239 from = ref;
240 }
241 else
242 {
243
244 for (Lane lane : positions.keySet())
245 {
246 if (lane.accessibleAdjacentLanesPhysical(direction, gtu.getType()).contains(ref.lane()))
247 {
248 from = new LanePosition(lane, positions.get(lane));
249 break;
250 }
251 }
252 }
253 Throw.when(from == null, RuntimeException.class, "From lane could not be determined during lane change.");
254
255
256 OtsLine2d path = laneChange.getPath(timeStep, gtu, from, startPosition, distance, direction);
257 LaneBasedOperationalPlan plan = new LaneBasedOperationalPlan(gtu, path, startTime, segments, true);
258 return plan;
259 }
260 catch (GtuException exception)
261 {
262 throw new RuntimeException("Error during creation of lane change plan.", exception);
263 }
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277 public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGtu gtu, final Time startTime,
278 final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
279 throws ParameterException, GtuException, NetworkException
280 {
281 Acceleration acc = gtu.getVehicleModel().boundAcceleration(simplePlan.getAcceleration(), gtu);
282
283 if (gtu.isInstantaneousLaneChange())
284 {
285 if (simplePlan.isLaneChange())
286 {
287 gtu.changeLaneInstantaneously(simplePlan.getLaneChangeDirection());
288 }
289 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
290 simplePlan.getDuration(), false);
291 }
292
293
294 if (!simplePlan.isLaneChange() && !laneChange.isChangingLane())
295 {
296 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
297 simplePlan.getDuration(), true);
298 }
299 if (gtu.getSpeed().si == 0.0 && acc.si <= 0.0)
300 {
301 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
302 simplePlan.getDuration(), false);
303 }
304 return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, simplePlan.getLaneChangeDirection(),
305 gtu.getLocation(), startTime, gtu.getSpeed(), acc, simplePlan.getDuration(), laneChange);
306 }
307
308
309
310
311
312
313
314
315
316
317
318 public static void scheduleLaneChangeFinalization(final LaneBasedGtu gtu, final Length distance,
319 final LateralDirectionality laneChangeDirection) throws SimRuntimeException
320 {
321 Time time = gtu.getOperationalPlan().timeAtDistance(distance);
322 if (Double.isNaN(time.si))
323 {
324
325 time = gtu.getOperationalPlan().getEndTime();
326 }
327 SimEventInterface<Duration> event = gtu.getSimulator().scheduleEventAbsTime(time, (short) 6, gtu, "finalizeLaneChange",
328 new Object[] {laneChangeDirection});
329 gtu.setFinalizeLaneChangeEvent(event);
330 }
331
332 }