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