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