1 package org.opentrafficsim.road.network.lane;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedHashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Map.Entry;
12 import java.util.NavigableMap;
13 import java.util.Set;
14 import java.util.SortedMap;
15 import java.util.TreeMap;
16
17 import org.djunits.unit.LengthUnit;
18 import org.djunits.unit.TimeUnit;
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djunits.value.vdouble.scalar.Speed;
21 import org.djunits.value.vdouble.scalar.Time;
22 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
23 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
24 import org.opentrafficsim.core.geometry.OTSGeometryException;
25 import org.opentrafficsim.core.gtu.GTUDirectionality;
26 import org.opentrafficsim.core.gtu.GTUException;
27 import org.opentrafficsim.core.gtu.GTUType;
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.LongitudinalDirectionality;
33 import org.opentrafficsim.core.network.NetworkException;
34 import org.opentrafficsim.core.network.Node;
35 import org.opentrafficsim.core.network.OTSNetwork;
36 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
37 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
38 import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
39 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
40 import org.opentrafficsim.road.network.lane.object.sensor.AbstractSensor;
41 import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
42
43 import nl.tudelft.simulation.dsol.SimRuntimeException;
44 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
45 import nl.tudelft.simulation.event.EventType;
46 import nl.tudelft.simulation.language.Throw;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class Lane extends CrossSectionElement implements Serializable
67 {
68
69 private static final long serialVersionUID = 20150826L;
70
71
72 private final LaneType laneType;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 private final Map<GTUType, LongitudinalDirectionality> directionalityMap;
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 private Map<GTUType, Speed> speedLimitMap;
105
106
107
108
109
110
111 private final SortedMap<Double, List<SingleSensor>> sensors = new TreeMap<>();
112
113
114
115
116
117
118 private final SortedMap<Double, List<LaneBasedObject>> laneBasedObjects = new TreeMap<>();
119
120
121 private final List<LaneBasedGTU> gtuList = new ArrayList<LaneBasedGTU>();
122
123
124
125
126
127
128 private Map<GTUType, Set<Lane>> leftNeighbors = null;
129
130
131
132
133
134
135 private Map<GTUType, Set<Lane>> rightNeighbors = null;
136
137
138
139
140
141 private Map<GTUType, Map<Lane, GTUDirectionality>> nextLanes = null;
142
143
144
145
146
147 private Map<GTUType, Map<Lane, GTUDirectionality>> prevLanes = null;
148
149
150
151
152
153 private Map<GTUType, Map<GTUDirectionality, Map<Lane, GTUDirectionality>>> downLanes = null;
154
155
156
157
158
159 private Map<GTUType, Map<GTUDirectionality, Map<Lane, GTUDirectionality>>> upLanes = null;
160
161
162
163 private final OvertakingConditions overtakingConditions;
164
165
166
167
168
169 public static final EventType GTU_ADD_EVENT = new EventType("GTU.ADD");
170
171
172
173
174
175 public static final EventType GTU_REMOVE_EVENT = new EventType("GTU.REMOVE");
176
177
178
179
180
181 public static final EventType SENSOR_ADD_EVENT = new EventType("SENSOR.ADD");
182
183
184
185
186
187 public static final EventType SENSOR_REMOVE_EVENT = new EventType("SENSOR.REMOVE");
188
189
190
191
192
193 public static final EventType OBJECT_ADD_EVENT = new EventType("OBJECT.ADD");
194
195
196
197
198
199 public static final EventType OBJECT_REMOVE_EVENT = new EventType("OBJECT.REMOVE");
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 @SuppressWarnings("checkstyle:parameternumber")
220 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
221 final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
222 final Map<GTUType, LongitudinalDirectionality> directionalityMap, final Map<GTUType, Speed> speedLimitMap,
223 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
224 {
225 super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
226 this.laneType = laneType;
227 this.directionalityMap = directionalityMap;
228 checkDirectionality();
229 this.speedLimitMap = speedLimitMap;
230 this.overtakingConditions = overtakingConditions;
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250 @SuppressWarnings("checkstyle:parameternumber")
251 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtStart,
252 final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth, final LaneType laneType,
253 final LongitudinalDirectionality directionality, final Speed speedLimit,
254 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
255 {
256 super(parentLink, id, lateralOffsetAtStart, lateralOffsetAtEnd, beginWidth, endWidth);
257 this.laneType = laneType;
258 this.directionalityMap = new LinkedHashMap<>(1);
259 this.directionalityMap.put(GTUType.ALL, directionality);
260 checkDirectionality();
261 this.speedLimitMap = new LinkedHashMap<>();
262 this.speedLimitMap.put(GTUType.ALL, speedLimit);
263 this.overtakingConditions = overtakingConditions;
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281 @SuppressWarnings("checkstyle:parameternumber")
282 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
283 final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
284 final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
285 throws OTSGeometryException, NetworkException
286 {
287 super(parentLink, id, lateralOffset, width);
288 this.laneType = laneType;
289 this.directionalityMap = directionalityMap;
290 checkDirectionality();
291 this.speedLimitMap = speedLimitMap;
292 this.overtakingConditions = overtakingConditions;
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309 @SuppressWarnings("checkstyle:parameternumber")
310 public Lane(final CrossSectionLink parentLink, final String id, final Length lateralOffset, final Length width,
311 final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
312 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
313 {
314 super(parentLink, id, lateralOffset, width);
315 this.laneType = laneType;
316 this.directionalityMap = new LinkedHashMap<>(1);
317 this.directionalityMap.put(GTUType.ALL, directionality);
318 checkDirectionality();
319 this.speedLimitMap = new LinkedHashMap<>();
320 this.speedLimitMap.put(GTUType.ALL, speedLimit);
321 this.overtakingConditions = overtakingConditions;
322 }
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 @SuppressWarnings("checkstyle:parameternumber")
341 public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
342 final LaneType laneType, final Map<GTUType, LongitudinalDirectionality> directionalityMap,
343 final Map<GTUType, Speed> speedLimitMap, final OvertakingConditions overtakingConditions)
344 throws OTSGeometryException, NetworkException
345 {
346 super(parentLink, id, crossSectionSlices);
347 this.laneType = laneType;
348 this.directionalityMap = directionalityMap;
349 checkDirectionality();
350 this.speedLimitMap = speedLimitMap;
351 this.overtakingConditions = overtakingConditions;
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369 @SuppressWarnings("checkstyle:parameternumber")
370 public Lane(final CrossSectionLink parentLink, final String id, final List<CrossSectionSlice> crossSectionSlices,
371 final LaneType laneType, final LongitudinalDirectionality directionality, final Speed speedLimit,
372 final OvertakingConditions overtakingConditions) throws OTSGeometryException, NetworkException
373 {
374 super(parentLink, id, crossSectionSlices);
375 this.laneType = laneType;
376 this.directionalityMap = new LinkedHashMap<>(1);
377 this.directionalityMap.put(GTUType.ALL, directionality);
378 checkDirectionality();
379 this.speedLimitMap = new LinkedHashMap<>();
380 this.speedLimitMap.put(GTUType.ALL, speedLimit);
381 this.overtakingConditions = overtakingConditions;
382 }
383
384
385
386
387
388
389
390
391
392
393 protected Lane(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation,
394 final Lane cse) throws NetworkException
395 {
396 super(newParentLink, newSimulator, animation, cse);
397 this.laneType = cse.laneType;
398 this.directionalityMap = new HashMap<GTUType, LongitudinalDirectionality>(cse.directionalityMap);
399 this.speedLimitMap = new HashMap<GTUType, Speed>(cse.speedLimitMap);
400 this.overtakingConditions = cse.overtakingConditions;
401
402 if (animation)
403 {
404 OTSNetwork.cloneAnimation(cse, this, cse.getParentLink().getSimulator(), newSimulator);
405 }
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419 private Set<Lane> neighbors(final LateralDirectionality direction, final GTUType gtuType)
420 {
421 if (this.leftNeighbors == null || this.rightNeighbors == null)
422 {
423 this.leftNeighbors = new LinkedHashMap<>(1);
424 this.rightNeighbors = new LinkedHashMap<>(1);
425 }
426
427 if (!this.leftNeighbors.containsKey(gtuType) || !this.rightNeighbors.containsKey(gtuType))
428 {
429 Set<Lane> leftSet = new LinkedHashSet<>(1);
430 Set<Lane> rightSet = new LinkedHashSet<>(1);
431 this.leftNeighbors.put(gtuType, leftSet);
432 this.rightNeighbors.put(gtuType, rightSet);
433 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
434 {
435 if (cse instanceof Lane && !cse.equals(this))
436 {
437 Lane lane = (Lane) cse;
438 if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.LEFT, gtuType))
439 {
440 leftSet.add(lane);
441 }
442 if (laterallyAdjacentAndAccessible(lane, LateralDirectionality.RIGHT, gtuType))
443 {
444 rightSet.add(lane);
445 }
446 }
447 }
448 }
449
450 Set<Lane> lanes = new LinkedHashSet<>();
451 if (direction.equals(LateralDirectionality.LEFT))
452 {
453 lanes.addAll(this.leftNeighbors.get(gtuType));
454 }
455 else
456 {
457 lanes.addAll(this.rightNeighbors.get(gtuType));
458 }
459 return lanes;
460 }
461
462
463 static final Length ADJACENT_MARGIN = new Length(0.2, LengthUnit.METER);
464
465
466
467
468
469
470
471
472
473
474
475
476 private boolean laterallyAdjacentAndAccessible(final Lane lane, final LateralDirectionality direction,
477 final GTUType gtuType)
478 {
479 if (!lane.getLaneType().isCompatible(gtuType) || gtuType.equals(GTUType.ALL) || gtuType.equals(GTUType.NONE))
480 {
481
482 return false;
483 }
484
485 if (direction.equals(LateralDirectionality.LEFT))
486 {
487
488 if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
489 - (lane.getDesignLineOffsetAtBegin().getSI() - lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
490 .getSI()
491 && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
492 - (lane.getDesignLineOffsetAtEnd().getSI() - lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
493 .getSI())
494 {
495
496 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
497 {
498 if (cse instanceof Stripe)
499 {
500 Stripe stripe = (Stripe) cse;
501
502 if (Math.abs((getDesignLineOffsetAtBegin().getSI() + getBeginWidth().getSI() / 2.0)
503 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
504 && Math.abs((getDesignLineOffsetAtEnd().getSI() + getEndWidth().getSI() / 2.0)
505 - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
506 {
507 if (!stripe.isPermeable(gtuType, LateralDirectionality.LEFT))
508 {
509
510 return false;
511 }
512 }
513 }
514 }
515
516
517 return true;
518 }
519 }
520
521 else
522
523 {
524
525 if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
526 - (lane.getDesignLineOffsetAtBegin().getSI() + lane.getBeginWidth().getSI() / 2.0)) < ADJACENT_MARGIN
527 .getSI()
528 && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
529 - (lane.getDesignLineOffsetAtEnd().getSI() + lane.getEndWidth().getSI() / 2.0)) < ADJACENT_MARGIN
530 .getSI())
531 {
532
533 for (CrossSectionElement cse : this.parentLink.getCrossSectionElementList())
534 {
535 if (cse instanceof Stripe)
536 {
537 Stripe stripe = (Stripe) cse;
538
539 if (Math.abs((getDesignLineOffsetAtBegin().getSI() - getBeginWidth().getSI() / 2.0)
540 - stripe.getDesignLineOffsetAtBegin().getSI()) < ADJACENT_MARGIN.getSI()
541 && Math.abs((getDesignLineOffsetAtEnd().getSI() - getEndWidth().getSI() / 2.0)
542 - stripe.getDesignLineOffsetAtEnd().getSI()) < ADJACENT_MARGIN.getSI())
543 {
544 if (!stripe.isPermeable(gtuType, LateralDirectionality.RIGHT))
545 {
546
547 return false;
548 }
549 }
550 }
551 }
552
553
554 return true;
555 }
556 }
557
558
559 return false;
560 }
561
562
563
564
565
566
567 public final void addSensor(final SingleSensor sensor) throws NetworkException
568 {
569 double position = sensor.getLongitudinalPosition().si;
570 if (position < 0 || position > getLength().getSI())
571 {
572 throw new NetworkException("Illegal position for sensor " + position + " valid range is 0.." + getLength().getSI());
573 }
574 List<SingleSensor> sensorList = this.sensors.get(position);
575 if (null == sensorList)
576 {
577 sensorList = new ArrayList<SingleSensor>(1);
578 this.sensors.put(position, sensorList);
579 }
580 sensorList.add(sensor);
581 fireTimedEvent(Lane.SENSOR_ADD_EVENT, new Object[] { sensor.getId(), sensor },
582 sensor.getSimulator().getSimulatorTime());
583 }
584
585
586
587
588
589
590 public final void removeSensor(final SingleSensor sensor) throws NetworkException
591 {
592 fireTimedEvent(Lane.SENSOR_REMOVE_EVENT, new Object[] { sensor.getId(), sensor },
593 sensor.getSimulator().getSimulatorTime());
594 List<SingleSensor> sensorList = this.sensors.get(sensor.getLongitudinalPosition().si);
595 if (null == sensorList)
596 {
597 throw new NetworkException("No sensor at " + sensor.getLongitudinalPosition().si);
598 }
599 sensorList.remove(sensor);
600 if (sensorList.size() == 0)
601 {
602 this.sensors.remove(sensor.getLongitudinalPosition().si);
603 }
604 }
605
606
607
608
609
610
611
612
613
614 public final List<SingleSensor> getSensors(final Length minimumPosition, final Length maximumPosition,
615 final GTUType gtuType)
616 {
617 List<SingleSensor> sensorList = new ArrayList<>(1);
618 for (List<SingleSensor> sl : this.sensors.values())
619 {
620 for (SingleSensor sensor : sl)
621 {
622 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL))
623 && sensor.getLongitudinalPosition().ge(minimumPosition)
624 && sensor.getLongitudinalPosition().le(maximumPosition))
625 {
626 sensorList.add(sensor);
627 }
628 }
629 }
630 return sensorList;
631 }
632
633
634
635
636
637
638
639 public final List<SingleSensor> getSensors(final GTUType gtuType)
640 {
641 List<SingleSensor> sensorList = new ArrayList<>(1);
642 for (List<SingleSensor> sl : this.sensors.values())
643 {
644 for (SingleSensor sensor : sl)
645 {
646 if ((sensor.getTriggeringGTUTypes().contains(gtuType) || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
647 {
648 sensorList.add(sensor);
649 }
650 }
651 }
652 return sensorList;
653 }
654
655
656
657
658
659 public final List<SingleSensor> getSensors()
660 {
661 if (this.sensors == null)
662 {
663 return new ArrayList<>();
664 }
665 List<SingleSensor> sensorList = new ArrayList<>(1);
666 for (List<SingleSensor> sl : this.sensors.values())
667 {
668 for (SingleSensor sensor : sl)
669 {
670 sensorList.add(sensor);
671 }
672 }
673 return sensorList;
674 }
675
676
677
678
679
680
681
682 public final SortedMap<Double, List<SingleSensor>> getSensorMap(final GTUType gtuType)
683 {
684 SortedMap<Double, List<SingleSensor>> sensorMap = new TreeMap<>();
685 for (double d : this.sensors.keySet())
686 {
687 List<SingleSensor> sensorList = new ArrayList<>(1);
688 for (List<SingleSensor> sl : this.sensors.values())
689 {
690 for (SingleSensor sensor : sl)
691 {
692 if (sensor.getLongitudinalPosition().si == d && (sensor.getTriggeringGTUTypes().contains(gtuType)
693 || sensor.getTriggeringGTUTypes().contains(GTUType.ALL)))
694 {
695 sensorList.add(sensor);
696 }
697 }
698 }
699 if (sensorList.size() > 0)
700 {
701 sensorMap.put(d, sensorList);
702 }
703 }
704
705
706
707
708
709
710
711
712
713 return sensorMap;
714 }
715
716
717
718
719
720
721
722
723
724 public final void scheduleSensorTriggers(final LaneBasedGTU gtu, final double referenceStartSI,
725 final double referenceMoveSI) throws NetworkException, SimRuntimeException
726 {
727
728 for (List<SingleSensor> sensorList : getSensorMap(gtu.getGTUType()).values())
729 {
730
731
732
733
734
735
736 for (SingleSensor sensor : sensorList)
737 {
738 for (RelativePosition relativePosition : gtu.getRelativePositions().values())
739 {
740
741
742 if (sensor.getPositionType().equals(relativePosition.getType())
743 && referenceStartSI + relativePosition.getDx().getSI() <= sensor.getLongitudinalPosition().si
744 && referenceStartSI + referenceMoveSI
745 + relativePosition.getDx().getSI() > sensor.getLongitudinalPosition().si)
746 {
747
748
749
750 double d = sensor.getLongitudinalPosition().si - referenceStartSI - relativePosition.getDx().getSI();
751 if (d < 0)
752 {
753 throw new NetworkException("scheduleTriggers for gtu: " + gtu + ", d<0 d=" + d);
754 }
755
756 OperationalPlan oPlan = gtu.getOperationalPlan();
757 Time triggerTime = oPlan.timeAtDistance(new Length(d, LengthUnit.METER));
758 if (triggerTime.gt(oPlan.getEndTime()))
759 {
760 System.err.println("Time=" + gtu.getSimulator().getSimulatorTime().getTime().getSI()
761 + " - Scheduling trigger at " + triggerTime.getSI() + "s. > " + oPlan.getEndTime().getSI()
762 + "s. (nextEvalTime) for sensor " + sensor + " , gtu " + gtu);
763 System.err.println(" v=" + gtu.getSpeed() + ", a=" + gtu.getAcceleration() + ", lane=" + toString()
764 + ", refStartSI=" + referenceStartSI + ", moveSI=" + referenceMoveSI);
765 triggerTime =
766 new Time(oPlan.getEndTime().getSI() - Math.ulp(oPlan.getEndTime().getSI()), TimeUnit.SI);
767
768
769 }
770
771
772
773 SimEvent<OTSSimTimeDouble> event = new SimEvent<OTSSimTimeDouble>(new OTSSimTimeDouble(triggerTime),
774 this, sensor, "trigger", new Object[] { gtu });
775 gtu.getSimulator().scheduleEvent(event);
776 gtu.addTrigger(this, event);
777 }
778 }
779 }
780 }
781 }
782
783
784
785
786
787
788 public final void addLaneBasedObject(final LaneBasedObject laneBasedObject) throws NetworkException
789 {
790 double position = laneBasedObject.getLongitudinalPosition().si;
791 if (position < 0 || position > getLength().getSI())
792 {
793 throw new NetworkException(
794 "Illegal position for laneBasedObject " + position + " valid range is 0.." + getLength().getSI());
795 }
796 List<LaneBasedObject> laneBasedObjectList = this.laneBasedObjects.get(position);
797 if (null == laneBasedObjectList)
798 {
799 laneBasedObjectList = new ArrayList<LaneBasedObject>(1);
800 this.laneBasedObjects.put(position, laneBasedObjectList);
801 }
802 laneBasedObjectList.add(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 }
826
827
828
829
830
831
832
833
834 public final List<LaneBasedObject> getLaneBasedObjects(final Length minimumPosition, final Length maximumPosition)
835 {
836 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
837 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
838 {
839 for (LaneBasedObject lbo : lbol)
840 {
841 if (lbo.getLongitudinalPosition().ge(minimumPosition) && lbo.getLongitudinalPosition().le(maximumPosition))
842 {
843 laneBasedObjectList.add(lbo);
844 }
845 }
846 }
847 return laneBasedObjectList;
848 }
849
850
851
852
853
854 public final List<LaneBasedObject> getLaneBasedObjects()
855 {
856 if (this.laneBasedObjects == null)
857 {
858 return new ArrayList<>();
859 }
860 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
861 for (List<LaneBasedObject> lbol : this.laneBasedObjects.values())
862 {
863 for (LaneBasedObject lbo : lbol)
864 {
865 laneBasedObjectList.add(lbo);
866 }
867 }
868 return laneBasedObjectList;
869 }
870
871
872
873
874
875 public final SortedMap<Double, List<LaneBasedObject>> getLaneBasedObjectMap()
876 {
877 SortedMap<Double, List<LaneBasedObject>> laneBasedObjectMap = new TreeMap<>();
878 for (double d : this.laneBasedObjects.keySet())
879 {
880 List<LaneBasedObject> laneBasedObjectList = new ArrayList<>(1);
881 for (LaneBasedObject lbo : this.laneBasedObjects.get(d))
882 {
883 laneBasedObjectList.add(lbo);
884 }
885 laneBasedObjectMap.put(d, laneBasedObjectList);
886 }
887 return laneBasedObjectMap;
888 }
889
890
891
892
893
894
895 public final Length position(final double fraction)
896 {
897 if (this.length.getUnit().isBaseSIUnit())
898 {
899 return new Length(this.length.si * fraction, LengthUnit.SI);
900 }
901 return new Length(this.length.getInUnit() * fraction, this.length.getUnit());
902 }
903
904
905
906
907
908
909 public final double positionSI(final double fraction)
910 {
911 return this.length.si * fraction;
912 }
913
914
915
916
917
918
919 public final double fraction(final Length position)
920 {
921 return position.si / this.length.si;
922 }
923
924
925
926
927
928
929 public final double fractionSI(final double positionSI)
930 {
931 return positionSI / this.length.si;
932 }
933
934
935
936
937
938
939
940
941
942 public final int addGTU(final LaneBasedGTU gtu, final double fractionalPosition) throws GTUException
943 {
944
945 int index;
946 for (index = 0; index < this.gtuList.size(); index++)
947 {
948 LaneBasedGTU otherGTU = this.gtuList.get(index);
949 if (gtu == otherGTU)
950 {
951 throw new GTUException(gtu + " already registered on Lane " + this + " [registered lanes: "
952 + gtu.positions(gtu.getFront()).keySet() + "] locations: " + gtu.positions(gtu.getFront()).values()
953 + " time: " + gtu.getSimulator().getSimulatorTime().getTime());
954 }
955 if (otherGTU.fractionalPosition(this, otherGTU.getFront()) >= fractionalPosition)
956 {
957 break;
958 }
959 }
960 this.gtuList.add(index, gtu);
961 fireTimedEvent(Lane.GTU_ADD_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size() },
962 gtu.getSimulator().getSimulatorTime());
963 getParentLink().addGTU(gtu);
964 return index;
965 }
966
967
968
969
970
971
972
973
974
975 public final int addGTU(final LaneBasedGTU gtu, final Length longitudinalPosition) throws GTUException
976 {
977 return addGTU(gtu, longitudinalPosition.getSI() / getLength().getSI());
978 }
979
980
981
982
983
984
985
986 public final void removeGTU(final LaneBasedGTU gtu, final boolean removeFromParentLink, Length position)
987 {
988 this.gtuList.remove(gtu);
989 fireTimedEvent(Lane.GTU_REMOVE_EVENT, new Object[] { gtu.getId(), gtu, this.gtuList.size(), position },
990 gtu.getSimulator().getSimulatorTime());
991 if (removeFromParentLink)
992 {
993 this.parentLink.removeGTU(gtu);
994 }
995 }
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009 public final LaneBasedGTU getGtuAhead(final Length position, final GTUDirectionality direction,
1010 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1011 {
1012 if (direction.equals(GTUDirectionality.DIR_PLUS))
1013 {
1014 for (LaneBasedGTU gtu : this.gtuList)
1015 {
1016 if (gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).gt(position))
1017 {
1018 return gtu;
1019 }
1020 }
1021 }
1022 else
1023 {
1024 for (int i = this.gtuList.size() - 1; i >= 0; i--)
1025 {
1026 LaneBasedGTU gtu = this.gtuList.get(i);
1027 if (gtu.position(this, gtu.getRelativePositions().get(relativePosition), when).lt(position))
1028 {
1029 return gtu;
1030 }
1031 }
1032 }
1033 return null;
1034 }
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046 public final List<LaneBasedObject> getObjectAhead(final Length position, final GTUDirectionality direction)
1047 {
1048 if (direction.equals(GTUDirectionality.DIR_PLUS))
1049 {
1050 for (double distance : this.laneBasedObjects.keySet())
1051 {
1052 if (distance > position.si)
1053 {
1054 return new ArrayList<>(this.laneBasedObjects.get(distance));
1055 }
1056 }
1057 }
1058 else
1059 {
1060 NavigableMap<Double, List<LaneBasedObject>> reverseLBO =
1061 (NavigableMap<Double, List<LaneBasedObject>>) this.laneBasedObjects;
1062 for (double distance : reverseLBO.descendingKeySet())
1063 {
1064 if (distance < position.si)
1065 {
1066 return new ArrayList<>(this.laneBasedObjects.get(distance));
1067 }
1068 }
1069 }
1070 return null;
1071 }
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083 public final List<LaneBasedObject> getObjectBehind(final Length position, final GTUDirectionality direction)
1084 {
1085 if (direction.equals(GTUDirectionality.DIR_PLUS))
1086 {
1087 return getObjectAhead(position, GTUDirectionality.DIR_MINUS);
1088 }
1089 return getObjectAhead(position, GTUDirectionality.DIR_PLUS);
1090 }
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104 public final LaneBasedGTU getGtuBehind(final Length position, final GTUDirectionality direction,
1105 final RelativePosition.TYPE relativePosition, final Time when) throws GTUException
1106 {
1107 if (direction.equals(GTUDirectionality.DIR_PLUS))
1108 {
1109 return getGtuAhead(position, GTUDirectionality.DIR_MINUS, relativePosition, when);
1110 }
1111 return getGtuAhead(position, direction, relativePosition, when);
1112 }
1113
1114
1115
1116
1117
1118
1119
1120 public static final Length MARGIN = new Length(0.5, LengthUnit.METER);
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135 public final Map<Lane, GTUDirectionality> nextLanes(final GTUType gtuType)
1136 {
1137 if (this.nextLanes == null)
1138 {
1139 this.nextLanes = new LinkedHashMap<>(1);
1140 }
1141 if (!this.nextLanes.containsKey(gtuType))
1142 {
1143 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1144 this.nextLanes.put(gtuType, laneMap);
1145
1146 for (Link link : getParentLink().getEndNode().getLinks())
1147 {
1148 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1149 {
1150 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1151 {
1152 if (cse instanceof Lane)
1153 {
1154 Lane lane = (Lane) cse;
1155 Length df = this.getCenterLine().getLast().distance(lane.getCenterLine().getFirst());
1156 Length dl = this.getCenterLine().getLast().distance(lane.getCenterLine().getLast());
1157
1158 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getEndNode()))
1159 {
1160
1161
1162 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1163 {
1164 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1165 }
1166 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1167 {
1168 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1169 }
1170 }
1171
1172 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getEndNode()))
1173 {
1174
1175
1176 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1177 {
1178 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1179 }
1180 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1181 {
1182 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1183 }
1184 }
1185 }
1186 }
1187 }
1188 }
1189 }
1190 return this.nextLanes.get(gtuType);
1191 }
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207 public final Map<Lane, GTUDirectionality> prevLanes(final GTUType gtuType)
1208 {
1209 if (this.prevLanes == null)
1210 {
1211 this.prevLanes = new LinkedHashMap<>(1);
1212 }
1213 if (!this.prevLanes.containsKey(gtuType))
1214 {
1215 Map<Lane, GTUDirectionality> laneMap = new LinkedHashMap<>(1);
1216 this.prevLanes.put(gtuType, laneMap);
1217
1218 for (Link link : getParentLink().getStartNode().getLinks())
1219 {
1220 if (!(link.equals(this.getParentLink())) && link instanceof CrossSectionLink)
1221 {
1222 for (CrossSectionElement cse : ((CrossSectionLink) link).getCrossSectionElementList())
1223 {
1224 if (cse instanceof Lane)
1225 {
1226 Lane lane = (Lane) cse;
1227 Length df = this.getCenterLine().getFirst().distance(lane.getCenterLine().getFirst());
1228 Length dl = this.getCenterLine().getFirst().distance(lane.getCenterLine().getLast());
1229
1230 if (df.lt(MARGIN) && df.lt(dl) && link.getStartNode().equals(getParentLink().getStartNode()))
1231 {
1232
1233
1234 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1235 {
1236 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1237 }
1238 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1239 {
1240 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1241 }
1242 }
1243
1244 else if (dl.lt(MARGIN) && dl.lt(df) && link.getEndNode().equals(getParentLink().getStartNode()))
1245 {
1246
1247
1248 if (lane.getDirectionality(gtuType).isForwardOrBoth())
1249 {
1250 laneMap.put(lane, GTUDirectionality.DIR_PLUS);
1251 }
1252 else if (lane.getDirectionality(gtuType).isBackwardOrBoth())
1253 {
1254 laneMap.put(lane, GTUDirectionality.DIR_MINUS);
1255 }
1256 }
1257 }
1258 }
1259 }
1260 }
1261 }
1262 return this.prevLanes.get(gtuType);
1263 }
1264
1265
1266
1267
1268
1269
1270
1271 public final Map<Lane, GTUDirectionality> downstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1272 {
1273 if (this.downLanes == null)
1274 {
1275 this.downLanes = new LinkedHashMap<>(1);
1276 }
1277 if (!this.downLanes.containsKey(gtuType))
1278 {
1279 this.downLanes.put(gtuType, new HashMap<>());
1280 }
1281 if (!this.downLanes.get(gtuType).containsKey(direction))
1282 {
1283 Map<Lane, GTUDirectionality> down = direction.isPlus() ? nextLanes(gtuType) : prevLanes(gtuType);
1284 down = new LinkedHashMap<>(down);
1285 Node downNode = direction.isPlus() ? getParentLink().getEndNode() : getParentLink().getStartNode();
1286 Iterator<Entry<Lane, GTUDirectionality>> iterator = down.entrySet().iterator();
1287 while (iterator.hasNext())
1288 {
1289 Entry<Lane, GTUDirectionality> entry = iterator.next();
1290 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getStartNode().equals(downNode))
1291 || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getEndNode().equals(downNode)))
1292 {
1293
1294 iterator.remove();
1295 }
1296 }
1297 this.downLanes.get(gtuType).put(direction, down);
1298 }
1299 return this.downLanes.get(gtuType).get(direction);
1300 }
1301
1302
1303
1304
1305
1306
1307
1308 public final Map<Lane, GTUDirectionality> upstreamLanes(final GTUDirectionality direction, final GTUType gtuType)
1309 {
1310 if (this.upLanes == null)
1311 {
1312 this.upLanes = new LinkedHashMap<>(1);
1313 }
1314 if (!this.upLanes.containsKey(gtuType))
1315 {
1316 this.upLanes.put(gtuType, new HashMap<>());
1317 }
1318 if (!this.upLanes.get(gtuType).containsKey(direction))
1319 {
1320 Map<Lane, GTUDirectionality> up = direction.isPlus() ? prevLanes(gtuType) : nextLanes(gtuType);
1321 up = new LinkedHashMap<>(up);
1322 Node upNode = direction.isPlus() ? getParentLink().getStartNode() : getParentLink().getEndNode();
1323 Iterator<Entry<Lane, GTUDirectionality>> iterator = up.entrySet().iterator();
1324 while (iterator.hasNext())
1325 {
1326 Entry<Lane, GTUDirectionality> entry = iterator.next();
1327 if ((entry.getValue().isPlus() && !entry.getKey().getParentLink().getEndNode().equals(upNode))
1328 || (entry.getValue().isMinus() && !entry.getKey().getParentLink().getStartNode().equals(upNode)))
1329 {
1330
1331 iterator.remove();
1332 }
1333 }
1334 this.upLanes.get(gtuType).put(direction, up);
1335 }
1336 return this.upLanes.get(gtuType).get(direction);
1337 }
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352 public final Set<Lane> accessibleAdjacentLanes(final LateralDirectionality lateralDirection, final GTUType gtuType)
1353 {
1354 Set<Lane> candidates = new LinkedHashSet<>(1);
1355 LateralDirectionality dir = this.getDirectionality(gtuType).isForwardOrBoth() ? lateralDirection
1356 : lateralDirection.isLeft() ? LateralDirectionality.RIGHT : LateralDirectionality.LEFT;
1357 for (Lane lane : neighbors(dir, gtuType))
1358 {
1359 if (lane.getDirectionality(gtuType).equals(LongitudinalDirectionality.DIR_BOTH)
1360 || lane.getDirectionality(gtuType).equals(this.getDirectionality(gtuType)))
1361 {
1362 candidates.add(lane);
1363 }
1364 }
1365 return candidates;
1366 }
1367
1368
1369
1370
1371
1372
1373
1374
1375 public final Speed getSpeedLimit(final GTUType gtuType) throws NetworkException
1376 {
1377 if (this.speedLimitMap.containsKey(gtuType))
1378 {
1379 return this.speedLimitMap.get(gtuType);
1380 }
1381 if (gtuType.getParent() != null)
1382 {
1383 return getSpeedLimit(gtuType.getParent());
1384 }
1385 throw new NetworkException("No speed limit set for GTUType " + gtuType + " on lane " + toString());
1386 }
1387
1388
1389
1390
1391
1392
1393 public final Speed getHighestSpeedLimit() throws NetworkException
1394 {
1395 Throw.when(this.speedLimitMap.isEmpty(), NetworkException.class, "Lane %s has no speed limits set.", toString());
1396 Speed out = Speed.ZERO;
1397 for (GTUType gtuType : this.speedLimitMap.keySet())
1398 {
1399 out = Speed.max(out, this.speedLimitMap.get(gtuType));
1400 }
1401 return out;
1402 }
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418 public final void setSpeedLimit(final GTUType gtuType, final Speed speedLimit)
1419 {
1420 this.speedLimitMap.put(gtuType, speedLimit);
1421 }
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431 public final void removeSpeedLimit(final GTUType gtuType)
1432 {
1433 this.speedLimitMap.remove(gtuType);
1434 }
1435
1436
1437
1438
1439 public final LaneType getLaneType()
1440 {
1441 return this.laneType;
1442 }
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461 public final LongitudinalDirectionality getDirectionality(final GTUType gtuType)
1462 {
1463 if (this.directionalityMap.containsKey(gtuType))
1464 {
1465 return this.directionalityMap.get(gtuType);
1466 }
1467 if (this.directionalityMap.containsKey(GTUType.ALL))
1468 {
1469 return this.directionalityMap.get(GTUType.ALL);
1470 }
1471 return LongitudinalDirectionality.DIR_NONE;
1472 }
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493 public void addDirectionality(final GTUType gtuType, final LongitudinalDirectionality directionality)
1494 throws NetworkException
1495 {
1496 this.directionalityMap.put(gtuType, directionality);
1497 checkDirectionality();
1498 }
1499
1500
1501
1502
1503
1504
1505
1506 public void removeDirectionality(final GTUType gtuType)
1507 {
1508 this.directionalityMap.remove(gtuType);
1509 }
1510
1511
1512
1513
1514
1515
1516
1517 private void checkDirectionality() throws NetworkException
1518 {
1519 for (GTUType gtuType : this.directionalityMap.keySet())
1520 {
1521 LongitudinalDirectionality directionality = this.directionalityMap.get(gtuType);
1522 if (!getParentLink().getDirectionality(gtuType).contains(directionality))
1523 {
1524 throw new NetworkException("Lane " + toString() + " allows " + gtuType + " a directionality of "
1525 + directionality + " which is not present in the overarching link " + getParentLink().toString());
1526 }
1527 }
1528 }
1529
1530
1531
1532
1533 public final List<LaneBasedGTU> getGtuList()
1534 {
1535 return this.gtuList == null ? new ArrayList<>() : this.gtuList;
1536 }
1537
1538
1539 @Override
1540 @SuppressWarnings("checkstyle:designforextension")
1541 protected double getZ()
1542 {
1543 return 0.0;
1544 }
1545
1546
1547
1548
1549 public final OvertakingConditions getOvertakingConditions()
1550 {
1551 return this.overtakingConditions;
1552 }
1553
1554
1555 public final String toString()
1556 {
1557 CrossSectionLink link = getParentLink();
1558 return String.format("Lane %s of %s", getId(), link.getId());
1559 }
1560
1561
1562 private Integer cachedHashCode = null;
1563
1564
1565 @SuppressWarnings("checkstyle:designforextension")
1566 @Override
1567 public int hashCode()
1568 {
1569 if (this.cachedHashCode == null)
1570 {
1571 final int prime = 31;
1572 int result = super.hashCode();
1573 result = prime * result + ((this.laneType == null) ? 0 : this.laneType.hashCode());
1574 this.cachedHashCode = result;
1575 }
1576 return this.cachedHashCode;
1577 }
1578
1579
1580 @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
1581 @Override
1582 public boolean equals(final Object obj)
1583 {
1584 if (this == obj)
1585 return true;
1586 if (!super.equals(obj))
1587 return false;
1588 if (getClass() != obj.getClass())
1589 return false;
1590 Lane other = (Lane) obj;
1591 if (this.laneType == null)
1592 {
1593 if (other.laneType != null)
1594 return false;
1595 }
1596 else if (!this.laneType.equals(other.laneType))
1597 return false;
1598 return true;
1599 }
1600
1601
1602 @Override
1603 @SuppressWarnings("checkstyle:designforextension")
1604 public Lane clone(final CrossSectionLink newParentLink, final OTSSimulatorInterface newSimulator, final boolean animation)
1605 throws NetworkException
1606 {
1607 Lane newLane = new Lane(newParentLink, newSimulator, animation, this);
1608
1609
1610 SortedMap<Double, List<SingleSensor>> newSensorMap = new TreeMap<>();
1611 for (double distance : this.sensors.keySet())
1612 {
1613 List<SingleSensor> newSensorList = new ArrayList<>();
1614 for (SingleSensor sensor : this.sensors.get(distance))
1615 {
1616 SingleSensor newSensor = ((AbstractSensor) sensor).clone(newLane, newSimulator, animation);
1617 newSensorList.add(newSensor);
1618 }
1619 newSensorMap.put(distance, newSensorList);
1620 }
1621 newLane.sensors.clear();
1622 newLane.sensors.putAll(newSensorMap);
1623
1624 SortedMap<Double, List<LaneBasedObject>> newLaneBasedObjectMap = new TreeMap<>();
1625 for (double distance : this.laneBasedObjects.keySet())
1626 {
1627 List<LaneBasedObject> newLaneBasedObjectList = new ArrayList<>();
1628 for (LaneBasedObject lbo : this.laneBasedObjects.get(distance))
1629 {
1630 AbstractLaneBasedObject laneBasedObject = (AbstractLaneBasedObject) lbo;
1631 LaneBasedObject newLbo = laneBasedObject.clone(newLane, newSimulator, animation);
1632 newLaneBasedObjectList.add(newLbo);
1633 }
1634 newLaneBasedObjectMap.put(distance, newLaneBasedObjectList);
1635 }
1636 newLane.laneBasedObjects.clear();
1637 newLane.laneBasedObjects.putAll(newLaneBasedObjectMap);
1638
1639 return newLane;
1640 }
1641 }