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