1 package org.opentrafficsim.road.gtu.lane;
2
3 import java.util.ArrayList;
4 import java.util.LinkedHashMap;
5 import java.util.LinkedHashSet;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.Set;
10 import java.util.SortedMap;
11
12 import org.djunits.unit.DirectionUnit;
13 import org.djunits.unit.LengthUnit;
14 import org.djunits.unit.PositionUnit;
15 import org.djunits.value.vdouble.scalar.Acceleration;
16 import org.djunits.value.vdouble.scalar.Direction;
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.djunits.value.vdouble.vector.PositionVector;
22 import org.djutils.draw.line.PolyLine2d;
23 import org.djutils.draw.point.OrientedPoint2d;
24 import org.djutils.draw.point.Point2d;
25 import org.djutils.event.EventType;
26 import org.djutils.exceptions.Throw;
27 import org.djutils.exceptions.Try;
28 import org.djutils.immutablecollections.ImmutableLinkedHashSet;
29 import org.djutils.immutablecollections.ImmutableSet;
30 import org.djutils.logger.CategoryLogger;
31 import org.djutils.metadata.MetaData;
32 import org.djutils.metadata.ObjectDescriptor;
33 import org.djutils.multikeymap.MultiKeyMap;
34 import org.opentrafficsim.base.geometry.OtsLine2d;
35 import org.opentrafficsim.base.geometry.OtsLine2d.FractionalFallback;
36 import org.opentrafficsim.base.parameters.ParameterException;
37 import org.opentrafficsim.core.gtu.Gtu;
38 import org.opentrafficsim.core.gtu.GtuException;
39 import org.opentrafficsim.core.gtu.GtuType;
40 import org.opentrafficsim.core.gtu.RelativePosition;
41 import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
42 import org.opentrafficsim.core.gtu.perception.EgoPerception;
43 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
44 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
45 import org.opentrafficsim.core.gtu.plan.operational.Segments;
46 import org.opentrafficsim.core.network.LateralDirectionality;
47 import org.opentrafficsim.core.network.Link;
48 import org.opentrafficsim.core.network.NetworkException;
49 import org.opentrafficsim.core.perception.Historical;
50 import org.opentrafficsim.core.perception.HistoricalValue;
51 import org.opentrafficsim.core.perception.HistoryManager;
52 import org.opentrafficsim.core.perception.collections.HistoricalArrayList;
53 import org.opentrafficsim.core.perception.collections.HistoricalList;
54 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
55 import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
56 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
57 import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
58 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.NeighborsPerception;
59 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu;
60 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneBasedOperationalPlan;
61 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
62 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
63 import org.opentrafficsim.road.network.RoadNetwork;
64 import org.opentrafficsim.road.network.lane.CrossSectionLink;
65 import org.opentrafficsim.road.network.lane.Lane;
66 import org.opentrafficsim.road.network.lane.LanePosition;
67 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
68 import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
69 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
70 import org.opentrafficsim.road.network.speed.SpeedLimitTypes;
71
72 import nl.tudelft.simulation.dsol.SimRuntimeException;
73 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
74
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 public class LaneBasedGtu extends Gtu implements LaneBasedObject
100 {
101
102 private static final long serialVersionUID = 20140822L;
103
104
105 private final HistoricalList<CrossSection> crossSections;
106
107
108 private int referenceLaneIndex = 0;
109
110
111 private double referencePositionTime = Double.NaN;
112
113
114 private LanePosition cachedReferencePosition = null;
115
116
117 private SimEventInterface<Duration> pendingLeaveTrigger;
118
119
120 private SimEventInterface<Duration> pendingEnterTrigger;
121
122
123 private SimEventInterface<Duration> finalizeLaneChangeEvent;
124
125
126 private Set<SimEventInterface<Duration>> sensorEvents = new LinkedHashSet<>();
127
128
129 private Speed cachedDesiredSpeed;
130
131
132 private Time desiredSpeedTime;
133
134
135 private Acceleration cachedCarFollowingAcceleration;
136
137
138 private Time carFollowingAccelerationTime;
139
140
141 private Object lock = new Object();
142
143
144 @SuppressWarnings("checkstyle:visibilitymodifier")
145 public static Length initialLocationThresholdDifference = new Length(1.0, LengthUnit.MILLIMETER);
146
147
148 public static Length eventMargin = Length.instantiateSI(50.0);
149
150
151 private final Historical<TurnIndicatorStatus> turnIndicatorStatus;
152
153
154
155 public static boolean CACHING = true;
156
157
158
159 public static int CACHED_POSITION = 0;
160
161
162
163 public static int NON_CACHED_POSITION = 0;
164
165
166 private VehicleModel vehicleModel = VehicleModel.MINMAX;
167
168
169 private boolean instantaneousLaneChange = false;
170
171
172 private Length noLaneChangeDistance;
173
174
175
176
177
178
179
180
181
182
183
184
185 public LaneBasedGtu(final String id, final GtuType gtuType, final Length length, final Length width,
186 final Speed maximumSpeed, final Length front, final RoadNetwork network) throws GtuException
187 {
188 super(id, gtuType, network.getSimulator(), network, length, width, maximumSpeed, front, Length.ZERO);
189 HistoryManager historyManager = network.getSimulator().getReplication().getHistoryManager(network.getSimulator());
190 this.crossSections = new HistoricalArrayList<>(historyManager, this);
191 this.turnIndicatorStatus = new HistoricalValue<>(historyManager, this, TurnIndicatorStatus.NOTPRESENT);
192 }
193
194
195
196
197
198
199
200
201
202 @SuppressWarnings("checkstyle:designforextension")
203 public void init(final LaneBasedStrategicalPlanner strategicalPlanner, final LanePosition longitudinalPosition,
204 final Speed initialSpeed) throws NetworkException, SimRuntimeException, GtuException
205 {
206 Throw.when(null == longitudinalPosition, GtuException.class, "InitialLongitudinalPositions is null");
207
208 OrientedPoint2d initialLocation = longitudinalPosition.getLocation();
209
210
211
212 Time now = getSimulator().getSimulatorAbsTime();
213 if (initialSpeed.si < OperationalPlan.DRIFTING_SPEED_SI)
214 {
215 setOperationalPlan(OperationalPlan.standStill(this, initialLocation, now, Duration.instantiateSI(1E-6)));
216 }
217 else
218 {
219 Point2d p2 = new Point2d(initialLocation.x + 1E-6 * Math.cos(initialLocation.getDirZ()),
220 initialLocation.y + 1E-6 * Math.sin(initialLocation.getDirZ()));
221 OtsLine2d path = new OtsLine2d(initialLocation, p2);
222 setOperationalPlan(new OperationalPlan(this, path, now,
223 Segments.off(initialSpeed, path.getTypedLength().divide(initialSpeed), Acceleration.ZERO)));
224 }
225
226 enterLaneRecursive(longitudinalPosition.lane(), longitudinalPosition.position(), 0);
227
228
229 super.init(strategicalPlanner, initialLocation, initialSpeed);
230
231 this.referencePositionTime = Double.NaN;
232 }
233
234
235
236
237 @Override
238 public synchronized void setParent(final Gtu gtu) throws GtuException
239 {
240 leaveAllLanes();
241 super.setParent(gtu);
242 }
243
244
245
246
247 private void leaveAllLanes()
248 {
249 for (CrossSection crossSection : this.crossSections)
250 {
251 boolean removeFromParentLink = true;
252 for (Lane lane : crossSection.getLanes())
253 {
254
255 Length pos = Try.assign(() -> position(lane, getReference()), "Unexpected exception.");
256 lane.removeGtu(this, removeFromParentLink, pos);
257 removeFromParentLink = false;
258 }
259 }
260 this.crossSections.clear();
261 }
262
263
264
265
266
267
268
269
270 public void reinit(final LanePosition initialLongitudinalPosition)
271 throws NetworkException, SimRuntimeException, GtuException
272 {
273 init(getStrategicalPlanner(), initialLongitudinalPosition, Speed.ZERO);
274 }
275
276
277
278
279
280
281 public synchronized void changeLaneInstantaneously(final LateralDirectionality laneChangeDirection) throws GtuException
282 {
283
284
285 LanePosition from = getReferencePosition();
286
287
288 Set<Lane> adjLanes = from.lane().accessibleAdjacentLanesPhysical(laneChangeDirection, getType());
289 Lane adjLane = adjLanes.iterator().next();
290 Length position = adjLane.position(from.lane().fraction(from.position()));
291 leaveAllLanes();
292 enterLaneRecursive(adjLane, position, 0);
293
294
295 this.referencePositionTime = Double.NaN;
296 this.cachedPositions.clear();
297
298
299 this.fireTimedEvent(
300 LaneBasedGtu.LANE_CHANGE_EVENT, new Object[] {getId(), laneChangeDirection.name(),
301 from.lane().getLink().getId(), from.lane().getId(), from.position()},
302 getSimulator().getSimulatorTime());
303
304 }
305
306
307
308
309
310
311
312
313
314 private void enterLaneRecursive(final Lane lane, final Length position, final int dir) throws GtuException
315 {
316 List<Lane> lanes = new ArrayList<>();
317 lanes.add(lane);
318 int index = dir > 0 ? this.crossSections.size() : 0;
319 this.crossSections.add(index, new CrossSection(lanes));
320 lane.addGtu(this, position);
321
322
323 if (dir < 1)
324 {
325 Length rear = position.plus(getRear().dx());
326 Length before = null;
327 if (rear.si < 0.0)
328 {
329 before = rear.neg();
330 }
331 if (before != null)
332 {
333 ImmutableSet<Lane> upstream = new ImmutableLinkedHashSet<>(lane.prevLanes(getType()));
334 if (!upstream.isEmpty())
335 {
336 Lane upLane = null;
337 for (Lane nextUp : upstream)
338 {
339 for (CrossSection crossSection : this.crossSections)
340 {
341 if (crossSection.getLanes().contains(nextUp))
342 {
343
344
345 upLane = nextUp;
346 break;
347 }
348 }
349 }
350 if (upLane == null)
351 {
352
353
354 upLane = upstream.iterator().next();
355 }
356 Lane next = upLane;
357
358 Length nextPos = next.getLength().minus(before).minus(getRear().dx());
359 enterLaneRecursive(next, nextPos, -1);
360 }
361 }
362 }
363
364
365 if (dir > -1)
366 {
367 Length front = position.plus(getFront().dx());
368 Length passed = null;
369 if (front.si > lane.getLength().si)
370 {
371 passed = front.minus(lane.getLength());
372 }
373 if (passed != null)
374 {
375 Lane next = getStrategicalPlanner() == null ? lane.nextLanes(getType()).iterator().next()
376 : getNextLaneForRoute(lane);
377
378 Length nextPos = passed.minus(getFront().dx());
379 enterLaneRecursive(next, nextPos, 1);
380 }
381 }
382 }
383
384
385
386
387
388
389 @SuppressWarnings("checkstyle:designforextension")
390 public synchronized void initLaneChange(final LateralDirectionality laneChangeDirection) throws GtuException
391 {
392 List<CrossSection> newLanes = new ArrayList<>();
393 int index = laneChangeDirection.isLeft() ? 0 : 1;
394 int numRegistered = 0;
395 OrientedPoint2d point = getLocation();
396 Map<Lane, Double> addToLanes = new LinkedHashMap<>();
397 for (CrossSection crossSection : this.crossSections)
398 {
399 List<Lane> resultingLanes = new ArrayList<>();
400 Lane lane = crossSection.getLanes().get(0);
401 resultingLanes.add(lane);
402 Set<Lane> laneSet = lane.accessibleAdjacentLanesPhysical(laneChangeDirection, getType());
403 if (laneSet.size() > 0)
404 {
405 numRegistered++;
406 Lane adjacentLane = laneSet.iterator().next();
407 double f = adjacentLane.getCenterLine().projectFractional(null, null, point.x, point.y, FractionalFallback.NaN);
408 if (Double.isNaN(f))
409 {
410
411
412
413 Length pos = position(lane, getReference());
414 addToLanes.put(adjacentLane, pos.si < lane.getLength().si / 2 ? 0.0 : 1.0);
415 }
416 else
417 {
418 addToLanes.put(adjacentLane, adjacentLane.getLength().times(f).si / adjacentLane.getLength().si);
419 }
420 resultingLanes.add(index, adjacentLane);
421 }
422 newLanes.add(new CrossSection(resultingLanes));
423 }
424 Throw.when(numRegistered == 0, GtuException.class, "Gtu %s starting %s lane change, but no adjacent lane found.",
425 getId(), laneChangeDirection);
426 this.crossSections.clear();
427 this.crossSections.addAll(newLanes);
428 for (Entry<Lane, Double> entry : addToLanes.entrySet())
429 {
430 entry.getKey().addGtu(this, entry.getValue());
431 }
432 this.referenceLaneIndex = 1 - index;
433 }
434
435
436
437
438
439
440 @SuppressWarnings("checkstyle:designforextension")
441 protected synchronized void finalizeLaneChange(final LateralDirectionality laneChangeDirection) throws GtuException
442 {
443 List<CrossSection> newLanes = new ArrayList<>();
444 Lane fromLane = null;
445 Length fromPosition = null;
446 for (CrossSection crossSection : this.crossSections)
447 {
448 Lane lane = crossSection.getLanes().get(this.referenceLaneIndex);
449 if (lane != null)
450 {
451 Length pos = position(lane, RelativePosition.REFERENCE_POSITION);
452 if (0.0 <= pos.si && pos.si <= lane.getLength().si)
453 {
454 fromLane = lane;
455 fromPosition = pos;
456 }
457 lane.removeGtu(this, false, pos);
458 }
459 List<Lane> remainingLane = new ArrayList<>();
460 remainingLane.add(crossSection.getLanes().get(1 - this.referenceLaneIndex));
461 newLanes.add(new CrossSection(remainingLane));
462 }
463 this.crossSections.clear();
464 this.crossSections.addAll(newLanes);
465 this.referenceLaneIndex = 0;
466
467 Throw.when(fromLane == null, RuntimeException.class, "No from lane for lane change event.");
468 LanePosition from = new LanePosition(fromLane, fromPosition);
469
470
471
472 this.fireTimedEvent(
473 LaneBasedGtu.LANE_CHANGE_EVENT, new Object[] {getId(), laneChangeDirection.name(),
474 from.lane().getLink().getId(), from.lane().getId(), from.position()},
475 getSimulator().getSimulatorTime());
476
477 this.finalizeLaneChangeEvent = null;
478 }
479
480
481
482
483
484 public void setFinalizeLaneChangeEvent(final SimEventInterface<Duration> event)
485 {
486 this.finalizeLaneChangeEvent = event;
487 }
488
489 @Override
490 @SuppressWarnings("checkstyle:designforextension")
491 protected synchronized boolean move(final OrientedPoint2d fromLocation)
492 throws SimRuntimeException, GtuException, NetworkException, ParameterException
493 {
494 if (this.isDestroyed())
495 {
496 return false;
497 }
498 try
499 {
500 if (this.crossSections.isEmpty())
501 {
502 destroy();
503 return false;
504 }
505
506
507
508
509 cancelAllEvents();
510
511
512
513 try
514 {
515 boolean error = super.move(fromLocation);
516 if (error)
517 {
518 return error;
519 }
520 }
521 catch (Exception exception)
522 {
523 System.err.println(exception.getMessage());
524 System.err.println(" GTU " + this + " DESTROYED AND REMOVED FROM THE SIMULATION");
525 this.destroy();
526 this.cancelAllEvents();
527 return true;
528 }
529
530 LanePosition dlp = getReferencePosition();
531
532 scheduleEnterEvent();
533 scheduleLeaveEvent();
534
535
536 for (CrossSection crossSection : this.crossSections)
537 {
538 for (Lane lane : crossSection.getLanes())
539 {
540 scheduleTriggers(lane);
541 }
542 }
543
544 fireTimedEvent(LaneBasedGtu.LANEBASED_MOVE_EVENT,
545 new Object[] {getId(),
546 new PositionVector(new double[] {fromLocation.x, fromLocation.y}, PositionUnit.METER),
547 new Direction(fromLocation.getDirZ(), DirectionUnit.EAST_RADIAN), getSpeed(), getAcceleration(),
548 getTurnIndicatorStatus().name(), getOdometer(), dlp.lane().getLink().getId(), dlp.lane().getId(),
549 dlp.position()},
550 getSimulator().getSimulatorTime());
551
552 return false;
553
554 }
555 catch (Exception ex)
556 {
557 try
558 {
559 getErrorHandler().handle(this, ex);
560 }
561 catch (Exception exception)
562 {
563 throw new GtuException(exception);
564 }
565 return true;
566 }
567
568 }
569
570
571
572
573 private void cancelAllEvents()
574 {
575 if (this.pendingEnterTrigger != null)
576 {
577 getSimulator().cancelEvent(this.pendingEnterTrigger);
578 }
579 if (this.pendingLeaveTrigger != null)
580 {
581 getSimulator().cancelEvent(this.pendingLeaveTrigger);
582 }
583 if (this.finalizeLaneChangeEvent != null)
584 {
585 getSimulator().cancelEvent(this.finalizeLaneChangeEvent);
586 }
587 for (SimEventInterface<Duration> event : this.sensorEvents)
588 {
589 if (event.getAbsoluteExecutionTime().gt(getSimulator().getSimulatorTime()))
590 {
591 getSimulator().cancelEvent(event);
592 }
593 }
594 this.sensorEvents.clear();
595 }
596
597
598
599
600
601
602
603 protected void scheduleEnterEvent() throws GtuException, SimRuntimeException
604 {
605 CrossSection lastCrossSection = this.crossSections.get(this.crossSections.size() - 1);
606
607 Length remain = remainingEventDistance();
608 Lane lane = lastCrossSection.getLanes().get(this.referenceLaneIndex);
609 Length position = position(lane, getFront());
610 boolean possiblyNearNextSection = lane.getLength().minus(position).lt(remain);
611 if (possiblyNearNextSection)
612 {
613 CrossSectionLink link = lastCrossSection.getLanes().get(0).getLink();
614 PolyLine2d enterLine = link.getEndLine();
615 Time enterTime = timeAtLine(enterLine, getFront());
616 if (enterTime != null)
617 {
618 if (Double.isNaN(enterTime.si))
619 {
620
621 enterTime = getSimulator().getSimulatorAbsTime();
622 CategoryLogger.always().error("GTU {} enters cross-section through hack.", getId());
623 }
624 if (enterTime.lt(getSimulator().getSimulatorAbsTime()))
625 {
626 System.err.println(
627 "Time travel? enterTime=" + enterTime + "; simulator time=" + getSimulator().getSimulatorAbsTime());
628 enterTime = getSimulator().getSimulatorAbsTime();
629 }
630 this.pendingEnterTrigger = getSimulator().scheduleEventAbsTime(enterTime, this, "enterCrossSection", null);
631 }
632 }
633 }
634
635
636
637
638
639
640 protected synchronized void enterCrossSection() throws GtuException, SimRuntimeException
641 {
642 CrossSection lastCrossSection = this.crossSections.get(this.crossSections.size() - 1);
643 Lane lcsLane = lastCrossSection.getLanes().get(this.referenceLaneIndex);
644 Lane nextLcsLane = getNextLaneForRoute(lcsLane);
645 if (nextLcsLane == null)
646 {
647 forceLaneChangeFinalization();
648 return;
649 }
650 List<Lane> nextLanes = new ArrayList<>();
651 for (int i = 0; i < lastCrossSection.getLanes().size(); i++)
652 {
653 if (i == this.referenceLaneIndex)
654 {
655 nextLanes.add(nextLcsLane);
656 }
657 else
658 {
659 Lane lane = lastCrossSection.getLanes().get(i);
660 Set<Lane> lanes = lane.nextLanes(getType());
661 if (lanes.size() == 1)
662 {
663 Lane nextLane = lanes.iterator().next();
664 nextLanes.add(nextLane);
665 }
666 else
667 {
668 boolean added = false;
669 for (Lane nextLane : lanes)
670 {
671 if (nextLane.getLink().equals(nextLcsLane.getLink())
672 && nextLane
673 .accessibleAdjacentLanesPhysical(this.referenceLaneIndex == 0
674 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT, getType())
675 .contains(nextLcsLane))
676 {
677 nextLanes.add(nextLane);
678 added = true;
679 break;
680 }
681 }
682 if (!added)
683 {
684 forceLaneChangeFinalization();
685 return;
686 }
687 }
688 }
689 }
690 this.crossSections.add(new CrossSection(nextLanes));
691 for (Lane lane : nextLanes)
692 {
693 lane.addGtu(this, 0.0);
694 }
695 this.pendingEnterTrigger = null;
696 scheduleEnterEvent();
697 for (Lane lane : nextLanes)
698 {
699 scheduleTriggers(lane);
700 }
701 }
702
703
704
705
706
707
708
709
710 private void forceLaneChangeFinalization() throws GtuException, SimRuntimeException
711 {
712 if (this.finalizeLaneChangeEvent != null)
713 {
714
715 SimEventInterface<Duration> tmp = this.finalizeLaneChangeEvent;
716 finalizeLaneChange(this.referenceLaneIndex == 0 ? LateralDirectionality.RIGHT : LateralDirectionality.LEFT);
717 getSimulator().cancelEvent(tmp);
718 enterCrossSection();
719 }
720
721 }
722
723
724
725
726
727
728
729 protected void scheduleLeaveEvent() throws GtuException, SimRuntimeException
730 {
731 if (this.crossSections.isEmpty())
732 {
733 CategoryLogger.always().error("GTU {} has empty crossSections", this);
734 return;
735 }
736 CrossSection firstCrossSection = this.crossSections.get(0);
737
738 boolean possiblyNearNextSection =
739 !getReferencePosition().lane().equals(firstCrossSection.getLanes().get(this.referenceLaneIndex));
740 if (!possiblyNearNextSection)
741 {
742 Length remain = remainingEventDistance();
743 Lane lane = firstCrossSection.getLanes().get(this.referenceLaneIndex);
744 Length position = position(lane, getRear());
745 possiblyNearNextSection = lane.getLength().minus(position).lt(remain);
746 }
747 if (possiblyNearNextSection)
748 {
749 CrossSectionLink link = firstCrossSection.getLanes().get(0).getLink();
750 PolyLine2d leaveLine = link.getEndLine();
751 Time leaveTime = timeAtLine(leaveLine, getRear());
752 if (leaveTime == null)
753 {
754
755 Lane lane = this.crossSections.get(0).getLanes().get(this.referenceLaneIndex);
756 Length pos = position(lane, getRear());
757 if (pos.gt(lane.getLength()))
758 {
759 pos = position(lane, getRear());
760 this.pendingLeaveTrigger = getSimulator().scheduleEventNow(this, "leaveCrossSection", null);
761 getSimulator().getLogger().always().info("Forcing leave for GTU {} on lane {}", getId(), lane.getFullId());
762 }
763 }
764 if (leaveTime != null)
765 {
766 if (Double.isNaN(leaveTime.si))
767 {
768
769 leaveTime = getSimulator().getSimulatorAbsTime();
770 CategoryLogger.always().error("GTU {} leaves cross-section through hack.", getId());
771 }
772 if (leaveTime.lt(getSimulator().getSimulatorAbsTime()))
773 {
774 System.err.println(
775 "Time travel? leaveTime=" + leaveTime + "; simulator time=" + getSimulator().getSimulatorAbsTime());
776 leaveTime = getSimulator().getSimulatorAbsTime();
777 }
778 this.pendingLeaveTrigger = getSimulator().scheduleEventAbsTime(leaveTime, this, "leaveCrossSection", null);
779 }
780 }
781 }
782
783
784
785
786
787
788
789 protected synchronized void leaveCrossSection() throws GtuException, SimRuntimeException
790 {
791
792 List<Lane> lanes = this.crossSections.get(0).getLanes();
793 for (int i = 0; i < lanes.size(); i++)
794 {
795 Lane lane = lanes.get(i);
796 if (lane != null)
797 {
798 lane.removeGtu(this, i == lanes.size() - 1, position(lane, getReference()));
799 }
800 }
801 this.crossSections.remove(0);
802 this.pendingLeaveTrigger = null;
803 scheduleLeaveEvent();
804 }
805
806
807
808
809
810
811
812 protected void scheduleTriggers(final Lane lane) throws GtuException, SimRuntimeException
813 {
814 Length remain = remainingEventDistance();
815 double min = position(lane, getRear()).si;
816 double max = min + remain.si + getLength().si;
817 SortedMap<Double, List<LaneDetector>> detectors = lane.getDetectorMap(getType()).subMap(min, max);
818 for (List<LaneDetector> list : detectors.values())
819 {
820 for (LaneDetector detector : list)
821 {
822 RelativePosition pos = this.getRelativePositions().get(detector.getPositionType());
823 Time time = timeAtLine(detector.getLine(), pos);
824 if (time != null && !Double.isNaN(time.si))
825 {
826 this.sensorEvents.add(getSimulator().scheduleEventAbsTime(time, detector, "trigger", new Object[] {this}));
827 }
828 }
829 }
830 }
831
832
833
834
835
836
837 private Length remainingEventDistance() throws OperationalPlanException
838 {
839 if (getOperationalPlan() instanceof LaneBasedOperationalPlan)
840 {
841 LaneBasedOperationalPlan plan = (LaneBasedOperationalPlan) getOperationalPlan();
842 return plan.getTotalLength().minus(plan.getTraveledDistance(getSimulator().getSimulatorAbsTime()))
843 .plus(eventMargin);
844 }
845 return getOperationalPlan().getTotalLength().plus(eventMargin);
846 }
847
848
849
850
851
852
853 public final Lane getNextLaneForRoute(final Lane lane)
854 {
855
856 Set<Lane> set = getNextLanesForRoute(lane);
857 if (set == null || set.isEmpty())
858 {
859 return null;
860 }
861 if (set.size() == 1)
862 {
863 return set.iterator().next();
864 }
865
866 for (Lane l : set)
867 {
868 if (l.getGtuList().contains(this))
869 {
870 return l;
871 }
872 }
873
874 return Try.assign(() -> getTacticalPlanner().chooseLaneAtSplit(lane, set),
875 "Could not find suitable lane at split after lane %s of link %s for GTU %s.", lane.getId(),
876 lane.getLink().getId(), getId());
877 }
878
879
880
881
882
883
884 public Set<Lane> getNextLanesForRoute(final Lane lane)
885 {
886 Set<Lane> out = new LinkedHashSet<>();
887 Set<Lane> nextPhysical = lane.nextLanes(null);
888 if (nextPhysical.isEmpty())
889 {
890 return out;
891 }
892 Link link;
893 try
894 {
895 link = getStrategicalPlanner().nextLink(lane.getLink(), getType());
896 }
897 catch (NetworkException exception)
898 {
899 throw new RuntimeException("Strategical planner experiences exception on network.", exception);
900 }
901 Set<Lane> next = lane.nextLanes(getType());
902 if (next.isEmpty())
903 {
904 next = nextPhysical;
905 }
906 for (Lane l : next)
907 {
908 if (l.getLink().equals(link))
909 {
910 out.add(l);
911 }
912 }
913 return out;
914 }
915
916
917
918
919
920
921
922
923
924
925 private Time timeAtLine(final PolyLine2d line, final RelativePosition relativePosition) throws GtuException
926 {
927 Throw.when(line.size() != 2, IllegalArgumentException.class, "Line to cross with path should have 2 points.");
928 OtsLine2d path = getOperationalPlan().getPath();
929 List<Point2d> points = new ArrayList<>(path.size() + 1);
930 points.addAll(path.getPointList());
931 double adjust;
932 if (relativePosition.dx().gt0())
933 {
934
935 points.add(path.getLocationExtendedSI(path.getLength() + relativePosition.dx().si));
936 adjust = -relativePosition.dx().si;
937 }
938 else if (relativePosition.dx().lt0())
939 {
940 points.add(0, path.getLocationExtendedSI(relativePosition.dx().si));
941 adjust = 0.0;
942 }
943 else
944 {
945 adjust = 0.0;
946 }
947
948
949 double cumul = 0.0;
950 for (int i = 0; i < points.size() - 1; i++)
951 {
952 Point2d intersect = Point2d.intersectionOfLineSegments(points.get(i), points.get(i + 1), line.get(0), line.get(1));
953
954
955
956
957
958
959
960 if (intersect == null)
961 {
962 double projectionFraction = line.projectOrthogonalFractionalExtended(points.get(i));
963 if (0.0 <= projectionFraction && projectionFraction <= 1.0)
964 {
965 try
966 {
967 Point2d projection = line.getLocationFraction(projectionFraction);
968 double distance = projection.distance(points.get(i));
969 if (distance < 1e-6)
970 {
971
972
973 intersect = projection;
974 }
975 }
976 catch (Exception e)
977 {
978 Point2d projection = line.getLocationFraction(projectionFraction);
979 }
980 }
981 }
982
983 if (intersect != null)
984 {
985 cumul += points.get(i).distance(intersect);
986 cumul += adjust;
987
988 if (cumul < 0.0)
989 {
990
991
992
993
994
995 return Time.instantiateSI(Double.NaN);
996 }
997 if (cumul <= getOperationalPlan().getTotalLength().si)
998 {
999 return getOperationalPlan().timeAtDistance(Length.instantiateSI(cumul));
1000 }
1001
1002 return null;
1003 }
1004 else if (i < points.size() - 2)
1005 {
1006 cumul += points.get(i).distance(points.get(i + 1));
1007 }
1008 }
1009
1010 return null;
1011 }
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023 public final Map<Lane, Length> positions(final RelativePosition relativePosition) throws GtuException
1024 {
1025 return positions(relativePosition, getSimulator().getSimulatorAbsTime());
1026 }
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037 public final Map<Lane, Length> positions(final RelativePosition relativePosition, final Time when) throws GtuException
1038 {
1039 Map<Lane, Length> positions = new LinkedHashMap<>();
1040 for (CrossSection crossSection : this.crossSections.get(when))
1041 {
1042 for (Lane lane : crossSection.getLanes())
1043 {
1044 positions.put(lane, position(lane, relativePosition, when));
1045 }
1046 }
1047 return positions;
1048 }
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058 public final Length position(final Lane lane, final RelativePosition relativePosition) throws GtuException
1059 {
1060 return position(lane, relativePosition, getSimulator().getSimulatorAbsTime());
1061 }
1062
1063
1064 private double cachePositionsTime = Double.NaN;
1065
1066
1067 private OperationalPlan cacheOperationalPlan = null;
1068
1069
1070 private MultiKeyMap<Length> cachedPositions = new MultiKeyMap<>(Lane.class, RelativePosition.class);
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080 public Length position(final Lane lane, final RelativePosition relativePosition, final Time when) throws GtuException
1081 {
1082 synchronized (this)
1083 {
1084 OperationalPlan plan = getOperationalPlan(when);
1085 if (CACHING)
1086 {
1087 if (when.si == this.cachePositionsTime && plan == this.cacheOperationalPlan)
1088 {
1089 Length l = this.cachedPositions.get(lane, relativePosition);
1090 if (l != null && (!Double.isNaN(l.si)))
1091 {
1092 CACHED_POSITION++;
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105 return l;
1106 }
1107 }
1108 if (when.si != this.cachePositionsTime || plan != this.cacheOperationalPlan)
1109 {
1110 this.cachePositionsTime = Double.NaN;
1111 this.cacheOperationalPlan = null;
1112 this.cachedPositions.clear();
1113 }
1114 }
1115 NON_CACHED_POSITION++;
1116
1117 synchronized (this.lock)
1118 {
1119 List<CrossSection> whenCrossSections = this.crossSections.get(when);
1120 double loc = Double.NaN;
1121
1122 try
1123 {
1124 int crossSectionIndex = -1;
1125 int lateralIndex = -1;
1126 for (int i = 0; i < whenCrossSections.size(); i++)
1127 {
1128 if (whenCrossSections.get(i).getLanes().contains(lane))
1129 {
1130 crossSectionIndex = i;
1131 lateralIndex = whenCrossSections.get(i).getLanes().indexOf(lane);
1132 break;
1133 }
1134 }
1135 Throw.when(lateralIndex == -1, GtuException.class, "GTU %s is not on lane %s.", this, lane);
1136
1137 OrientedPoint2d p = plan.getLocation(when, relativePosition);
1138 double f = lane.getCenterLine().projectFractional(lane.getLink().getStartNode().getHeading(),
1139 lane.getLink().getEndNode().getHeading(), p.x, p.y, FractionalFallback.NaN);
1140 if (!Double.isNaN(f))
1141 {
1142 loc = f * lane.getLength().si;
1143 }
1144 else
1145 {
1146
1147
1148 double distance = 0.0;
1149 for (int i = crossSectionIndex - 1; i >= 0; i--)
1150 {
1151 Lane tryLane = whenCrossSections.get(i).getLanes().get(lateralIndex);
1152 f = tryLane.getCenterLine().projectFractional(tryLane.getLink().getStartNode().getHeading(),
1153 tryLane.getLink().getEndNode().getHeading(), p.x, p.y, FractionalFallback.NaN);
1154 if (!Double.isNaN(f))
1155 {
1156 f = 1 - f;
1157 loc = distance - f * tryLane.getLength().si;
1158 break;
1159 }
1160 distance -= tryLane.getLength().si;
1161 }
1162
1163 if (Double.isNaN(loc))
1164 {
1165 distance = lane.getLength().si;
1166 for (int i = crossSectionIndex + 1; i < whenCrossSections.size(); i++)
1167 {
1168 Lane tryLane = whenCrossSections.get(i).getLanes().get(lateralIndex);
1169 f = tryLane.getCenterLine().projectFractional(tryLane.getLink().getStartNode().getHeading(),
1170 tryLane.getLink().getEndNode().getHeading(), p.x, p.y, FractionalFallback.NaN);
1171 if (!Double.isNaN(f))
1172 {
1173 loc = distance + f * tryLane.getLength().si;
1174 break;
1175 }
1176 distance += tryLane.getLength().si;
1177 }
1178 }
1179
1180 }
1181
1182 if (Double.isNaN(loc))
1183 {
1184
1185
1186
1187 f = lane.getCenterLine().projectFractional(null, null, p.x, p.y, FractionalFallback.ENDPOINT);
1188 if (Double.isNaN(f))
1189 {
1190 CategoryLogger.always().error("GTU {} at location {} cannot project itself onto {}; p is {}", this,
1191 getLocation(), lane.getCenterLine(), p);
1192 plan.getLocation(when, relativePosition);
1193 }
1194 loc = lane.getLength().si * f;
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210 }
1211 }
1212 catch (Exception e)
1213 {
1214
1215 throw new GtuException(e);
1216 }
1217
1218 Length length = Length.instantiateSI(loc);
1219 if (CACHING)
1220 {
1221 this.cachedPositions.put(length, lane, relativePosition);
1222 this.cachePositionsTime = when.si;
1223 this.cacheOperationalPlan = plan;
1224 }
1225 return length;
1226 }
1227 }
1228 }
1229
1230
1231
1232
1233
1234
1235 @SuppressWarnings("checkstyle:designforextension")
1236 public LanePosition getReferencePosition() throws GtuException
1237 {
1238 synchronized (this)
1239 {
1240 if (this.referencePositionTime == getSimulator().getSimulatorAbsTime().si)
1241 {
1242 return this.cachedReferencePosition;
1243 }
1244 Lane refLane = null;
1245 for (CrossSection crossSection : this.crossSections)
1246 {
1247 Lane lane = crossSection.getLanes().get(this.referenceLaneIndex);
1248 double fraction = fractionalPosition(lane, getReference());
1249 if (fraction >= 0.0 && fraction <= 1.0)
1250 {
1251 refLane = lane;
1252 break;
1253 }
1254 }
1255 if (refLane != null)
1256 {
1257 this.cachedReferencePosition = new LanePosition(refLane, position(refLane, getReference()));
1258 this.referencePositionTime = getSimulator().getSimulatorAbsTime().si;
1259 return this.cachedReferencePosition;
1260 }
1261 CategoryLogger.always().error("The reference point of GTU {} is not on any of the lanes on which it is registered",
1262 this);
1263 for (CrossSection crossSection : this.crossSections)
1264 {
1265 Lane lane = crossSection.getLanes().get(this.referenceLaneIndex);
1266 double fraction = fractionalPosition(lane, getReference());
1267 CategoryLogger.always().error("\tGTU is on lane \"{}\" at fraction {}", lane, fraction);
1268 }
1269 throw new GtuException(
1270 "The reference point of GTU " + this + " is not on any of the lanes on which it is registered");
1271 }
1272 }
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282 public final Map<Lane, Double> fractionalPositions(final RelativePosition relativePosition) throws GtuException
1283 {
1284 return fractionalPositions(relativePosition, getSimulator().getSimulatorAbsTime());
1285 }
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297 public final Map<Lane, Double> fractionalPositions(final RelativePosition relativePosition, final Time when)
1298 throws GtuException
1299 {
1300 Map<Lane, Double> positions = new LinkedHashMap<>();
1301 for (CrossSection crossSection : this.crossSections)
1302 {
1303 for (Lane lane : crossSection.getLanes())
1304 {
1305 positions.put(lane, fractionalPosition(lane, relativePosition, when));
1306 }
1307 }
1308 return positions;
1309 }
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321 public final double fractionalPosition(final Lane lane, final RelativePosition relativePosition, final Time when)
1322 throws GtuException
1323 {
1324 return position(lane, relativePosition, when).getSI() / lane.getLength().getSI();
1325 }
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336 public final double fractionalPosition(final Lane lane, final RelativePosition relativePosition) throws GtuException
1337 {
1338 return position(lane, relativePosition).getSI() / lane.getLength().getSI();
1339 }
1340
1341
1342
1343
1344
1345
1346 public final void addTrigger(final Lane lane, final SimEventInterface<Duration> event)
1347 {
1348 throw new UnsupportedOperationException("Method addTrigger is not supported.");
1349 }
1350
1351
1352
1353
1354
1355 public void setVehicleModel(final VehicleModel vehicleModel)
1356 {
1357 this.vehicleModel = vehicleModel;
1358 }
1359
1360
1361
1362
1363
1364 public VehicleModel getVehicleModel()
1365 {
1366 return this.vehicleModel;
1367 }
1368
1369 @Override
1370 @SuppressWarnings("checkstyle:designforextension")
1371 public void destroy()
1372 {
1373 LanePosition dlp = null;
1374 try
1375 {
1376 dlp = getReferencePosition();
1377 }
1378 catch (GtuException e)
1379 {
1380
1381 }
1382 OrientedPoint2d location = this.getOperationalPlan() == null ? new OrientedPoint2d(0.0, 0.0, 0.0) : getLocation();
1383 synchronized (this.lock)
1384 {
1385 for (CrossSection crossSection : this.crossSections)
1386 {
1387 boolean removeFromParentLink = true;
1388 for (Lane lane : crossSection.getLanes())
1389 {
1390 Length position;
1391 try
1392 {
1393 position = position(lane, getReference());
1394 }
1395 catch (GtuException exception)
1396 {
1397
1398
1399 throw new RuntimeException(exception);
1400 }
1401 lane.removeGtu(this, removeFromParentLink, position);
1402 removeFromParentLink = false;
1403 }
1404 }
1405 }
1406 if (dlp != null && dlp.lane() != null)
1407 {
1408 Lane referenceLane = dlp.lane();
1409 fireTimedEvent(LaneBasedGtu.LANEBASED_DESTROY_EVENT,
1410 new Object[] {getId(), new PositionVector(new double[] {location.x, location.y}, PositionUnit.METER),
1411 new Direction(location.getDirZ(), DirectionUnit.EAST_RADIAN), getOdometer(),
1412 referenceLane.getLink().getId(), referenceLane.getId(), dlp.position()},
1413 getSimulator().getSimulatorTime());
1414 }
1415 else
1416 {
1417 fireTimedEvent(LaneBasedGtu.LANEBASED_DESTROY_EVENT,
1418 new Object[] {getId(), new PositionVector(new double[] {location.x, location.y}, PositionUnit.METER),
1419 new Direction(location.getDirZ(), DirectionUnit.EAST_RADIAN), getOdometer(), null, null, null},
1420 getSimulator().getSimulatorTime());
1421 }
1422 cancelAllEvents();
1423
1424 super.destroy();
1425 }
1426
1427 @Override
1428 public final LaneBasedStrategicalPlanner getStrategicalPlanner()
1429 {
1430 return (LaneBasedStrategicalPlanner) super.getStrategicalPlanner();
1431 }
1432
1433 @Override
1434 public final LaneBasedStrategicalPlanner getStrategicalPlanner(final Time time)
1435 {
1436 return (LaneBasedStrategicalPlanner) super.getStrategicalPlanner(time);
1437 }
1438
1439
1440 public RoadNetwork getNetwork()
1441 {
1442 return (RoadNetwork) super.getPerceivableContext();
1443 }
1444
1445
1446
1447
1448
1449 public Speed getDesiredSpeed()
1450 {
1451 synchronized (this)
1452 {
1453 Time simTime = getSimulator().getSimulatorAbsTime();
1454 if (this.desiredSpeedTime == null || this.desiredSpeedTime.si < simTime.si)
1455 {
1456 InfrastructurePerception infra =
1457 getTacticalPlanner().getPerception().getPerceptionCategoryOrNull(InfrastructurePerception.class);
1458 SpeedLimitInfo speedInfo;
1459 if (infra == null)
1460 {
1461 speedInfo = new SpeedLimitInfo();
1462 speedInfo.addSpeedInfo(SpeedLimitTypes.MAX_VEHICLE_SPEED, getMaximumSpeed());
1463 }
1464 else
1465 {
1466
1467 speedInfo = infra.getSpeedLimitProspect(RelativeLane.CURRENT).getSpeedLimitInfo(Length.ZERO);
1468 }
1469 this.cachedDesiredSpeed =
1470 Try.assign(() -> getTacticalPlanner().getCarFollowingModel().desiredSpeed(getParameters(), speedInfo),
1471 "Parameter exception while obtaining the desired speed.");
1472 this.desiredSpeedTime = simTime;
1473 }
1474 return this.cachedDesiredSpeed;
1475 }
1476 }
1477
1478
1479
1480
1481
1482
1483 public Acceleration getCarFollowingAcceleration()
1484 {
1485 synchronized (this)
1486 {
1487 Time simTime = getSimulator().getSimulatorAbsTime();
1488 if (this.carFollowingAccelerationTime == null || this.carFollowingAccelerationTime.si < simTime.si)
1489 {
1490 LanePerception perception = getTacticalPlanner().getPerception();
1491
1492 EgoPerception<?, ?> ego = perception.getPerceptionCategoryOrNull(EgoPerception.class);
1493 Throw.whenNull(ego, "EgoPerception is required to determine the speed.");
1494 Speed speed = ego.getSpeed();
1495
1496 InfrastructurePerception infra = perception.getPerceptionCategoryOrNull(InfrastructurePerception.class);
1497 Throw.whenNull(infra, "InfrastructurePerception is required to determine the desired speed.");
1498 SpeedLimitInfo speedInfo = infra.getSpeedLimitProspect(RelativeLane.CURRENT).getSpeedLimitInfo(Length.ZERO);
1499
1500 NeighborsPerception neighbors = perception.getPerceptionCategoryOrNull(NeighborsPerception.class);
1501 Throw.whenNull(neighbors, "NeighborsPerception is required to determine the car-following acceleration.");
1502 PerceptionCollectable<HeadwayGtu, LaneBasedGtu> leaders = neighbors.getLeaders(RelativeLane.CURRENT);
1503
1504 this.cachedCarFollowingAcceleration =
1505 Try.assign(() -> getTacticalPlanner().getCarFollowingModel().followingAcceleration(getParameters(),
1506 speed, speedInfo, leaders), "Parameter exception while obtaining the desired speed.");
1507 this.carFollowingAccelerationTime = simTime;
1508 }
1509 return this.cachedCarFollowingAcceleration;
1510 }
1511 }
1512
1513
1514 public final TurnIndicatorStatus getTurnIndicatorStatus()
1515 {
1516 return this.turnIndicatorStatus.get();
1517 }
1518
1519
1520
1521
1522
1523 public final TurnIndicatorStatus getTurnIndicatorStatus(final Time time)
1524 {
1525 return this.turnIndicatorStatus.get(time);
1526 }
1527
1528
1529
1530
1531
1532 public final void setTurnIndicatorStatus(final TurnIndicatorStatus turnIndicatorStatus)
1533 {
1534 this.turnIndicatorStatus.set(turnIndicatorStatus);
1535 }
1536
1537 @Override
1538 public Length getHeight()
1539 {
1540 return Length.ZERO;
1541 }
1542
1543 @Override
1544 public String getFullId()
1545 {
1546 return getId();
1547 }
1548
1549 @Override
1550 public Lane getLane()
1551 {
1552 return Try.assign(() -> getReferencePosition().lane(), "no reference position");
1553 }
1554
1555 @Override
1556 public Length getLongitudinalPosition()
1557 {
1558 return Try.assign(() -> getReferencePosition().position(), "no reference position");
1559 }
1560
1561
1562
1563
1564
1565
1566
1567 public Length getLateralPosition(final Lane lane) throws GtuException
1568 {
1569 OperationalPlan plan = getOperationalPlan();
1570 if (plan instanceof LaneBasedOperationalPlan && !((LaneBasedOperationalPlan) plan).isDeviative())
1571 {
1572 return Length.ZERO;
1573 }
1574 LanePosition ref = getReferencePosition();
1575 int latIndex = -1;
1576 int longIndex = -1;
1577 for (int i = 0; i < this.crossSections.size(); i++)
1578 {
1579 List<Lane> lanes = this.crossSections.get(i).getLanes();
1580 if (lanes.contains(lane))
1581 {
1582 latIndex = lanes.indexOf(lane);
1583 }
1584 if (lanes.contains(ref.lane()))
1585 {
1586 longIndex = i;
1587 }
1588 }
1589 Throw.when(latIndex == -1 || longIndex == -1, GtuException.class, "GTU %s is not on %s", getId(), lane);
1590 Lane refCrossSectionLane = this.crossSections.get(longIndex).getLanes().get(latIndex);
1591 OrientedPoint2d loc = getLocation();
1592 double f = refCrossSectionLane.getCenterLine().projectOrthogonalSnap(loc.x, loc.y);
1593 OrientedPoint2d p = Try.assign(() -> refCrossSectionLane.getCenterLine().getLocationPointFraction(f),
1594 GtuException.class, "GTU %s is not orthogonal to the reference lane.", getId());
1595 double d = p.distance(loc);
1596 if (this.crossSections.get(0).getLanes().size() > 1)
1597 {
1598 return Length.instantiateSI(latIndex == 0 ? -d : d);
1599 }
1600 double x2 = p.x + Math.cos(p.getDirZ());
1601 double y2 = p.y + Math.sin(p.getDirZ());
1602 double det = (loc.x - p.x) * (y2 - p.y) - (loc.y - p.y) * (x2 - p.x);
1603 return Length.instantiateSI(det < 0.0 ? -d : d);
1604 }
1605
1606
1607
1608
1609
1610 public void setInstantaneousLaneChange(final boolean instantaneous)
1611 {
1612 this.instantaneousLaneChange = instantaneous;
1613 }
1614
1615
1616
1617
1618
1619 public boolean isInstantaneousLaneChange()
1620 {
1621 return this.instantaneousLaneChange;
1622 }
1623
1624 @Override
1625 public LaneBasedTacticalPlanner getTacticalPlanner()
1626 {
1627 return getStrategicalPlanner().getTacticalPlanner();
1628 }
1629
1630 @Override
1631 public LaneBasedTacticalPlanner getTacticalPlanner(final Time time)
1632 {
1633 return getStrategicalPlanner(time).getTacticalPlanner(time);
1634 }
1635
1636
1637
1638
1639
1640 public final void setNoLaneChangeDistance(final Length distance)
1641 {
1642 this.noLaneChangeDistance = distance;
1643 }
1644
1645
1646
1647
1648
1649 public final boolean laneChangeAllowed()
1650 {
1651 return this.noLaneChangeDistance == null ? true : getOdometer().gt(this.noLaneChangeDistance);
1652 }
1653
1654
1655
1656
1657
1658 public boolean isBrakingLightsOn()
1659 {
1660 return isBrakingLightsOn(getSimulator().getSimulatorAbsTime());
1661 }
1662
1663
1664
1665
1666
1667
1668 public boolean isBrakingLightsOn(final Time when)
1669 {
1670 return getVehicleModel().isBrakingLightsOn(getSpeed(when), getAcceleration(when));
1671 }
1672
1673
1674
1675
1676
1677
1678
1679 public Length getProjectedLength(final Lane lane) throws GtuException
1680 {
1681 Length front = position(lane, getFront());
1682 Length rear = position(lane, getRear());
1683 return front.minus(rear);
1684 }
1685
1686 @Override
1687 @SuppressWarnings("checkstyle:designforextension")
1688 public String toString()
1689 {
1690 return String.format("GTU " + getId());
1691 }
1692
1693
1694 private static class CrossSection
1695 {
1696
1697
1698 private final List<Lane> lanes;
1699
1700
1701
1702
1703 protected CrossSection(final List<Lane> lanes)
1704 {
1705 this.lanes = lanes;
1706 }
1707
1708
1709
1710
1711 protected List<Lane> getLanes()
1712 {
1713 return this.lanes;
1714 }
1715
1716 }
1717
1718
1719
1720
1721
1722
1723
1724 public static final EventType LANEBASED_MOVE_EVENT = new EventType("LANEBASEDGTU.MOVE", new MetaData("Lane based GTU moved",
1725 "Lane based GTU moved",
1726 new ObjectDescriptor[] {new ObjectDescriptor("GTU id", "GTU id", String.class),
1727 new ObjectDescriptor("Position", "Position", PositionVector.class),
1728 new ObjectDescriptor("Direction", "Direction", Direction.class),
1729 new ObjectDescriptor("Speed", "Speed", Speed.class),
1730 new ObjectDescriptor("Acceleration", "Acceleration", Acceleration.class),
1731 new ObjectDescriptor("TurnIndicatorStatus", "Turn indicator status", String.class),
1732 new ObjectDescriptor("Odometer", "Odometer value", Length.class),
1733 new ObjectDescriptor("Link id", "Link id", String.class),
1734 new ObjectDescriptor("Lane id", "Lane id", String.class),
1735 new ObjectDescriptor("Longitudinal position on lane", "Longitudinal position on lane", Length.class)}));
1736
1737
1738
1739
1740
1741
1742 public static final EventType LANEBASED_DESTROY_EVENT = new EventType("LANEBASEDGTU.DESTROY", new MetaData(
1743 "Lane based GTU destroyed", "Lane based GTU destroyed",
1744 new ObjectDescriptor[] {new ObjectDescriptor("GTU id", "GTU id", String.class),
1745 new ObjectDescriptor("Position", "Position", PositionVector.class),
1746 new ObjectDescriptor("Direction", "Direction", Direction.class),
1747 new ObjectDescriptor("Odometer", "Odometer value", Length.class),
1748 new ObjectDescriptor("Link id", "Link id", String.class),
1749 new ObjectDescriptor("Lane id", "Lane id", String.class),
1750 new ObjectDescriptor("Longitudinal position on lane", "Longitudinal position on lane", Length.class)}));
1751
1752
1753
1754
1755
1756
1757
1758 public static final EventType LANE_ENTER_EVENT = new EventType("LANE.ENTER",
1759 new MetaData("Lane based GTU entered lane", "Front of lane based GTU entered lane",
1760 new ObjectDescriptor[] {new ObjectDescriptor("GTU id", "GTU id", String.class),
1761 new ObjectDescriptor("Link id", "Link id", String.class),
1762 new ObjectDescriptor("Lane id", "Lane id", String.class)}));
1763
1764
1765
1766
1767
1768
1769 public static final EventType LANE_EXIT_EVENT = new EventType("LANE.EXIT",
1770 new MetaData("Lane based GTU exited lane", "Rear of lane based GTU exited lane",
1771 new ObjectDescriptor[] {new ObjectDescriptor("GTU id", "GTU id", String.class),
1772 new ObjectDescriptor("Link id", "Link id", String.class),
1773 new ObjectDescriptor("Lane id", "Lane id", String.class)}));
1774
1775
1776
1777
1778
1779 public static final EventType LANE_CHANGE_EVENT = new EventType("LANE.CHANGE",
1780 new MetaData("Lane based GTU changes lane", "Lane based GTU changes lane",
1781 new ObjectDescriptor[] {new ObjectDescriptor("GTU id", "GTU id", String.class),
1782 new ObjectDescriptor("Lateral direction of lane change", "Lateral direction of lane change",
1783 String.class),
1784 new ObjectDescriptor("Link id", "Link id", String.class),
1785 new ObjectDescriptor("Lane id of vacated lane", "Lane id of vacated lane", String.class),
1786 new ObjectDescriptor("Position along vacated lane", "Position along vacated lane", Length.class)}));
1787
1788 }