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