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