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