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