1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.LinkedHashMap;
7 import java.util.LinkedHashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.NavigableMap;
11 import java.util.Set;
12 import java.util.SortedMap;
13 import java.util.TreeMap;
14
15 import org.djunits.unit.LengthUnit;
16 import org.djunits.unit.TimeUnit;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Length;
19 import org.djunits.value.vdouble.scalar.Speed;
20 import org.djunits.value.vdouble.scalar.Time;
21 import org.djutils.event.EventType;
22 import org.djutils.exceptions.Throw;
23 import org.djutils.immutablecollections.Immutable;
24 import org.djutils.immutablecollections.ImmutableArrayList;
25 import org.djutils.immutablecollections.ImmutableList;
26 import org.djutils.metadata.MetaData;
27 import org.djutils.metadata.ObjectDescriptor;
28 import org.djutils.multikeymap.MultiKeyMap;
29 import org.opentrafficsim.base.HierarchicallyTyped;
30 import org.opentrafficsim.core.gtu.GtuException;
31 import org.opentrafficsim.core.gtu.GtuType;
32 import org.opentrafficsim.core.gtu.RelativePosition;
33 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
34 import org.opentrafficsim.core.network.LateralDirectionality;
35 import org.opentrafficsim.core.network.Link;
36 import org.opentrafficsim.core.network.NetworkException;
37 import org.opentrafficsim.core.object.Detector;
38 import org.opentrafficsim.core.perception.HistoryManager;
39 import org.opentrafficsim.core.perception.collections.HistoricalArrayList;
40 import org.opentrafficsim.core.perception.collections.HistoricalList;
41 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
42 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
43 import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
44 import org.opentrafficsim.road.network.lane.object.detector.SinkDetector;
45
46 import nl.tudelft.simulation.dsol.SimRuntimeException;
47 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class Lane extends CrossSectionElement implements HierarchicallyTyped<LaneType, Lane>, Serializable
67 {
68
69 private static final long serialVersionUID = 20150826L;
70
71
72 private final LaneType laneType;
73
74
75
76
77
78 private final Map<GtuType, Speed> speedLimitMap = new LinkedHashMap<>();
79
80
81 private final Map<GtuType, Speed> cachedSpeedLimits = new LinkedHashMap<>();
82
83
84
85
86
87 private final SortedMap<Double, List<LaneDetector>> detectors = new TreeMap<>();
88
89
90
91
92
93 private final SortedMap<Double, List<LaneBasedObject>> laneBasedObjects = new TreeMap<>();
94
95
96 private final HistoricalList<LaneBasedGtu> gtuList;
97
98
99 private List<LaneBasedGtu> gtuListAtTime = null;
100
101
102 private Time gtuListTime = null;
103
104
105
106
107
108
109 private final MultiKeyMap<Set<Lane>> leftNeighbours = new MultiKeyMap<>(GtuType.class, Boolean.class);
110
111
112
113
114
115
116 private final MultiKeyMap<Set<Lane>> rightNeighbours = new MultiKeyMap<>(GtuType.class, Boolean.class);
117
118
119
120
121
122 private final Map<GtuType, Set<Lane>> nextLanes = new LinkedHashMap<>(1);
123
124
125
126
127
128 private final Map<GtuType, Set<Lane>> prevLanes = new LinkedHashMap<>(1);
129
130
131
132
133
134 public static final EventType GTU_ADD_EVENT = new EventType("LANE.GTU.ADD",
135 new MetaData("Lane GTU add", "GTU id, number of GTUs after addition, lane id, link id",
136 new ObjectDescriptor("GTU id", "Id of GTU", String.class),
137 new ObjectDescriptor("GTU count", "New number of GTUs on lane", Integer.class),
138 new ObjectDescriptor("Lane id", "Id of the lane", String.class),
139 new ObjectDescriptor("Link id", "Id of the link", String.class)));
140
141
142
143
144
145
146 public static final EventType GTU_REMOVE_EVENT = new EventType("LANE.GTU.REMOVE",
147 new MetaData("Lane GTU remove", "GTU id, gtu, number of GTUs after removal, position, lane id, link id",
148 new ObjectDescriptor("GTU id", "Id of GTU", String.class),
149 new ObjectDescriptor("GTU", "The GTU itself", LaneBasedGtu.class),
150 new ObjectDescriptor("GTU count", "New number of GTUs on lane", Integer.class),
151 new ObjectDescriptor("Position", "Last position of GTU on the lane", Length.class),
152 new ObjectDescriptor("Lane id", "Id of the lane", String.class),
153 new ObjectDescriptor("Link id", "Id of the link", String.class)));
154
155
156
157
158
159 public static final EventType DETECTOR_ADD_EVENT = new EventType("LANE.DETECTOR.ADD",
160 new MetaData("Lane detector add", "Detector id, detector",
161 new ObjectDescriptor("detector id", "id of detector", String.class),
162 new ObjectDescriptor("detector", "detector itself", Detector.class)));
163
164
165
166
167
168 public static final EventType DETECTOR_REMOVE_EVENT = new EventType("LANE.DETECTOR.REMOVE",
169 new MetaData("Lane detector remove", "Detector id, detector",
170 new ObjectDescriptor("detector id", "id of detector", String.class),
171 new ObjectDescriptor("detector", "detector itself", Detector.class)));
172
173
174
175
176
177 public static final EventType OBJECT_ADD_EVENT = new EventType("LANE.OBJECT.ADD", new MetaData("Lane object add", "Object",
178 new ObjectDescriptor("GTU", "The lane-based GTU", LaneBasedObject.class)));
179
180
181
182
183
184 public static final EventType OBJECT_REMOVE_EVENT = new EventType("LANE.OBJECT.REMOVE", new MetaData("Lane object remove",
185 "Object", new ObjectDescriptor("GTU", "The lane-based GTU", LaneBasedObject.class)));
186
187
188
189
190
191
192
193
194
195 public Lane(final CrossSectionLink link, final String id, final CrossSectionGeometry geometry, final LaneType laneType,
196 final Map<GtuType, Speed> speedLimitMap)
197 {
198 super(link, id, geometry);
199 this.laneType = laneType;
200 this.speedLimitMap.putAll(speedLimitMap);
201 this.gtuList = new HistoricalArrayList<>(getManager(link), this);
202 }
203
204
205
206
207
208
209 private HistoryManager getManager(final CrossSectionLink parentLink)
210 {
211 return parentLink.getSimulator().getReplication().getHistoryManager(parentLink.getSimulator());
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226 private Set<Lane> neighbors(final LateralDirectionality direction, final GtuType gtuType, final boolean legal)
227 {
228 MultiKeyMap<Set<Lane>> cache = direction.isLeft() ? this.leftNeighbours : this.rightNeighbours;
229 return cache.get(() ->
230 {
231 Set<Lane> lanes = new LinkedHashSet<>(1);
232 for (CrossSectionElement cse : this.link.getCrossSectionElementList())
233 {
234 if (cse instanceof Lane && !cse.equals(this))
235 {
236 Lane lane = (Lane) cse;
237 if (laterallyAdjacentAndAccessible(lane, direction, gtuType, legal))
238 {
239 lanes.add(lane);
240 }
241 }
242 }
243 return lanes;
244 }, gtuType, legal);
245 }
246
247
248 static final Length ADJACENT_MARGIN = new Length(0.2, LengthUnit.METER);
249
250
251
252
253
254
255
256
257
258
259
260
261
262 private boolean laterallyAdjacentAndAccessible(final Lane lane, final LateralDirectionality direction,
263 final GtuType gtuType, final boolean legal)
264 {
265 if (legal && !lane.getType().isCompatible(gtuType))
266 {
267
268 return false;
269 }
270 if (direction.equals(LateralDirectionality.LEFT))
271 {
272
273 if (lane.getOffsetAtBegin().si + ADJACENT_MARGIN.si > getOffsetAtBegin().si
274 && lane.getOffsetAtEnd().si + ADJACENT_MARGIN.si > getOffsetAtEnd().si
275 && (lane.getOffsetAtBegin().si - lane.getBeginWidth().si / 2.0)
276 - (getOffsetAtBegin().si + getBeginWidth().si / 2.0) < ADJACENT_MARGIN.si
277 && (lane.getOffsetAtEnd().si - lane.getEndWidth().si / 2.0)
278 - (getOffsetAtEnd().si + getEndWidth().si / 2.0) < ADJACENT_MARGIN.si)
279 {
280
281 if (!(this instanceof Shoulder) && legal)
282 {
283 for (CrossSectionElement cse : this.link.getCrossSectionElementList())
284 {
285 if (cse instanceof Stripe)
286 {
287 Stripe stripe = (Stripe) cse;
288
289 if ((getOffsetAtBegin().si < stripe.getOffsetAtBegin().si
290 && stripe.getOffsetAtBegin().si < lane.getOffsetAtBegin().si)
291 || (getOffsetAtEnd().si < stripe.getOffsetAtEnd().si
292 && stripe.getOffsetAtEnd().si < lane.getOffsetAtEnd().si))
293 {
294 if (!stripe.isPermeable(gtuType, LateralDirectionality.LEFT))
295 {
296
297 return false;
298 }
299 }
300 }
301 }
302 }
303
304
305 return true;
306 }
307 }
308
309 else
310
311 {
312
313 if (lane.getOffsetAtBegin().si < getOffsetAtBegin().si + ADJACENT_MARGIN.si
314 && lane.getOffsetAtEnd().si < getOffsetAtEnd().si + ADJACENT_MARGIN.si
315 && (getOffsetAtBegin().si - getBeginWidth().si / 2.0)
316 - (lane.getOffsetAtBegin().si + lane.getBeginWidth().si / 2.0) < ADJACENT_MARGIN.si
317 && (getOffsetAtEnd().si - getEndWidth().si / 2.0)
318 - (lane.getOffsetAtEnd().si + lane.getEndWidth().si / 2.0) < ADJACENT_MARGIN.si)
319 {
320
321 if (!(this instanceof Shoulder) && legal)
322 {
323 for (CrossSectionElement cse : this.link.getCrossSectionElementList())
324 {
325 if (cse instanceof Stripe)
326 {
327 Stripe stripe = (Stripe) cse;
328
329 if ((getOffsetAtBegin().si > stripe.getOffsetAtBegin().si
330 && stripe.getOffsetAtBegin().si > lane.getOffsetAtBegin().si)
331 || (getOffsetAtEnd().si > stripe.getOffsetAtEnd().si
332 && stripe.getOffsetAtEnd().si > lane.getOffsetAtEnd().si))
333 {
334 if (!stripe.isPermeable(gtuType, LateralDirectionality.RIGHT))
335 {
336
337 return false;
338 }
339 }
340 }
341 }
342 }
343
344
345 return true;
346 }
347 }
348
349
350 return false;
351 }
352
353
354
355
356
357
358 public final void addDetector(final LaneDetector detector) throws NetworkException
359 {
360 double position = detector.getLongitudinalPosition().si;
361 if (position < 0 || position > getLength().getSI())
362 {
363 throw new NetworkException(
364 "Illegal position for detector " + position + " valid range is 0.." + getLength().getSI());
365 }
366 if (this.link.getNetwork().containsObject(detector.getFullId()))
367 {
368 throw new NetworkException("Network already contains an object with the name " + detector.getFullId());
369 }
370 List<LaneDetector> detectorList = this.detectors.get(position);
371 if (null == detectorList)
372 {
373 detectorList = new ArrayList<>(1);
374 this.detectors.put(position, detectorList);
375 }
376 detectorList.add(detector);
377 this.link.getNetwork().addObject(detector);
378 fireTimedEvent(Lane.DETECTOR_ADD_EVENT, new Object[] {detector.getId(), detector},
379 detector.getSimulator().getSimulatorTime());
380 }
381
382
383
384
385
386
387 public final void removeDetector(final LaneDetector detector) throws NetworkException
388 {
389 fireTimedEvent(Lane.DETECTOR_REMOVE_EVENT, new Object[] {detector.getId(), detector},
390 detector.getSimulator().getSimulatorTime());
391 List<LaneDetector> detectorList = this.detectors.get(detector.getLongitudinalPosition().si);
392 if (null == detectorList)
393 {
394 throw new NetworkException("No detector at " + detector.getLongitudinalPosition().si);
395 }
396 detectorList.remove(detector);
397 if (detectorList.size() == 0)
398 {
399 this.detectors.remove(detector.getLongitudinalPosition().si);
400 }
401 this.link.getNetwork().removeObject(detector);
402 }
403
404
405
406
407
408
409
410
411
412 public final List<LaneDetector> getDetectors(final Length minimumPosition, final Length maximumPosition,
413 final GtuType gtuType)
414 {
415 List<LaneDetector> detectorList = new ArrayList<>(1);
416 for (List<LaneDetector> dets : this.detectors.values())
417 {
418 for (LaneDetector detector : dets)
419 {
420 if (detector.isCompatible(gtuType) && detector.getLongitudinalPosition().ge(minimumPosition)
421 && detector.getLongitudinalPosition().le(maximumPosition))
422 {
423 detectorList.add(detector);
424 }
425 }
426 }
427 return detectorList;
428 }
429
430
431
432
433
434
435
436 public final List<LaneDetector> getDetectors(final GtuType gtuType)
437 {
438 List<LaneDetector> detectorList = new ArrayList<>(1);
439 for (List<LaneDetector> dets : this.detectors.values())
440 {
441 for (LaneDetector detector : dets)
442 {
443 if (detector.isCompatible(gtuType))
444 {
445 detectorList.add(detector);
446 }
447 }
448 }
449 return detectorList;
450 }
451
452
453
454
455
456 public final List<LaneDetector> getDetectors()
457 {
458 if (this.detectors == null)
459 {
460 return new ArrayList<>();
461 }
462 List<LaneDetector> detectorList = new ArrayList<>(1);
463 for (List<LaneDetector> dets : this.detectors.values())
464 {
465 for (LaneDetector detector : dets)
466 {
467 detectorList.add(detector);
468 }
469 }
470 return detectorList;
471 }
472
473
474
475
476
477
478 public final SortedMap<Double, List<LaneDetector>> getDetectorMap(final GtuType gtuType)
479 {
480 SortedMap<Double, List<LaneDetector>> detectorMap = new TreeMap<>();
481 for (double d : this.detectors.keySet())
482 {
483 List<LaneDetector> detectorList = new ArrayList<>(1);
484 for (List<LaneDetector> dets : this.detectors.values())
485 {
486 for (LaneDetector detector : dets)
487 {
488 if (detector.getLongitudinalPosition().si == d && detector.isCompatible(gtuType))
489 {
490 detectorList.add(detector);
491 }
492 }
493 }
494 if (detectorList.size() > 0)
495 {
496 detectorMap.put(d, detectorList);
497 }
498 }
499 return detectorMap;
500 }
501
502
503
504
505
506
507
508
509
510 public final void scheduleDetectorTriggers(final LaneBasedGtu gtu, final double referenceStartSI,
511 final double referenceMoveSI) throws NetworkException, SimRuntimeException
512 {
513 double minPos = referenceStartSI + gtu.getRear().dx().si;
514 double maxPos = referenceStartSI + gtu.getFront().dx().si + referenceMoveSI;
515 Map<Double, List<LaneDetector>> map = this.detectors.subMap(minPos, maxPos);
516 for (double pos : map.keySet())
517 {
518 for (LaneDetector detector : map.get(pos))
519 {
520 if (detector.isCompatible(gtu.getType()))
521 {
522 double dx = gtu.getRelativePositions().get(detector.getPositionType()).dx().si;
523 minPos = referenceStartSI + dx;
524 maxPos = minPos + referenceMoveSI;
525 if (minPos <= detector.getLongitudinalPosition().si && maxPos > detector.getLongitudinalPosition().si)
526 {
527 double d = detector.getLongitudinalPosition().si - minPos;
528 if (d < 0)
529 {
530 throw new NetworkException("scheduleTriggers for gtu: " + gtu + ", d<0 d=" + d);
531 }
532 OperationalPlan oPlan = gtu.getOperationalPlan();
533 Time triggerTime = oPlan.timeAtDistance(Length.instantiateSI(d));
534 if (triggerTime.gt(oPlan.getEndTime()))
535 {
536 System.err.println("Time=" + gtu.getSimulator().getSimulatorTime().getSI()
537 + " - Scheduling trigger at " + triggerTime.getSI() + "s. > " + oPlan.getEndTime().getSI()
538 + "s. (nextEvalTime) for detector " + detector + " , gtu " + gtu);
539 System.err.println(" v=" + gtu.getSpeed() + ", a=" + gtu.getAcceleration() + ", lane=" + toString()
540 + ", refStartSI=" + referenceStartSI + ", moveSI=" + referenceMoveSI);
541 triggerTime = new Time(oPlan.getEndTime().getSI() - Math.ulp(oPlan.getEndTime().getSI()),
542 TimeUnit.DEFAULT);
543 }
544 SimEvent<Duration> event =
545 new SimEvent<>(new Duration(triggerTime.minus(gtu.getSimulator().getStartTimeAbs())), detector,
546 "trigger", new Object[] {gtu});
547 gtu.getSimulator().scheduleEvent(event);
548 gtu.addTrigger(this, event);
549 }
550 else if (detector.getLongitudinalPosition().si < minPos && detector instanceof SinkDetector)
551 {
552
553
554 SimEvent<Duration> event = new SimEvent<>(new Duration(gtu.getSimulator().getSimulatorTime()), detector,
555 "trigger", new Object[] {gtu});
556 gtu.getSimulator().scheduleEvent(event);
557 gtu.addTrigger(this, event);
558 }
559 }
560 }
561 }
562 }
563
564
565
566
567
568
569
570 public final synchronized void addLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
571 {
572 double position = laneBasedObject.getLongitudinalPosition().si;
573 if (position < 0 || position > getLength().getSI())
574 {
575 throw new NetworkException(
576 "Illegal position for laneBasedObject " + position + " valid range is 0.." + getLength().getSI());
577 }
578 if (this.link.getNetwork().containsObject(laneBasedObject.getFullId()))
579 {
580 throw new NetworkException("Network already contains an object with the name " + laneBasedObject.getFullId());
581 }
582 List<LaneBasedObject> laneBasedObjectList = this.laneBasedObjects.get(position);
583 if (null == laneBasedObjectList)
584 {
585 laneBasedObjectList = new ArrayList<>(1);
586 this.laneBasedObjects.put(position, laneBasedObjectList);
587 }
588 laneBasedObjectList.add(laneBasedObject);
589 this.link.getNetwork().addObject(laneBasedObject);
590 fireTimedEvent(Lane.OBJECT_ADD_EVENT, new Object[] {laneBasedObject}, getLink().getSimulator().getSimulatorTime());
591 }
592
593
594
595
596
597
598 public final synchronized void removeLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
599 {
600 fireTimedEvent(Lane.OBJECT_REMOVE_EVENT, new Object[] {laneBasedObject}, getLink().getSimulator().getSimulatorTime());
601 List<LaneBasedObject> laneBasedObjectList =
602 this.laneBasedObjects.get(laneBasedObject.getLongitudinalPosition().getSI());
603 if (null == laneBasedObjectList)
604 {
605 throw new NetworkException("No laneBasedObject at " + laneBasedObject.getLongitudinalPosition().si);
606 }
607 laneBasedObjectList.remove(laneBasedObject);
608 if (laneBasedObjectList.isEmpty())
609 {
610 this.laneBasedObjects.remove(laneBasedObject.getLongitudinalPosition().doubleValue());
611 }
612 this.link.getNetwork().removeObject(laneBasedObject);
613 }
614
615
616
617
618
619
620
621
622 public final List<LaneBasedObject> getLaneBasedObjects(final Length minimumPosition, final Length maximumPosition)
623 {
624 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
625 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
626 {
627 for (LaneBasedObject lbo : lbol)
628 {
629 if (lbo.getLongitudinalPosition().ge(minimumPosition) && lbo.getLongitudinalPosition().le(maximumPosition))
630 {
631 laneBasedObjectList.add(lbo);
632 }
633 }
634 }
635 return laneBasedObjectList;
636 }
637
638
639
640
641
642 public final List<LaneBasedObject> getLaneBasedObjects()
643 {
644 if (this.laneBasedObjects == null)
645 {
646 return new ArrayList<>();
647 }
648 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
649 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
650 {
651 for (LaneBasedObject lbo : lbol)
652 {
653 laneBasedObjectList.add(lbo);
654 }
655 }
656 return laneBasedObjectList;
657 }
658
659
660
661
662
663 public final SortedMap<Double, List<LaneBasedObject>> getLaneBasedObjectMap()
664 {
665 SortedMap<Double, List<LaneBasedObject>> laneBasedObjectMap = new TreeMap<>();
666 for (double d : this.laneBasedObjects.keySet())
667 {
668 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
669 for (LaneBasedObject lbo : this.laneBasedObjects.get(d))
670 {
671 laneBasedObjectList.add(lbo);
672 }
673 laneBasedObjectMap.put(d, laneBasedObjectList);
674 }
675 return laneBasedObjectMap;
676 }
677
678
679
680
681
682
683 public final Length position(final double fraction)
684 {
685 if (getLength().getDisplayUnit().isBaseSIUnit())
686 {
687 return new Length(getLength().si * fraction, LengthUnit.SI);
688 }
689 return new Length(getLength().getInUnit() * fraction, getLength().getDisplayUnit());
690 }
691
692
693
694
695
696
697 public final double positionSI(final double fraction)
698 {
699 return getLength().si * fraction;
700 }
701
702
703
704
705
706
707 public final double fraction(final Length position)
708 {
709 return position.si / getLength().si;
710 }
711
712
713
714
715
716
717 public final double fractionSI(final double positionSI)
718 {
719 return positionSI / getLength().si;
720 }
721
722
723
724
725
726
727
728
729
730
731 public final int addGtu(final LaneBasedGtu gtu, final double fractionalPosition) throws GtuException
732 {
733 int index;
734
735 if (this.gtuList.size() == 0)
736 {
737 this.gtuList.add(gtu);
738 index = 0;
739 }
740 else
741 {
742
743 for (index = 0; index < this.gtuList.size(); index++)
744 {
745 LaneBasedGtu otherGTU = this.gtuList.get(index);
746 if (gtu == otherGTU)
747 {
748 throw new GtuException(gtu + " already registered on Lane " + this + " [registered lanes: "
749 + gtu.positions(gtu.getFront()).keySet() + "] locations: " + gtu.positions(gtu.getFront()).values()
750 + " time: " + gtu.getSimulator().getSimulatorTime());
751 }
752 if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
753 {
754 break;
755 }
756 }
757 this.gtuList.add(index, gtu);
758 }
759
760 fireTimedEvent(Lane.GTU_ADD_EVENT, new Object[] {gtu.getId(), this.gtuList.size(), getId(), getLink().getId()},
761 gtu.getSimulator().getSimulatorTime());
762
763 getLink().addGTU(gtu);
764 return index;
765 }
766
767
768
769
770
771
772
773
774
775 public final int addGtu(final LaneBasedGtu gtu, final Length longitudinalPosition) throws GtuException
776 {
777 return addGtu(gtu, longitudinalPosition.getSI() / getLength().getSI());
778 }
779
780
781
782
783
784
785
786
787 public final void removeGtu(final LaneBasedGtu gtu, final boolean removeFromParentLink, final Length position)
788 {
789 boolean contained = this.gtuList.remove(gtu);
790 if (contained)
791 {
792
793 fireTimedEvent(Lane.GTU_REMOVE_EVENT,
794 new Object[] {gtu.getId(), gtu, this.gtuList.size(), position, getId(), getLink().getId()},
795 gtu.getSimulator().getSimulatorTime());
796
797 }
798 if (removeFromParentLink)
799 {
800 this.link.removeGTU(gtu);
801 }
802 }
803
804
805
806
807
808
809 public final LaneBasedGtu getLastGtu() throws GtuException
810 {
811 if (this.gtuList.size() == 0)
812 {
813 return null;
814 }
815 return this.gtuList.get(this.gtuList.size() - 1);
816 }
817
818
819
820
821
822
823 public final LaneBasedGtu getFirstGtu() throws GtuException
824 {
825 if (this.gtuList.size() == 0)
826 {
827 return null;
828 }
829 return this.gtuList.get(0);
830 }
831
832
833
834
835
836
837
838
839
840
841 public final LaneBasedGtu getGtuAhead(final Length position, final RelativePosition.Type relativePosition, final Time when)
842 throws GtuException
843 {
844 List<LaneBasedGtu> list = this.gtuList.get(when);
845 if (list.isEmpty())
846 {
847 return null;
848 }
849 int[] search = lineSearch((final int index) ->
850 {
851 LaneBasedGtu gtu = list.get(index);
852 return gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).si;
853 }, list.size(), position.si);
854 if (search[1] < list.size())
855 {
856 return list.get(search[1]);
857 }
858 return null;
859 }
860
861
862
863
864
865
866
867
868
869
870 public final LaneBasedGtu getGtuBehind(final Length position, final RelativePosition.Type relativePosition, final Time when)
871 throws GtuException
872 {
873 List<LaneBasedGtu> list = this.gtuList.get(when);
874 if (list.isEmpty())
875 {
876 return null;
877 }
878 int[] search = lineSearch((final int index) ->
879 {
880 LaneBasedGtu gtu = list.get(index);
881 return gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).si;
882 }, list.size(), position.si);
883 if (search[0] >= 0)
884 {
885 return list.get(search[0]);
886 }
887 return null;
888 }
889
890
891
892
893
894
895
896
897
898
899
900 private int[] lineSearch(final Positions positions, final int listSize, final double position) throws GtuException
901 {
902 int[] out = new int[2];
903
904 double pos0 = positions.get(0);
905 double posEnd;
906 if (position < pos0)
907 {
908 out[0] = -1;
909 out[1] = 0;
910 }
911 else if (position == pos0)
912 {
913 out[0] = -1;
914 out[1] = 1;
915 }
916 else if (position > (posEnd = positions.get(listSize - 1)))
917 {
918 out[0] = listSize - 1;
919 out[1] = listSize;
920 }
921 else if (position == posEnd)
922 {
923 out[0] = listSize - 2;
924 out[1] = listSize;
925 }
926 else
927 {
928 int low = 0;
929 int mid = (int) ((listSize - 1) * position / getLength().si);
930 mid = mid < 0 ? 0 : mid >= listSize ? listSize - 1 : mid;
931 int high = listSize - 1;
932 while (high - low > 1)
933 {
934 double midPos = positions.get(mid);
935 if (midPos < position)
936 {
937 low = mid;
938 }
939 else if (midPos > position)
940 {
941 high = mid;
942 }
943 else
944 {
945 low = mid - 1;
946 high = mid + 1;
947 break;
948 }
949 mid = (low + high) / 2;
950 }
951 out[0] = low;
952 out[1] = high;
953 }
954 return out;
955 }
956
957
958
959
960
961
962
963
964 public final List<LaneBasedObject> getObjectAhead(final Length position)
965 {
966 for (double distance : this.laneBasedObjects.keySet())
967 {
968 if (distance > position.si)
969 {
970 return new ArrayList<>(this.laneBasedObjects.get(distance));
971 }
972 }
973 return null;
974 }
975
976
977
978
979
980
981
982
983 public final List<LaneBasedObject> getObjectBehind(final Length position)
984 {
985 NavigableMap<Double, List<LaneBasedObject>> reverseLBO =
986 (NavigableMap<Double, List<LaneBasedObject>>) this.laneBasedObjects;
987 for (double distance : reverseLBO.descendingKeySet())
988 {
989 if (distance < position.si)
990 {
991 return new ArrayList<>(this.laneBasedObjects.get(distance));
992 }
993 }
994 return null;
995 }
996
997
998
999
1000
1001
1002
1003 public static final Length MARGIN = new Length(0.5, LengthUnit.METER);
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020 public final Set<Lane> nextLanes(final GtuType gtuType)
1021 {
1022 if (!this.nextLanes.containsKey(gtuType))
1023 {
1024
1025 Set<Lane> laneSet = new LinkedHashSet<>(1);
1026 this.nextLanes.put(gtuType, laneSet);
1027 if (gtuType == null)
1028 {
1029
1030 for (Link link : getLink().getEndNode().getLinks())
1031 {
1032 if (!(link.equals(this.getLink())) && link instanceof CrossSectionLink)
1033 {
1034 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1035 {
1036 if (cse instanceof Lane)
1037 {
1038 Lane lane = (Lane) cse;
1039 double jumpToStart = this.getCenterLine().getLast().distance(lane.getCenterLine().getFirst());
1040 double jumpToEnd = this.getCenterLine().getLast().distance(lane.getCenterLine().getLast());
1041 if (jumpToStart < MARGIN.si && jumpToStart < jumpToEnd
1042 && link.getStartNode().equals(getLink().getEndNode()))
1043 {
1044
1045 laneSet.add(lane);
1046 }
1047
1048 }
1049 }
1050 }
1051 }
1052 }
1053 else
1054 {
1055 nextLanes(null).stream().filter((lane) -> lane.getType().isCompatible(gtuType))
1056 .forEach((lane) -> laneSet.add(lane));
1057 }
1058 }
1059 return this.nextLanes.get(gtuType);
1060 }
1061
1062
1063
1064
1065
1066 public void forceNextLanes(final Set<Lane> lanes)
1067 {
1068 Throw.whenNull(lanes, "Lanes should not be null. Use an empty set instead.");
1069 this.nextLanes.clear();
1070 this.nextLanes.put(null, lanes);
1071 }
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089 public final Set<Lane> prevLanes(final GtuType gtuType)
1090 {
1091 if (!this.prevLanes.containsKey(gtuType))
1092 {
1093 Set<Lane> laneSet = new LinkedHashSet<>(1);
1094 this.prevLanes.put(gtuType, laneSet);
1095
1096 if (gtuType == null)
1097 {
1098 for (Link link : getLink().getStartNode().getLinks())
1099 {
1100 if (!(link.equals(this.getLink())) && link instanceof CrossSectionLink)
1101 {
1102 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1103 {
1104 if (cse instanceof Lane)
1105 {
1106 Lane lane = (Lane) cse;
1107 double jumpToStart = this.getCenterLine().getFirst().distance(lane.getCenterLine().getFirst());
1108 double jumpToEnd = this.getCenterLine().getFirst().distance(lane.getCenterLine().getLast());
1109 if (jumpToEnd < MARGIN.si && jumpToEnd < jumpToStart
1110 && link.getEndNode().equals(getLink().getStartNode()))
1111 {
1112
1113 laneSet.add(lane);
1114 }
1115
1116 }
1117 }
1118 }
1119 }
1120 }
1121 else
1122 {
1123 prevLanes(null).stream().filter((lane) -> lane.getType().isCompatible(gtuType))
1124 .forEach((lane) -> laneSet.add(lane));
1125 }
1126 }
1127 return this.prevLanes.get(gtuType);
1128 }
1129
1130
1131
1132
1133
1134 public void forcePrevLanes(final Set<Lane> lanes)
1135 {
1136 Throw.whenNull(lanes, "Lanes should not be null. Use an empty set instead.");
1137 this.prevLanes.clear();
1138 this.prevLanes.put(null, lanes);
1139 }
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152 public final Set<Lane> accessibleAdjacentLanesPhysical(final LateralDirectionality lateralDirection, final GtuType gtuType)
1153 {
1154 return neighbors(lateralDirection, gtuType, false);
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170 public final Set<Lane> accessibleAdjacentLanesLegal(final LateralDirectionality lateralDirection, final GtuType gtuType)
1171 {
1172 Set<Lane> candidates = new LinkedHashSet<>(1);
1173 for (Lane lane : neighbors(lateralDirection, gtuType, true))
1174 {
1175 if (lane.getType().isCompatible(gtuType))
1176 {
1177 candidates.add(lane);
1178 }
1179 }
1180 return candidates;
1181 }
1182
1183
1184
1185
1186
1187
1188
1189 public Lane getAdjacentLane(final LateralDirectionality laneChangeDirection, final GtuType gtuType)
1190 {
1191 Set<Lane> adjLanes = accessibleAdjacentLanesLegal(laneChangeDirection, gtuType);
1192 if (!adjLanes.isEmpty())
1193 {
1194 return adjLanes.iterator().next();
1195 }
1196 return null;
1197 }
1198
1199
1200
1201
1202
1203
1204
1205
1206 public Speed getSpeedLimit(final GtuType gtuType) throws NetworkException
1207 {
1208 Speed speedLimit = this.cachedSpeedLimits.get(gtuType);
1209 if (speedLimit == null)
1210 {
1211 if (this.speedLimitMap.containsKey(gtuType))
1212 {
1213 speedLimit = this.speedLimitMap.get(gtuType);
1214 }
1215 else if (gtuType.getParent() != null)
1216 {
1217 speedLimit = getSpeedLimit(gtuType.getParent());
1218 }
1219 else
1220 {
1221 throw new NetworkException("No speed limit set for GtuType " + gtuType + " on lane " + toString());
1222 }
1223 this.cachedSpeedLimits.put(gtuType, speedLimit);
1224 }
1225 return speedLimit;
1226 }
1227
1228
1229
1230
1231
1232
1233 public final Speed getLowestSpeedLimit() throws NetworkException
1234 {
1235 Throw.when(this.speedLimitMap.isEmpty(), NetworkException.class, "Lane %s has no speed limits set.", toString());
1236 Speed out = Speed.POSITIVE_INFINITY;
1237 for (GtuType gtuType : this.speedLimitMap.keySet())
1238 {
1239 out = Speed.min(out, this.speedLimitMap.get(gtuType));
1240 }
1241 return out;
1242 }
1243
1244
1245
1246
1247
1248
1249 public final Speed getHighestSpeedLimit() throws NetworkException
1250 {
1251 Throw.when(this.speedLimitMap.isEmpty(), NetworkException.class, "Lane %s has no speed limits set.", toString());
1252 Speed out = Speed.ZERO;
1253 for (GtuType gtuType : this.speedLimitMap.keySet())
1254 {
1255 out = Speed.max(out, this.speedLimitMap.get(gtuType));
1256 }
1257 return out;
1258 }
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274 public final void setSpeedLimit(final GtuType gtuType, final Speed speedLimit)
1275 {
1276 this.speedLimitMap.put(gtuType, speedLimit);
1277 this.cachedSpeedLimits.clear();
1278 }
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288 public final void removeSpeedLimit(final GtuType gtuType)
1289 {
1290 this.speedLimitMap.remove(gtuType);
1291 this.cachedSpeedLimits.clear();
1292 }
1293
1294 @Override
1295 public final LaneType getType()
1296 {
1297 return this.laneType;
1298 }
1299
1300
1301
1302
1303 public final ImmutableList<LaneBasedGtu> getGtuList()
1304 {
1305
1306 return this.gtuList == null ? new ImmutableArrayList<>(new ArrayList<>())
1307 : new ImmutableArrayList<>(this.gtuList, Immutable.COPY);
1308 }
1309
1310
1311
1312
1313
1314
1315 public final List<LaneBasedGtu> getGtuList(final Time time)
1316 {
1317 if (time.equals(this.gtuListTime))
1318 {
1319 return this.gtuListAtTime;
1320 }
1321 this.gtuListTime = time;
1322 this.gtuListAtTime = this.gtuList == null ? new ArrayList<>() : this.gtuList.get(time);
1323 return this.gtuListAtTime;
1324 }
1325
1326
1327
1328
1329
1330 public final int numberOfGtus()
1331 {
1332 return this.gtuList.size();
1333 }
1334
1335
1336
1337
1338
1339
1340 public final int numberOfGtus(final Time time)
1341 {
1342 return getGtuList(time).size();
1343 }
1344
1345
1346
1347
1348
1349
1350 public final int indexOfGtu(final LaneBasedGtu gtu)
1351 {
1352 return Collections.binarySearch(this.gtuList, gtu, (gtu1, gtu2) ->
1353 {
1354 try
1355 {
1356 return gtu1.position(this, gtu1.getReference()).compareTo(gtu2.position(this, gtu2.getReference()));
1357 }
1358 catch (GtuException exception)
1359 {
1360 throw new RuntimeException(exception);
1361 }
1362 });
1363 }
1364
1365
1366
1367
1368
1369
1370
1371 public final int indexOfGtu(final LaneBasedGtu gtu, final Time time)
1372 {
1373 return Collections.binarySearch(getGtuList(time), gtu, (gtu1, gtu2) ->
1374 {
1375 try
1376 {
1377 return Double.compare(gtu1.fractionalPosition(this, gtu1.getReference(), time),
1378 gtu2.fractionalPosition(this, gtu2.getReference(), time));
1379 }
1380 catch (GtuException exception)
1381 {
1382 throw new RuntimeException(exception);
1383 }
1384 });
1385 }
1386
1387
1388
1389
1390
1391
1392 public final LaneBasedGtu getGtu(final int index)
1393 {
1394 return this.gtuList.get(index);
1395 }
1396
1397
1398
1399
1400
1401
1402
1403 public final LaneBasedGtu getGtu(final int index, final Time time)
1404 {
1405 return getGtuList(time).get(index);
1406 }
1407
1408
1409
1410
1411
1412
1413 public final Length coveredDistance(final double fraction)
1414 {
1415 return getLength().times(fraction);
1416 }
1417
1418
1419
1420
1421
1422
1423 public final Length remainingDistance(final double fraction)
1424 {
1425 return getLength().times(1.0 - fraction);
1426 }
1427
1428
1429
1430
1431
1432
1433 @Deprecated
1434 public final double fractionAtCoveredDistance(final Length distance)
1435 {
1436 return fraction(distance);
1437 }
1438
1439 @Override
1440 public final String toString()
1441 {
1442 CrossSectionLink link = getLink();
1443 return String.format("Lane %s of %s", getId(), link.getId());
1444 }
1445
1446
1447 private Integer cachedHashCode = null;
1448
1449 @SuppressWarnings("checkstyle:designforextension")
1450 @Override
1451 public int hashCode()
1452 {
1453 if (this.cachedHashCode == null)
1454 {
1455 final int prime = 31;
1456 int result = super.hashCode();
1457 result = prime * result + ((this.laneType == null) ? 0 : this.laneType.hashCode());
1458 this.cachedHashCode = result;
1459 }
1460 return this.cachedHashCode;
1461 }
1462
1463 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
1464 @Override
1465 public boolean equals(final Object obj)
1466 {
1467 if (this == obj)
1468 return true;
1469 if (!super.equals(obj))
1470 return false;
1471 if (getClass() != obj.getClass())
1472 return false;
1473 Lane other = (Lane) obj;
1474 if (this.laneType == null)
1475 {
1476 if (other.laneType != null)
1477 return false;
1478 }
1479 else if (!this.laneType.equals(other.laneType))
1480 return false;
1481 return true;
1482 }
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495 private interface Positions
1496 {
1497
1498
1499
1500
1501
1502
1503 double get(int index) throws GtuException;
1504 }
1505
1506 }