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