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