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