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