1 package org.opentrafficsim.road.gtu.lane;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.HashSet;
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.Set;
13
14 import javax.media.j3d.Bounds;
15 import javax.vecmath.Point3d;
16
17 import org.djunits.unit.DurationUnit;
18 import org.djunits.unit.LengthUnit;
19 import org.djunits.value.vdouble.scalar.Acceleration;
20 import org.djunits.value.vdouble.scalar.Duration;
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.base.parameters.ParameterException;
25 import org.opentrafficsim.core.geometry.OTSGeometryException;
26 import org.opentrafficsim.core.geometry.OTSLine3D;
27 import org.opentrafficsim.core.geometry.OTSLine3D.FractionalFallback;
28 import org.opentrafficsim.core.geometry.OTSPoint3D;
29 import org.opentrafficsim.core.gtu.AbstractGTU;
30 import org.opentrafficsim.core.gtu.GTU;
31 import org.opentrafficsim.core.gtu.GTUDirectionality;
32 import org.opentrafficsim.core.gtu.GTUException;
33 import org.opentrafficsim.core.gtu.GTUType;
34 import org.opentrafficsim.core.gtu.RelativePosition;
35 import org.opentrafficsim.core.gtu.Try;
36 import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
37 import org.opentrafficsim.core.gtu.perception.EgoPerception;
38 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
39 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanBuilder;
40 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
41 import org.opentrafficsim.core.network.LateralDirectionality;
42 import org.opentrafficsim.core.network.Link;
43 import org.opentrafficsim.core.network.NetworkException;
44 import org.opentrafficsim.core.network.OTSNetwork;
45 import org.opentrafficsim.core.perception.Historical;
46 import org.opentrafficsim.core.perception.HistoricalValue;
47 import org.opentrafficsim.core.perception.HistoryManager;
48 import org.opentrafficsim.core.perception.collections.HistoricalLinkedHashMap;
49 import org.opentrafficsim.core.perception.collections.HistoricalMap;
50 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
51 import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
52 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
53 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
54 import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
55 import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
56 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
57 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneBasedOperationalPlan;
58 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
59 import org.opentrafficsim.road.network.lane.CrossSectionElement;
60 import org.opentrafficsim.road.network.lane.CrossSectionLink;
61 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
62 import org.opentrafficsim.road.network.lane.Lane;
63 import org.opentrafficsim.road.network.lane.LaneDirection;
64 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
65 import org.opentrafficsim.road.network.speed.SpeedLimitTypes;
66
67 import nl.tudelft.simulation.dsol.SimRuntimeException;
68 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
69 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
70 import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
71 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
72 import nl.tudelft.simulation.language.Throw;
73 import nl.tudelft.simulation.language.d3.BoundingBox;
74 import nl.tudelft.simulation.language.d3.DirectedPoint;
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 public abstract class AbstractLaneBasedGTU extends AbstractGTU implements LaneBasedGTU
102 {
103
104 private static final long serialVersionUID = 20140822L;
105
106
107
108
109
110
111 private HistoricalMap<Link, Double> fractionalLinkPositions;
112
113
114
115
116
117
118
119 private final HistoricalMap<Lane, GTUDirectionality> currentLanes;
120
121
122 private Map<Lane, List<SimEventInterface<SimTimeDoubleUnit>>> pendingLeaveTriggers = new HashMap<>();
123
124
125 private Map<Lane, List<SimEventInterface<SimTimeDoubleUnit>>> pendingEnterTriggers = new HashMap<>();
126
127
128 private Speed cachedDesiredSpeed;
129
130
131 private Time desiredSpeedTime;
132
133
134 private Acceleration cachedCarFollowingAcceleration;
135
136
137 private Time carFollowingAccelerationTime;
138
139
140 private Object lock = new Object();
141
142
143 @SuppressWarnings("checkstyle:visibilitymodifier")
144 public static Length initialLocationThresholdDifference = new Length(1.0, LengthUnit.MILLIMETER);
145
146
147 private final Historical<TurnIndicatorStatus> turnIndicatorStatus;
148
149
150
151 public static boolean CACHING = true;
152
153
154
155 public static int CACHED_POSITION = 0;
156
157
158
159 public static int NON_CACHED_POSITION = 0;
160
161
162 private VehicleModel vehicleModel = VehicleModel.MINMAX;
163
164
165
166
167
168
169
170
171
172 public AbstractLaneBasedGTU(final String id, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
173 final OTSNetwork network) throws GTUException
174 {
175 super(id, gtuType, simulator, network);
176 this.fractionalLinkPositions = new HistoricalLinkedHashMap<>(HistoryManager.get(simulator));
177 this.currentLanes = new HistoricalLinkedHashMap<>(HistoryManager.get(simulator));
178 this.turnIndicatorStatus = new HistoricalValue<>(HistoryManager.get(simulator), TurnIndicatorStatus.NOTPRESENT);
179 }
180
181
182
183
184
185
186
187
188
189
190 @SuppressWarnings("checkstyle:designforextension")
191 public void init(final LaneBasedStrategicalPlanner strategicalPlanner,
192 final Set<DirectedLanePosition> initialLongitudinalPositions, final Speed initialSpeed)
193 throws NetworkException, SimRuntimeException, GTUException, OTSGeometryException
194 {
195 Throw.when(null == initialLongitudinalPositions, GTUException.class, "InitialLongitudinalPositions is null");
196 Throw.when(0 == initialLongitudinalPositions.size(), GTUException.class, "InitialLongitudinalPositions is empty set");
197
198 DirectedPoint lastPoint = null;
199 for (DirectedLanePosition pos : initialLongitudinalPositions)
200 {
201
202
203 lastPoint = pos.getLocation();
204 }
205 DirectedPoint initialLocation = lastPoint;
206
207
208 Time now = getSimulator().getSimulatorTime();
209 try
210 {
211 if (initialSpeed.si < OperationalPlan.DRIFTING_SPEED_SI)
212 {
213 this.operationalPlan
214 .set(new OperationalPlan(this, initialLocation, now, new Duration(1E-6, DurationUnit.SECOND)));
215 }
216 else
217 {
218 OTSPoint3D p2 = new OTSPoint3D(initialLocation.x + 1E-6 * Math.cos(initialLocation.getRotZ()),
219 initialLocation.y + 1E-6 * Math.sin(initialLocation.getRotZ()), initialLocation.z);
220 OTSLine3D path = new OTSLine3D(new OTSPoint3D(initialLocation), p2);
221 this.operationalPlan.set(OperationalPlanBuilder.buildConstantSpeedPlan(this, path, now, initialSpeed));
222 }
223 }
224 catch (OperationalPlanException e)
225 {
226 throw new RuntimeException("Initial operational plan could not be created.", e);
227 }
228
229
230 for (DirectedLanePosition directedLanePosition : initialLongitudinalPositions)
231 {
232 Lane lane = directedLanePosition.getLane();
233 addLaneToGtu(lane, directedLanePosition.getPosition(), directedLanePosition.getGtuDirection());
234 }
235
236
237 DirectedLanePosition referencePosition = getReferencePosition();
238 fireTimedEvent(LaneBasedGTU.LANEBASED_INIT_EVENT,
239 new Object[] { getId(), initialLocation, getLength(), getWidth(), getBaseColor(), referencePosition.getLane(),
240 referencePosition.getPosition(), referencePosition.getGtuDirection(), getGTUType() },
241 getSimulator().getSimulatorTime());
242
243
244 for (DirectedLanePosition directedLanePosition : initialLongitudinalPositions)
245 {
246 Lane lane = directedLanePosition.getLane();
247 lane.addGTU(this, directedLanePosition.getPosition());
248 }
249
250
251 super.init(strategicalPlanner, initialLocation, initialSpeed);
252
253 this.referencePositionTime = Double.NaN;
254
255 }
256
257
258
259
260 @Override
261 public void setParent(final GTU gtu) throws GTUException
262 {
263 for (Lane lane : new HashSet<>(this.currentLanes.keySet()))
264 {
265 leaveLane(lane);
266 }
267 super.setParent(gtu);
268 }
269
270
271
272
273
274
275
276
277
278 public void reinit(final Set<DirectedLanePosition> initialLongitudinalPositions)
279 throws NetworkException, SimRuntimeException, GTUException, OTSGeometryException
280 {
281 init(getStrategicalPlanner(), initialLongitudinalPositions, Speed.ZERO);
282 }
283
284
285
286
287
288
289 public final boolean isSafeToChange() throws GTUException
290 {
291 return this.fractionalLinkPositions.get(getReferencePosition().getLane().getParentLink()) > 0.0;
292 }
293
294
295 @Override
296 @SuppressWarnings("checkstyle:designforextension")
297 public void enterLane(final Lane lane, final Length position, final GTUDirectionality gtuDirection) throws GTUException
298 {
299 if (lane == null || gtuDirection == null || position == null)
300 {
301 throw new GTUException("enterLane - one of the arguments is null");
302 }
303 addLaneToGtu(lane, position, gtuDirection);
304 addGtuToLane(lane, position);
305 }
306
307
308
309
310
311
312
313
314
315 private void addLaneToGtu(final Lane lane, final Length position, final GTUDirectionality gtuDirection) throws GTUException
316 {
317 if (this.currentLanes.containsKey(lane))
318 {
319 System.err.println(this + " is already registered on lane: " + lane + " at fractional position "
320 + this.fractionalPosition(lane, RelativePosition.REFERENCE_POSITION) + " intended position is " + position
321 + " length of lane is " + lane.getLength());
322 return;
323 }
324
325
326 if (!this.fractionalLinkPositions.containsKey(lane.getParentLink()))
327 {
328 this.fractionalLinkPositions.put(lane.getParentLink(), lane.fraction(position));
329 }
330 this.currentLanes.put(lane, gtuDirection);
331 }
332
333
334
335
336
337
338
339 protected void addGtuToLane(final Lane lane, final Length position) throws GTUException
340 {
341 List<SimEventInterface<SimTimeDoubleUnit>> pending = this.pendingEnterTriggers.get(lane);
342 if (null != pending)
343 {
344 for (SimEventInterface<SimTimeDoubleUnit> event : pending)
345 {
346 if (event.getAbsoluteExecutionTime().get().ge(getSimulator().getSimulatorTime()))
347 {
348 boolean result = getSimulator().cancelEvent(event);
349 if (!result && event.getAbsoluteExecutionTime().get().ne(getSimulator().getSimulatorTime()))
350 {
351 System.err.println("addLaneToGtu, trying to remove event: NOTHING REMOVED -- result=" + result
352 + ", simTime=" + getSimulator().getSimulatorTime() + ", eventTime="
353 + event.getAbsoluteExecutionTime().get());
354 }
355 }
356 }
357 this.pendingEnterTriggers.remove(lane);
358 }
359 lane.addGTU(this, position);
360 }
361
362
363 @Override
364 @SuppressWarnings("checkstyle:designforextension")
365 public void leaveLane(final Lane lane) throws GTUException
366 {
367 leaveLane(lane, false);
368 }
369
370
371
372
373
374
375
376 @SuppressWarnings("checkstyle:designforextension")
377 public void leaveLane(final Lane lane, final boolean beingDestroyed) throws GTUException
378 {
379 Length position = position(lane, getReference());
380 this.currentLanes.remove(lane);
381 removePendingEvents(lane, this.pendingLeaveTriggers);
382 removePendingEvents(lane, this.pendingEnterTriggers);
383
384 boolean found = false;
385 for (Lane l : this.currentLanes.keySet())
386 {
387 if (l.getParentLink().equals(lane.getParentLink()))
388 {
389 found = true;
390 }
391 }
392 if (!found)
393 {
394 this.fractionalLinkPositions.remove(lane.getParentLink());
395 }
396 lane.removeGTU(this, !found, position);
397 if (this.currentLanes.size() == 0 && !beingDestroyed)
398 {
399 System.err.println("leaveLane: lanes.size() = 0 for GTU " + getId());
400 }
401 }
402
403
404
405
406
407
408 private void removePendingEvents(final Lane lane, final Map<Lane, List<SimEventInterface<SimTimeDoubleUnit>>> triggers)
409 {
410 List<SimEventInterface<SimTimeDoubleUnit>> pending = triggers.get(lane);
411 if (null != pending)
412 {
413 for (SimEventInterface<SimTimeDoubleUnit> event : pending)
414 {
415 if (event.getAbsoluteExecutionTime().get().ge(getSimulator().getSimulatorTime()))
416 {
417 boolean result = getSimulator().cancelEvent(event);
418 if (!result && event.getAbsoluteExecutionTime().get().ne(getSimulator().getSimulatorTime()))
419 {
420 System.err.println("leaveLane, trying to remove event: NOTHING REMOVED -- result=" + result
421 + ", simTime=" + getSimulator().getSimulatorTime() + ", eventTime="
422 + event.getAbsoluteExecutionTime().get());
423 }
424 }
425 }
426 triggers.remove(lane);
427 }
428 }
429
430
431 @Override
432 public void changeLaneInstantaneously(final LateralDirectionality laneChangeDirection) throws GTUException
433 {
434
435
436 DirectedLanePosition from = getReferencePosition();
437
438
439 Set<Lane> lanesToBeRemoved = new LinkedHashSet<>(this.currentLanes.keySet());
440
441
442
443
444 Map<Link, Double> newLinkPositionsLC = new LinkedHashMap<>(this.fractionalLinkPositions);
445
446
447 Set<Lane> adjLanes = from.getLane().accessibleAdjacentLanesPhysical(laneChangeDirection, getGTUType(),
448 this.currentLanes.get(from.getLane()));
449 Lane adjLane = adjLanes.iterator().next();
450 Length position = adjLane.position(from.getLane().fraction(from.getPosition()));
451 GTUDirectionality direction = getDirection(from.getLane());
452 Length planLength = Try.assign(() -> getOperationalPlan().getTraveledDistance(getSimulator().getSimulatorTime()),
453 "Exception while determining plan length.");
454 enterLaneRecursive(new LaneDirection(adjLane, direction), position, newLinkPositionsLC, planLength, lanesToBeRemoved,
455 0);
456
457
458 this.fractionalLinkPositions.clear();
459 this.fractionalLinkPositions.putAll(newLinkPositionsLC);
460
461
462 for (Lane lane : lanesToBeRemoved)
463 {
464 leaveLane(lane);
465 }
466
467
468 this.referencePositionTime = Double.NaN;
469 this.cachedPositions.clear();
470
471
472 this.fireTimedEvent(LaneBasedGTU.LANE_CHANGE_EVENT, new Object[] { getId(), laneChangeDirection, from },
473 getSimulator().getSimulatorTime());
474
475 }
476
477
478
479
480
481
482
483
484
485
486
487 private void enterLaneRecursive(final LaneDirection lane, final Length position, final Map<Link, Double> newLinkPositionsLC,
488 final Length planLength, final Set<Lane> lanesToBeRemoved, final int dir) throws GTUException
489 {
490 enterLane(lane.getLane(), position, lane.getDirection());
491 lanesToBeRemoved.remove(lane);
492 Length adjusted = lane.getDirection().isPlus() ? position.minus(planLength) : position.plus(planLength);
493 newLinkPositionsLC.put(lane.getLane().getParentLink(), adjusted.si / lane.getLength().si);
494
495
496 if (dir < 1)
497 {
498 Length rear = lane.getDirection().isPlus() ? position.plus(getRear().getDx()) : position.minus(getRear().getDx());
499 Length before = null;
500 if (lane.getDirection().isPlus() && rear.si < 0.0)
501 {
502 before = rear.neg();
503 }
504 else if (lane.getDirection().isMinus() && rear.si > lane.getLength().si)
505 {
506 before = rear.minus(lane.getLength());
507 }
508 if (before != null)
509 {
510 GTUDirectionality upDir = lane.getDirection();
511 Map<Lane, GTUDirectionality> upstream = lane.getLane().upstreamLanes(upDir, getGTUType());
512 if (!upstream.isEmpty())
513 {
514 Lane upLane = null;
515 for (Lane nextUp : upstream.keySet())
516 {
517 if (newLinkPositionsLC.containsKey(nextUp.getParentLink()))
518 {
519
520
521 upLane = nextUp;
522 break;
523 }
524 }
525 if (upLane == null)
526 {
527
528
529
530 upLane = upstream.keySet().iterator().next();
531 }
532 upDir = upstream.get(upLane);
533 LaneDirection next = new LaneDirection(upLane, upDir);
534 Length nextPos = upDir.isPlus() ? next.getLength().minus(before).minus(getRear().getDx())
535 : before.plus(getRear().getDx());
536 enterLaneRecursive(next, nextPos, newLinkPositionsLC, planLength, lanesToBeRemoved, -1);
537 }
538 }
539 }
540
541
542 if (dir > -1)
543 {
544 Length front =
545 lane.getDirection().isPlus() ? position.plus(getFront().getDx()) : position.minus(getFront().getDx());
546 Length passed = null;
547 if (lane.getDirection().isPlus() && front.si > lane.getLength().si)
548 {
549 passed = front.minus(lane.getLength());
550 }
551 else if (lane.getDirection().isMinus() && front.si < 0.0)
552 {
553 passed = front.neg();
554 }
555 if (passed != null)
556 {
557 LaneDirection next = lane.getNextLaneDirection(this);
558 Length nextPos = next.getDirection().isPlus() ? passed.minus(getFront().getDx())
559 : next.getLength().minus(passed).plus(getFront().getDx());
560 enterLaneRecursive(next, nextPos, newLinkPositionsLC, planLength, lanesToBeRemoved, 1);
561 }
562 }
563 }
564
565
566
567
568
569
570 @SuppressWarnings("checkstyle:designforextension")
571 public void initLaneChange(final LateralDirectionality laneChangeDirection) throws GTUException
572 {
573 Map<Lane, GTUDirectionality> lanesCopy = new LinkedHashMap<>(this.currentLanes);
574 Map<Lane, Double> fractionalLanePositions = new HashMap<>();
575 for (Lane lane : lanesCopy.keySet())
576 {
577 fractionalLanePositions.put(lane, fractionalPosition(lane, getReference()));
578 }
579 int numRegistered = 0;
580 for (Lane lane : lanesCopy.keySet())
581 {
582 Set<Lane> laneSet = lane.accessibleAdjacentLanesLegal(laneChangeDirection, getGTUType(), getDirection(lane));
583 if (laneSet.size() > 0)
584 {
585 numRegistered++;
586 Lane adjacentLane = laneSet.iterator().next();
587 enterLane(adjacentLane, adjacentLane.getLength().multiplyBy(fractionalLanePositions.get(lane)),
588 lanesCopy.get(lane));
589 }
590 }
591 Throw.when(numRegistered == 0, GTUException.class, "Gtu %s starting %s lane change, but no adjacent lane found.",
592 getId(), laneChangeDirection);
593 }
594
595
596
597
598
599 @SuppressWarnings("checkstyle:designforextension")
600 protected void finalizeLaneChange(final LateralDirectionality laneChangeDirection)
601 {
602 Map<Lane, GTUDirectionality> lanesCopy = new LinkedHashMap<>(this.currentLanes);
603 Set<Lane> lanesToBeRemoved = new LinkedHashSet<>();
604 Lane fromLane = null;
605 Length fromPosition = null;
606 GTUDirectionality fromDirection = null;
607 try
608 {
609
610 for (Lane lane : lanesCopy.keySet())
611 {
612 Iterator<Lane> iterator =
613 lane.accessibleAdjacentLanesPhysical(laneChangeDirection, getGTUType(), getDirection(lane)).iterator();
614 if (iterator.hasNext() && lanesCopy.keySet().contains(iterator.next()))
615 {
616 lanesToBeRemoved.add(lane);
617 }
618 }
619
620 boolean added = true;
621 while (added)
622 {
623 added = false;
624 Set<Lane> lanesToAlsoBeRemoved = new LinkedHashSet<>();
625 for (Lane lane : lanesToBeRemoved)
626 {
627 GTUDirectionality direction = getDirection(lane);
628 for (Lane nextLane : direction.isPlus() ? lane.nextLanes(getGTUType()).keySet()
629 : lane.prevLanes(getGTUType()).keySet())
630 {
631 if (lanesCopy.containsKey(nextLane) && !lanesToBeRemoved.contains(nextLane))
632 {
633 added = true;
634 lanesToAlsoBeRemoved.add(nextLane);
635 }
636 }
637 }
638 lanesToBeRemoved.addAll(lanesToAlsoBeRemoved);
639 }
640 for (Lane lane : lanesToBeRemoved)
641 {
642 double fractionalPosition = this.fractionalLinkPositions.get(lane.getParentLink());
643 if (0.0 <= fractionalPosition && fractionalPosition <= 1.0)
644 {
645 fromLane = lane;
646 fromPosition = lane.getLength().multiplyBy(fractionalPosition);
647 fromDirection = getDirection(lane);
648 }
649 leaveLane(lane);
650 }
651 }
652 catch (GTUException exception)
653 {
654
655 throw new RuntimeException("fractionalPosition on lane not possible", exception);
656 }
657 this.referencePositionTime = Double.NaN;
658 Throw.when(fromLane == null, RuntimeException.class, "No from lane for lane change event.");
659 DirectedLanePosition from;
660 try
661 {
662 from = new DirectedLanePosition(fromLane, fromPosition, fromDirection);
663 }
664 catch (GTUException exception)
665 {
666 throw new RuntimeException(exception);
667 }
668 this.fireTimedEvent(LaneBasedGTU.LANE_CHANGE_EVENT, new Object[] { getId(), laneChangeDirection, from },
669 getSimulator().getSimulatorTime());
670 }
671
672
673 @Override
674 public final GTUDirectionality getDirection(final Lane lane) throws GTUException
675 {
676 Throw.when(!this.currentLanes.containsKey(lane), GTUException.class, "getDirection: Lanes %s does not contain %s",
677 this.currentLanes.keySet(), lane);
678 return this.currentLanes.get(lane);
679 }
680
681
682 @Override
683 @SuppressWarnings("checkstyle:designforextension")
684 protected void move(final DirectedPoint fromLocation)
685 throws SimRuntimeException, GTUException, OperationalPlanException, NetworkException, ParameterException
686 {
687 DirectedPoint currentPoint = getLocation();
688
689
690 if (this.currentLanes.isEmpty())
691 {
692 destroy();
693 return;
694 }
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712 Length covered = getOperationalPlan().getTraveledDistance(getSimulator().getSimulatorTime());
713
714
715 super.move(fromLocation);
716
717
718
719
720
721 Map<Link, Double> newLinkPositions = new LinkedHashMap<>(this.fractionalLinkPositions);
722 Set<Link> done = new LinkedHashSet<>();
723 for (Lane lane : this.currentLanes.keySet())
724 {
725 if (newLinkPositions.containsKey(lane.getParentLink()) && !done.contains(lane.getParentLink()))
726 {
727 double f = newLinkPositions.get(lane.getParentLink());
728 f = getDirection(lane).isPlus() ? f + covered.si / lane.getLength().si : f - covered.si / lane.getLength().si;
729 newLinkPositions.put(lane.getParentLink(), f);
730 done.add(lane.getParentLink());
731 }
732 }
733 this.fractionalLinkPositions.clear();
734 this.fractionalLinkPositions.putAll(newLinkPositions);
735
736 DirectedLanePosition dlp = getReferencePosition();
737 fireTimedEvent(
738 LaneBasedGTU.LANEBASED_MOVE_EVENT, new Object[] { getId(), fromLocation, getSpeed(), getAcceleration(),
739 getTurnIndicatorStatus(), getOdometer(), dlp.getLane(), dlp.getPosition(), dlp.getGtuDirection() },
740 getSimulator().getSimulatorTime());
741
742 if (getOperationalPlan().getAcceleration(Duration.ZERO).si < -10
743 && getOperationalPlan().getSpeed(Duration.ZERO).si > 2.5)
744 {
745 System.err.println("GTU: " + getId() + " - getOperationalPlan().getAcceleration(Duration.ZERO).si < -10)");
746 System.err.println("Lanes in current plan: " + this.currentLanes.keySet());
747 if (getTacticalPlanner().getPerception().contains(DefaultSimplePerception.class))
748 {
749 DefaultSimplePerception p =
750 getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class);
751 System.err.println("HeadwayGTU: " + p.getForwardHeadwayGTU());
752 System.err.println("HeadwayObject: " + p.getForwardHeadwayObject());
753 }
754 }
755 DirectedPoint currentPointAfterMove = getLocation();
756 if (currentPoint.distance(currentPointAfterMove) > 0.1)
757 {
758 System.err.println(this.getId() + " jumped");
759 }
760
761 scheduleEnterLeaveTriggers();
762 }
763
764
765 @Override
766 public final Map<Lane, Length> positions(final RelativePosition relativePosition) throws GTUException
767 {
768 return positions(relativePosition, getSimulator().getSimulatorTime());
769 }
770
771
772 @Override
773 public final Map<Lane, Length> positions(final RelativePosition relativePosition, final Time when) throws GTUException
774 {
775 Map<Lane, Length> positions = new LinkedHashMap<>();
776 for (Lane lane : this.currentLanes.keySet())
777 {
778 positions.put(lane, position(lane, relativePosition, when));
779 }
780 return positions;
781 }
782
783
784 @Override
785 public final Length position(final Lane lane, final RelativePosition relativePosition) throws GTUException
786 {
787 return position(lane, relativePosition, getSimulator().getSimulatorTime());
788 }
789
790
791 @Override
792 @SuppressWarnings("checkstyle:designforextension")
793 public Length translatedPosition(final Lane projectionLane, final RelativePosition relativePosition, final Time when)
794 throws GTUException
795 {
796 CrossSectionLink link = projectionLane.getParentLink();
797 for (CrossSectionElement cse : link.getCrossSectionElementList())
798 {
799 if (cse instanceof Lane)
800 {
801 Lane cseLane = (Lane) cse;
802 if (null != this.currentLanes.get(cseLane))
803 {
804 double fractionalPosition = fractionalPosition(cseLane, RelativePosition.REFERENCE_POSITION, when);
805 Length pos = new Length(projectionLane.getLength().getSI() * fractionalPosition, LengthUnit.SI);
806 if (this.currentLanes.get(cseLane).isPlus())
807 {
808 return pos.plus(relativePosition.getDx());
809 }
810 return pos.minus(relativePosition.getDx());
811 }
812 }
813 }
814 throw new GTUException(this + " is not on any lane of Link " + link);
815 }
816
817
818 @Override
819 @SuppressWarnings("checkstyle:designforextension")
820 public Length projectedPosition(final Lane projectionLane, final RelativePosition relativePosition, final Time when)
821 throws GTUException
822 {
823 CrossSectionLink link = projectionLane.getParentLink();
824 for (CrossSectionElement cse : link.getCrossSectionElementList())
825 {
826 if (cse instanceof Lane)
827 {
828 Lane cseLane = (Lane) cse;
829 if (null != this.currentLanes.get(cseLane))
830 {
831 double fractionalPosition = fractionalPosition(cseLane, relativePosition, when);
832 return new Length(projectionLane.getLength().getSI() * fractionalPosition, LengthUnit.SI);
833 }
834 }
835 }
836 throw new GTUException(this + " is not on any lane of Link " + link);
837 }
838
839
840 private double cachePositionsTime = Double.NaN;
841
842
843 private Map<Integer, Length> cachedPositions = new HashMap<>();
844
845
846 @Override
847 @SuppressWarnings("checkstyle:designforextension")
848 public Length position(final Lane lane, final RelativePosition relativePosition, final Time when) throws GTUException
849 {
850 int cacheIndex = 0;
851 if (CACHING)
852 {
853 cacheIndex = 17 * lane.hashCode() + relativePosition.hashCode();
854 Length l;
855 if (when.si == this.cachePositionsTime && (l = this.cachedPositions.get(cacheIndex)) != null)
856 {
857
858
859
860
861
862
863
864 CACHED_POSITION++;
865 return l;
866 }
867 if (when.si != this.cachePositionsTime)
868 {
869 this.cachedPositions.clear();
870 this.cachePositionsTime = when.si;
871 }
872 }
873 NON_CACHED_POSITION++;
874
875 synchronized (this.lock)
876 {
877 double longitudinalPosition;
878 try
879 {
880 longitudinalPosition = lane.positionSI(this.fractionalLinkPositions.get(when).get(lane.getParentLink()));
881 }
882 catch (NullPointerException exception)
883 {
884 throw exception;
885 }
886 double loc;
887 try
888 {
889 OperationalPlan plan = getOperationalPlan(when);
890 if (!(plan instanceof LaneBasedOperationalPlan) || !((LaneBasedOperationalPlan) plan).isDeviative())
891 {
892 if (this.currentLanes.get(when).get(lane).isPlus())
893 {
894 loc = longitudinalPosition + plan.getTraveledDistanceSI(when) + relativePosition.getDx().si;
895 }
896 else
897 {
898 loc = longitudinalPosition - plan.getTraveledDistanceSI(when) - relativePosition.getDx().si;
899 }
900 }
901 else
902 {
903
904 DirectedPoint p = plan.getLocation(when, relativePosition);
905 double f = lane.getCenterLine().projectFractional(null, null, p.x, p.y, FractionalFallback.NaN);
906 if (!Double.isNaN(f))
907 {
908 loc = f * lane.getLength().si;
909 }
910 else
911 {
912
913
914
915 boolean upstream = this.fractionalLinkPositions.get(lane.getParentLink()) < 0.0 ? true : false;
916
917
918 int i = 0;
919 while (true)
920 {
921 double distance = getDistanceAtOtherLane(lane, when, upstream, 0.0, p);
922 if (!Double.isNaN(distance))
923 {
924 loc = upstream ? -distance : distance + lane.getLength().si;
925 break;
926 }
927 else if (i == 1)
928 {
929
930
931
932
933 loc = lane.getLength().si * lane.getCenterLine().projectFractional(null, null, p.x, p.y,
934 FractionalFallback.ENDPOINT);
935 break;
936 }
937
938 i++;
939 upstream = !upstream;
940 }
941 }
942 }
943 }
944 catch (NullPointerException e)
945 {
946 throw new GTUException("lanesCurrentOperationalPlan or fractionalLinkPositions is null", e);
947 }
948 catch (Exception e)
949 {
950 System.err.println(toString());
951 System.err.println(this.currentLanes.get(when));
952 System.err.println(this.fractionalLinkPositions.get(when));
953 throw new GTUException(e);
954 }
955 if (Double.isNaN(loc))
956 {
957 System.out.println("loc is NaN");
958 }
959 Length length = Length.createSI(loc);
960 if (CACHING)
961 {
962 this.cachedPositions.put(cacheIndex, length);
963 }
964 return length;
965 }
966 }
967
968
969
970
971
972
973
974
975
976
977
978
979 private double getDistanceAtOtherLane(final Lane lane, final Time when, final boolean upstream, final double distance,
980 final DirectedPoint point)
981 {
982 Set<Lane> nextLanes = new HashSet<>(upstream == this.currentLanes.get(lane).isPlus()
983 ? lane.prevLanes(getGTUType()).keySet() : lane.nextLanes(getGTUType()).keySet());
984 nextLanes.retainAll(this.currentLanes.keySet());
985
986
987
988
989
990
991
992
993
994
995 if (nextLanes.size() == 0)
996 {
997 return Double.NaN;
998 }
999 Throw.when(nextLanes.size() > 1, IllegalStateException.class,
1000 "A position (%s) of GTU %s is not on any of the current registered lanes.", point, this.getId());
1001 Lane nextLane = nextLanes.iterator().next();
1002 double f = nextLane.getCenterLine().projectFractional(null, null, point.x, point.y, FractionalFallback.NaN);
1003 if (Double.isNaN(f))
1004 {
1005 getDistanceAtOtherLane(nextLane, when, upstream, distance + nextLane.getLength().si, point);
1006 }
1007 return distance + (upstream == this.currentLanes.get(nextLane).isPlus() ? 1.0 - f : f) * nextLane.getLength().si;
1008 }
1009
1010
1011 private double referencePositionTime = Double.NaN;
1012
1013
1014 private DirectedLanePosition cachedReferencePosition = null;
1015
1016
1017 @Override
1018 @SuppressWarnings("checkstyle:designforextension")
1019 public DirectedLanePosition getReferencePosition() throws GTUException
1020 {
1021 if (this.referencePositionTime == getSimulator().getSimulatorTime().si)
1022 {
1023 return this.cachedReferencePosition;
1024 }
1025 Lane refLane = null;
1026 Double closest = Double.POSITIVE_INFINITY;
1027 for (Lane lane : this.currentLanes.keySet())
1028 {
1029 double fraction = fractionalPosition(lane, getReference());
1030 if (fraction >= 0.0 && fraction <= 1.0)
1031 {
1032
1033
1034 if (refLane == null)
1035 {
1036 refLane = lane;
1037 }
1038 else
1039 {
1040 DirectedPoint loc = getLocation();
1041 try
1042 {
1043 double f =
1044 lane.getCenterLine().projectFractional(null, null, loc.x, loc.y, FractionalFallback.ENDPOINT);
1045 double distance = loc.distance(lane.getCenterLine().getLocationFraction(f));
1046 if (distance < closest)
1047 {
1048 refLane = lane;
1049 closest = distance;
1050 }
1051 }
1052 catch (OTSGeometryException exception)
1053 {
1054 throw new RuntimeException("Exception while determining reference position between lanes.", exception);
1055 }
1056 }
1057 }
1058 }
1059 if (refLane != null)
1060 {
1061 this.cachedReferencePosition =
1062 new DirectedLanePosition(refLane, position(refLane, getReference()), this.getDirection(refLane));
1063 this.referencePositionTime = getSimulator().getSimulatorTime().si;
1064 return this.cachedReferencePosition;
1065 }
1066
1067
1068
1069
1070
1071
1072
1073 throw new GTUException("The reference point of GTU " + this + " is not on any of the lanes on which it is registered");
1074 }
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084 @SuppressWarnings("checkstyle:designforextension")
1085 protected void scheduleEnterLeaveTriggers() throws NetworkException, SimRuntimeException, GTUException
1086 {
1087
1088
1089 double moveSI = getOperationalPlan().getTotalLength().si;
1090
1091
1092 Map<Lane, GTUDirectionality> lanesCopy = new LinkedHashMap<>(this.currentLanes);
1093 Iterator<Lane> it = lanesCopy.keySet().iterator();
1094 Lane enteredLane = null;
1095 while (it.hasNext() || enteredLane != null)
1096 {
1097
1098 Lane lane;
1099 GTUDirectionality laneDir;
1100 if (enteredLane == null)
1101 {
1102 lane = it.next();
1103 laneDir = lanesCopy.get(lane);
1104 }
1105 else
1106 {
1107 lane = enteredLane;
1108 laneDir = this.currentLanes.get(lane);
1109 }
1110 double sign = laneDir.isPlus() ? 1.0 : -1.0;
1111 enteredLane = null;
1112
1113
1114 if (!Collections.disjoint(this.currentLanes.keySet(), lane.downstreamLanes(laneDir, getGTUType()).keySet()))
1115 {
1116 continue;
1117 }
1118
1119
1120 double referenceStartSI = this.fractionalLinkPositions.get(lane.getParentLink()) * lane.getLength().getSI();
1121
1122
1123 if (laneDir.isPlus())
1124 {
1125 lane.scheduleSensorTriggers(this, referenceStartSI, moveSI);
1126 }
1127 else
1128 {
1129 lane.scheduleSensorTriggers(this, referenceStartSI - moveSI, moveSI);
1130 }
1131
1132 double nextFrontPosSI = referenceStartSI + sign * (moveSI + getFront().getDx().si);
1133
1134
1135 Lane nextLane = null;
1136 GTUDirectionality nextDirection = null;
1137 Length refPosAtLastTimestep = null;
1138 DirectedPoint end = null;
1139 if (laneDir.isPlus() ? nextFrontPosSI > lane.getLength().si : nextFrontPosSI < 0.0)
1140 {
1141 LaneDirection next = new LaneDirection(lane, laneDir).getNextLaneDirection(this);
1142
1143
1144
1145
1146
1147 nextLane = next.getLane();
1148 nextDirection = next.getDirection();
1149 double endPos = laneDir.isPlus() ? lane.getLength().si - getFront().getDx().si : getFront().getDx().si;
1150 Lane endLane = lane;
1151 GTUDirectionality endLaneDir = laneDir;
1152 while (endLaneDir.isPlus() ? endPos < 0.0 : endPos > endLane.getLength().si)
1153 {
1154
1155 Map<Lane, GTUDirectionality> map = endLane.upstreamLanes(endLaneDir, getGTUType());
1156 map.keySet().retainAll(this.currentLanes.keySet());
1157 double remain = endLaneDir.isPlus() ? -endPos : endPos - endLane.getLength().si;
1158 endLane = map.keySet().iterator().next();
1159 endLaneDir = map.get(endLane);
1160 endPos = endLaneDir.isPlus() ? endLane.getLength().si - remain : remain;
1161 }
1162 end = endLane.getCenterLine().getLocationExtendedSI(endPos);
1163 if (laneDir.isPlus())
1164 {
1165 refPosAtLastTimestep = nextDirection.isPlus() ? Length.createSI(referenceStartSI - lane.getLength().si)
1166 : Length.createSI(nextLane.getLength().si - referenceStartSI + lane.getLength().si);
1167 }
1168 else
1169 {
1170 refPosAtLastTimestep = nextDirection.isPlus() ? Length.createSI(-referenceStartSI)
1171 : Length.createSI(nextLane.getLength().si + referenceStartSI);
1172 }
1173 }
1174
1175 if (end != null)
1176 {
1177 Time enterTime = getOperationalPlan().timeAtPoint(end, false);
1178 if (!Double.isNaN(enterTime.si))
1179 {
1180 addLaneToGtu(nextLane, refPosAtLastTimestep, nextDirection);
1181 enteredLane = nextLane;
1182 Length coveredDistance = Try.assign(() -> getOperationalPlan().getTraveledDistance(enterTime),
1183 "Enter time of lane beyond plan.");
1184 SimEventInterface<SimTimeDoubleUnit> event = getSimulator().scheduleEventAbs(enterTime, this, this,
1185 "addGtuToLane", new Object[] { nextLane, refPosAtLastTimestep.plus(coveredDistance) });
1186 addEnterTrigger(nextLane, event);
1187
1188
1189 }
1190 }
1191 }
1192
1193
1194 for (Lane lane : this.currentLanes.keySet())
1195 {
1196 double referenceStartSI = this.fractionalLinkPositions.get(lane.getParentLink()) * lane.getLength().getSI();
1197 Time exitTime = null;
1198
1199 if (this.currentLanes.get(lane).equals(GTUDirectionality.DIR_PLUS))
1200 {
1201 if (referenceStartSI + getRear().getDx().si + moveSI > lane.getLength().si)
1202 {
1203 try
1204 {
1205
1206
1207
1208
1209
1210
1211 exitTime = getOperationalPlan().timeAtPoint(lane.getCenterLine().getLocationFraction(1.0),
1212 this.fractionalLinkPositions.get(lane.getParentLink()) > 1.0);
1213 }
1214 catch (OTSGeometryException exception)
1215 {
1216 throw new RuntimeException(exception);
1217 }
1218 }
1219 }
1220 else
1221 {
1222 if (referenceStartSI - getRear().getDx().si - moveSI < 0.0)
1223 {
1224 try
1225 {
1226 exitTime = getOperationalPlan().timeAtPoint(lane.getCenterLine().getLocationFraction(0.0),
1227 this.fractionalLinkPositions.get(lane.getParentLink()) < 0.0);
1228 }
1229 catch (OTSGeometryException exception)
1230 {
1231 throw new RuntimeException(exception);
1232 }
1233 }
1234 }
1235 if (exitTime != null && !Double.isNaN(exitTime.si))
1236 {
1237
1238
1239
1240
1241
1242 SimEvent<SimTimeDoubleUnit> event = new SimEvent<>(new SimTimeDoubleUnit(exitTime), this, this, "leaveLane",
1243 new Object[] { lane, new Boolean(false) });
1244 getSimulator().scheduleEvent(event);
1245 addTrigger(lane, event);
1246 }
1247 }
1248 }
1249
1250
1251 @Override
1252 public final Map<Lane, Double> fractionalPositions(final RelativePosition relativePosition) throws GTUException
1253 {
1254 return fractionalPositions(relativePosition, getSimulator().getSimulatorTime());
1255 }
1256
1257
1258 @Override
1259 public final Map<Lane, Double> fractionalPositions(final RelativePosition relativePosition, final Time when)
1260 throws GTUException
1261 {
1262 Map<Lane, Double> positions = new LinkedHashMap<>();
1263 for (Lane lane : this.currentLanes.keySet())
1264 {
1265 positions.put(lane, fractionalPosition(lane, relativePosition, when));
1266 }
1267 return positions;
1268 }
1269
1270
1271 @Override
1272 public final double fractionalPosition(final Lane lane, final RelativePosition relativePosition, final Time when)
1273 throws GTUException
1274 {
1275 return position(lane, relativePosition, when).getSI() / lane.getLength().getSI();
1276 }
1277
1278
1279 @Override
1280 public final double fractionalPosition(final Lane lane, final RelativePosition relativePosition) throws GTUException
1281 {
1282 return position(lane, relativePosition).getSI() / lane.getLength().getSI();
1283 }
1284
1285
1286 @Override
1287 public final void addTrigger(final Lane lane, final SimEventInterface<SimTimeDoubleUnit> event)
1288 {
1289 List<SimEventInterface<SimTimeDoubleUnit>> list = this.pendingLeaveTriggers.get(lane);
1290 if (null == list)
1291 {
1292 list = new ArrayList<>();
1293 }
1294 list.add(event);
1295 this.pendingLeaveTriggers.put(lane, list);
1296 }
1297
1298
1299
1300
1301
1302
1303 private void addEnterTrigger(final Lane lane, final SimEventInterface<SimTimeDoubleUnit> event)
1304 {
1305 List<SimEventInterface<SimTimeDoubleUnit>> list = this.pendingEnterTriggers.get(lane);
1306 if (null == list)
1307 {
1308 list = new ArrayList<>();
1309 }
1310 list.add(event);
1311 this.pendingEnterTriggers.put(lane, list);
1312 }
1313
1314
1315
1316
1317
1318 public void setVehicleModel(final VehicleModel vehicleModel)
1319 {
1320 this.vehicleModel = vehicleModel;
1321 }
1322
1323
1324 @Override
1325 public VehicleModel getVehicleModel()
1326 {
1327 return this.vehicleModel;
1328 }
1329
1330
1331 @Override
1332 @SuppressWarnings("checkstyle:designforextension")
1333 public void destroy()
1334 {
1335 DirectedLanePosition dlp = null;
1336 try
1337 {
1338 dlp = getReferencePosition();
1339 }
1340 catch (@SuppressWarnings("unused") GTUException e)
1341 {
1342
1343 }
1344 DirectedPoint location = this.getOperationalPlan() == null ? new DirectedPoint(0.0, 0.0, 0.0) : getLocation();
1345
1346 synchronized (this.lock)
1347 {
1348 Set<Lane> laneSet = new LinkedHashSet<>(this.currentLanes.keySet());
1349
1350 for (Lane lane : laneSet)
1351 {
1352 try
1353 {
1354 leaveLane(lane, true);
1355 }
1356 catch (@SuppressWarnings("unused") GTUException e)
1357 {
1358
1359 }
1360 }
1361 }
1362
1363 if (dlp != null && dlp.getLane() != null)
1364 {
1365 Lane referenceLane = dlp.getLane();
1366 fireTimedEvent(LaneBasedGTU.LANEBASED_DESTROY_EVENT,
1367 new Object[] { getId(), location, getOdometer(), referenceLane, dlp.getPosition(), dlp.getGtuDirection() },
1368 getSimulator().getSimulatorTime());
1369 }
1370 else
1371 {
1372 fireTimedEvent(LaneBasedGTU.LANEBASED_DESTROY_EVENT,
1373 new Object[] { getId(), location, getOdometer(), null, Length.ZERO, null },
1374 getSimulator().getSimulatorTime());
1375 }
1376
1377 super.destroy();
1378 }
1379
1380
1381 @Override
1382 public final Bounds getBounds()
1383 {
1384 double dx = 0.5 * getLength().doubleValue();
1385 double dy = 0.5 * getWidth().doubleValue();
1386 return new BoundingBox(new Point3d(-dx, -dy, 0.0), new Point3d(dx, dy, 0.0));
1387 }
1388
1389
1390 @Override
1391 public final LaneBasedStrategicalPlanner getStrategicalPlanner()
1392 {
1393 return (LaneBasedStrategicalPlanner) super.getStrategicalPlanner();
1394 }
1395
1396
1397 @Override
1398 public final LaneBasedStrategicalPlanner getStrategicalPlanner(final Time time)
1399 {
1400 return (LaneBasedStrategicalPlanner) super.getStrategicalPlanner(time);
1401 }
1402
1403
1404 @Override
1405 public Speed getDesiredSpeed()
1406 {
1407 Time simTime = getSimulator().getSimulatorTime();
1408 if (this.desiredSpeedTime == null || this.desiredSpeedTime.si < simTime.si)
1409 {
1410 InfrastructurePerception infra =
1411 getTacticalPlanner().getPerception().getPerceptionCategoryOrNull(InfrastructurePerception.class);
1412 SpeedLimitInfo speedInfo;
1413 if (infra == null)
1414 {
1415 speedInfo = new SpeedLimitInfo();
1416 speedInfo.addSpeedInfo(SpeedLimitTypes.MAX_VEHICLE_SPEED, getMaximumSpeed());
1417 }
1418 else
1419 {
1420
1421 speedInfo = infra.getSpeedLimitProspect(RelativeLane.CURRENT).getSpeedLimitInfo(Length.ZERO);
1422 }
1423 this.cachedDesiredSpeed =
1424 Try.assign(() -> getTacticalPlanner().getCarFollowingModel().desiredSpeed(getParameters(), speedInfo),
1425 "Parameter exception while obtaining the desired speed.");
1426 this.desiredSpeedTime = simTime;
1427 }
1428 return this.cachedDesiredSpeed;
1429 }
1430
1431
1432 @Override
1433 public Acceleration getCarFollowingAcceleration()
1434 {
1435 Time simTime = getSimulator().getSimulatorTime();
1436 if (this.carFollowingAccelerationTime == null || this.carFollowingAccelerationTime.si < simTime.si)
1437 {
1438 LanePerception perception = getTacticalPlanner().getPerception();
1439
1440 EgoPerception ego = perception.getPerceptionCategoryOrNull(EgoPerception.class);
1441 Throw.whenNull(ego, "EgoPerception is required to determine the speed.");
1442 Speed speed = ego.getSpeed();
1443
1444 InfrastructurePerception infra = perception.getPerceptionCategoryOrNull(InfrastructurePerception.class);
1445 Throw.whenNull(infra, "InfrastructurePerception is required to determine the desired speed.");
1446 SpeedLimitInfo speedInfo = infra.getSpeedLimitProspect(RelativeLane.CURRENT).getSpeedLimitInfo(Length.ZERO);
1447
1448 NeighborsPerception neighbors = perception.getPerceptionCategoryOrNull(NeighborsPerception.class);
1449 Throw.whenNull(neighbors, "NeighborsPerception is required to determine the car-following acceleration.");
1450 PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders = neighbors.getLeaders(RelativeLane.CURRENT);
1451
1452 this.cachedCarFollowingAcceleration =
1453 Try.assign(() -> getTacticalPlanner().getCarFollowingModel().followingAcceleration(getParameters(), speed,
1454 speedInfo, leaders), "Parameter exception while obtaining the desired speed.");
1455 this.carFollowingAccelerationTime = simTime;
1456 }
1457 return this.cachedCarFollowingAcceleration;
1458 }
1459
1460
1461 @Override
1462 public final TurnIndicatorStatus getTurnIndicatorStatus()
1463 {
1464 return this.turnIndicatorStatus.get();
1465 }
1466
1467
1468 @Override
1469 public final TurnIndicatorStatus getTurnIndicatorStatus(final Time time)
1470 {
1471 return this.turnIndicatorStatus.get(time);
1472 }
1473
1474
1475 @Override
1476 public final void setTurnIndicatorStatus(final TurnIndicatorStatus turnIndicatorStatus)
1477 {
1478 this.turnIndicatorStatus.set(turnIndicatorStatus);
1479 }
1480
1481
1482 @Override
1483 @SuppressWarnings("checkstyle:designforextension")
1484 public String toString()
1485 {
1486 return String.format("GTU " + getId());
1487 }
1488
1489 }