1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.djunits.value.vdouble.scalar.Length;
12 import org.opentrafficsim.base.OTSClassUtil;
13 import org.opentrafficsim.base.parameters.ParameterTypeClass;
14 import org.opentrafficsim.base.parameters.ParameterTypeDuration;
15 import org.opentrafficsim.base.parameters.ParameterTypeLength;
16 import org.opentrafficsim.base.parameters.ParameterTypes;
17 import org.opentrafficsim.base.parameters.constraint.ClassConstraint;
18 import org.opentrafficsim.core.geometry.OTSGeometryException;
19 import org.opentrafficsim.core.geometry.OTSLine3D;
20 import org.opentrafficsim.core.gtu.GTUDirectionality;
21 import org.opentrafficsim.core.gtu.GTUException;
22 import org.opentrafficsim.core.network.LateralDirectionality;
23 import org.opentrafficsim.core.network.Link;
24 import org.opentrafficsim.core.network.LinkDirection;
25 import org.opentrafficsim.core.network.NetworkException;
26 import org.opentrafficsim.core.network.Node;
27 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
28 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
29 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
30 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRS;
31 import org.opentrafficsim.road.network.lane.CrossSectionElement;
32 import org.opentrafficsim.road.network.lane.CrossSectionLink;
33 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
34 import org.opentrafficsim.road.network.lane.Lane;
35 import org.opentrafficsim.road.network.lane.LaneDirection;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public abstract class AbstractLaneBasedTacticalPlanner implements LaneBasedTacticalPlanner, Serializable
51 {
52
53
54 public static final ParameterTypeClass<LaneBasedTacticalPlanner> TACTICAL_PLANNER;
55
56 static
57 {
58 Class<LaneBasedTacticalPlanner> type = LaneBasedTacticalPlanner.class;
59 TACTICAL_PLANNER = new ParameterTypeClass<>("tactical planner", "Tactical planner class.",
60 OTSClassUtil.getTypedClass(type), LMRS.class, ClassConstraint.newInstance(type, LMRS.class));
61 }
62
63
64 private static final long serialVersionUID = 20151125L;
65
66
67 protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
68
69
70 protected static final ParameterTypeDuration DT = ParameterTypes.DT;
71
72
73 private CarFollowingModel carFollowingModel;
74
75
76 private final LanePerception lanePerception;
77
78
79 private final LaneBasedGTU gtu;
80
81
82
83
84
85
86
87 public AbstractLaneBasedTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu,
88 final LanePerception lanePerception)
89 {
90 setCarFollowingModel(carFollowingModel);
91 this.gtu = gtu;
92 this.lanePerception = lanePerception;
93 }
94
95
96 @Override
97 public final LaneBasedGTU getGtu()
98 {
99 return this.gtu;
100 }
101
102
103
104
105
106
107
108
109
110
111 public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway)
112 throws GTUException, NetworkException
113 {
114 DirectedLanePosition dlp = gtu.getReferencePosition();
115 return buildLanePathInfo(gtu, maxHeadway, dlp.getLane(), dlp.getPosition(), dlp.getGtuDirection());
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
133 final Length position, final GTUDirectionality startDirectionality) throws GTUException, NetworkException
134 {
135 List<LaneDirection> laneListForward = new ArrayList<>();
136 Lane lane = startLane;
137 GTUDirectionality lastGtuDir = startDirectionality;
138 Length startPosition = position;
139 Lane lastLane = lane;
140 laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
141 Length distanceToEndOfLane;
142 OTSLine3D path;
143 try
144 {
145 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
146 {
147 distanceToEndOfLane = lane.getLength().minus(position);
148 path = lane.getCenterLine().extract(position, lane.getLength());
149 }
150 else
151 {
152 distanceToEndOfLane = position;
153 path = lane.getCenterLine().extract(Length.ZERO, position).reverse();
154 }
155 }
156 catch (@SuppressWarnings("unused") OTSGeometryException exception)
157 {
158
159
160
161
162
163
164 path = null;
165 distanceToEndOfLane = Length.ZERO;
166 laneListForward.clear();
167 startPosition = Length.ZERO;
168 }
169
170 while (distanceToEndOfLane.lt(maxHeadway))
171 {
172 Map<Lane, GTUDirectionality> lanes = lastGtuDir.equals(GTUDirectionality.DIR_PLUS)
173 ? lane.nextLanes(gtu.getGTUType()) : lane.prevLanes(gtu.getGTUType());
174 if (lanes.size() == 0)
175 {
176
177 return new LanePathInfo(path, laneListForward, startPosition);
178 }
179 else if (lanes.size() == 1)
180 {
181
182
183 LinkDirection ld = null;
184 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), lastGtuDir, gtu.getGTUType());
185 lane = lanes.keySet().iterator().next();
186 if (ld != null && !lane.getParentLink().equals(ld.getLink()))
187 {
188
189 return new LanePathInfo(path, laneListForward, startPosition);
190 }
191 }
192 else
193 {
194
195
196 LinkDirection ld;
197 try
198 {
199 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(),
200 lastGtuDir, gtu.getGTUType());
201 }
202 catch (@SuppressWarnings("unused") NetworkException ne)
203 {
204
205
206 return new LanePathInfo(path, laneListForward, startPosition);
207 }
208 Link nextLink = ld.getLink();
209 Lane newLane = null;
210 for (Lane nextLane : lanes.keySet())
211 {
212 if (nextLane.getParentLink().equals(nextLink))
213 {
214 newLane = nextLane;
215 break;
216 }
217 }
218 if (newLane == null)
219 {
220
221
222 return new LanePathInfo(path, laneListForward, startPosition);
223 }
224
225 lane = newLane;
226 }
227
228
229 try
230 {
231 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
232 {
233 if (lastLane.getParentLink().getEndNode().equals(lane.getParentLink().getStartNode()))
234 {
235
236 path = concatenateNull(path, lane.getCenterLine());
237
238 lastGtuDir = GTUDirectionality.DIR_PLUS;
239 }
240 else
241 {
242
243 path = concatenateNull(path, lane.getCenterLine().reverse());
244
245 lastGtuDir = GTUDirectionality.DIR_MINUS;
246 }
247 }
248 else
249 {
250 if (lastLane.getParentLink().getStartNode().equals(lane.getParentLink().getStartNode()))
251 {
252
253 path = concatenateNull(path, lane.getCenterLine());
254
255 lastGtuDir = GTUDirectionality.DIR_PLUS;
256 }
257 else
258 {
259
260 path = concatenateNull(path, lane.getCenterLine().reverse());
261
262 lastGtuDir = GTUDirectionality.DIR_MINUS;
263 }
264 }
265 lastLane = lane;
266 }
267 catch (OTSGeometryException exception)
268 {
269 throw new GTUException(exception);
270 }
271
272 laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
273 distanceToEndOfLane = distanceToEndOfLane.plus(lastLane.getLength());
274
275 }
276 return new LanePathInfo(path, laneListForward, startPosition);
277 }
278
279
280
281
282
283
284
285
286 public static OTSLine3D concatenateNull(final OTSLine3D path, final OTSLine3D centerLine) throws OTSGeometryException
287 {
288 if (path == null)
289 {
290 return centerLine;
291 }
292 return OTSLine3D.concatenate(Lane.MARGIN.si, path, centerLine);
293 }
294
295
296
297
298
299
300
301
302
303
304
305 public static NextSplitInfo determineNextSplit(final LaneBasedGTU gtu, final Length maxHeadway)
306 throws GTUException, NetworkException
307 {
308 Node nextSplitNode = null;
309 Set<Lane> correctCurrentLanes = new HashSet<>();
310 DirectedLanePosition dlp = gtu.getReferencePosition();
311 Lane referenceLane = dlp.getLane();
312 double refFrac = dlp.getPosition().si / referenceLane.getLength().si;
313 Link lastLink = referenceLane.getParentLink();
314 GTUDirectionality lastGtuDir = dlp.getGtuDirection();
315 GTUDirectionality referenceLaneDirectionality = lastGtuDir;
316 Length lengthForward;
317 Length position = dlp.getPosition();
318 Node lastNode;
319 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
320 {
321 lengthForward = referenceLane.getLength().minus(position);
322 lastNode = referenceLane.getParentLink().getEndNode();
323 }
324 else
325 {
326 lengthForward = gtu.position(referenceLane, gtu.getReference());
327 lastNode = referenceLane.getParentLink().getStartNode();
328 }
329
330
331 while (lengthForward.lt(maxHeadway) && nextSplitNode == null)
332 {
333
334 Set<Link> links = lastNode.getLinks().toSet();
335 Iterator<Link> linkIterator = links.iterator();
336 while (linkIterator.hasNext())
337 {
338 Link link = linkIterator.next();
339 GTUDirectionality drivingDirection =
340 lastNode.equals(link.getStartNode()) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
341 if (!link.getDirectionality(gtu.getGTUType()).getDirectionalities().contains(drivingDirection))
342 {
343 linkIterator.remove();
344 }
345
346
347
348
349
350
351 }
352
353
354 boolean laneChange = false;
355 if (links.size() == 1)
356 {
357 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
358 {
359 if (cse instanceof Lane && lastGtuDir.isPlus())
360 {
361 Lane lane = (Lane) cse;
362 if (lane.nextLanes(gtu.getGTUType()).size() == 0)
363 {
364 laneChange = true;
365 }
366 }
367 if (cse instanceof Lane && lastGtuDir.isMinus())
368 {
369 Lane lane = (Lane) cse;
370 if (lane.prevLanes(gtu.getGTUType()).size() == 0)
371 {
372 laneChange = true;
373 }
374 }
375 }
376 }
377
378
379 if (laneChange)
380 {
381 nextSplitNode = lastNode;
382
383
384 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
385 {
386 if (cse instanceof Lane)
387 {
388 Lane l = (Lane) cse;
389
390 if (noLaneDrop(gtu, maxHeadway, l, l.getLength().multiplyBy(refFrac), referenceLaneDirectionality))
391 {
392 correctCurrentLanes.add(l);
393 }
394 }
395 }
396 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
397 }
398
399
400 if (links.size() > 1)
401 {
402 nextSplitNode = lastNode;
403 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType());
404
405
406 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
407 {
408 if (cse instanceof Lane)
409 {
410 Lane l = (Lane) cse;
411
412 if (connectsToPath(gtu, maxHeadway, l, l.getLength().multiplyBy(refFrac), referenceLaneDirectionality,
413 ld.getLink()))
414 {
415 correctCurrentLanes.add(l);
416 }
417 }
418 }
419 if (correctCurrentLanes.size() > 0)
420 {
421 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
422 }
423
424 Set<Lane> correctLanes = new HashSet<>();
425 Set<Lane> wrongLanes = new HashSet<>();
426 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
427 {
428 if (cse instanceof Lane)
429 {
430 Lane l = (Lane) cse;
431 if (connectsToPath(gtu, maxHeadway.plus(l.getLength()), l, Length.ZERO, lastGtuDir, ld.getLink()))
432 {
433 correctLanes.add(l);
434 }
435 else
436 {
437 wrongLanes.add(l);
438 }
439 }
440 }
441 for (Lane wrongLane : wrongLanes)
442 {
443 for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtu.getGTUType(),
444 referenceLaneDirectionality))
445 {
446 if (correctLanes.contains(adjLane))
447 {
448 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.LEFT);
449 }
450 }
451 for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtu.getGTUType(),
452 referenceLaneDirectionality))
453 {
454 if (correctLanes.contains(adjLane))
455 {
456 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.RIGHT);
457 }
458 }
459 }
460 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, null);
461 }
462
463 if (links.size() == 0)
464 {
465 return new NextSplitInfo(null, correctCurrentLanes);
466 }
467
468
469 Link link = links.iterator().next();
470
471
472 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
473 {
474 if (lastLink.getEndNode().equals(link.getStartNode()))
475 {
476
477 lastGtuDir = GTUDirectionality.DIR_PLUS;
478 lastNode = link.getEndNode();
479 }
480 else
481 {
482
483 lastGtuDir = GTUDirectionality.DIR_MINUS;
484 lastNode = link.getEndNode();
485 }
486 }
487 else
488 {
489 if (lastLink.getStartNode().equals(link.getStartNode()))
490 {
491
492 lastNode = link.getStartNode();
493 lastGtuDir = GTUDirectionality.DIR_PLUS;
494 }
495 else
496 {
497
498 lastNode = link.getStartNode();
499 lastGtuDir = GTUDirectionality.DIR_MINUS;
500 }
501 }
502 lastLink = links.iterator().next();
503 lengthForward = lengthForward.plus(lastLink.getLength());
504 }
505
506 return new NextSplitInfo(null, correctCurrentLanes);
507 }
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522 protected static boolean connectsToPath(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
523 final Length startLanePosition, final GTUDirectionality startDirectionality, final Link linkAfterSplit)
524 throws GTUException, NetworkException
525 {
526 List<LaneDirection> laneDirections =
527 buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality).getLaneDirectionList();
528 for (LaneDirection laneDirection : laneDirections)
529 {
530 if (laneDirection.getLane().getParentLink().equals(linkAfterSplit))
531 {
532 return true;
533 }
534 }
535 return false;
536 }
537
538
539
540
541
542
543
544
545
546
547
548
549
550 protected static boolean noLaneDrop(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
551 final Length startLanePosition, final GTUDirectionality startDirectionality) throws GTUException, NetworkException
552 {
553 LanePathInfo lpi = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality);
554 if (lpi.getPath().getLength().lt(maxHeadway))
555 {
556 return false;
557 }
558 return true;
559 }
560
561
562
563
564
565
566
567
568
569 protected static List<LinkDirection> buildLinkListForward(final LaneBasedGTU gtu, final Length maxHeadway)
570 throws GTUException, NetworkException
571 {
572 List<LinkDirection> linkList = new ArrayList<>();
573 DirectedLanePosition dlp = gtu.getReferencePosition();
574 Lane referenceLane = dlp.getLane();
575 Link lastLink = referenceLane.getParentLink();
576 GTUDirectionality lastGtuDir = dlp.getGtuDirection();
577 linkList.add(new LinkDirection(lastLink, lastGtuDir));
578 Length lengthForward;
579 Length position = dlp.getPosition();
580 Node lastNode;
581 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
582 {
583 lengthForward = referenceLane.getLength().minus(position);
584 lastNode = referenceLane.getParentLink().getEndNode();
585 }
586 else
587 {
588 lengthForward = gtu.position(referenceLane, gtu.getReference());
589 lastNode = referenceLane.getParentLink().getStartNode();
590 }
591
592
593 while (lengthForward.lt(maxHeadway))
594 {
595
596 Set<Link> links = lastNode.getLinks().toSet();
597 Iterator<Link> linkIterator = links.iterator();
598 while (linkIterator.hasNext())
599 {
600 Link link = linkIterator.next();
601 GTUDirectionality drivingDirection =
602 lastNode.equals(link.getStartNode()) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
603 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType(), drivingDirection))
604 {
605 linkIterator.remove();
606 }
607 }
608
609 if (links.size() == 0)
610 {
611 return linkList;
612 }
613
614 Link link;
615 if (links.size() > 1)
616 {
617 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(lastLink, lastGtuDir, gtu.getGTUType());
618 link = ld.getLink();
619 }
620 else
621 {
622 link = links.iterator().next();
623 }
624
625
626 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
627 {
628 if (lastLink.getEndNode().equals(link.getStartNode()))
629 {
630
631 lastGtuDir = GTUDirectionality.DIR_PLUS;
632 lastNode = lastLink.getEndNode();
633 }
634 else
635 {
636
637 lastGtuDir = GTUDirectionality.DIR_MINUS;
638 lastNode = lastLink.getEndNode();
639 }
640 }
641 else
642 {
643 if (lastLink.getStartNode().equals(link.getStartNode()))
644 {
645
646 lastNode = lastLink.getStartNode();
647 lastGtuDir = GTUDirectionality.DIR_PLUS;
648 }
649 else
650 {
651
652 lastNode = lastLink.getStartNode();
653 lastGtuDir = GTUDirectionality.DIR_MINUS;
654 }
655 }
656 lastLink = link;
657 linkList.add(new LinkDirection(lastLink, lastGtuDir));
658 lengthForward = lengthForward.plus(lastLink.getLength());
659 }
660 return linkList;
661 }
662
663
664 @Override
665 public final CarFollowingModel getCarFollowingModel()
666 {
667 return this.carFollowingModel;
668 }
669
670
671
672
673
674 public final void setCarFollowingModel(final CarFollowingModel carFollowingModel)
675 {
676 this.carFollowingModel = carFollowingModel;
677 }
678
679
680 @Override
681 public final LanePerception getPerception()
682 {
683 return this.lanePerception;
684 }
685
686 }