1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Iterator;
6 import java.util.LinkedHashSet;
7 import java.util.List;
8 import java.util.Set;
9
10 import org.djunits.value.vdouble.scalar.Length;
11 import org.opentrafficsim.base.geometry.OtsLine2d;
12 import org.opentrafficsim.base.parameters.ParameterTypeClass;
13 import org.opentrafficsim.base.parameters.ParameterTypeDuration;
14 import org.opentrafficsim.base.parameters.ParameterTypeLength;
15 import org.opentrafficsim.base.parameters.ParameterTypes;
16 import org.opentrafficsim.core.gtu.GtuException;
17 import org.opentrafficsim.core.network.LateralDirectionality;
18 import org.opentrafficsim.core.network.Link;
19 import org.opentrafficsim.core.network.NetworkException;
20 import org.opentrafficsim.core.network.Node;
21 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
22 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
23 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
24 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs;
25 import org.opentrafficsim.road.network.lane.CrossSectionElement;
26 import org.opentrafficsim.road.network.lane.CrossSectionLink;
27 import org.opentrafficsim.road.network.lane.Lane;
28 import org.opentrafficsim.road.network.lane.LanePosition;
29
30
31
32
33
34
35
36
37
38
39
40
41 public abstract class AbstractLaneBasedTacticalPlanner implements LaneBasedTacticalPlanner, Serializable
42 {
43
44
45 public static final ParameterTypeClass<LaneBasedTacticalPlanner> LANE_TACTICAL_PLANNER = new ParameterTypeClass<>(
46 "lane tactical planner", "Lane-based tactical planner class.", LaneBasedTacticalPlanner.class, Lmrs.class);
47
48
49 private static final long serialVersionUID = 20151125L;
50
51
52 protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
53
54
55 protected static final ParameterTypeDuration DT = ParameterTypes.DT;
56
57
58 private CarFollowingModel carFollowingModel;
59
60
61 private final LanePerception lanePerception;
62
63
64 private final LaneBasedGtu gtu;
65
66
67
68
69
70
71
72 public AbstractLaneBasedTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGtu gtu,
73 final LanePerception lanePerception)
74 {
75 setCarFollowingModel(carFollowingModel);
76 this.gtu = gtu;
77 this.lanePerception = lanePerception;
78 }
79
80 @Override
81 public final LaneBasedGtu getGtu()
82 {
83 return this.gtu;
84 }
85
86
87
88
89
90
91
92
93
94
95 public static LanePathInfo buildLanePathInfo(final LaneBasedGtu gtu, final Length maxHeadway)
96 throws GtuException, NetworkException
97 {
98 LanePosition dlp = gtu.getReferencePosition();
99 return buildLanePathInfo(gtu, maxHeadway, dlp.lane(), dlp.position());
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public static LanePathInfo buildLanePathInfo(final LaneBasedGtu gtu, final Length maxHeadway, final Lane startLane,
116 final Length position) throws GtuException, NetworkException
117 {
118 List<Lane> laneListForward = new ArrayList<>();
119 Lane lane = startLane;
120 Length startPosition = position;
121 Lane lastLane = lane;
122 laneListForward.add(lastLane);
123 Length distanceToEndOfLane;
124 OtsLine2d path;
125 distanceToEndOfLane = lane.getLength().minus(position);
126 path = lane.getCenterLine().extract(position, lane.getLength());
127
128 while (distanceToEndOfLane.lt(maxHeadway))
129 {
130 Set<Lane> lanes = lane.nextLanes(gtu.getType());
131 if (lanes.size() == 0)
132 {
133
134 return new LanePathInfo(path, laneListForward, startPosition);
135 }
136 else if (lanes.size() == 1)
137 {
138
139
140 Link link = gtu.getStrategicalPlanner().nextLink(lane.getLink(), gtu.getType());
141 lane = lanes.iterator().next();
142 if (link != null && !lane.getLink().equals(link))
143 {
144
145 return new LanePathInfo(path, laneListForward, startPosition);
146 }
147 }
148 else
149 {
150
151
152 Link link;
153 try
154 {
155 link = gtu.getStrategicalPlanner().nextLink(lane.getLink(), gtu.getType());
156 }
157 catch (NetworkException ne)
158 {
159
160 return new LanePathInfo(path, laneListForward, startPosition);
161 }
162 Link nextLink = link;
163 Lane newLane = null;
164 for (Lane nextLane : lanes)
165 {
166 if (nextLane.getLink().equals(nextLink))
167 {
168 newLane = nextLane;
169 break;
170 }
171 }
172 if (newLane == null)
173 {
174
175
176 return new LanePathInfo(path, laneListForward, startPosition);
177 }
178
179 lane = newLane;
180 }
181
182
183 path = concatenateNull(path, lane.getCenterLine());
184
185
186 laneListForward.add(lastLane);
187 distanceToEndOfLane = distanceToEndOfLane.plus(lastLane.getLength());
188
189 }
190 return new LanePathInfo(path, laneListForward, startPosition);
191 }
192
193
194
195
196
197
198
199 public static OtsLine2d concatenateNull(final OtsLine2d path, final OtsLine2d centerLine)
200 {
201 if (path == null)
202 {
203 return centerLine;
204 }
205 return OtsLine2d.concatenate(Lane.MARGIN.si, path, centerLine);
206 }
207
208
209
210
211
212
213
214
215
216
217
218 public static NextSplitInfo determineNextSplit(final LaneBasedGtu gtu, final Length maxHeadway)
219 throws GtuException, NetworkException
220 {
221 Node nextSplitNode = null;
222 Set<Lane> correctCurrentLanes = new LinkedHashSet<>();
223 LanePosition dlp = gtu.getReferencePosition();
224 Lane referenceLane = dlp.lane();
225 double refFrac = dlp.position().si / referenceLane.getLength().si;
226 Link lastLink = referenceLane.getLink();
227 Length position = dlp.position();
228 Length lengthForward = referenceLane.getLength().minus(position);
229 Node lastNode = referenceLane.getLink().getEndNode();
230
231
232 while (lengthForward.lt(maxHeadway) && nextSplitNode == null)
233 {
234
235 Set<Link> links = lastNode.getLinks().toSet();
236 Iterator<Link> linkIterator = links.iterator();
237 while (linkIterator.hasNext())
238 {
239 Link link = linkIterator.next();
240 if (!link.getType().isCompatible(gtu.getType()) || link.getEndNode().equals(lastNode))
241 {
242 linkIterator.remove();
243 }
244 }
245
246
247 boolean laneChange = false;
248 if (links.size() == 1)
249 {
250 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
251 {
252 if (cse instanceof Lane)
253 {
254 Lane lane = (Lane) cse;
255 if (lane.nextLanes(gtu.getType()).size() == 0)
256 {
257 laneChange = true;
258 }
259 }
260 }
261 }
262
263
264 if (laneChange)
265 {
266 nextSplitNode = lastNode;
267
268
269 for (CrossSectionElement cse : referenceLane.getLink().getCrossSectionElementList())
270 {
271 if (cse instanceof Lane)
272 {
273 Lane l = (Lane) cse;
274 if (noLaneDrop(gtu, maxHeadway, l, l.getLength().times(refFrac)))
275 {
276 correctCurrentLanes.add(l);
277 }
278 }
279 }
280 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
281 }
282
283
284 if (links.size() > 1)
285 {
286 nextSplitNode = lastNode;
287 Link nextLink = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
288
289
290 for (CrossSectionElement cse : referenceLane.getLink().getCrossSectionElementList())
291 {
292 if (cse instanceof Lane)
293 {
294 Lane l = (Lane) cse;
295 if (connectsToPath(gtu, maxHeadway, l, l.getLength().times(refFrac), nextLink))
296 {
297 correctCurrentLanes.add(l);
298 }
299 }
300 }
301 if (correctCurrentLanes.size() > 0)
302 {
303 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
304 }
305
306 Set<Lane> correctLanes = new LinkedHashSet<>();
307 Set<Lane> wrongLanes = new LinkedHashSet<>();
308 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
309 {
310 if (cse instanceof Lane)
311 {
312 Lane l = (Lane) cse;
313 if (connectsToPath(gtu, maxHeadway.plus(l.getLength()), l, Length.ZERO, nextLink))
314 {
315 correctLanes.add(l);
316 }
317 else
318 {
319 wrongLanes.add(l);
320 }
321 }
322 }
323 for (Lane wrongLane : wrongLanes)
324 {
325 for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtu.getType()))
326 {
327 if (correctLanes.contains(adjLane))
328 {
329 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.LEFT);
330 }
331 }
332 for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtu.getType()))
333 {
334 if (correctLanes.contains(adjLane))
335 {
336 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.RIGHT);
337 }
338 }
339 }
340 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, null);
341 }
342
343 if (links.size() == 0)
344 {
345 return new NextSplitInfo(null, correctCurrentLanes);
346 }
347
348
349 Link link = links.iterator().next();
350
351
352 lastNode = link.getEndNode();
353 lastLink = links.iterator().next();
354 lengthForward = lengthForward.plus(lastLink.getLength());
355 }
356
357 return new NextSplitInfo(null, correctCurrentLanes);
358 }
359
360
361
362
363
364
365
366
367
368
369
370
371
372 protected static boolean connectsToPath(final LaneBasedGtu gtu, final Length maxHeadway, final Lane startLane,
373 final Length startLanePosition, final Link linkAfterSplit) throws GtuException, NetworkException
374 {
375 List<Lane> lanes = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition).laneList();
376 for (Lane lane : lanes)
377 {
378 if (lane.getLink().equals(linkAfterSplit))
379 {
380 return true;
381 }
382 }
383 return false;
384 }
385
386
387
388
389
390
391
392
393
394
395
396
397 protected static boolean noLaneDrop(final LaneBasedGtu gtu, final Length maxHeadway, final Lane startLane,
398 final Length startLanePosition) throws GtuException, NetworkException
399 {
400 LanePathInfo lpi = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition);
401 if (lpi.path().getTypedLength().lt(maxHeadway))
402 {
403 return false;
404 }
405 return true;
406 }
407
408
409
410
411
412
413
414
415
416 protected static List<Link> buildLinkListForward(final LaneBasedGtu gtu, final Length maxHeadway)
417 throws GtuException, NetworkException
418 {
419 List<Link> linkList = new ArrayList<>();
420 LanePosition dlp = gtu.getReferencePosition();
421 Lane referenceLane = dlp.lane();
422 Link lastLink = referenceLane.getLink();
423 linkList.add(lastLink);
424 Length position = dlp.position();
425 Length lengthForward = referenceLane.getLength().minus(position);
426 Node lastNode = referenceLane.getLink().getEndNode();
427
428
429 while (lengthForward.lt(maxHeadway))
430 {
431
432 Set<Link> links = lastNode.getLinks().toSet();
433 Iterator<Link> linkIterator = links.iterator();
434 while (linkIterator.hasNext())
435 {
436 Link link = linkIterator.next();
437 if (link.equals(lastLink) || !link.getType().isCompatible(gtu.getType()))
438 {
439 linkIterator.remove();
440 }
441 }
442 if (links.size() == 0)
443 {
444 return linkList;
445 }
446
447 Link link;
448 if (links.size() > 1)
449 {
450 link = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
451 }
452 else
453 {
454 link = links.iterator().next();
455 }
456
457
458 lastNode = lastLink.getEndNode();
459 lastLink = link;
460 linkList.add(lastLink);
461 lengthForward = lengthForward.plus(lastLink.getLength());
462 }
463 return linkList;
464 }
465
466 @Override
467 public final CarFollowingModel getCarFollowingModel()
468 {
469 return this.carFollowingModel;
470 }
471
472
473
474
475
476 public final void setCarFollowingModel(final CarFollowingModel carFollowingModel)
477 {
478 this.carFollowingModel = carFollowingModel;
479 }
480
481 @Override
482 public final LanePerception getPerception()
483 {
484 return this.lanePerception;
485 }
486
487 }