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