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