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