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