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.djunits.value.vdouble.scalar.Time;
13 import org.opentrafficsim.core.geometry.OTSGeometryException;
14 import org.opentrafficsim.core.geometry.OTSLine3D;
15 import org.opentrafficsim.core.gtu.GTUDirectionality;
16 import org.opentrafficsim.core.gtu.GTUException;
17 import org.opentrafficsim.core.gtu.RelativePosition;
18 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
19 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
20 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
21 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
22 import org.opentrafficsim.core.network.Link;
23 import org.opentrafficsim.core.network.LinkDirection;
24 import org.opentrafficsim.core.network.NetworkException;
25 import org.opentrafficsim.core.network.OTSNode;
26 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
27 import org.opentrafficsim.road.gtu.lane.perception.CategorialLanePerception;
28 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
29 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultAlexander;
30 import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
31 import org.opentrafficsim.road.gtu.lane.perception.categories.IntersectionPerception;
32 import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
33 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneBasedOperationalPlan;
34 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
35 import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
36 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder.LaneChange;
37 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
38 import org.opentrafficsim.road.network.lane.CrossSectionElement;
39 import org.opentrafficsim.road.network.lane.CrossSectionLink;
40 import org.opentrafficsim.road.network.lane.Lane;
41 import org.opentrafficsim.road.network.lane.LaneDirection;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public abstract class AbstractLaneBasedTacticalPlanner implements LaneBasedTacticalPlanner, Serializable
57 {
58
59 private static final long serialVersionUID = 20151125L;
60
61
62 private CarFollowingModel carFollowingModel;
63
64
65 private final LanePerception lanePerception;
66
67
68 private final LaneBasedGTU gtu;
69
70
71
72
73
74
75 public AbstractLaneBasedTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu)
76 {
77 setCarFollowingModel(carFollowingModel);
78 this.gtu = gtu;
79 CategorialLanePerception perception = new CategorialLanePerception(gtu);
80 perception.addPerceptionCategory(new DefaultAlexander(perception));
81 perception.addPerceptionCategory(new InfrastructurePerception(perception));
82 perception.addPerceptionCategory(new NeighborsPerception(perception));
83 perception.addPerceptionCategory(new IntersectionPerception(perception));
84 this.lanePerception = perception;
85 }
86
87
88 @Override
89 public final LaneBasedGTU getGtu()
90 {
91 return this.gtu;
92 }
93
94
95
96
97
98
99
100
101
102
103
104 public static Lane getReferenceLane(final LaneBasedGTU gtu) throws GTUException
105 {
106 for (Lane lane : gtu.getLanes().keySet())
107 {
108 double fraction = gtu.fractionalPosition(lane, gtu.getReference());
109 if (fraction >= 0.0 && fraction <= 1.0)
110 {
111
112
113 return lane;
114 }
115 }
116
117
118 System.err.println(gtu + " does not have a reference lane with pos between 0 and length...");
119 if (gtu.getLanes().size() > 0)
120 {
121 return gtu.getLanes().keySet().iterator().next();
122 }
123
124 throw new GTUException("The reference point of GTU " + gtu + " is not on any of the lanes on which it is registered");
125 }
126
127
128
129
130
131
132
133
134
135
136 public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway) throws GTUException,
137 NetworkException
138 {
139 Lane startLane = getReferenceLane(gtu);
140 Length startPosition = gtu.position(startLane, gtu.getReference());
141 return buildLanePathInfo(gtu, startLane, startPosition, maxHeadway);
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155 public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Lane startLane, final Length startPosition,
156 final Length maxHeadway) throws GTUException, NetworkException
157 {
158 return buildLanePathInfo(gtu, maxHeadway, startLane, startLane.fraction(startPosition), gtu.getLanes()
159 .get(startLane));
160 }
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
177 final double startLaneFractionalPosition, final GTUDirectionality startDirectionality) throws GTUException,
178 NetworkException
179 {
180 List<LaneDirection> laneListForward = new ArrayList<>();
181 Lane lane = startLane;
182 GTUDirectionality lastGtuDir = startDirectionality;
183 Length position = lane.position(startLaneFractionalPosition);
184 Length startPosition = position;
185 Lane lastLane = lane;
186 laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
187 Length distanceToEndOfLane;
188 OTSLine3D path;
189 try
190 {
191 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
192 {
193 distanceToEndOfLane = lane.getLength().minus(position);
194 path = lane.getCenterLine().extract(position, lane.getLength());
195 }
196 else
197 {
198 distanceToEndOfLane = position;
199 path = lane.getCenterLine().extract(Length.ZERO, position).reverse();
200 }
201 }
202 catch (OTSGeometryException exception)
203 {
204 System.err.println(gtu + ": " + exception.getMessage());
205 System.err.println(lane + ", len=" + lane.getLength());
206 System.err.println(position);
207 throw new GTUException(exception);
208 }
209
210 while (distanceToEndOfLane.lt(maxHeadway))
211 {
212 Map<Lane, GTUDirectionality> lanes =
213 lastGtuDir.equals(GTUDirectionality.DIR_PLUS) ? lane.nextLanes(gtu.getGTUType()) : lane.prevLanes(gtu
214 .getGTUType());
215 if (lanes.size() == 0)
216 {
217
218 return new LanePathInfo(path, laneListForward, startPosition);
219 }
220 else if (lanes.size() == 1)
221 {
222
223
224 LinkDirection ld = null;
225 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), lastGtuDir, gtu.getGTUType());
226 lane = lanes.keySet().iterator().next();
227 if (ld != null && !lane.getParentLink().equals(ld.getLink()))
228 {
229
230 return new LanePathInfo(path, laneListForward, startPosition);
231 }
232 }
233 else
234 {
235
236
237 LinkDirection ld;
238 try
239 {
240 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(),
241 lastGtuDir, gtu.getGTUType());
242 }
243 catch (NetworkException ne)
244 {
245
246
247 return new LanePathInfo(path, laneListForward, startPosition);
248 }
249 Link nextLink = ld.getLink();
250 Lane newLane = null;
251 for (Lane nextLane : lanes.keySet())
252 {
253 if (nextLane.getParentLink().equals(nextLink))
254 {
255 newLane = nextLane;
256 break;
257 }
258 }
259 if (newLane == null)
260 {
261
262
263 return new LanePathInfo(path, laneListForward, startPosition);
264 }
265
266 lane = newLane;
267 }
268
269
270 try
271 {
272 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
273 {
274 if (lastLane.getParentLink().getEndNode().equals(lane.getParentLink().getStartNode()))
275 {
276
277 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
278 lastGtuDir = GTUDirectionality.DIR_PLUS;
279 }
280 else
281 {
282
283 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
284 lastGtuDir = GTUDirectionality.DIR_MINUS;
285 }
286 }
287 else
288 {
289 if (lastLane.getParentLink().getStartNode().equals(lane.getParentLink().getStartNode()))
290 {
291
292 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
293 lastGtuDir = GTUDirectionality.DIR_PLUS;
294 }
295 else
296 {
297
298 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
299 lastGtuDir = GTUDirectionality.DIR_MINUS;
300 }
301 }
302 lastLane = lane;
303 }
304 catch (OTSGeometryException exception)
305 {
306 throw new GTUException(exception);
307 }
308
309 laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
310 distanceToEndOfLane = distanceToEndOfLane.plus(lastLane.getLength());
311
312 }
313 return new LanePathInfo(path, laneListForward, startPosition);
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328 protected static List<Lane> buildLaneListForwardXXX(final LaneBasedGTU gtu, final Length maxHeadway,
329 final Lane startLane, final double startLaneFractionalPosition, final GTUDirectionality startDirectionality)
330 throws GTUException, NetworkException
331 {
332 List<Lane> laneListForward = new ArrayList<>();
333 Lane lane = startLane;
334 Lane lastLane = startLane;
335 GTUDirectionality lastGtuDir = startDirectionality;
336 laneListForward.add(lane);
337 Length position = lane.position(startLaneFractionalPosition);
338 Length lengthForward = lastGtuDir.equals(GTUDirectionality.DIR_PLUS) ? lane.getLength().minus(position) : position;
339
340 while (lengthForward.lt(maxHeadway))
341 {
342 Map<Lane, GTUDirectionality> lanes =
343 lastGtuDir.equals(GTUDirectionality.DIR_PLUS) ? lane.nextLanes(gtu.getGTUType()) : lane.prevLanes(gtu
344 .getGTUType());
345 if (lanes.size() == 0)
346 {
347
348 return laneListForward;
349 }
350 if (lanes.size() == 1)
351 {
352 lane = lanes.keySet().iterator().next();
353 }
354 else
355 {
356
357
358 LinkDirection ld =
359 gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), gtu.getLanes().get(lane),
360 gtu.getGTUType());
361 Link nextLink = ld.getLink();
362 for (Lane nextLane : lanes.keySet())
363 {
364 if (nextLane.getParentLink().equals(nextLink))
365 {
366 lane = nextLane;
367 break;
368 }
369 }
370 }
371 laneListForward.add(lane);
372 lengthForward = lengthForward.plus(lane.getLength());
373
374
375 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
376 {
377 if (lastLane.getParentLink().getEndNode().equals(lane.getParentLink().getStartNode()))
378 {
379
380 lastGtuDir = GTUDirectionality.DIR_PLUS;
381 }
382 else
383 {
384
385 lastGtuDir = GTUDirectionality.DIR_MINUS;
386 }
387 }
388 else
389 {
390 if (lastLane.getParentLink().getStartNode().equals(lane.getParentLink().getStartNode()))
391 {
392
393 lastGtuDir = GTUDirectionality.DIR_PLUS;
394 }
395 else
396 {
397
398 lastGtuDir = GTUDirectionality.DIR_MINUS;
399 }
400 }
401 lastLane = lane;
402 }
403 return laneListForward;
404 }
405
406
407
408
409
410
411
412
413
414
415
416 public static NextSplitInfo determineNextSplit(final LaneBasedGTU gtu, final Length maxHeadway) throws GTUException,
417 NetworkException
418 {
419 OTSNode nextSplitNode = null;
420 Set<Lane> correctCurrentLanes = new HashSet<>();
421 Lane referenceLane = getReferenceLane(gtu);
422 Link lastLink = referenceLane.getParentLink();
423 GTUDirectionality lastGtuDir = gtu.getLanes().get(referenceLane);
424 GTUDirectionality referenceLaneDirectionality = lastGtuDir;
425 Length lengthForward;
426 Length position = gtu.position(referenceLane, gtu.getReference());
427 OTSNode lastNode;
428 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
429 {
430 lengthForward = referenceLane.getLength().minus(position);
431 lastNode = referenceLane.getParentLink().getEndNode();
432 }
433 else
434 {
435 lengthForward = gtu.position(referenceLane, gtu.getReference());
436 lastNode = referenceLane.getParentLink().getStartNode();
437 }
438 double referenceLaneFractionalPosition = position.si / referenceLane.getLength().si;
439
440
441 while (lengthForward.lt(maxHeadway) && nextSplitNode == null)
442 {
443
444 Set<Link> links = lastNode.getLinks();
445 Iterator<Link> linkIterator = links.iterator();
446 while (linkIterator.hasNext())
447 {
448 Link link = linkIterator.next();
449 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType()))
450 {
451 linkIterator.remove();
452 }
453 }
454
455
456 boolean laneChange = false;
457 if (links.size() == 1)
458 {
459 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
460 {
461 if (cse instanceof Lane && lastGtuDir.isPlus())
462 {
463 Lane lane = (Lane) cse;
464 if (lane.nextLanes(gtu.getGTUType()).size() == 0)
465 {
466 laneChange = true;
467 }
468 }
469 if (cse instanceof Lane && lastGtuDir.isMinus())
470 {
471 Lane lane = (Lane) cse;
472 if (lane.prevLanes(gtu.getGTUType()).size() == 0)
473 {
474 laneChange = true;
475 }
476 }
477 }
478 }
479
480
481 if (laneChange)
482 {
483 nextSplitNode = lastNode;
484
485
486 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
487 {
488 if (cse instanceof Lane)
489 {
490 Lane l = (Lane) cse;
491 if (noLaneDrop(gtu, maxHeadway, l, referenceLaneFractionalPosition, referenceLaneDirectionality))
492 {
493 correctCurrentLanes.add(l);
494 }
495 }
496 }
497 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
498 }
499
500
501 if (links.size() > 1)
502 {
503 nextSplitNode = lastNode;
504 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType());
505
506
507 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
508 {
509 if (cse instanceof Lane)
510 {
511 Lane l = (Lane) cse;
512 if (connectsToPath(gtu, maxHeadway, l, referenceLaneFractionalPosition, referenceLaneDirectionality,
513 ld.getLink()))
514 {
515 correctCurrentLanes.add(l);
516 }
517 }
518 }
519 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
520 }
521
522 if (links.size() == 0)
523 {
524 return new NextSplitInfo(null, correctCurrentLanes);
525 }
526
527
528 Link link = links.iterator().next();
529
530
531 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
532 {
533 if (lastLink.getEndNode().equals(link.getStartNode()))
534 {
535
536 lastGtuDir = GTUDirectionality.DIR_PLUS;
537 lastNode = (OTSNode) lastLink.getEndNode();
538 }
539 else
540 {
541
542 lastGtuDir = GTUDirectionality.DIR_MINUS;
543 lastNode = (OTSNode) lastLink.getEndNode();
544 }
545 }
546 else
547 {
548 if (lastLink.getStartNode().equals(link.getStartNode()))
549 {
550
551 lastNode = (OTSNode) lastLink.getStartNode();
552 lastGtuDir = GTUDirectionality.DIR_PLUS;
553 }
554 else
555 {
556
557 lastNode = (OTSNode) lastLink.getStartNode();
558 lastGtuDir = GTUDirectionality.DIR_MINUS;
559 }
560 }
561 lastLink = links.iterator().next();
562 lengthForward = lengthForward.plus(lastLink.getLength());
563 }
564
565 return new NextSplitInfo(null, correctCurrentLanes);
566 }
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581 protected static boolean connectsToPath(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
582 final double startLaneFractionalPosition, final GTUDirectionality startDirectionality, final Link linkAfterSplit)
583 throws GTUException, NetworkException
584 {
585 List<LaneDirection> laneDirections =
586 buildLanePathInfo(gtu, maxHeadway, startLane, startLaneFractionalPosition, startDirectionality)
587 .getLaneDirectionList();
588 for (LaneDirection laneDirection : laneDirections)
589 {
590 if (laneDirection.getLane().getParentLink().equals(linkAfterSplit))
591 {
592 return true;
593 }
594 }
595 return false;
596 }
597
598
599
600
601
602
603
604
605
606
607
608
609
610 protected static boolean noLaneDrop(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
611 final double startLaneFractionalPosition, final GTUDirectionality startDirectionality) throws GTUException,
612 NetworkException
613 {
614 LanePathInfo lpi = buildLanePathInfo(gtu, maxHeadway, startLane, startLaneFractionalPosition, startDirectionality);
615 if (lpi.getPath().getLength().lt(maxHeadway))
616 {
617 return false;
618 }
619 return true;
620 }
621
622
623
624
625
626
627
628
629
630 protected static List<LinkDirection> buildLinkListForward(final LaneBasedGTU gtu, final Length maxHeadway)
631 throws GTUException, NetworkException
632 {
633 List<LinkDirection> linkList = new ArrayList<>();
634 Lane referenceLane = getReferenceLane(gtu);
635 Link lastLink = referenceLane.getParentLink();
636 GTUDirectionality lastGtuDir = gtu.getLanes().get(referenceLane);
637 linkList.add(new LinkDirection(lastLink, lastGtuDir));
638 Length lengthForward;
639 Length position = gtu.position(referenceLane, gtu.getReference());
640 OTSNode lastNode;
641 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
642 {
643 lengthForward = referenceLane.getLength().minus(position);
644 lastNode = referenceLane.getParentLink().getEndNode();
645 }
646 else
647 {
648 lengthForward = gtu.position(referenceLane, gtu.getReference());
649 lastNode = referenceLane.getParentLink().getStartNode();
650 }
651
652
653 while (lengthForward.lt(maxHeadway))
654 {
655
656 Set<Link> links = lastNode.getLinks();
657 Iterator<Link> linkIterator = links.iterator();
658 while (linkIterator.hasNext())
659 {
660 Link link = linkIterator.next();
661 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType()))
662 {
663 linkIterator.remove();
664 }
665 }
666
667 if (links.size() == 0)
668 {
669 return linkList;
670 }
671
672 Link link;
673 if (links.size() > 1)
674 {
675 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(lastLink, lastGtuDir, gtu.getGTUType());
676 link = ld.getLink();
677 }
678 else
679 {
680 link = links.iterator().next();
681 }
682
683
684 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
685 {
686 if (lastLink.getEndNode().equals(link.getStartNode()))
687 {
688
689 lastGtuDir = GTUDirectionality.DIR_PLUS;
690 lastNode = (OTSNode) lastLink.getEndNode();
691 }
692 else
693 {
694
695 lastGtuDir = GTUDirectionality.DIR_MINUS;
696 lastNode = (OTSNode) lastLink.getEndNode();
697 }
698 }
699 else
700 {
701 if (lastLink.getStartNode().equals(link.getStartNode()))
702 {
703
704 lastNode = (OTSNode) lastLink.getStartNode();
705 lastGtuDir = GTUDirectionality.DIR_PLUS;
706 }
707 else
708 {
709
710 lastNode = (OTSNode) lastLink.getStartNode();
711 lastGtuDir = GTUDirectionality.DIR_MINUS;
712 }
713 }
714 lastLink = link;
715 linkList.add(new LinkDirection(lastLink, lastGtuDir));
716 lengthForward = lengthForward.plus(lastLink.getLength());
717 }
718 return linkList;
719 }
720
721
722 @Override
723 public final CarFollowingModel getCarFollowingModel()
724 {
725 return this.carFollowingModel;
726 }
727
728
729
730
731
732 public final void setCarFollowingModel(final CarFollowingModel carFollowingModel)
733 {
734 this.carFollowingModel = carFollowingModel;
735 }
736
737
738 @Override
739 public final LanePerception getPerception()
740 {
741 return this.lanePerception;
742 }
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757 public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
758 final BehavioralCharacteristics bc, final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
759 throws ParameterException, GTUException, NetworkException, OperationalPlanException
760 {
761 Length forwardHeadway = bc.getParameter(ParameterTypes.LOOKAHEAD);
762 List<Lane> lanes = buildLanePathInfo(gtu, forwardHeadway).getLanes();
763 if (simplePlan.getLaneChangeDirection() == null)
764 {
765 Length firstLanePosition = gtu.position(getReferenceLane(gtu), RelativePosition.REFERENCE_POSITION);
766 try
767 {
768 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, lanes, firstLanePosition, startTime, gtu
769 .getSpeed(), simplePlan.getAcceleration(), bc.getParameter(ParameterTypes.DT));
770 }
771 catch (OTSGeometryException exception)
772 {
773 throw new OperationalPlanException(exception);
774 }
775 }
776
777 try
778 {
779 return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, lanes, simplePlan
780 .getLaneChangeDirection(), gtu.getLocation(), startTime, gtu.getSpeed(), simplePlan.getAcceleration(), bc
781 .getParameter(ParameterTypes.DT), laneChange);
782 }
783 catch (OTSGeometryException exception)
784 {
785 throw new OperationalPlanException(exception);
786 }
787 }
788 }