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