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