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.getId()))
578 {
579 throw new NetworkException("Network already contains an object with the name " + sensor.getId());
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.SI);
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 public final void addLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
798 {
799 double position = laneBasedObject.getLongitudinalPosition().si;
800 if (position < 0 || position > getLength().getSI())
801 {
802 throw new NetworkException(
803 "Illegal position for laneBasedObject " + position + " valid range is 0.." + getLength().getSI());
804 }
805 if (this.parentLink.getNetwork().containsObject(laneBasedObject.getId()))
806 {
807 throw new NetworkException("Network already contains an object with the name " + laneBasedObject.getId());
808 }
809 List<LaneBasedObject> laneBasedObjectList = this.laneBasedObjects.get(position);
810 if (null == laneBasedObjectList)
811 {
812 laneBasedObjectList = new ArrayList<LaneBasedObject>(1);
813 this.laneBasedObjects.put(position, laneBasedObjectList);
814 }
815 laneBasedObjectList.add(laneBasedObject);
816 this.parentLink.getNetwork().addObject(laneBasedObject);
817 fireEvent(Lane.OBJECT_ADD_EVENT, new Object[] { laneBasedObject });
818 }
819
820
821
822
823
824
825 public final void removeLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
826 {
827 fireEvent(Lane.OBJECT_REMOVE_EVENT, new Object[] { laneBasedObject });
828 List<LaneBasedObject> laneBasedObjectList =
829 this.laneBasedObjects.get(laneBasedObject.getLongitudinalPosition().getSI());
830 if (null == laneBasedObjectList)
831 {
832 throw new NetworkException("No laneBasedObject at " + laneBasedObject.getLongitudinalPosition().si);
833 }
834 laneBasedObjectList.remove(laneBasedObject);
835 if (laneBasedObjectList.isEmpty())
836 {
837 this.laneBasedObjects.remove(laneBasedObject.getLongitudinalPosition().doubleValue());
838 }
839 this.parentLink.getNetwork().removeObject(laneBasedObject);
840 }
841
842
843
844
845
846
847
848
849 public final List<LaneBasedObject> getLaneBasedObjects(final Length minimumPosition, final Length maximumPosition)
850 {
851 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
852 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
853 {
854 for (LaneBasedObject lbo : lbol)
855 {
856 if (lbo.getLongitudinalPosition().ge(minimumPosition) && lbo.getLongitudinalPosition().le(maximumPosition))
857 {
858 laneBasedObjectList.add(lbo);
859 }
860 }
861 }
862 return laneBasedObjectList;
863 }
864
865
866
867
868
869 public final List<LaneBasedObject> getLaneBasedObjects()
870 {
871 if (this.laneBasedObjects == null)
872 {
873 return new ArrayList<>();
874 }
875 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
876 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
877 {
878 for (LaneBasedObject lbo : lbol)
879 {
880 laneBasedObjectList.add(lbo);
881 }
882 }
883 return laneBasedObjectList;
884 }
885
886
887
888
889
890 public final SortedMap<Double, List<LaneBasedObject>> getLaneBasedObjectMap()
891 {
892 SortedMap<Double, List<LaneBasedObject>> laneBasedObjectMap = new TreeMap<>();
893 for (double d : this.laneBasedObjects.keySet())
894 {
895 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
896 for (LaneBasedObject lbo : this.laneBasedObjects.get(d))
897 {
898 laneBasedObjectList.add(lbo);
899 }
900 laneBasedObjectMap.put(d, laneBasedObjectList);
901 }
902 return laneBasedObjectMap;
903 }
904
905
906
907
908
909
910 public final Length position(final double fraction)
911 {
912 if (this.length.getUnit().isBaseSIUnit())
913 {
914 return new Length(this.length.si * fraction, LengthUnit.SI);
915 }
916 return new Length(this.length.getInUnit() * fraction, this.length.getUnit());
917 }
918
919
920
921
922
923
924 public final double positionSI(final double fraction)
925 {
926 return this.length.si * fraction;
927 }
928
929
930
931
932
933
934 public final double fraction(final Length position)
935 {
936 return position.si / this.length.si;
937 }
938
939
940
941
942
943
944 public final double fractionSI(final double positionSI)
945 {
946 return positionSI / this.length.si;
947 }
948
949
950
951
952
953
954
955
956
957 public final int addGTU(final LaneBasedGTU gtu, final double fractionalPosition) throws GTUException
958 {
959 int index;
960
961 if (this.gtuList.size() == 0)
962 {
963 this.gtuList.add(gtu);
964 index = 0;
965 }
966 else
967 {
968
969 LaneBasedGTU lastGTU = this.gtuList.get(this.gtuList.size() - 1);
970 if (fractionalPosition > lastGTU.fractionalPosition(this, lastGTU.getFront()))
971 {
972 this.gtuList.add(gtu);
973 index = this.gtuList.size() - 1;
974 }
975 else
976 {
977
978 for (index = 0; index < this.gtuList.size(); index++)
979 {
980 LaneBasedGTU otherGTU = this.gtuList.get(index);
981 if (gtu == otherGTU)
982 {
983 throw new GTUException(gtu + " already registered on Lane " + this + " [registered lanes: "
984 + gtu.positions(gtu.getFront()).keySet() + "] locations: "
985 + gtu.positions(gtu.getFront()).values() + " time: "
986 + gtu.getSimulator().getSimulatorTime().getTime());
987 }
988 if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
989 {
990 break;
991 }
992 }
993 this.gtuList.add(index, gtu);
994 }
995 }
996 fireTimedEvent(Lane.GTU_ADD_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size() },
997 gtu.getSimulator().getSimulatorTime());
998 getParentLink().addGTU(gtu);
999 return index;
1000 }
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010 public final int addGTU(final LaneBasedGTU gtu, final Length longitudinalPosition) throws GTUException
1011 {
1012 return addGTU(gtu, longitudinalPosition.getSI() / getLength().getSI());
1013 }
1014
1015
1016
1017
1018
1019
1020
1021 public final void removeGTU(final LaneBasedGTU gtu, final boolean removeFromParentLink, final Length position)
1022 {
1023 this.gtuList.remove(gtu);
1024 fireTimedEvent(Lane.GTU_REMOVE_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size(), position },
1025 gtu.getSimulator().getSimulatorTime());
1026 if (removeFromParentLink)
1027 {
1028 this.parentLink.removeGTU(gtu);
1029 }
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039 public final LaneBasedGTU getLastGtu(final GTUDirectionality direction) throws GTUException
1040 {
1041 if (this.gtuList.size() == 0)
1042 {
1043 return null;
1044 }
1045 if (direction.equals(GTUDirectionality.DIR_PLUS))
1046 {
1047 return this.gtuList.get(this.gtuList.size() - 1);
1048 }
1049 else
1050 {
1051 return this.gtuList.get(0);
1052 }
1053 }
1054
1055
1056
1057
1058
1059
1060
1061
1062 public final LaneBasedGTU getFirstGtu(final GTUDirectionality direction) throws GTUException
1063 {
1064 if (this.gtuList.size() == 0)
1065 {
1066 return null;
1067 }
1068 if (direction.equals(GTUDirectionality.DIR_PLUS))
1069 {
1070 return this.gtuList.get(0);
1071 }
1072 else
1073 {
1074 return this.gtuList.get(this.gtuList.size() - 1);
1075 }
1076 }
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090 public final LaneBasedGTU getGtuAhead(final Length position, final GTUDirectionality direction,
1091 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1092 {
1093 if (direction.equals(GTUDirectionality.DIR_PLUS))
1094 {
1095 for (LaneBasedGTU gtu : this.gtuList)
1096 {
1097 Length gtuPos = gtu.position(this, gtu.getRelativePositions().get(relativePosition), when);
1098 if (gtuPos.gt(position))
1099 {
1100 return gtu;
1101 }
1102 }
1103 }
1104 else
1105 {
1106 for (int i = this.gtuList.size() - 1; i >= 0; i--)
1107 {
1108 LaneBasedGTU gtu = this.gtuList.get(i);
1109 if (gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).lt(position))
1110 {
1111 return gtu;
1112 }
1113 }
1114 }
1115 return null;
1116 }
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128 public final List<LaneBasedObject> getObjectAhead(final Length position, final GTUDirectionality direction)
1129 {
1130 if (direction.equals(GTUDirectionality.DIR_PLUS))
1131 {
1132 for (double distance : this.laneBasedObjects.keySet())
1133 {
1134 if (distance > position.si)
1135 {
1136 return new ArrayList<>(this.laneBasedObjects.get(distance));
1137 }
1138 }
1139 }
1140 else
1141 {
1142 NavigableMap<Double, List<LaneBasedObject>> reverseLBO =
1143 (NavigableMap<Double, List<LaneBasedObject>>) this.laneBasedObjects;
1144 for (double distance : reverseLBO.descendingKeySet())
1145 {
1146 if (distance < position.si)
1147 {
1148 return new ArrayList<>(this.laneBasedObjects.get(distance));
1149 }
1150 }
1151 }
1152 return null;
1153 }
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165 public final List<LaneBasedObject> getObjectBehind(final Length position, final GTUDirectionality direction)
1166 {
1167 if (direction.equals(GTUDirectionality.DIR_PLUS))
1168 {
1169 return getObjectAhead(position, GTUDirectionality.DIR_MINUS);
1170 }
1171 return getObjectAhead(position, GTUDirectionality.DIR_PLUS);
1172 }
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186 public final LaneBasedGTU getGtuBehind(final Length position, final GTUDirectionality direction,
1187 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1188 {
1189 if (direction.equals(GTUDirectionality.DIR_PLUS))
1190 {
1191 return getGtuAhead(position, GTUDirectionality.DIR_MINUS, relativePosition, when);
1192 }
1193 return getGtuAhead(position, GTUDirectionality.DIR_PLUS, relativePosition, when);
1194 }
1195
1196
1197
1198
1199
1200
1201
1202 public static final Length MARGIN = new Length(0.5, LengthUnit.METER);
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217 public final Map<Lane, GTUDirectionality> nextLanes(final GTUType gtuType)
1218 {
1219 if (this.nextLanes == null)
1220 {
1221 this.nextLanes = new LinkedHashMap<>(1);
1222 }
1223 if (!this.nextLanes.containsKey(gtuType))
1224 {
1225 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1226 this.nextLanes.put(gtuType, laneMap);
1227
1228 for (Link link : getParentLink().getEndNode().getLinks())
1229 {
1230 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1231 {
1232 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1233 {
1234 if (cse instanceof Lane)
1235 {
1236 Lane lane = (Lane) cse;
1237 Length df = this.getCenterLine().getLast().distance(lane.getCenterLine().getFirst());
1238 Length dl = this.getCenterLine().getLast().distance(lane.getCenterLine().getLast());
1239
1240 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getEndNode()))
1241 {
1242
1243
1244 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1245 {
1246 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1247 }
1248 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1249 {
1250 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1251 }
1252 }
1253
1254 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getEndNode()))
1255 {
1256
1257
1258 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1259 {
1260 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1261 }
1262 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1263 {
1264 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1265 }
1266 }
1267 }
1268 }
1269 }
1270 }
1271 }
1272 return this.nextLanes.get(gtuType);
1273 }
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289 public final Map<Lane, GTUDirectionality> prevLanes(final GTUType gtuType)
1290 {
1291 if (this.prevLanes == null)
1292 {
1293 this.prevLanes = new LinkedHashMap<>(1);
1294 }
1295 if (!this.prevLanes.containsKey(gtuType))
1296 {
1297 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1298 this.prevLanes.put(gtuType, laneMap);
1299
1300 for (Link link : getParentLink().getStartNode().getLinks())
1301 {
1302 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1303 {
1304 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1305 {
1306 if (cse instanceof Lane)
1307 {
1308 Lane lane = (Lane) cse;
1309 Length df = this.getCenterLine().getFirst().distance(lane.getCenterLine().getFirst());
1310 Length dl = this.getCenterLine().getFirst().distance(lane.getCenterLine().getLast());
1311
1312 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getStartNode()))
1313 {
1314
1315
1316 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1317 {
1318 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1319 }
1320 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1321 {
1322 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1323 }
1324 }
1325
1326 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getStartNode()))
1327 {
1328
1329
1330 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1331 {
1332 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1333 }
1334 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1335 {
1336 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1337 }
1338 }
1339 }
1340 }
1341 }
1342 }
1343 }
1344 return this.prevLanes.get(gtuType);
1345 }
1346
1347
1348
1349
1350
1351
1352
1353 public final Map<Lane, GTUDirectionality> downstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1354 {
1355 if (this.downLanes == null)
1356 {
1357 this.downLanes = new LinkedHashMap<>(1);
1358 }
1359 if (!this.downLanes.containsKey(gtuType))
1360 {
1361 this.downLanes.put(gtuType, new HashMap<>());
1362 }
1363 if (!this.downLanes.get(gtuType).containsKey(direction))
1364 {
1365 Map<Lane, GTUDirectionality> down = direction.isPlus() ? nextLanes(gtuType) : prevLanes(gtuType);
1366 down = new LinkedHashMap<>(down);
1367 Node downNode = direction.isPlus() ? getParentLink().getEndNode() : getParentLink().getStartNode();
1368 Iterator<Entry<Lane, GTUDirectionality>> iterator = down.entrySet().iterator();
1369 while (iterator.hasNext())
1370 {
1371 Entry<Lane, GTUDirectionality> entry = iterator.next();
1372 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getStartNode().equals(downNode))
1373 || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getEndNode().equals(downNode)))
1374 {
1375
1376 iterator.remove();
1377 }
1378 }
1379 this.downLanes.get(gtuType).put(direction, down);
1380 }
1381 return this.downLanes.get(gtuType).get(direction);
1382 }
1383
1384
1385
1386
1387
1388
1389
1390 public final Map<Lane, GTUDirectionality> upstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1391 {
1392 if (this.upLanes == null)
1393 {
1394 this.upLanes = new LinkedHashMap<>(1);
1395 }
1396 if (!this.upLanes.containsKey(gtuType))
1397 {
1398 this.upLanes.put(gtuType, new HashMap<>());
1399 }
1400 if (!this.upLanes.get(gtuType).containsKey(direction))
1401 {
1402 Map<Lane, GTUDirectionality> up = direction.isPlus() ? prevLanes(gtuType) : nextLanes(gtuType);
1403 up = new LinkedHashMap<>(up);
1404 Node upNode = direction.isPlus() ? getParentLink().getStartNode() : getParentLink().getEndNode();
1405 Iterator<Entry<Lane, GTUDirectionality>> iterator = up.entrySet().iterator();
1406 while (iterator.hasNext())
1407 {
1408 Entry<Lane, GTUDirectionality> entry = iterator.next();
1409 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getEndNode().equals(upNode))
1410 || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getStartNode().equals(upNode)))
1411 {
1412
1413 iterator.remove();
1414 }
1415 }
1416 this.upLanes.get(gtuType).put(direction, up);
1417 }
1418 return this.upLanes.get(gtuType).get(direction);
1419 }
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434 public final Set<Lane> accessibleAdjacentLanes(final LateralDirectionality lateralDirection, final GTUType gtuType)
1435 {
1436 Set<Lane> candidates = new LinkedHashSet<>(1);
1437 LateralDirectionality dir = this.getDirectionality(gtuType).isForwardOrBoth() ? lateralDirection
1438 : lateralDirection.isLeft() ? LateralDirectionality.RIGHT : LateralDirectionality.LEFT;
1439 for (Lane lane : neighbors(dir, gtuType))
1440 {
1441 if (lane.getDirectionality(gtuType).equals(LongitudinalDirectionality.DIR_BOTH)
1442 || lane.getDirectionality(gtuType).equals(this.getDirectionality(gtuType)))
1443 {
1444 candidates.add(lane);
1445 }
1446 }
1447 return candidates;
1448 }
1449
1450
1451
1452
1453
1454
1455
1456
1457 public final Speed getSpeedLimit(final GTUType gtuType) throws NetworkException
1458 {
1459 if (this.speedLimitMap.containsKey(gtuType))
1460 {
1461 return this.speedLimitMap.get(gtuType);
1462 }
1463 if (gtuType.getParent() != null)
1464 {
1465 return getSpeedLimit(gtuType.getParent());
1466 }
1467 throw new NetworkException("No speed limit set for GTUType " + gtuType + " on lane " + toString());
1468 }
1469
1470
1471
1472
1473
1474
1475 public final Speed getHighestSpeedLimit() throws NetworkException
1476 {
1477 Throw.when(this.speedLimitMap.isEmpty(), NetworkException.class, "Lane %s has no speed limits set.", toString());
1478 Speed out = Speed.ZERO;
1479 for (GTUType gtuType : this.speedLimitMap.keySet())
1480 {
1481 out = Speed.max(out, this.speedLimitMap.get(gtuType));
1482 }
1483 return out;
1484 }
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500 public final void setSpeedLimit(final GTUType gtuType, final Speed speedLimit)
1501 {
1502 this.speedLimitMap.put(gtuType, speedLimit);
1503 }
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513 public final void removeSpeedLimit(final GTUType gtuType)
1514 {
1515 this.speedLimitMap.remove(gtuType);
1516 }
1517
1518
1519
1520
1521 public final LaneType getLaneType()
1522 {
1523 return this.laneType;
1524 }
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543 public final LongitudinalDirectionality getDirectionality(final GTUType gtuType)
1544 {
1545 if (this.directionalityMap.containsKey(gtuType))
1546 {
1547 return this.directionalityMap.get(gtuType);
1548 }
1549 if (this.directionalityMap.containsKey(GTUType.ALL))
1550 {
1551 return this.directionalityMap.get(GTUType.ALL);
1552 }
1553 return LongitudinalDirectionality.DIR_NONE;
1554 }
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575 public void addDirectionality(final GTUType gtuType, final LongitudinalDirectionality directionality)
1576 throws NetworkException
1577 {
1578 this.directionalityMap.put(gtuType, directionality);
1579 checkDirectionality();
1580 }
1581
1582
1583
1584
1585
1586
1587
1588 public void removeDirectionality(final GTUType gtuType)
1589 {
1590 this.directionalityMap.remove(gtuType);
1591 }
1592
1593
1594
1595
1596
1597
1598
1599 private void checkDirectionality() throws NetworkException
1600 {
1601 for (GTUType gtuType : this.directionalityMap.keySet())
1602 {
1603 LongitudinalDirectionality directionality = this.directionalityMap.get(gtuType);
1604 if (!getParentLink().getDirectionality(gtuType).contains(directionality))
1605 {
1606 throw new NetworkException("Lane " + toString() + " allows " + gtuType + " a directionality of "
1607 + directionality + " which is not present in the overarching link " + getParentLink().toString());
1608 }
1609 }
1610 }
1611
1612
1613
1614
1615 public final ImmutableList<LaneBasedGTU> getGtuList()
1616 {
1617 return this.gtuList == null ? new ImmutableArrayList<>(new ArrayList<>())
1618 : new ImmutableArrayList<>(this.gtuList, Immutable.WRAP);
1619 }
1620
1621
1622 @Override
1623 @SuppressWarnings("checkstyle:designforextension")
1624 protected double getZ()
1625 {
1626 return 0.0;
1627 }
1628
1629
1630
1631
1632 public final OvertakingConditions getOvertakingConditions()
1633 {
1634 return this.overtakingConditions;
1635 }
1636
1637
1638 @Override
1639 public final String toString()
1640 {
1641 CrossSectionLink link = getParentLink();
1642 return String.format("Lane %s of %s", getId(), link.getId());
1643 }
1644
1645
1646 private Integer cachedHashCode = null;
1647
1648
1649 @SuppressWarnings("checkstyle:designforextension")
1650 @Override
1651 public int hashCode()
1652 {
1653 if (this.cachedHashCode == null)
1654 {
1655 final int prime = 31;
1656 int result = super.hashCode();
1657 result = prime * result + ((this.laneType == null) ? 0 : this.laneType.hashCode());
1658 this.cachedHashCode = result;
1659 }
1660 return this.cachedHashCode;
1661 }
1662
1663
1664 @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
1665 @Override
1666 public boolean equals(final Object obj)
1667 {
1668 if (this == obj)
1669 return true;
1670 if (!super.equals(obj))
1671 return false;
1672 if (getClass() != obj.getClass())
1673 return false;
1674 Lane other = (Lane) obj;
1675 if (this.laneType == null)
1676 {
1677 if (other.laneType != null)
1678 return false;
1679 }
1680 else if (!this.laneType.equals(other.laneType))
1681 return false;
1682 return true;
1683 }
1684
1685
1686 @Override
1687 @SuppressWarnings("checkstyle:designforextension")
1688 public Lane clone(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation)
1689 throws NetworkException
1690 {
1691 Lane newLane = new Lane(newParentLink, newSimulator, animation, this);
1692
1693
1694 SortedMap<Double, List<SingleSensor>> newSensorMap = new TreeMap<>();
1695 for (double distance : this.sensors.keySet())
1696 {
1697 List<SingleSensor> newSensorList = new ArrayList<>();
1698 for (SingleSensor sensor : this.sensors.get(distance))
1699 {
1700 SingleSensor newSensor = ((AbstractSensor) sensor).clone(newLane, newSimulator, animation);
1701 newSensorList.add(newSensor);
1702 }
1703 newSensorMap.put(distance, newSensorList);
1704 }
1705 newLane.sensors.clear();
1706 newLane.sensors.putAll(newSensorMap);
1707
1708 SortedMap<Double, List<LaneBasedObject>> newLaneBasedObjectMap = new TreeMap<>();
1709 for (double distance : this.laneBasedObjects.keySet())
1710 {
1711 List<LaneBasedObject> newLaneBasedObjectList = new ArrayList<>();
1712 for (LaneBasedObject lbo : this.laneBasedObjects.get(distance))
1713 {
1714 AbstractLaneBasedObject laneBasedObject = (AbstractLaneBasedObject) lbo;
1715 LaneBasedObject newLbo = laneBasedObject.clone(newLane, newSimulator, animation);
1716 newLaneBasedObjectList.add(newLbo);
1717 }
1718 newLaneBasedObjectMap.put(distance, newLaneBasedObjectList);
1719 }
1720 newLane.laneBasedObjects.clear();
1721 newLane.laneBasedObjects.putAll(newLaneBasedObjectMap);
1722
1723 return newLane;
1724 }
1725 }