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