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