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