1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.io.Serializable;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedHashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Set;
12 import java.util.SortedSet;
13 import java.util.TreeMap;
14 import java.util.TreeSet;
15
16 import org.djunits.value.vdouble.scalar.Length;
17 import org.djunits.value.vdouble.scalar.Time;
18 import org.djutils.exceptions.Throw;
19 import org.opentrafficsim.core.gtu.GTUDirectionality;
20 import org.opentrafficsim.core.gtu.GTUException;
21 import org.opentrafficsim.core.gtu.GTUType;
22 import org.opentrafficsim.core.gtu.RelativePosition;
23 import org.opentrafficsim.core.network.LateralDirectionality;
24 import org.opentrafficsim.core.network.Node;
25 import org.opentrafficsim.core.network.route.Route;
26 import org.opentrafficsim.core.perception.Historical;
27 import org.opentrafficsim.core.perception.HistoricalValue;
28 import org.opentrafficsim.core.perception.HistoryManager;
29 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
30 import org.opentrafficsim.road.gtu.lane.perception.RollingLaneStructureRecord.RecordLink;
31 import org.opentrafficsim.road.network.lane.CrossSectionLink;
32 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
33 import org.opentrafficsim.road.network.lane.Lane;
34 import org.opentrafficsim.road.network.lane.LaneDirection;
35 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public class RollingLaneStructure implements LaneStructure, Serializable
103 {
104
105 private static final long serialVersionUID = 20160400L;
106
107
108 private final Historical<RollingLaneStructureRecord> root;
109
110
111 private Length lookAhead;
112
113
114 private Route previousRoute;
115
116
117 private TreeMap<RelativeLane, RollingLaneStructureRecord> crossSectionRecords = new TreeMap<>();
118
119
120 private TreeMap<RelativeLane, RollingLaneStructureRecord> firstRecords = new TreeMap<>();
121
122
123 private Map<RelativeLane, Set<RollingLaneStructureRecord>> relativeLaneMap = new HashMap<>();
124
125
126 private Map<LaneStructureRecord, RelativeLane> relativeLanes = new HashMap<>();
127
128
129 private final Set<Lane> ignoreSet = new HashSet<>();
130
131
132 private final Set<RollingLaneStructureRecord> upstreamEdge = new LinkedHashSet<>();
133
134
135 private final Set<RollingLaneStructureRecord> downstreamEdge = new LinkedHashSet<>();
136
137
138 private final Length down;
139
140
141 private final Length up;
142
143
144 private final Length downSplit;
145
146
147 private final Length upMerge;
148
149
150 private final LaneBasedGTU containingGtu;
151
152
153 @SuppressWarnings("checkstyle:visibilitymodifier")
154 public AnimationAccess animationAccess = new AnimationAccess();
155
156
157
158
159
160
161
162
163
164
165 public RollingLaneStructure(final Length lookAhead, final Length down, final Length up, final Length downSplit,
166 final Length upMerge, final LaneBasedGTU gtu)
167 {
168 HistoryManager historyManager = gtu.getSimulator().getReplication().getHistoryManager(gtu.getSimulator());
169 this.root = new HistoricalValue<>(historyManager);
170 this.lookAhead = lookAhead;
171 this.down = down;
172 this.up = up;
173 this.downSplit = downSplit;
174 this.upMerge = upMerge;
175 this.containingGtu = gtu;
176
177
178
179
180 }
181
182
183
184
185
186
187
188
189 @Override
190 public final void update(final DirectedLanePosition pos, final Route route, final GTUType gtuType) throws GTUException
191 {
192
193
194
195
196
197
198
199
200
201
202
203 Lane lane = pos.getLane();
204 GTUDirectionality direction = pos.getGtuDirection();
205 Length position = pos.getPosition();
206 double fracPos = direction.isPlus() ? position.si / lane.getLength().si : 1.0 - position.si / lane.getLength().si;
207
208
209 if (this.previousRoute != route || this.root.get() == null)
210 {
211
212 this.previousRoute = route;
213
214
215 this.upstreamEdge.clear();
216 this.downstreamEdge.clear();
217 this.crossSectionRecords.clear();
218 this.relativeLanes.clear();
219
220
221 RollingLaneStructureRecord newRoot = constructRecord(lane, direction, null, RecordLink.CROSS, RelativeLane.CURRENT);
222 this.root.set(newRoot);
223 this.crossSectionRecords.put(RelativeLane.CURRENT, newRoot);
224 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
225 LateralDirectionality.RIGHT })
226 {
227 RollingLaneStructureRecord current = newRoot;
228 RelativeLane relativeLane = RelativeLane.CURRENT;
229 Set<Lane> adjacentLanes =
230 current.getLane().accessibleAdjacentLanesPhysical(latDirection, gtuType, current.getDirection());
231 while (!adjacentLanes.isEmpty())
232 {
233 Throw.when(adjacentLanes.size() > 1, RuntimeException.class,
234 "Multiple adjacent lanes encountered during construction of lane map.");
235 relativeLane = latDirection.isLeft() ? relativeLane.getLeft() : relativeLane.getRight();
236 Lane adjacentLane = adjacentLanes.iterator().next();
237 RollingLaneStructureRecord adjacentRecord =
238 constructRecord(adjacentLane, direction, current, RecordLink.CROSS, relativeLane);
239 this.crossSectionRecords.put(relativeLane, adjacentRecord);
240 if (latDirection.isLeft())
241 {
242 if (adjacentLane
243 .accessibleAdjacentLanesPhysical(LateralDirectionality.RIGHT, gtuType, current.getDirection())
244 .contains(current.getLane()))
245 {
246 adjacentRecord.setRight(current, gtuType);
247 }
248 current.setLeft(adjacentRecord, gtuType);
249 }
250 else
251 {
252 if (adjacentLane
253 .accessibleAdjacentLanesPhysical(LateralDirectionality.LEFT, gtuType, current.getDirection())
254 .contains(current.getLane()))
255 {
256 adjacentRecord.setLeft(current, gtuType);
257 }
258 current.setRight(adjacentRecord, gtuType);
259 }
260 current = adjacentRecord;
261 adjacentLanes =
262 current.getLane().accessibleAdjacentLanesPhysical(latDirection, gtuType, current.getDirection());
263 }
264 }
265 this.upstreamEdge.addAll(this.crossSectionRecords.values());
266 this.downstreamEdge.addAll(this.crossSectionRecords.values());
267 this.firstRecords.putAll(this.crossSectionRecords);
268
269
270 newRoot.updateStartDistance(fracPos, this);
271
272
273 expandUpstreamEdge(gtuType, fracPos);
274
275
276 deriveFirstRecords();
277 }
278 else
279 {
280
281
282 RollingLaneStructureRecord newRoot = this.root.get();
283 if (!lane.equals(newRoot.getLane()))
284 {
285
286
287 newRoot = null;
288 RelativeLane lateralMove = null;
289 double closest = Double.POSITIVE_INFINITY;
290 for (RelativeLane relativeLane : this.relativeLaneMap.keySet())
291 {
292 for (RollingLaneStructureRecord record : this.relativeLaneMap.get(relativeLane))
293 {
294 if (record.getLane().equals(lane) && record.getStartDistance().si < closest
295 && record.getStartDistance().si + record.getLength().si > 0.0)
296 {
297 newRoot = record;
298 lateralMove = relativeLane;
299
300 closest = record.getStartDistance().si;
301 }
302 }
303 }
304
305 this.root.set(newRoot);
306
307
308 updateStartDistanceSources();
309
310
311 if (!lateralMove.isCurrent())
312 {
313 RelativeLane delta =
314 new RelativeLane(lateralMove.getLateralDirectionality().flip(), lateralMove.getNumLanes());
315
316 TreeMap<RelativeLane, Set<RollingLaneStructureRecord>> newRelativeLaneMap = new TreeMap<>();
317 for (RelativeLane relativeLane : this.relativeLaneMap.keySet())
318 {
319 RelativeLane newRelativeLane = relativeLane.add(delta);
320 newRelativeLaneMap.put(newRelativeLane, this.relativeLaneMap.get(relativeLane));
321 }
322 this.relativeLaneMap = newRelativeLaneMap;
323
324 Map<LaneStructureRecord, RelativeLane> newRelativeLanes = new HashMap<>();
325 for (LaneStructureRecord record : this.relativeLanes.keySet())
326 {
327 newRelativeLanes.put(record, this.relativeLanes.get(record).add(delta));
328 }
329 this.relativeLanes = newRelativeLanes;
330 }
331
332 this.crossSectionRecords.clear();
333 this.crossSectionRecords.put(RelativeLane.CURRENT, newRoot);
334 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
335 LateralDirectionality.RIGHT })
336 {
337 RollingLaneStructureRecord record = newRoot;
338 RollingLaneStructureRecord next = newRoot;
339 RelativeLane delta = new RelativeLane(latDirection, 1);
340 RelativeLane relLane = RelativeLane.CURRENT;
341 while (next != null)
342 {
343 next = latDirection.isLeft() ? record.getLeft() : record.getRight();
344 if (next != null)
345 {
346 next.changeStartDistanceSource(record, RecordLink.CROSS);
347 relLane = relLane.add(delta);
348 this.crossSectionRecords.put(relLane, next);
349 record = next;
350 }
351 }
352 }
353
354 }
355 newRoot.updateStartDistance(fracPos, this);
356
357
358 retreatUpstreamEdge();
359
360
361 deriveFirstRecords();
362
363 }
364
365
366 expandDownstreamEdge(gtuType, fracPos, route);
367
368 }
369
370
371
372
373 private void deriveFirstRecords()
374 {
375 this.firstRecords.clear();
376 this.firstRecords.putAll(this.crossSectionRecords);
377 for (RelativeLane lane : this.relativeLaneMap.keySet())
378 {
379 getFirstRecord(lane);
380 }
381 }
382
383
384
385
386
387
388
389
390 private void updateStartDistanceSources()
391 {
392
393
394 Set<RollingLaneStructureRecord> set = new HashSet<>();
395 RollingLaneStructureRecord rootRecord = this.root.get();
396 set.add(rootRecord);
397 rootRecord.changeStartDistanceSource(null, RecordLink.CROSS);
398 RollingLaneStructureRecord prev = rootRecord;
399 RollingLaneStructureRecord next = prev.getLeft();
400 while (next != null)
401 {
402 set.add(next);
403 next.changeStartDistanceSource(prev, RecordLink.CROSS);
404 prev = next;
405 next = next.getLeft();
406 }
407 prev = rootRecord;
408 next = prev.getRight();
409 while (next != null)
410 {
411 set.add(next);
412 next.changeStartDistanceSource(prev, RecordLink.CROSS);
413 prev = next;
414 next = next.getRight();
415 }
416
417 while (!set.isEmpty())
418 {
419
420 Set<RollingLaneStructureRecord> newSet = new HashSet<>();
421 for (RollingLaneStructureRecord record : set)
422 {
423 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
424 LateralDirectionality.RIGHT })
425 {
426 prev = record;
427 next = latDirection.isLeft() ? record.getLeft() : record.getRight();
428 while (next != null && !set.contains(next))
429 {
430 next.changeStartDistanceSource(prev, RecordLink.LATERAL_END);
431 removeDownstream(next, latDirection.flip());
432 newSet.add(next);
433 prev = next;
434 next = latDirection.isLeft() ? next.getLeft() : next.getRight();
435 }
436 }
437 }
438 set.addAll(newSet);
439
440
441 newSet.clear();
442 for (RollingLaneStructureRecord record : set)
443 {
444 for (RollingLaneStructureRecord prevRecord : record.getPrev())
445 {
446 Iterator<RollingLaneStructureRecord> it = prevRecord.getNext().iterator();
447 while (it.hasNext())
448 {
449 RollingLaneStructureRecord otherDown = it.next();
450 if (!otherDown.getLane().getParentLink().equals(record.getLane().getParentLink()))
451 {
452
453 otherDown.changeStartDistanceSource(null, null);
454
455 removeDownstream(otherDown, LateralDirectionality.NONE);
456 removeRecord(otherDown);
457 it.remove();
458 }
459 }
460 LaneStructureRecord source = prevRecord.getStartDistanceSource();
461 if (source == null || (source != null && !source.equals(record)))
462 {
463 prevRecord.changeStartDistanceSource(record, RecordLink.UP);
464 newSet.add(prevRecord);
465 }
466 }
467 }
468
469
470 set = newSet;
471 }
472 }
473
474
475
476
477
478
479 private void removeDownstream(final RollingLaneStructureRecord record, final LateralDirectionality lat)
480 {
481 for (RollingLaneStructureRecord next : record.getNext())
482 {
483 RollingLaneStructureRecord adj = lat.isLeft() ? next.getLeft() : lat.isRight() ? next.getRight() : null;
484 if (adj == null)
485 {
486 next.changeStartDistanceSource(null, null);
487 removeDownstream(next, lat);
488 removeRecord(next);
489 }
490 }
491 record.clearNextList();
492 }
493
494
495
496
497
498 private void removeRecord(final RollingLaneStructureRecord record)
499 {
500 RelativeLane lane = this.relativeLanes.get(record);
501 if (lane != null)
502 {
503 this.relativeLanes.remove(record);
504 }
505 record.changeStartDistanceSource(null, null);
506
507 }
508
509
510
511
512
513
514
515 private void expandUpstreamEdge(final GTUType gtuType, final double fractionalPosition) throws GTUException
516 {
517 this.ignoreSet.clear();
518 for (LaneStructureRecord record : this.upstreamEdge)
519 {
520 this.ignoreSet.add(record.getLane());
521 }
522 Set<RollingLaneStructureRecord> nextSet = new LinkedHashSet<>();
523 boolean expand = true;
524 while (expand)
525 {
526 expand = false;
527
528
529 Iterator<RollingLaneStructureRecord> iterator = this.upstreamEdge.iterator();
530 Set<RollingLaneStructureRecord> modifiedEdge = new LinkedHashSet<>(this.upstreamEdge);
531 while (iterator.hasNext())
532 {
533 RollingLaneStructureRecord prev = iterator.next();
534 Map<Lane, GTUDirectionality> nexts = prev.getLane().upstreamLanes(prev.getDirection(), gtuType);
535 if (prev.getStartDistance().si < this.up.si)
536 {
537
538 prev.setCutOffStart(this.up.minus(prev.getStartDistance()));
539 for (Lane prevLane : nexts.keySet())
540 {
541 this.ignoreSet.add(prevLane);
542 }
543 }
544 else
545 {
546
547 prev.clearCutOffStart();
548 iterator.remove();
549 if (!nexts.isEmpty())
550 {
551 for (Lane prevLane : nexts.keySet())
552 {
553 RelativeLane relativeLane = this.relativeLanes.get(prev);
554 RollingLaneStructureRecord next =
555 constructRecord(prevLane, nexts.get(prevLane), prev, RecordLink.UP, relativeLane);
556 this.ignoreSet.add(prevLane);
557 next.updateStartDistance(fractionalPosition, this);
558 connectLaterally(next, gtuType, modifiedEdge);
559 next.addNext(prev);
560 prev.addPrev(next);
561 nextSet.add(next);
562 modifiedEdge.add(next);
563 }
564 }
565 }
566 }
567 this.upstreamEdge.addAll(nextSet);
568 expand |= !nextSet.isEmpty();
569 nextSet.clear();
570
571
572 Set<RollingLaneStructureRecord> lateralSet =
573 expandLateral(this.upstreamEdge, RecordLink.LATERAL_END, gtuType, fractionalPosition);
574 nextSet.addAll(lateralSet);
575
576
577 this.upstreamEdge.addAll(nextSet);
578 expand |= !nextSet.isEmpty();
579 nextSet.clear();
580 }
581 }
582
583
584
585
586
587
588
589
590
591
592 private Set<RollingLaneStructureRecord> expandLateral(final Set<RollingLaneStructureRecord> edge,
593 final RecordLink recordLink, final GTUType gtuType, final double fractionalPosition)
594 {
595 Set<RollingLaneStructureRecord> nextSet = new LinkedHashSet<>();
596 Set<Lane> laneSet = new HashSet<>();
597 for (LaneStructureRecord record : edge)
598 {
599 laneSet.add(record.getLane());
600 }
601 Iterator<RollingLaneStructureRecord> iterator = edge.iterator();
602 while (iterator.hasNext())
603 {
604 RollingLaneStructureRecord record = iterator.next();
605 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
606 LateralDirectionality.RIGHT })
607 {
608 RelativeLane relativeLane = this.relativeLanes.get(record);
609 RollingLaneStructureRecord prev = record;
610 Set<Lane> adjacentLanes =
611 prev.getLane().accessibleAdjacentLanesPhysical(latDirection, gtuType, prev.getDirection());
612 while (!adjacentLanes.isEmpty())
613 {
614 Throw.when(adjacentLanes.size() > 1, RuntimeException.class,
615 "Multiple adjacent lanes encountered during construction of lane map.");
616 relativeLane = latDirection.isLeft() ? relativeLane.getLeft() : relativeLane.getRight();
617 Lane nextLane = adjacentLanes.iterator().next();
618 if (!laneSet.contains(nextLane) && !this.ignoreSet.contains(nextLane))
619 {
620 RollingLaneStructureRecord next =
621 constructRecord(nextLane, record.getDirection(), prev, recordLink, relativeLane);
622 this.ignoreSet.add(nextLane);
623 next.updateStartDistance(fractionalPosition, this);
624 nextSet.add(next);
625 laneSet.add(nextLane);
626 if (latDirection.isLeft())
627 {
628 prev.setLeft(next, gtuType);
629 if (nextLane
630 .accessibleAdjacentLanesPhysical(LateralDirectionality.RIGHT, gtuType, prev.getDirection())
631 .contains(prev.getLane()))
632 {
633 next.setRight(prev, gtuType);
634 }
635 for (RollingLaneStructureRecord edgeRecord : edge)
636 {
637 if (!edgeRecord.equals(prev)
638 && edgeRecord.getLane().getParentLink().equals(next.getLane().getParentLink()))
639 {
640 for (Lane adjLane : edgeRecord.getLane().accessibleAdjacentLanesPhysical(
641 LateralDirectionality.RIGHT, gtuType, edgeRecord.getDirection()))
642 {
643 if (adjLane.equals(next.getLane()))
644 {
645 edgeRecord.setRight(next, gtuType);
646 next.setLeft(edgeRecord, gtuType);
647 }
648 }
649 }
650 }
651 }
652 else
653 {
654 prev.setRight(next, gtuType);
655 if (nextLane
656 .accessibleAdjacentLanesPhysical(LateralDirectionality.LEFT, gtuType, prev.getDirection())
657 .contains(prev.getLane()))
658 {
659 next.setLeft(prev, gtuType);
660 }
661 for (RollingLaneStructureRecord edgeRecord : edge)
662 {
663 if (!edgeRecord.equals(prev)
664 && edgeRecord.getLane().getParentLink().equals(next.getLane().getParentLink()))
665 {
666 for (Lane adjLane : edgeRecord.getLane().accessibleAdjacentLanesPhysical(
667 LateralDirectionality.LEFT, gtuType, edgeRecord.getDirection()))
668 {
669 if (adjLane.equals(next.getLane()))
670 {
671 edgeRecord.setLeft(next, gtuType);
672 next.setRight(edgeRecord, gtuType);
673 }
674 }
675 }
676 }
677 }
678 prev = next;
679 adjacentLanes =
680 prev.getLane().accessibleAdjacentLanesPhysical(latDirection, gtuType, prev.getDirection());
681 }
682 else
683 {
684 break;
685 }
686 }
687 }
688 }
689 return nextSet;
690 }
691
692
693
694
695
696 private void retreatUpstreamEdge() throws GTUException
697 {
698 boolean moved = true;
699 Set<RollingLaneStructureRecord> nexts = new LinkedHashSet<>();
700 while (moved)
701 {
702 moved = false;
703 nexts.clear();
704 Iterator<RollingLaneStructureRecord> iterator = this.upstreamEdge.iterator();
705 while (iterator.hasNext())
706 {
707 RollingLaneStructureRecord prev = iterator.next();
708
709 if (!nexts.contains(prev) && prev.getStartDistance().si + prev.getLane().getLength().si < this.up.si)
710 {
711 for (RollingLaneStructureRecord next : prev.getNext())
712 {
713 next.clearPrevList();
714 next.setCutOffStart(this.up.minus(next.getStartDistance()));
715 moved = true;
716 nexts.add(next);
717 RollingLaneStructureRecord lat = next.getLeft();
718 while (lat != null && lat.getPrev().isEmpty())
719 {
720 nexts.add(lat);
721 lat = lat.getLeft();
722 }
723 lat = next.getRight();
724 while (lat != null && lat.getPrev().isEmpty())
725 {
726 nexts.add(lat);
727 lat = lat.getRight();
728 }
729 }
730 prev.clearNextList();
731 removeRecord(prev);
732 iterator.remove();
733 }
734 else
735 {
736 Length cutOff = this.up.minus(prev.getStartDistance());
737 if (cutOff.si > 0)
738 {
739 prev.setCutOffStart(cutOff);
740 }
741 }
742 }
743 this.upstreamEdge.addAll(nexts);
744
745
746 for (RollingLaneStructureRecord record : nexts)
747 {
748 RollingLaneStructureRecord prev = record;
749 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
750 LateralDirectionality.RIGHT })
751 {
752 while (prev != null)
753 {
754 RollingLaneStructureRecord next = latDirection.isLeft() ? prev.getLeft() : prev.getRight();
755 if (next != null && !this.upstreamEdge.contains(next))
756 {
757 moved |= findUpstreamEdge(next);
758 }
759 prev = next;
760 }
761 }
762 }
763 }
764 }
765
766
767
768
769
770
771
772
773
774 private boolean findUpstreamEdge(final RollingLaneStructureRecord record) throws GTUException
775 {
776 Length cutOff = this.up.minus(record.getStartDistance());
777 boolean moved = false;
778 if (cutOff.gt0())
779 {
780 if (cutOff.lt(record.getLane().getLength()))
781 {
782 record.clearPrevList();
783 record.setCutOffStart(cutOff);
784 this.upstreamEdge.add(record);
785 moved = true;
786 }
787 else
788 {
789 if (this.relativeLanes.containsKey(record))
790 {
791
792 removeRecord(record);
793 }
794 for (RollingLaneStructureRecord next : record.getNext())
795 {
796 moved |= findUpstreamEdge(next);
797 }
798 }
799 }
800 return moved;
801 }
802
803
804
805
806
807
808
809
810 private void expandDownstreamEdge(final GTUType gtuType, final double fractionalPosition, final Route route)
811 throws GTUException
812 {
813 this.ignoreSet.clear();
814 for (LaneStructureRecord record : this.downstreamEdge)
815 {
816 this.ignoreSet.add(record.getLane());
817 }
818 Set<RollingLaneStructureRecord> nextSet = new LinkedHashSet<>();
819 Set<RollingLaneStructureRecord> splitSet = new LinkedHashSet<>();
820 boolean expand = true;
821 while (expand)
822 {
823 expand = false;
824
825
826 Iterator<RollingLaneStructureRecord> iterator = this.downstreamEdge.iterator();
827 Set<RollingLaneStructureRecord> modifiedEdge = new LinkedHashSet<>(this.downstreamEdge);
828 while (iterator.hasNext())
829 {
830 RollingLaneStructureRecord record = iterator.next();
831 Map<Lane, GTUDirectionality> nexts = record.getLane().downstreamLanes(record.getDirection(), gtuType);
832 if (record.getStartDistance().si + record.getLane().getLength().si > this.down.si)
833 {
834
835 record.setCutOffEnd(this.down.minus(record.getStartDistance()));
836 for (Lane nextLane : nexts.keySet())
837 {
838 this.ignoreSet.add(nextLane);
839 }
840 }
841 else
842 {
843
844
845
846 LaneDirection nextLaneDirection =
847 new LaneDirection(record.getLane(), record.getDirection()).getNextLaneDirection(this.containingGtu);
848
849 record.clearCutOffEnd();
850 iterator.remove();
851 for (Lane nextLane : nexts.keySet())
852 {
853 if (nextLaneDirection != null
854 && nextLane.getParentLink().equals(nextLaneDirection.getLane().getParentLink())
855 && !nextLane.equals(nextLaneDirection.getLane()))
856 {
857
858 continue;
859 }
860 RelativeLane relativeLane = this.relativeLanes.get(record);
861 GTUDirectionality dir = nexts.get(nextLane);
862 RollingLaneStructureRecord next = constructRecord(nextLane, dir, record, RecordLink.DOWN, relativeLane);
863 this.ignoreSet.add(nextLane);
864 next.updateStartDistance(fractionalPosition, this);
865 record.addNext(next);
866 next.addPrev(record);
867 connectLaterally(next, gtuType, modifiedEdge);
868
869 int from = route == null ? 0 : route.indexOf(next.getFromNode());
870 int to = route == null ? 1 : route.indexOf(next.getToNode());
871 if (to < 0 || to - from != 1)
872 {
873
874 splitSet.add(next);
875 }
876 else
877 {
878
879 nextSet.add(next);
880 }
881 modifiedEdge.add(next);
882
883 Set<RollingLaneStructureRecord> set = new HashSet<>();
884 set.add(next);
885 expandUpstreamMerge(set, gtuType, fractionalPosition, route);
886 }
887 }
888 }
889 this.downstreamEdge.addAll(nextSet);
890 expand |= !nextSet.isEmpty();
891 nextSet.clear();
892
893
894 expandDownstreamSplit(splitSet, gtuType, fractionalPosition, route);
895 splitSet.clear();
896
897
898 Set<RollingLaneStructureRecord> lateralSet =
899 expandLateral(this.downstreamEdge, RecordLink.LATERAL_END, gtuType, fractionalPosition);
900 nextSet.addAll(lateralSet);
901 expandUpstreamMerge(lateralSet, gtuType, fractionalPosition, route);
902
903
904 this.downstreamEdge.addAll(nextSet);
905 expand |= !nextSet.isEmpty();
906 nextSet.clear();
907 }
908 }
909
910
911
912
913
914
915
916
917
918 private void expandDownstreamSplit(final Set<RollingLaneStructureRecord> set, final GTUType gtuType,
919 final double fractionalPosition, final Route route) throws GTUException
920 {
921 Map<RollingLaneStructureRecord, Length> prevs = new LinkedHashMap<>();
922 Map<RollingLaneStructureRecord, Length> nexts = new LinkedHashMap<>();
923 for (RollingLaneStructureRecord record : set)
924 {
925 prevs.put(record, record.getStartDistance().plus(this.downSplit));
926 }
927 while (!prevs.isEmpty())
928 {
929 for (RollingLaneStructureRecord prev : prevs.keySet())
930 {
931 Map<Lane, GTUDirectionality> nextLanes = prev.getLane().downstreamLanes(prev.getDirection(), gtuType);
932 RelativeLane relativeLane = this.relativeLanes.get(prev);
933 for (Lane nextLane : nextLanes.keySet())
934 {
935 GTUDirectionality dir = nextLanes.get(nextLane);
936 Node fromNode =
937 dir.isPlus() ? nextLane.getParentLink().getStartNode() : nextLane.getParentLink().getEndNode();
938 Node toNode =
939 dir.isPlus() ? nextLane.getParentLink().getEndNode() : nextLane.getParentLink().getStartNode();
940 int from = route.indexOf(fromNode);
941 int to = route.indexOf(toNode);
942 if (from == -1 || to == -1 || to - from != 1)
943 {
944 RollingLaneStructureRecord next = constructRecord(nextLane, dir, prev, RecordLink.DOWN, relativeLane);
945 next.updateStartDistance(fractionalPosition, this);
946 next.addPrev(prev);
947 prev.addNext(next);
948 connectLaterally(next, gtuType, nexts.keySet());
949 Length downHere = prevs.get(prev);
950 if (next.getStartDistance().si > downHere.si)
951 {
952 next.setCutOffEnd(downHere.minus(next.getStartDistance()));
953 }
954 else
955 {
956 nexts.put(next, downHere);
957 }
958 }
959 }
960 }
961 prevs = nexts;
962 nexts = new LinkedHashMap<>();
963 }
964 }
965
966
967
968
969
970
971
972
973
974 private void expandUpstreamMerge(final Set<RollingLaneStructureRecord> set, final GTUType gtuType,
975 final double fractionalPosition, final Route route) throws GTUException
976 {
977 Map<RollingLaneStructureRecord, Length> prevs = new LinkedHashMap<>();
978 Map<RollingLaneStructureRecord, Length> nexts = new LinkedHashMap<>();
979 for (RollingLaneStructureRecord record : set)
980 {
981 prevs.put(record, record.getStartDistance().plus(this.upMerge));
982 }
983 while (!prevs.isEmpty())
984 {
985 for (RollingLaneStructureRecord prev : prevs.keySet())
986 {
987 Map<Lane, GTUDirectionality> nextLanes = prev.getLane().upstreamLanes(prev.getDirection(), gtuType);
988 boolean anyAdded = false;
989 for (Lane nextLane : nextLanes.keySet())
990 {
991 GTUDirectionality dir = nextLanes.get(nextLane);
992 Node fromNode =
993 dir.isPlus() ? nextLane.getParentLink().getStartNode() : nextLane.getParentLink().getEndNode();
994 Node toNode =
995 dir.isPlus() ? nextLane.getParentLink().getEndNode() : nextLane.getParentLink().getStartNode();
996 int from = route == null ? 0 : route.indexOf(fromNode);
997 int to = route == null ? 1 : route.indexOf(toNode);
998
999
1000 if (from == -1 || to == -1 || to - from != 1)
1001 {
1002 anyAdded = true;
1003 RelativeLane relativeLane = this.relativeLanes.get(prev);
1004 RollingLaneStructureRecord next =
1005 constructRecord(nextLane, nextLanes.get(nextLane), prev, RecordLink.UP, relativeLane);
1006 next.updateStartDistance(fractionalPosition, this);
1007 next.addNext(prev);
1008 prev.addPrev(next);
1009 connectLaterally(next, gtuType, nexts.keySet());
1010 Length upHere = prevs.get(prev);
1011 if (next.getStartDistance().si < upHere.si)
1012 {
1013 next.setCutOffStart(upHere.minus(next.getStartDistance()));
1014 this.upstreamEdge.add(next);
1015 }
1016 else
1017 {
1018 nexts.put(next, upHere);
1019 }
1020 }
1021 }
1022 if (!anyAdded && !set.contains(prev))
1023 {
1024 this.upstreamEdge.add(prev);
1025 }
1026 }
1027 prevs = nexts;
1028 nexts = new LinkedHashMap<>();
1029 }
1030 }
1031
1032
1033
1034
1035
1036
1037
1038 private void connectLaterally(final RollingLaneStructureRecord record, final GTUType gtuType,
1039 final Set<RollingLaneStructureRecord> nextSet)
1040 {
1041 for (RollingLaneStructureRecord other : nextSet)
1042 {
1043 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
1044 LateralDirectionality.RIGHT })
1045 {
1046 if ((latDirection.isLeft() ? other.getLeft() : other.getRight()) == null)
1047 {
1048 for (Lane otherLane : other.getLane().accessibleAdjacentLanesPhysical(latDirection, gtuType,
1049 other.getDirection()))
1050 {
1051 if (otherLane.equals(record.getLane()))
1052 {
1053 if (latDirection.isLeft())
1054 {
1055 other.setLeft(record, gtuType);
1056 record.setRight(other, gtuType);
1057 }
1058 else
1059 {
1060 other.setRight(record, gtuType);
1061 record.setLeft(other, gtuType);
1062 }
1063 }
1064 }
1065 }
1066 }
1067 }
1068 }
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079 private RollingLaneStructureRecord constructRecord(final Lane lane, final GTUDirectionality direction,
1080 final RollingLaneStructureRecord startDistanceSource, final RecordLink recordLink, final RelativeLane relativeLane)
1081 {
1082 RollingLaneStructureRecord record = new RollingLaneStructureRecord(lane, direction, startDistanceSource, recordLink);
1083 if (!this.relativeLaneMap.containsKey(relativeLane))
1084 {
1085 this.relativeLaneMap.put(relativeLane, new LinkedHashSet<>());
1086 }
1087 this.relativeLaneMap.get(relativeLane).add(record);
1088 this.relativeLanes.put(record, relativeLane);
1089 return record;
1090 }
1091
1092
1093 @Override
1094 public final LaneStructureRecord getRootRecord()
1095 {
1096 return this.root.get();
1097 }
1098
1099
1100
1101
1102
1103 public final LaneStructureRecord getRootRecord(final Time time)
1104 {
1105 return this.root.get(time);
1106 }
1107
1108
1109 @Override
1110 public final SortedSet<RelativeLane> getExtendedCrossSection()
1111 {
1112 return this.firstRecords.navigableKeySet();
1113 }
1114
1115
1116
1117
1118
1119
1120
1121 @Override
1122 public final RollingLaneStructureRecord getFirstRecord(final RelativeLane lane)
1123 {
1124 if (this.firstRecords.containsKey(lane))
1125 {
1126 return this.firstRecords.get(lane);
1127 }
1128
1129 RelativeLane rel = RelativeLane.CURRENT;
1130 int dMin = Integer.MAX_VALUE;
1131 for (RelativeLane relLane : this.crossSectionRecords.keySet())
1132 {
1133 if (relLane.getLateralDirectionality().equals(lane.getLateralDirectionality()))
1134 {
1135 int d = lane.getNumLanes() - relLane.getNumLanes();
1136 if (d < dMin)
1137 {
1138 rel = relLane;
1139 d = dMin;
1140 }
1141 }
1142 }
1143 RollingLaneStructureRecord record = this.crossSectionRecords.get(rel);
1144
1145 while (rel.getNumLanes() < lane.getNumLanes())
1146 {
1147 RollingLaneStructureRecord adj = lane.getLateralDirectionality().isLeft() ? record.getLeft() : record.getRight();
1148 if (adj != null)
1149 {
1150 rel = lane.getLateralDirectionality().isLeft() ? rel.getLeft() : rel.getRight();
1151 record = adj;
1152 }
1153 else if (!record.getNext().isEmpty())
1154 {
1155 LaneDirection laneDir =
1156 new LaneDirection(record.getLane(), record.getDirection()).getNextLaneDirection(this.containingGtu);
1157 if (laneDir == null)
1158 {
1159 record = null;
1160 break;
1161 }
1162 RollingLaneStructureRecord chosenNext = null;
1163 for (RollingLaneStructureRecord next : record.getNext())
1164 {
1165 if (next.getLane().equals(laneDir.getLane()))
1166 {
1167 chosenNext = next;
1168 break;
1169 }
1170 }
1171 Throw.when(chosenNext == null, RuntimeException.class,
1172 "Unexpected exception while deriving first record not on the cross-section.");
1173 record = chosenNext;
1174 }
1175 else
1176 {
1177
1178 record = null;
1179 break;
1180 }
1181 }
1182 if (record != null)
1183 {
1184
1185 while (record.getPrev().size() == 1 && record.getStartDistance().gt0())
1186 {
1187 record = record.getPrev().get(0);
1188 }
1189 this.firstRecords.put(lane, record);
1190 }
1191 return record;
1192 }
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204 @Override
1205 public final <T extends LaneBasedObject> Map<RelativeLane, SortedSet<Entry<T>>> getDownstreamObjects(final Class<T> clazz,
1206 final LaneBasedGTU gtu, final RelativePosition.TYPE pos) throws GTUException
1207 {
1208 Map<RelativeLane, SortedSet<Entry<T>>> out = new HashMap<>();
1209 for (RelativeLane relativeLane : this.relativeLaneMap.keySet())
1210 {
1211 out.put(relativeLane, getDownstreamObjects(relativeLane, clazz, gtu, pos));
1212 }
1213 return out;
1214 }
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227 @Override
1228 @SuppressWarnings("unchecked")
1229 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjects(final RelativeLane lane,
1230 final Class<T> clazz, final LaneBasedGTU gtu, final RelativePosition.TYPE pos) throws GTUException
1231 {
1232 LaneStructureRecord record = getFirstRecord(lane);
1233 SortedSet<Entry<T>> set = new TreeSet<>();
1234 if (record != null)
1235 {
1236 double ds = gtu.getRelativePositions().get(pos).getDx().si - gtu.getReference().getDx().si;
1237 if (record.isDownstreamBranch())
1238 {
1239
1240 Length minimumPosition;
1241 Length maximumPosition;
1242 if (record.getDirection().isPlus())
1243 {
1244 minimumPosition = Length.createSI(ds - record.getStartDistance().si);
1245 maximumPosition = Length.createSI(record.getLane().getLength().si);
1246 }
1247 else
1248 {
1249 minimumPosition = Length.ZERO;
1250 maximumPosition = Length.createSI(record.getLane().getLength().si + record.getStartDistance().si - ds);
1251 }
1252
1253 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
1254 {
1255 if (clazz.isAssignableFrom(object.getClass())
1256 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
1257 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
1258 {
1259
1260 double distance = record.getDistanceToPosition(object.getLongitudinalPosition()).si - ds;
1261 if (distance <= this.lookAhead.si)
1262 {
1263 set.add(new Entry<>(Length.createSI(distance), (T) object));
1264 }
1265 }
1266 }
1267 }
1268 getDownstreamObjectsRecursive(set, record, clazz, ds);
1269 }
1270 return set;
1271 }
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285 @Override
1286 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjectsOnRoute(final RelativeLane lane,
1287 final Class<T> clazz, final LaneBasedGTU gtu, final RelativePosition.TYPE pos, final Route route)
1288 throws GTUException
1289 {
1290 SortedSet<Entry<T>> set = getDownstreamObjects(lane, clazz, gtu, pos);
1291 if (route != null)
1292 {
1293 Iterator<Entry<T>> iterator = set.iterator();
1294 while (iterator.hasNext())
1295 {
1296 Entry<T> entry = iterator.next();
1297 CrossSectionLink link = entry.getLaneBasedObject().getLane().getParentLink();
1298 if (!route.contains(link.getStartNode()) || !route.contains(link.getEndNode())
1299 || Math.abs(route.indexOf(link.getStartNode()) - route.indexOf(link.getEndNode())) != 1)
1300 {
1301 iterator.remove();
1302 }
1303 }
1304 }
1305 return set;
1306 }
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316 @SuppressWarnings("unchecked")
1317 private <T extends LaneBasedObject> void getDownstreamObjectsRecursive(final SortedSet<Entry<T>> set,
1318 final LaneStructureRecord record, final Class<T> clazz, final double ds)
1319 {
1320 if (record.getNext().isEmpty() || record.getNext().get(0).getStartDistance().gt(this.lookAhead))
1321 {
1322 return;
1323 }
1324 for (LaneStructureRecord next : record.getNext())
1325 {
1326 if (next.isDownstreamBranch())
1327 {
1328 List<LaneBasedObject> list = next.getLane().getLaneBasedObjects();
1329 int iStart, di;
1330 if (record.getDirection().isPlus())
1331 {
1332 iStart = 0;
1333 di = 1;
1334 }
1335 else
1336 {
1337 iStart = list.size() - 1;
1338 di = -1;
1339 }
1340 for (int i = iStart; i >= 0 & i < list.size(); i += di)
1341 {
1342 LaneBasedObject object = list.get(i);
1343 if (clazz.isAssignableFrom(object.getClass())
1344 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
1345 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
1346 {
1347
1348 double distance = next.getDistanceToPosition(object.getLongitudinalPosition()).si - ds;
1349 if (distance <= this.lookAhead.si)
1350 {
1351 set.add(new Entry<>(Length.createSI(distance), (T) object));
1352 }
1353 else
1354 {
1355 return;
1356 }
1357 }
1358 }
1359 }
1360 getDownstreamObjectsRecursive(set, next, clazz, ds);
1361 }
1362 }
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375 @Override
1376 public final <T extends LaneBasedObject> Map<RelativeLane, SortedSet<Entry<T>>> getDownstreamObjectsOnRoute(
1377 final Class<T> clazz, final LaneBasedGTU gtu, final RelativePosition.TYPE pos, final Route route)
1378 throws GTUException
1379 {
1380 Map<RelativeLane, SortedSet<Entry<T>>> out = new HashMap<>();
1381 for (RelativeLane relativeLane : this.relativeLaneMap.keySet())
1382 {
1383 out.put(relativeLane, getDownstreamObjectsOnRoute(relativeLane, clazz, gtu, pos, route));
1384 }
1385 return out;
1386 }
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399 @Override
1400 @SuppressWarnings("unchecked")
1401 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getUpstreamObjects(final RelativeLane lane,
1402 final Class<T> clazz, final LaneBasedGTU gtu, final RelativePosition.TYPE pos) throws GTUException
1403 {
1404 SortedSet<Entry<T>> set = new TreeSet<>();
1405 LaneStructureRecord record = this.getFirstRecord(lane);
1406 if (record.getStartDistance().gt0())
1407 {
1408 return set;
1409 }
1410 Length ds = gtu.getReference().getDx().minus(gtu.getRelativePositions().get(pos).getDx());
1411
1412 Length minimumPosition;
1413 Length maximumPosition;
1414 if (record.getDirection().isPlus())
1415 {
1416 minimumPosition = Length.ZERO;
1417 maximumPosition = record.getStartDistance().neg().minus(ds);
1418 }
1419 else
1420 {
1421 minimumPosition = record.getLane().getLength().plus(record.getStartDistance()).plus(ds);
1422 maximumPosition = record.getLane().getLength();
1423 }
1424 Length distance;
1425 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
1426 {
1427 if (clazz.isAssignableFrom(object.getClass())
1428 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
1429 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
1430 {
1431 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
1432
1433 set.add(new Entry<>(distance, (T) object));
1434 }
1435 }
1436 getUpstreamObjectsRecursive(set, record, clazz, ds);
1437 return set;
1438 }
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448 @SuppressWarnings("unchecked")
1449 private <T extends LaneBasedObject> void getUpstreamObjectsRecursive(final SortedSet<Entry<T>> set,
1450 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
1451 {
1452 for (LaneStructureRecord prev : record.getPrev())
1453 {
1454 Length distance;
1455 for (LaneBasedObject object : prev.getLane().getLaneBasedObjects())
1456 {
1457 if (clazz.isAssignableFrom(object.getClass())
1458 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
1459 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
1460 {
1461 distance = prev.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
1462
1463 set.add(new Entry<>(distance, (T) object));
1464 }
1465 }
1466 getUpstreamObjectsRecursive(set, prev, clazz, ds);
1467 }
1468 }
1469
1470
1471 @Override
1472 public final String toString()
1473 {
1474 return "LaneStructure [rootLSR=" + this.root + "]";
1475 }
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485 public class AnimationAccess
1486 {
1487
1488
1489
1490 @SuppressWarnings("synthetic-access")
1491 public TreeMap<RelativeLane, RollingLaneStructureRecord> getCrossSectionRecords()
1492 {
1493 return RollingLaneStructure.this.crossSectionRecords;
1494 }
1495
1496
1497
1498
1499 @SuppressWarnings("synthetic-access")
1500 public Set<RollingLaneStructureRecord> getUpstreamEdge()
1501 {
1502 return RollingLaneStructure.this.upstreamEdge;
1503 }
1504
1505
1506
1507
1508 @SuppressWarnings("synthetic-access")
1509 public Set<RollingLaneStructureRecord> getDownstreamEdge()
1510 {
1511 return RollingLaneStructure.this.downstreamEdge;
1512 }
1513 }
1514 }