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