1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedHashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Map.Entry;
12 import java.util.NavigableMap;
13 import java.util.Set;
14 import java.util.SortedMap;
15 import java.util.TreeMap;
16
17 import org.djunits.unit.LengthUnit;
18 import org.djunits.unit.TimeUnit;
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djunits.value.vdouble.scalar.Speed;
21 import org.djunits.value.vdouble.scalar.Time;
22 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
23 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
24 import org.opentrafficsim.core.geometry.OTSGeometryException;
25 import org.opentrafficsim.core.gtu.GTUDirectionality;
26 import org.opentrafficsim.core.gtu.GTUException;
27 import org.opentrafficsim.core.gtu.GTUType;
28 import org.opentrafficsim.core.gtu.RelativePosition;
29 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
30 import org.opentrafficsim.core.network.LateralDirectionality;
31 import org.opentrafficsim.core.network.Link;
32 import org.opentrafficsim.core.network.LongitudinalDirectionality;
33 import org.opentrafficsim.core.network.NetworkException;
34 import org.opentrafficsim.core.network.Node;
35 import org.opentrafficsim.core.network.OTSNetwork;
36 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
37 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
38 import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
39 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
40 import org.opentrafficsim.road.network.lane.object.sensor.AbstractSensor;
41 import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
42
43 import nl.tudelft.simulation.dsol.SimRuntimeException;
44 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
45 import nl.tudelft.simulation.event.EventType;
46 import nl.tudelft.simulation.immutablecollections.Immutable;
47 import nl.tudelft.simulation.immutablecollections.ImmutableArrayList;
48 import nl.tudelft.simulation.immutablecollections.ImmutableList;
49 import nl.tudelft.simulation.language.Throw;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class Lane extends CrossSectionElement implements Serializable
70 {
71
72 private static final long serialVersionUID = 20150826L;
73
74
75 private final LaneType laneType;
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 private final Map<GTUType, LongitudinalDirectionality> directionalityMap;
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 private Map<GTUType, Speed> speedLimitMap;
108
109
110
111
112
113
114 private final SortedMap<Double, List<SingleSensor>> sensors = new TreeMap<>();
115
116
117
118
119
120
121 private final SortedMap<Double, List<LaneBasedObject>> laneBasedObjects = new TreeMap<>();
122
123
124 private final List<LaneBasedGTU> gtuList = new ArrayList<LaneBasedGTU>();
125
126
127
128
129
130
131 private Map<GTUType, Set<Lane>> leftNeighbors = null;
132
133
134
135
136
137
138 private Map<GTUType, Set<Lane>> rightNeighbors = null;
139
140
141
142
143
144 private Map<GTUType, Map<Lane, GTUDirectionality>> nextLanes = null;
145
146
147
148
149
150 private Map<GTUType, Map<Lane, GTUDirectionality>> prevLanes = null;
151
152
153
154
155
156 private Map<GTUType, Map<GTUDirectionality, Map<Lane, GTUDirectionality>>> downLanes = null;
157
158
159
160
161
162 private Map<GTUType, Map<GTUDirectionality, Map<Lane, GTUDirectionality>>> upLanes = null;
163
164
165
166 private final OvertakingConditions overtakingConditions;
167
168
169
170
171
172 public static final EventType GTU_ADD_EVENT = new EventType("GTU.ADD");
173
174
175
176
177
178 public static final EventType GTU_REMOVE_EVENT = new EventType("GTU.REMOVE");
179
180
181
182
183
184 public static final EventType SENSOR_ADD_EVENT = new EventType("SENSOR.ADD");
185
186
187
188
189
190 public static final EventType SENSOR_REMOVE_EVENT = new EventType("SENSOR.REMOVE");
191
192
193
194
195
196 public static final EventType OBJECT_ADD_EVENT = new EventType("OBJECT.ADD");
197
198
199
200
201
202 public static final EventType OBJECT_REMOVE_EVENT = new EventType("OBJECT.REMOVE");
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 @SuppressWarnings("checkstyle:parameternumber")
223 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
224 final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
225 final Map<GTUType, LongitudinalDirectionality> directionalityMap, final Map<GTUType, Speed> speedLimitMap,
226 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
227 {
228 super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
229 this.laneType = laneType;
230 this.directionalityMap = directionalityMap;
231 checkDirectionality();
232 this.speedLimitMap = speedLimitMap;
233 this.overtakingConditions = overtakingConditions;
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253 @SuppressWarnings("checkstyle:parameternumber")
254 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
255 final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
256 final LongitudinalDirectionality directionality, final Speed speedLimit,
257 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
258 {
259 super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
260 this.laneType = laneType;
261 this.directionalityMap = new LinkedHashMap<>(1);
262 this.directionalityMap.put(GTUType.ALL, directionality);
263 checkDirectionality();
264 this.speedLimitMap = new LinkedHashMap<>();
265 this.speedLimitMap.put(GTUType.ALL, speedLimit);
266 this.overtakingConditions = overtakingConditions;
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 @SuppressWarnings("checkstyle:parameternumber")
285 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
286 final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
287 final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
288 throws OTSGeometryException, NetworkException
289 {
290 super(parentLink, id, lateralOffset, width);
291 this.laneType = laneType;
292 this.directionalityMap = directionalityMap;
293 checkDirectionality();
294 this.speedLimitMap = speedLimitMap;
295 this.overtakingConditions = overtakingConditions;
296 }
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312 @SuppressWarnings("checkstyle:parameternumber")
313 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
314 final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
315 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
316 {
317 super(parentLink, id, lateralOffset, width);
318 this.laneType = laneType;
319 this.directionalityMap = new LinkedHashMap<>(1);
320 this.directionalityMap.put(GTUType.ALL, directionality);
321 checkDirectionality();
322 this.speedLimitMap = new LinkedHashMap<>();
323 this.speedLimitMap.put(GTUType.ALL, speedLimit);
324 this.overtakingConditions = overtakingConditions;
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343 @SuppressWarnings("checkstyle:parameternumber")
344 public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
345 final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
346 final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
347 throws OTSGeometryException, NetworkException
348 {
349 super(parentLink, id, crossSectionSlices);
350 this.laneType = laneType;
351 this.directionalityMap = directionalityMap;
352 checkDirectionality();
353 this.speedLimitMap = speedLimitMap;
354 this.overtakingConditions = overtakingConditions;
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372 @SuppressWarnings("checkstyle:parameternumber")
373 public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
374 final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
375 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
376 {
377 super(parentLink, id, crossSectionSlices);
378 this.laneType = laneType;
379 this.directionalityMap = new LinkedHashMap<>(1);
380 this.directionalityMap.put(GTUType.ALL, directionality);
381 checkDirectionality();
382 this.speedLimitMap = new LinkedHashMap<>();
383 this.speedLimitMap.put(GTUType.ALL, speedLimit);
384 this.overtakingConditions = overtakingConditions;
385 }
386
387
388
389
390
391
392
393
394
395
396 protected Lane(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation,
397 final Lane cse) throws NetworkException
398 {
399 super(newParentLink, newSimulator, animation, cse);
400 this.laneType = cse.laneType;
401 this.directionalityMap = new HashMap<GTUType, LongitudinalDirectionality>(cse.directionalityMap);
402 this.speedLimitMap = new HashMap<GTUType, Speed>(cse.speedLimitMap);
403 this.overtakingConditions = cse.overtakingConditions;
404
405 if (animation)
406 {
407 OTSNetwork.cloneAnimation(cse, this, cse.getParentLink().getSimulator(), newSimulator);
408 }
409 }
410
411
412
413
414
415
416
417
418
419
420
421
422 private Set<Lane> neighbors(final LateralDirectionality direction, final GTUType gtuType)
423 {
424 if (this.leftNeighbors == null || this.rightNeighbors == null)
425 {
426 this.leftNeighbors = new LinkedHashMap<>(1);
427 this.rightNeighbors = new LinkedHashMap<>(1);
428 }
429
430 if (!this.leftNeighbors.containsKey(gtuType) || !this.rightNeighbors.containsKey(gtuType))
431 {
432 Set<Lane> leftSet = new LinkedHashSet<>(1);
433 Set<Lane> rightSet = new LinkedHashSet<>(1);
434 this.leftNeighbors.put(gtuType, leftSet);
435 this.rightNeighbors.put(gtuType, rightSet);
436 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
437 {
438 if (cse instanceof Lane && !cse.equals(this))
439 {
440 Lane lane = (Lane) cse;
441 if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.LEFT, gtuType))
442 {
443 leftSet.add(lane);
444 }
445 if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.RIGHT, gtuType))
446 {
447 rightSet.add(lane);
448 }
449 }
450 }
451 }
452
453 Set<Lane> lanes = new LinkedHashSet<>();
454 if (direction.equals(LateralDirectionality.LEFT))
455 {
456 lanes.addAll(this.leftNeighbors.get(gtuType));
457 }
458 else
459 {
460 lanes.addAll(this.rightNeighbors.get(gtuType));
461 }
462 return lanes;
463 }
464
465
466 static final Length ADJACENT_MARGIN = new Length(0.2, LengthUnit.METER);
467
468
469
470
471
472
473
474
475
476
477
478
479 private boolean laterallyAdjacentAndAccessible(final Lane lane, final LateralDirectionality direction,
480 final GTUType gtuType)
481 {
482 if (!lane.getLaneType().isCompatible(gtuType) || gtuType.equals(GTUType.ALL) || gtuType.equals(GTUType.NONE))
483 {
484
485 return false;
486 }
487
488 if (direction.equals(LateralDirectionality.LEFT))
489 {
490
491 if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
492 - (lane.getDesignLineOffsetAtBegin().getSI() - lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
493 .getSI()
494 && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
495 - (lane.getDesignLineOffsetAtEnd().getSI() - lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
496 .getSI())
497 {
498
499 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
500 {
501 if (cse instanceof Stripe)
502 {
503 Stripe stripe = (Stripe) cse;
504
505 if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
506 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
507 && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
508 - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
509 {
510 if (!stripe.isPermeable(gtuType, LateralDirectionality.LEFT))
511 {
512
513 return false;
514 }
515 }
516 }
517 }
518
519
520 return true;
521 }
522 }
523
524 else
525
526 {
527
528 if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
529 - (lane.getDesignLineOffsetAtBegin().getSI() + lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
530 .getSI()
531 && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
532 - (lane.getDesignLineOffsetAtEnd().getSI() + lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
533 .getSI())
534 {
535
536 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
537 {
538 if (cse instanceof Stripe)
539 {
540 Stripe stripe = (Stripe) cse;
541
542 if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
543 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
544 && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
545 - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
546 {
547 if (!stripe.isPermeable(gtuType, LateralDirectionality.RIGHT))
548 {
549
550 return false;
551 }
552 }
553 }
554 }
555
556
557 return true;
558 }
559 }
560
561
562 return false;
563 }
564
565
566
567
568
569
570 public final void addSensor(final SingleSensor sensor) throws NetworkException
571 {
572 double position = sensor.getLongitudinalPosition().si;
573 if (position < 0 || position > getLength().getSI())
574 {
575 throw new NetworkException("Illegal position for sensor " + position + " valid range is 0.." + getLength().getSI());
576 }
577 if (this.parentLink.getNetwork().containsObject(sensor.getFullId()))
578 {
579 throw new NetworkException("Network already contains an object with the name " + sensor.getFullId());
580 }
581 List<SingleSensor> sensorList = this.sensors.get(position);
582 if (null == sensorList)
583 {
584 sensorList = new ArrayList<SingleSensor>(1);
585 this.sensors.put(position, sensorList);
586 }
587 sensorList.add(sensor);
588 this.parentLink.getNetwork().addObject(sensor);
589 fireTimedEvent(Lane.SENSOR_ADD_EVENT, new Object[] { sensor.getId(), sensor },
590 sensor.getSimulator().getSimulatorTime());
591 }
592
593
594
595
596
597
598 public final void removeSensor(final SingleSensor sensor) throws NetworkException
599 {
600 fireTimedEvent(Lane.SENSOR_REMOVE_EVENT, new Object[] { sensor.getId(), sensor },
601 sensor.getSimulator().getSimulatorTime());
602 List<SingleSensor> sensorList = this.sensors.get(sensor.getLongitudinalPosition().si);
603 if (null == sensorList)
604 {
605 throw new NetworkException("No sensor at " + sensor.getLongitudinalPosition().si);
606 }
607 sensorList.remove(sensor);
608 if (sensorList.size() == 0)
609 {
610 this.sensors.remove(sensor.getLongitudinalPosition().si);
611 }
612 this.parentLink.getNetwork().removeObject(sensor);
613 }
614
615
616
617
618
619
620
621
622
623 public final List<SingleSensor> getSensors(final Length minimumPosition, final Length maximumPosition,
624 final GTUType gtuType)
625 {
626 List<SingleSensor> sensorList = new ArrayList<>(1);
627 for (List<SingleSensor> sl : this.sensors.values())
628 {
629 for (SingleSensor sensor : sl)
630 {
631 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL))
632 && sensor.getLongitudinalPosition().ge(minimumPosition)
633 && sensor.getLongitudinalPosition().le(maximumPosition))
634 {
635 sensorList.add(sensor);
636 }
637 }
638 }
639 return sensorList;
640 }
641
642
643
644
645
646
647
648 public final List<SingleSensor> getSensors(final GTUType gtuType)
649 {
650 List<SingleSensor> sensorList = new ArrayList<>(1);
651 for (List<SingleSensor> sl : this.sensors.values())
652 {
653 for (SingleSensor sensor : sl)
654 {
655 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
656 {
657 sensorList.add(sensor);
658 }
659 }
660 }
661 return sensorList;
662 }
663
664
665
666
667
668 public final List<SingleSensor> getSensors()
669 {
670 if (this.sensors == null)
671 {
672 return new ArrayList<>();
673 }
674 List<SingleSensor> sensorList = new ArrayList<>(1);
675 for (List<SingleSensor> sl : this.sensors.values())
676 {
677 for (SingleSensor sensor : sl)
678 {
679 sensorList.add(sensor);
680 }
681 }
682 return sensorList;
683 }
684
685
686
687
688
689
690
691 public final SortedMap<Double, List<SingleSensor>> getSensorMap(final GTUType gtuType)
692 {
693 SortedMap<Double, List<SingleSensor>> sensorMap = new TreeMap<>();
694 for (double d : this.sensors.keySet())
695 {
696 List<SingleSensor> sensorList = new ArrayList<>(1);
697 for (List<SingleSensor> sl : this.sensors.values())
698 {
699 for (SingleSensor sensor : sl)
700 {
701 if (sensor.getLongitudinalPosition().si == d && (sensor.getTriggeringGTUTypes().contains(gtuType)
702 || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
703 {
704 sensorList.add(sensor);
705 }
706 }
707 }
708 if (sensorList.size() > 0)
709 {
710 sensorMap.put(d, sensorList);
711 }
712 }
713
714
715
716
717
718
719
720
721
722 return sensorMap;
723 }
724
725
726
727
728
729
730
731
732
733 public final void scheduleSensorTriggers(final LaneBasedGTU gtu, final double referenceStartSI,
734 final double referenceMoveSI) throws NetworkException, SimRuntimeException
735 {
736
737 for (List<SingleSensor> sensorList : getSensorMap(gtu.getGTUType()).values())
738 {
739
740
741
742
743
744
745 for (SingleSensor sensor : sensorList)
746 {
747 for (RelativePosition relativePosition : gtu.getRelativePositions().values())
748 {
749
750
751 if (sensor.getPositionType().equals(relativePosition.getType())
752 && referenceStartSI + relativePosition.getDx().getSI() <= sensor.getLongitudinalPosition().si
753 && referenceStartSI + referenceMoveSI
754 + relativePosition.getDx().getSI() > sensor.getLongitudinalPosition().si)
755 {
756
757
758
759 double d = sensor.getLongitudinalPosition().si - referenceStartSI - relativePosition.getDx().getSI();
760 if (d < 0)
761 {
762 throw new NetworkException("scheduleTriggers for gtu: " + gtu + ", d<0 d=" + d);
763 }
764
765 OperationalPlan oPlan = gtu.getOperationalPlan();
766 Time triggerTime = oPlan.timeAtDistance(new Length(d, LengthUnit.METER));
767 if (triggerTime.gt(oPlan.getEndTime()))
768 {
769 System.err.println("Time=" + gtu.getSimulator().getSimulatorTime().getTime().getSI()
770 + " - Scheduling trigger at " + triggerTime.getSI() + "s. > " + oPlan.getEndTime().getSI()
771 + "s. (nextEvalTime) for sensor " + sensor + " , gtu " + gtu);
772 System.err.println(" v=" + gtu.getSpeed() + ", a=" + gtu.getAcceleration() + ", lane=" + toString()
773 + ", refStartSI=" + referenceStartSI + ", moveSI=" + referenceMoveSI);
774 triggerTime =
775 new Time(oPlan.getEndTime().getSI() - Math.ulp(oPlan.getEndTime().getSI()), TimeUnit.BASE);
776
777
778 }
779
780
781
782 SimEvent<OTSSimTimeDouble> event = new SimEvent<OTSSimTimeDouble>(new OTSSimTimeDouble(triggerTime),
783 this, sensor, "trigger", new Object[] { gtu });
784 gtu.getSimulator().scheduleEvent(event);
785 gtu.addTrigger(this, event);
786 }
787 }
788 }
789 }
790 }
791
792
793
794
795
796
797
798 public final void addLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
799 {
800 double position = laneBasedObject.getLongitudinalPosition().si;
801 if (position < 0 || position > getLength().getSI())
802 {
803 throw new NetworkException(
804 "Illegal position for laneBasedObject " + position + " valid range is 0.." + getLength().getSI());
805 }
806 if (this.parentLink.getNetwork().containsObject(laneBasedObject.getFullId()))
807 {
808 throw new NetworkException("Network already contains an object with the name " + laneBasedObject.getFullId());
809 }
810 List<LaneBasedObject> laneBasedObjectList = this.laneBasedObjects.get(position);
811 if (null == laneBasedObjectList)
812 {
813 laneBasedObjectList = new ArrayList<LaneBasedObject>(1);
814 this.laneBasedObjects.put(position, laneBasedObjectList);
815 }
816 laneBasedObjectList.add(laneBasedObject);
817 this.parentLink.getNetwork().addObject(laneBasedObject);
818 fireEvent(Lane.OBJECT_ADD_EVENT, new Object[] { laneBasedObject });
819 }
820
821
822
823
824
825
826 public final void removeLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
827 {
828 fireEvent(Lane.OBJECT_REMOVE_EVENT, new Object[] { laneBasedObject });
829 List<LaneBasedObject> laneBasedObjectList =
830 this.laneBasedObjects.get(laneBasedObject.getLongitudinalPosition().getSI());
831 if (null == laneBasedObjectList)
832 {
833 throw new NetworkException("No laneBasedObject at " + laneBasedObject.getLongitudinalPosition().si);
834 }
835 laneBasedObjectList.remove(laneBasedObject);
836 if (laneBasedObjectList.isEmpty())
837 {
838 this.laneBasedObjects.remove(laneBasedObject.getLongitudinalPosition().doubleValue());
839 }
840 this.parentLink.getNetwork().removeObject(laneBasedObject);
841 }
842
843
844
845
846
847
848
849
850 public final List<LaneBasedObject> getLaneBasedObjects(final Length minimumPosition, final Length maximumPosition)
851 {
852 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
853 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
854 {
855 for (LaneBasedObject lbo : lbol)
856 {
857 if (lbo.getLongitudinalPosition().ge(minimumPosition) && lbo.getLongitudinalPosition().le(maximumPosition))
858 {
859 laneBasedObjectList.add(lbo);
860 }
861 }
862 }
863 return laneBasedObjectList;
864 }
865
866
867
868
869
870 public final List<LaneBasedObject> getLaneBasedObjects()
871 {
872 if (this.laneBasedObjects == null)
873 {
874 return new ArrayList<>();
875 }
876 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
877 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
878 {
879 for (LaneBasedObject lbo : lbol)
880 {
881 laneBasedObjectList.add(lbo);
882 }
883 }
884 return laneBasedObjectList;
885 }
886
887
888
889
890
891 public final SortedMap<Double, List<LaneBasedObject>> getLaneBasedObjectMap()
892 {
893 SortedMap<Double, List<LaneBasedObject>> laneBasedObjectMap = new TreeMap<>();
894 for (double d : this.laneBasedObjects.keySet())
895 {
896 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
897 for (LaneBasedObject lbo : this.laneBasedObjects.get(d))
898 {
899 laneBasedObjectList.add(lbo);
900 }
901 laneBasedObjectMap.put(d, laneBasedObjectList);
902 }
903 return laneBasedObjectMap;
904 }
905
906
907
908
909
910
911 public final Length position(final double fraction)
912 {
913 if (this.length.getUnit().isBaseSIUnit())
914 {
915 return new Length(this.length.si * fraction, LengthUnit.SI);
916 }
917 return new Length(this.length.getInUnit() * fraction, this.length.getUnit());
918 }
919
920
921
922
923
924
925 public final double positionSI(final double fraction)
926 {
927 return this.length.si * fraction;
928 }
929
930
931
932
933
934
935 public final double fraction(final Length position)
936 {
937 return position.si / this.length.si;
938 }
939
940
941
942
943
944
945 public final double fractionSI(final double positionSI)
946 {
947 return positionSI / this.length.si;
948 }
949
950
951
952
953
954
955
956
957
958 public final int addGTU(final LaneBasedGTU gtu, final double fractionalPosition) throws GTUException
959 {
960 int index;
961
962 if (this.gtuList.size() == 0)
963 {
964 this.gtuList.add(gtu);
965 index = 0;
966 }
967 else
968 {
969
970 LaneBasedGTU lastGTU = this.gtuList.get(this.gtuList.size() - 1);
971 if (fractionalPosition > lastGTU.fractionalPosition(this, lastGTU.getFront()))
972 {
973 this.gtuList.add(gtu);
974 index = this.gtuList.size() - 1;
975 }
976 else
977 {
978
979 for (index = 0; index < this.gtuList.size(); index++)
980 {
981 LaneBasedGTU otherGTU = this.gtuList.get(index);
982 if (gtu == otherGTU)
983 {
984 throw new GTUException(gtu + " already registered on Lane " + this + " [registered lanes: "
985 + gtu.positions(gtu.getFront()).keySet() + "] locations: "
986 + gtu.positions(gtu.getFront()).values() + " time: "
987 + gtu.getSimulator().getSimulatorTime().getTime());
988 }
989 if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
990 {
991 break;
992 }
993 }
994 this.gtuList.add(index, gtu);
995 }
996 }
997 fireTimedEvent(Lane.GTU_ADD_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size() },
998 gtu.getSimulator().getSimulatorTime());
999 getParentLink().addGTU(gtu);
1000 return index;
1001 }
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011 public final int addGTU(final LaneBasedGTU gtu, final Length longitudinalPosition) throws GTUException
1012 {
1013 return addGTU(gtu, longitudinalPosition.getSI() / getLength().getSI());
1014 }
1015
1016
1017
1018
1019
1020
1021
1022 public final void removeGTU(final LaneBasedGTU gtu, final boolean removeFromParentLink, final Length position)
1023 {
1024 this.gtuList.remove(gtu);
1025 fireTimedEvent(Lane.GTU_REMOVE_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size(), position },
1026 gtu.getSimulator().getSimulatorTime());
1027 if (removeFromParentLink)
1028 {
1029 this.parentLink.removeGTU(gtu);
1030 }
1031 }
1032
1033
1034
1035
1036
1037
1038
1039
1040 public final LaneBasedGTU getLastGtu(final GTUDirectionality direction) throws GTUException
1041 {
1042 if (this.gtuList.size() == 0)
1043 {
1044 return null;
1045 }
1046 if (direction.equals(GTUDirectionality.DIR_PLUS))
1047 {
1048 return this.gtuList.get(this.gtuList.size() - 1);
1049 }
1050 else
1051 {
1052 return this.gtuList.get(0);
1053 }
1054 }
1055
1056
1057
1058
1059
1060
1061
1062
1063 public final LaneBasedGTU getFirstGtu(final GTUDirectionality direction) throws GTUException
1064 {
1065 if (this.gtuList.size() == 0)
1066 {
1067 return null;
1068 }
1069 if (direction.equals(GTUDirectionality.DIR_PLUS))
1070 {
1071 return this.gtuList.get(0);
1072 }
1073 else
1074 {
1075 return this.gtuList.get(this.gtuList.size() - 1);
1076 }
1077 }
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091 public final LaneBasedGTU getGtuAhead(final Length position, final GTUDirectionality direction,
1092 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1093 {
1094 if (direction.equals(GTUDirectionality.DIR_PLUS))
1095 {
1096 for (LaneBasedGTU gtu : this.gtuList)
1097 {
1098 Length gtuPos = gtu.position(this, gtu.getRelativePositions().get(relativePosition), when);
1099 if (gtuPos.gt(position))
1100 {
1101 return gtu;
1102 }
1103 }
1104 }
1105 else
1106 {
1107 for (int i = this.gtuList.size() - 1; i >= 0; i--)
1108 {
1109 LaneBasedGTU gtu = this.gtuList.get(i);
1110 if (gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).lt(position))
1111 {
1112 return gtu;
1113 }
1114 }
1115 }
1116 return null;
1117 }
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129 public final List<LaneBasedObject> getObjectAhead(final Length position, final GTUDirectionality direction)
1130 {
1131 if (direction.equals(GTUDirectionality.DIR_PLUS))
1132 {
1133 for (double distance : this.laneBasedObjects.keySet())
1134 {
1135 if (distance > position.si)
1136 {
1137 return new ArrayList<>(this.laneBasedObjects.get(distance));
1138 }
1139 }
1140 }
1141 else
1142 {
1143 NavigableMap<Double, List<LaneBasedObject>> reverseLBO =
1144 (NavigableMap<Double, List<LaneBasedObject>>) this.laneBasedObjects;
1145 for (double distance : reverseLBO.descendingKeySet())
1146 {
1147 if (distance < position.si)
1148 {
1149 return new ArrayList<>(this.laneBasedObjects.get(distance));
1150 }
1151 }
1152 }
1153 return null;
1154 }
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166 public final List<LaneBasedObject> getObjectBehind(final Length position, final GTUDirectionality direction)
1167 {
1168 if (direction.equals(GTUDirectionality.DIR_PLUS))
1169 {
1170 return getObjectAhead(position, GTUDirectionality.DIR_MINUS);
1171 }
1172 return getObjectAhead(position, GTUDirectionality.DIR_PLUS);
1173 }
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187 public final LaneBasedGTU getGtuBehind(final Length position, final GTUDirectionality direction,
1188 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1189 {
1190 if (direction.equals(GTUDirectionality.DIR_PLUS))
1191 {
1192 return getGtuAhead(position, GTUDirectionality.DIR_MINUS, relativePosition, when);
1193 }
1194 return getGtuAhead(position, GTUDirectionality.DIR_PLUS, relativePosition, when);
1195 }
1196
1197
1198
1199
1200
1201
1202
1203 public static final Length MARGIN = new Length(0.5, LengthUnit.METER);
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218 public final Map<Lane, GTUDirectionality> nextLanes(final GTUType gtuType)
1219 {
1220 if (this.nextLanes == null)
1221 {
1222 this.nextLanes = new LinkedHashMap<>(1);
1223 }
1224 if (!this.nextLanes.containsKey(gtuType))
1225 {
1226 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1227 this.nextLanes.put(gtuType, laneMap);
1228
1229 for (Link link : getParentLink().getEndNode().getLinks())
1230 {
1231 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1232 {
1233 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1234 {
1235 if (cse instanceof Lane)
1236 {
1237 Lane lane = (Lane) cse;
1238 Length df = this.getCenterLine().getLast().distance(lane.getCenterLine().getFirst());
1239 Length dl = this.getCenterLine().getLast().distance(lane.getCenterLine().getLast());
1240
1241 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getEndNode()))
1242 {
1243
1244
1245 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1246 {
1247 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1248 }
1249 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1250 {
1251 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1252 }
1253 }
1254
1255 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getEndNode()))
1256 {
1257
1258
1259 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1260 {
1261 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1262 }
1263 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1264 {
1265 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1266 }
1267 }
1268 }
1269 }
1270 }
1271 }
1272 }
1273 return this.nextLanes.get(gtuType);
1274 }
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290 public final Map<Lane, GTUDirectionality> prevLanes(final GTUType gtuType)
1291 {
1292 if (this.prevLanes == null)
1293 {
1294 this.prevLanes = new LinkedHashMap<>(1);
1295 }
1296 if (!this.prevLanes.containsKey(gtuType))
1297 {
1298 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1299 this.prevLanes.put(gtuType, laneMap);
1300
1301 for (Link link : getParentLink().getStartNode().getLinks())
1302 {
1303 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1304 {
1305 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1306 {
1307 if (cse instanceof Lane)
1308 {
1309 Lane lane = (Lane) cse;
1310 Length df = this.getCenterLine().getFirst().distance(lane.getCenterLine().getFirst());
1311 Length dl = this.getCenterLine().getFirst().distance(lane.getCenterLine().getLast());
1312
1313 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getStartNode()))
1314 {
1315
1316
1317 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1318 {
1319 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1320 }
1321 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1322 {
1323 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1324 }
1325 }
1326
1327 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getStartNode()))
1328 {
1329
1330
1331 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1332 {
1333 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1334 }
1335 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1336 {
1337 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1338 }
1339 }
1340 }
1341 }
1342 }
1343 }
1344 }
1345 return this.prevLanes.get(gtuType);
1346 }
1347
1348
1349
1350
1351
1352
1353
1354 public final Map<Lane, GTUDirectionality> downstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1355 {
1356 if (this.downLanes == null)
1357 {
1358 this.downLanes = new LinkedHashMap<>(1);
1359 }
1360 if (!this.downLanes.containsKey(gtuType))
1361 {
1362 this.downLanes.put(gtuType, new HashMap<>());
1363 }
1364 if (!this.downLanes.get(gtuType).containsKey(direction))
1365 {
1366 Map<Lane, GTUDirectionality> down = direction.isPlus() ? nextLanes(gtuType) : prevLanes(gtuType);
1367 down = new LinkedHashMap<>(down);
1368 Node downNode = direction.isPlus() ? getParentLink().getEndNode() : getParentLink().getStartNode();
1369 Iterator<Entry<Lane, GTUDirectionality>> iterator = down.entrySet().iterator();
1370 while (iterator.hasNext())
1371 {
1372 Entry<Lane, GTUDirectionality> entry = iterator.next();
1373 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getStartNode().equals(downNode))
1374 || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getEndNode().equals(downNode)))
1375 {
1376
1377 iterator.remove();
1378 }
1379 }
1380 this.downLanes.get(gtuType).put(direction, down);
1381 }
1382 return this.downLanes.get(gtuType).get(direction);
1383 }
1384
1385
1386
1387
1388
1389
1390
1391 public final Map<Lane, GTUDirectionality> upstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1392 {
1393 if (this.upLanes == null)
1394 {
1395 this.upLanes = new LinkedHashMap<>(1);
1396 }
1397 if (!this.upLanes.containsKey(gtuType))
1398 {
1399 this.upLanes.put(gtuType, new HashMap<>());
1400 }
1401 if (!this.upLanes.get(gtuType).containsKey(direction))
1402 {
1403 Map<Lane, GTUDirectionality> up = direction.isPlus() ? prevLanes(gtuType) : nextLanes(gtuType);
1404 up = new LinkedHashMap<>(up);
1405 Node upNode = direction.isPlus() ? getParentLink().getStartNode() : getParentLink().getEndNode();
1406 Iterator<Entry<Lane, GTUDirectionality>> iterator = up.entrySet().iterator();
1407 while (iterator.hasNext())
1408 {
1409 Entry<Lane, GTUDirectionality> entry = iterator.next();
1410 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getEndNode().equals(upNode))
1411 || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getStartNode().equals(upNode)))
1412 {
1413
1414 iterator.remove();
1415 }
1416 }
1417 this.upLanes.get(gtuType).put(direction, up);
1418 }
1419 return this.upLanes.get(gtuType).get(direction);
1420 }
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435 public final Set<Lane> accessibleAdjacentLanes(final LateralDirectionality lateralDirection, final GTUType gtuType)
1436 {
1437 Set<Lane> candidates = new LinkedHashSet<>(1);
1438 LateralDirectionality dir = this.getDirectionality(gtuType).isForwardOrBoth() ? lateralDirection
1439 : lateralDirection.isLeft() ? LateralDirectionality.RIGHT : LateralDirectionality.LEFT;
1440 for (Lane lane : neighbors(dir, gtuType))
1441 {
1442 if (lane.getDirectionality(gtuType).equals(LongitudinalDirectionality.DIR_BOTH)
1443 || lane.getDirectionality(gtuType).equals(this.getDirectionality(gtuType)))
1444 {
1445 candidates.add(lane);
1446 }
1447 }
1448 return candidates;
1449 }
1450
1451
1452
1453
1454
1455
1456
1457
1458 public final Speed getSpeedLimit(final GTUType gtuType) throws NetworkException
1459 {
1460 if (this.speedLimitMap.containsKey(gtuType))
1461 {
1462 return this.speedLimitMap.get(gtuType);
1463 }
1464 if (gtuType.getParent() != null)
1465 {
1466 return getSpeedLimit(gtuType.getParent());
1467 }
1468 throw new NetworkException("No speed limit set for GTUType " + gtuType + " on lane " + toString());
1469 }
1470
1471
1472
1473
1474
1475
1476 public final Speed getHighestSpeedLimit() throws NetworkException
1477 {
1478 Throw.when(this.speedLimitMap.isEmpty(), NetworkException.class, "Lane %s has no speed limits set.", toString());
1479 Speed out = Speed.ZERO;
1480 for (GTUType gtuType : this.speedLimitMap.keySet())
1481 {
1482 out = Speed.max(out, this.speedLimitMap.get(gtuType));
1483 }
1484 return out;
1485 }
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501 public final void setSpeedLimit(final GTUType gtuType, final Speed speedLimit)
1502 {
1503 this.speedLimitMap.put(gtuType, speedLimit);
1504 }
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514 public final void removeSpeedLimit(final GTUType gtuType)
1515 {
1516 this.speedLimitMap.remove(gtuType);
1517 }
1518
1519
1520
1521
1522 public final LaneType getLaneType()
1523 {
1524 return this.laneType;
1525 }
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544 public final LongitudinalDirectionality getDirectionality(final GTUType gtuType)
1545 {
1546 if (this.directionalityMap.containsKey(gtuType))
1547 {
1548 return this.directionalityMap.get(gtuType);
1549 }
1550 if (this.directionalityMap.containsKey(GTUType.ALL))
1551 {
1552 return this.directionalityMap.get(GTUType.ALL);
1553 }
1554 return LongitudinalDirectionality.DIR_NONE;
1555 }
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576 public void addDirectionality(final GTUType gtuType, final LongitudinalDirectionality directionality)
1577 throws NetworkException
1578 {
1579 this.directionalityMap.put(gtuType, directionality);
1580 checkDirectionality();
1581 }
1582
1583
1584
1585
1586
1587
1588
1589 public void removeDirectionality(final GTUType gtuType)
1590 {
1591 this.directionalityMap.remove(gtuType);
1592 }
1593
1594
1595
1596
1597
1598
1599
1600 private void checkDirectionality() throws NetworkException
1601 {
1602 for (GTUType gtuType : this.directionalityMap.keySet())
1603 {
1604 LongitudinalDirectionality directionality = this.directionalityMap.get(gtuType);
1605 if (!getParentLink().getDirectionality(gtuType).contains(directionality))
1606 {
1607 throw new NetworkException("Lane " + toString() + " allows " + gtuType + " a directionality of "
1608 + directionality + " which is not present in the overarching link " + getParentLink().toString());
1609 }
1610 }
1611 }
1612
1613
1614
1615
1616 public final ImmutableList<LaneBasedGTU> getGtuList()
1617 {
1618 return this.gtuList == null ? new ImmutableArrayList<>(new ArrayList<>())
1619 : new ImmutableArrayList<>(this.gtuList, Immutable.WRAP);
1620 }
1621
1622
1623 @Override
1624 @SuppressWarnings("checkstyle:designforextension")
1625 protected double getZ()
1626 {
1627 return 0.0;
1628 }
1629
1630
1631
1632
1633 public final OvertakingConditions getOvertakingConditions()
1634 {
1635 return this.overtakingConditions;
1636 }
1637
1638
1639 @Override
1640 public final String toString()
1641 {
1642 CrossSectionLink link = getParentLink();
1643 return String.format("Lane %s of %s", getId(), link.getId());
1644 }
1645
1646
1647 private Integer cachedHashCode = null;
1648
1649
1650 @SuppressWarnings("checkstyle:designforextension")
1651 @Override
1652 public int hashCode()
1653 {
1654 if (this.cachedHashCode == null)
1655 {
1656 final int prime = 31;
1657 int result = super.hashCode();
1658 result = prime * result + ((this.laneType == null) ? 0 : this.laneType.hashCode());
1659 this.cachedHashCode = result;
1660 }
1661 return this.cachedHashCode;
1662 }
1663
1664
1665 @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
1666 @Override
1667 public boolean equals(final Object obj)
1668 {
1669 if (this == obj)
1670 return true;
1671 if (!super.equals(obj))
1672 return false;
1673 if (getClass() != obj.getClass())
1674 return false;
1675 Lane other = (Lane) obj;
1676 if (this.laneType == null)
1677 {
1678 if (other.laneType != null)
1679 return false;
1680 }
1681 else if (!this.laneType.equals(other.laneType))
1682 return false;
1683 return true;
1684 }
1685
1686
1687 @Override
1688 @SuppressWarnings("checkstyle:designforextension")
1689 public Lane clone(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation)
1690 throws NetworkException
1691 {
1692 Lane newLane = new Lane(newParentLink, newSimulator, animation, this);
1693
1694
1695 SortedMap<Double, List<SingleSensor>> newSensorMap = new TreeMap<>();
1696 for (double distance : this.sensors.keySet())
1697 {
1698 List<SingleSensor> newSensorList = new ArrayList<>();
1699 for (SingleSensor sensor : this.sensors.get(distance))
1700 {
1701 SingleSensor newSensor = ((AbstractSensor) sensor).clone(newLane, newSimulator, animation);
1702 newSensorList.add(newSensor);
1703 }
1704 newSensorMap.put(distance, newSensorList);
1705 }
1706 newLane.sensors.clear();
1707 newLane.sensors.putAll(newSensorMap);
1708
1709 SortedMap<Double, List<LaneBasedObject>> newLaneBasedObjectMap = new TreeMap<>();
1710 for (double distance : this.laneBasedObjects.keySet())
1711 {
1712 List<LaneBasedObject> newLaneBasedObjectList = new ArrayList<>();
1713 for (LaneBasedObject lbo : this.laneBasedObjects.get(distance))
1714 {
1715 AbstractLaneBasedObject laneBasedObject = (AbstractLaneBasedObject) lbo;
1716 LaneBasedObject newLbo = laneBasedObject.clone(newLane, newSimulator, animation);
1717 newLaneBasedObjectList.add(newLbo);
1718 }
1719 newLaneBasedObjectMap.put(distance, newLaneBasedObjectList);
1720 }
1721 newLane.laneBasedObjects.clear();
1722 newLane.laneBasedObjects.putAll(newLaneBasedObjectMap);
1723
1724 return newLane;
1725 }
1726 }