1 package org.opentrafficsim.road.gtu.lane;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import javax.media.j3d.Bounds;
13 import javax.vecmath.Point3d;
14
15 import nl.tudelft.simulation.dsol.SimRuntimeException;
16 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
17 import nl.tudelft.simulation.language.Throw;
18 import nl.tudelft.simulation.language.d3.BoundingBox;
19 import nl.tudelft.simulation.language.d3.DirectedPoint;
20
21 import org.djunits.unit.LengthUnit;
22 import org.djunits.value.vdouble.scalar.Duration;
23 import org.djunits.value.vdouble.scalar.Length;
24 import org.djunits.value.vdouble.scalar.Speed;
25 import org.djunits.value.vdouble.scalar.Time;
26 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
27 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
28 import org.opentrafficsim.core.geometry.OTSGeometryException;
29 import org.opentrafficsim.core.gtu.AbstractGTU;
30 import org.opentrafficsim.core.gtu.GTUDirectionality;
31 import org.opentrafficsim.core.gtu.GTUException;
32 import org.opentrafficsim.core.gtu.GTUType;
33 import org.opentrafficsim.core.gtu.RelativePosition;
34 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
35 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
36 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
37 import org.opentrafficsim.core.network.LateralDirectionality;
38 import org.opentrafficsim.core.network.Link;
39 import org.opentrafficsim.core.network.NetworkException;
40 import org.opentrafficsim.core.network.Node;
41 import org.opentrafficsim.core.network.OTSNetwork;
42 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
43 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
44 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
45 import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
46 import org.opentrafficsim.road.network.lane.CrossSectionElement;
47 import org.opentrafficsim.road.network.lane.CrossSectionLink;
48 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
49 import org.opentrafficsim.road.network.lane.Lane;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public abstract class AbstractLaneBasedGTU extends AbstractGTU implements LaneBasedGTU
77 {
78
79 private static final long serialVersionUID = 20140822L;
80
81
82
83
84
85
86 private Map<Link, Double> fractionalLinkPositions = new LinkedHashMap<>();
87
88
89
90
91
92
93
94 private final Map<Lane, GTUDirectionality> lanesCurrentOperationalPlan = new HashMap<>();
95
96
97 private Map<Lane, List<SimEvent<OTSSimTimeDouble>>> pendingTriggers = new HashMap<>();
98
99
100 private Object lock = new Object();
101
102
103 @SuppressWarnings("checkstyle:visibilitymodifier")
104 public static Length initialLocationThresholdDifference = new Length(1.0, LengthUnit.MILLIMETER);
105
106
107
108
109
110
111
112
113
114 public AbstractLaneBasedGTU(final String id, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
115 final OTSNetwork network) throws GTUException
116 {
117 super(id, gtuType, simulator, network);
118 }
119
120
121
122
123
124
125
126
127
128
129 @SuppressWarnings("checkstyle:designforextension")
130 public void init(final LaneBasedStrategicalPlanner strategicalPlanner,
131 final Set<DirectedLanePosition> initialLongitudinalPositions, final Speed initialSpeed)
132 throws NetworkException, SimRuntimeException, GTUException, OTSGeometryException
133 {
134 Throw.when(null == initialLongitudinalPositions, GTUException.class, "InitialLongitudinalPositions is null");
135 Throw.when(0 == initialLongitudinalPositions.size(), GTUException.class, "InitialLongitudinalPositions is empty set");
136
137 DirectedPoint lastPoint = null;
138 for (DirectedLanePosition pos : initialLongitudinalPositions)
139 {
140 Throw.when(lastPoint != null && pos.getLocation().distance(lastPoint) > initialLocationThresholdDifference.si,
141 GTUException.class, "initial locations for GTU have distance > " + initialLocationThresholdDifference);
142 lastPoint = pos.getLocation();
143 }
144 DirectedPoint initialLocation = lastPoint;
145
146
147 for (DirectedLanePosition directedLanePosition : initialLongitudinalPositions)
148 {
149 Lane lane = directedLanePosition.getLane();
150 enterLane(lane, directedLanePosition.getPosition(), directedLanePosition.getGtuDirection());
151 }
152
153
154
155
156
157 super.init(strategicalPlanner, initialLocation, initialSpeed);
158
159 Lane referenceLane = getReferencePosition().getLane();
160 fireTimedEvent(LaneBasedGTU.LANEBASED_INIT_EVENT,
161 new Object[] { getId(), initialLocation, getLength(), getWidth(), getBaseColor(), referenceLane,
162 position(referenceLane, getReference()), this.lanesCurrentOperationalPlan.get(referenceLane),
163 getGTUType() },
164 getSimulator().getSimulatorTime());
165 }
166
167
168
169
170
171
172 public final boolean isSafeToChange() throws GTUException
173 {
174 return this.fractionalLinkPositions.get(getReferencePosition().getLane().getParentLink()) > 0.0;
175 }
176
177
178 @Override
179 @SuppressWarnings("checkstyle:designforextension")
180 public void enterLane(final Lane lane, final Length position, final GTUDirectionality gtuDirection)
181 throws GTUException
182 {
183 if (lane == null || gtuDirection == null || position == null)
184 {
185 throw new GTUException("enterLane - one of the arguments is null");
186 }
187 if (this.lanesCurrentOperationalPlan.containsKey(lane))
188 {
189 System.err.println(this + " is already registered on lane: " + lane + " at fractional position "
190 + this.fractionalPosition(lane, RelativePosition.REFERENCE_POSITION) + " intended position is " + position
191 + " length of lane is " + lane.getLength());
192 return;
193 }
194
195
196
197 if (!this.fractionalLinkPositions.containsKey(lane.getParentLink()))
198 {
199 this.fractionalLinkPositions.put(lane.getParentLink(), lane.fraction(position));
200 }
201 this.lanesCurrentOperationalPlan.put(lane, gtuDirection);
202 lane.addGTU(this, position);
203 }
204
205
206 @Override
207 @SuppressWarnings("checkstyle:designforextension")
208 public void leaveLane(final Lane lane) throws GTUException
209 {
210 leaveLane(lane, false);
211 }
212
213
214
215
216
217
218
219 @SuppressWarnings("checkstyle:designforextension")
220 public void leaveLane(final Lane lane, final boolean beingDestroyed) throws GTUException
221 {
222 Length position = position(lane, getReference());
223 this.lanesCurrentOperationalPlan.remove(lane);
224 List<SimEvent<OTSSimTimeDouble>> pending = this.pendingTriggers.get(lane);
225 if (null != pending)
226 {
227 for (SimEvent<OTSSimTimeDouble> event : pending)
228 {
229 if (event.getAbsoluteExecutionTime().get().ge(getSimulator().getSimulatorTime().get()))
230 {
231 boolean result = getSimulator().cancelEvent(event);
232 if (!result && event.getAbsoluteExecutionTime().get().ne(getSimulator().getSimulatorTime().get()))
233 {
234 System.err.println("leaveLane, trying to remove event: NOTHING REMOVED");
235 }
236 }
237 }
238 this.pendingTriggers.remove(lane);
239 }
240
241 boolean found = false;
242 for (Lane l : this.lanesCurrentOperationalPlan.keySet())
243 {
244 if (l.getParentLink().equals(lane.getParentLink()))
245 {
246 found = true;
247 }
248 }
249 if (!found)
250 {
251 this.fractionalLinkPositions.remove(lane.getParentLink());
252 }
253 lane.removeGTU(this, !found, position);
254 if (this.lanesCurrentOperationalPlan.size() == 0 && !beingDestroyed)
255 {
256 System.err.println("leaveLane: lanes.size() = 0 for GTU " + getId());
257 }
258 }
259
260
261 @Override
262 @SuppressWarnings("checkstyle:designforextension")
263 public void changeLaneInstantaneously(final LateralDirectionality laneChangeDirection) throws GTUException
264 {
265
266
267 Map<Lane, GTUDirectionality> lanesCopy = new HashMap<>(this.lanesCurrentOperationalPlan);
268 Set<Lane> lanesToBeRemoved = new HashSet<>();
269 Map<Lane, Double> fractionalLanePositions = new HashMap<>();
270
271 for (Lane lane : lanesCopy.keySet())
272 {
273 fractionalLanePositions.put(lane, fractionalPosition(lane, getReference()));
274 lanesToBeRemoved.add(lane);
275 }
276
277
278
279
280 Map<Link, Double> newLinkPositions = new HashMap<>(this.fractionalLinkPositions);
281
282 for (Lane lane : lanesCopy.keySet())
283 {
284 Set<Lane> laneSet = lane.accessibleAdjacentLanes(laneChangeDirection, getGTUType());
285 if (laneSet.size() > 0)
286 {
287 Lane adjacentLane = laneSet.iterator().next();
288 try
289 {
290 Length pos = adjacentLane.position(lane.fraction(position(lane, getReference())));
291 Length adjustedStart =
292 pos.minus(getOperationalPlan().getTraveledDistance(getSimulator().getSimulatorTime().getTime()));
293 newLinkPositions.put(lane.getParentLink(), adjustedStart.si / adjacentLane.getLength().si);
294 }
295 catch (OperationalPlanException exception)
296 {
297 exception.printStackTrace();
298 }
299 enterLane(adjacentLane, adjacentLane.getLength().multiplyBy(fractionalLanePositions.get(lane)),
300 lanesCopy.get(lane));
301 lanesToBeRemoved.remove(adjacentLane);
302 }
303
304 }
305
306
307 this.fractionalLinkPositions = newLinkPositions;
308
309 for (Lane lane : lanesToBeRemoved)
310 {
311 leaveLane(lane);
312 }
313
314 }
315
316
317
318
319
320
321 @SuppressWarnings("checkstyle:designforextension")
322 public void initLaneChange(final LateralDirectionality laneChangeDirection) throws GTUException
323 {
324 Map<Lane, GTUDirectionality> lanesCopy = new HashMap<>(this.lanesCurrentOperationalPlan);
325 Map<Lane, Double> fractionalLanePositions = new HashMap<>();
326 for (Lane lane : lanesCopy.keySet())
327 {
328 fractionalLanePositions.put(lane, fractionalPosition(lane, getReference()));
329 }
330 int numRegistered = 0;
331 for (Lane lane : lanesCopy.keySet())
332 {
333 Set<Lane> laneSet = lane.accessibleAdjacentLanes(laneChangeDirection, getGTUType());
334 if (laneSet.size() > 0)
335 {
336 numRegistered++;
337 Lane adjacentLane = laneSet.iterator().next();
338 enterLane(adjacentLane, adjacentLane.getLength().multiplyBy(fractionalLanePositions.get(lane)),
339 lanesCopy.get(lane));
340 }
341 }
342 Throw.when(numRegistered == 0, GTUException.class, "Gtu %s starting %s lane change, but no adjacent lane found.",
343 getId(), laneChangeDirection);
344 }
345
346
347
348
349
350
351
352 @SuppressWarnings("checkstyle:designforextension")
353 public void finalizeLaneChange(final LateralDirectionality laneChangeDirection, final Time time) throws GTUException
354 {
355 try
356 {
357 getSimulator().scheduleEventAbs(time, this, this, "executeLaneChangeFinalization",
358 new Object[] { laneChangeDirection });
359 }
360 catch (SimRuntimeException exception)
361 {
362 throw new RuntimeException("Error when finalizing lane change.", exception);
363 }
364 }
365
366
367
368
369
370 @SuppressWarnings("checkstyle:designforextension")
371 protected void executeLaneChangeFinalization(final LateralDirectionality laneChangeDirection)
372 {
373 Map<Lane, GTUDirectionality> lanesCopy = new HashMap<>(this.lanesCurrentOperationalPlan);
374 Set<Lane> lanesToBeRemoved = new HashSet<>();
375 Map<Lane, Double> fractionalLanePositions = new HashMap<>();
376 try
377 {
378 for (Lane lane : lanesCopy.keySet())
379 {
380
381 fractionalLanePositions.put(lane, fractionalPosition(lane, getReference()));
382 }
383 for (Lane lane : lanesCopy.keySet())
384 {
385 Iterator<Lane> iterator = lane.accessibleAdjacentLanes(laneChangeDirection, getGTUType()).iterator();
386 if (iterator.hasNext() && lanesCopy.keySet().contains(iterator.next()))
387 {
388 lanesToBeRemoved.add(lane);
389 }
390 }
391 for (Lane lane : lanesToBeRemoved)
392 {
393 leaveLane(lane);
394 }
395 }
396 catch (GTUException exception)
397 {
398
399 throw new RuntimeException("fractionalPosition on lane not possible", exception);
400 }
401 }
402
403
404 @Override
405 public final GTUDirectionality getDirection(final Lane lane) throws GTUException
406 {
407 Throw.when(!this.lanesCurrentOperationalPlan.containsKey(lane), GTUException.class,
408 "getDirection: Lanes %s does not contain %s", this.lanesCurrentOperationalPlan.keySet(), lane);
409 return this.lanesCurrentOperationalPlan.get(lane);
410 }
411
412
413 @Override
414 @SuppressWarnings("checkstyle:designforextension")
415 protected void move(final DirectedPoint fromLocation)
416 throws SimRuntimeException, GTUException, OperationalPlanException, NetworkException, ParameterException
417 {
418
419
420 if (this.lanesCurrentOperationalPlan.isEmpty())
421 {
422 destroy();
423 return;
424 }
425
426
427 Map<Link, Double> newLinkPositions = new HashMap<>();
428 for (Lane lane : this.lanesCurrentOperationalPlan.keySet())
429 {
430 newLinkPositions.put(lane.getParentLink(), lane.fraction(position(lane, getReference())));
431 }
432
433
434 super.move(fromLocation);
435
436
437 this.fractionalLinkPositions = newLinkPositions;
438
439 DirectedLanePosition dlp = getReferencePosition();
440 fireTimedEvent(
441 LaneBasedGTU.LANEBASED_MOVE_EVENT, new Object[] { getId(), fromLocation, getSpeed(), getAcceleration(),
442 getTurnIndicatorStatus(), getOdometer(), dlp.getLane(), dlp.getPosition(), dlp.getGtuDirection() },
443 getSimulator().getSimulatorTime());
444
445 if (getOperationalPlan().getAcceleration(Duration.ZERO).si < -10
446 && getOperationalPlan().getSpeed(Duration.ZERO).si > 2.5)
447 {
448 System.err.println("GTU: " + getId() + " - getOperationalPlan().getAcceleration(Duration.ZERO).si < -10)");
449 System.err.println("Lanes in current plan: " + this.lanesCurrentOperationalPlan.keySet());
450 DefaultSimplePerception p =
451 getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class);
452 System.err.println("HeadwayGTU: " + p.getForwardHeadwayGTU());
453 System.err.println("HeadwayObject: " + p.getForwardHeadwayObject());
454 }
455
456
457 scheduleEnterLeaveTriggers();
458 }
459
460
461 @Override
462 public final Map<Lane, Length> positions(final RelativePosition relativePosition) throws GTUException
463 {
464 return positions(relativePosition, getSimulator().getSimulatorTime().getTime());
465 }
466
467
468 @Override
469 public final Map<Lane, Length> positions(final RelativePosition relativePosition, final Time when) throws GTUException
470 {
471 Map<Lane, Length> positions = new LinkedHashMap<>();
472 for (Lane lane : this.lanesCurrentOperationalPlan.keySet())
473 {
474 positions.put(lane, position(lane, relativePosition, when));
475 }
476 return positions;
477 }
478
479
480 @Override
481 public final Length position(final Lane lane, final RelativePosition relativePosition) throws GTUException
482 {
483 return position(lane, relativePosition, getSimulator().getSimulatorTime().getTime());
484 }
485
486
487 @Override
488 @SuppressWarnings("checkstyle:designforextension")
489 public Length projectedPosition(final Lane projectionLane, final RelativePosition relativePosition, final Time when)
490 throws GTUException
491 {
492
493 CrossSectionLink link = projectionLane.getParentLink();
494 for (CrossSectionElement cse : link.getCrossSectionElementList())
495 {
496 if (cse instanceof Lane)
497 {
498 Lane cseLane = (Lane) cse;
499 if (null != this.lanesCurrentOperationalPlan.get(cseLane))
500 {
501 double fractionalPosition = fractionalPosition(cseLane, RelativePosition.REFERENCE_POSITION, when);
502 Length pos = new Length(projectionLane.getLength().getSI() * fractionalPosition, LengthUnit.SI);
503 if (this.lanesCurrentOperationalPlan.get(cseLane).isPlus())
504 {
505 return pos.plus(relativePosition.getDx());
506 }
507 return pos.minus(relativePosition.getDx());
508 }
509 }
510 }
511 throw new GTUException(this + " is not on any lane of Link " + link);
512 }
513
514
515 @Override
516 @SuppressWarnings("checkstyle:designforextension")
517 public Length position(final Lane lane, final RelativePosition relativePosition, final Time when) throws GTUException
518 {
519 if (null == lane)
520 {
521 throw new GTUException("lane is null");
522 }
523 synchronized (this.lock)
524 {
525 if (!this.lanesCurrentOperationalPlan.containsKey(lane))
526 {
527 throw new GTUException("position() : GTU " + toString() + " is not on lane " + lane);
528 }
529 if (!this.fractionalLinkPositions.containsKey(lane.getParentLink()))
530 {
531
532 throw new GTUException(this + " does not have a fractional position on " + lane.toString());
533 }
534 double longitudinalPosition = lane.positionSI(this.fractionalLinkPositions.get(lane.getParentLink()));
535 if (getOperationalPlan() == null)
536 {
537
538 return new Length(longitudinalPosition + relativePosition.getDx().si, LengthUnit.SI);
539 }
540 double loc;
541 try
542 {
543 if (this.lanesCurrentOperationalPlan.get(lane).isPlus())
544 {
545 loc = longitudinalPosition + getOperationalPlan().getTraveledDistanceSI(when) + relativePosition.getDx().si;
546 }
547 else
548 {
549 loc = longitudinalPosition - getOperationalPlan().getTraveledDistanceSI(when) - relativePosition.getDx().si;
550 }
551 }
552 catch (Exception e)
553 {
554 System.err.println(toString());
555 System.err.println(this.lanesCurrentOperationalPlan);
556 System.err.println(this.fractionalLinkPositions);
557 throw new GTUException(e);
558 }
559 if (Double.isNaN(loc))
560 {
561 System.out.println("loc is NaN");
562 }
563 return new Length(loc, LengthUnit.SI);
564 }
565 }
566
567
568 @Override
569 @SuppressWarnings("checkstyle:designforextension")
570 public DirectedLanePosition getReferencePosition() throws GTUException
571 {
572 for (Lane lane : this.lanesCurrentOperationalPlan.keySet())
573 {
574 double fraction = fractionalPosition(lane, getReference());
575 if (fraction >= 0.0 && fraction <= 1.0)
576 {
577
578
579 return new DirectedLanePosition(lane, position(lane, getReference()), this.getDirection(lane));
580 }
581 }
582 throw new GTUException("The reference point of GTU " + this + " is not on any of the lanes on which it is registered");
583 }
584
585
586
587
588
589
590
591
592
593 @SuppressWarnings("checkstyle:designforextension")
594 protected void scheduleEnterLeaveTriggers() throws NetworkException, SimRuntimeException, GTUException
595 {
596
597
598
599
600
601 double moveSI = getOperationalPlan().getTotalLength().si;
602 Map<Lane, GTUDirectionality> lanesCopy = new LinkedHashMap<>(this.lanesCurrentOperationalPlan);
603 for (Lane lane : lanesCopy.keySet())
604 {
605
606 double referenceStartSI = this.fractionalLinkPositions.get(lane.getParentLink()) * lane.getLength().getSI();
607 double sign = lanesCopy.get(lane).equals(GTUDirectionality.DIR_PLUS) ? 1.0 : -1.0;
608 if (lanesCopy.get(lane).equals(GTUDirectionality.DIR_PLUS))
609 {
610 lane.scheduleSensorTriggers(this, referenceStartSI, moveSI);
611 }
612 else
613 {
614
615 lane.scheduleSensorTriggers(this, referenceStartSI - moveSI, moveSI);
616 }
617
618
619
620
621 double frontPosSI = referenceStartSI + sign * getFront().getDx().getSI();
622 double nextFrontPosSI = frontPosSI + sign * moveSI;
623
624
625 if (lanesCopy.get(lane).equals(GTUDirectionality.DIR_PLUS))
626 {
627 while (frontPosSI <= lane.getLength().si && nextFrontPosSI > lane.getLength().si)
628 {
629
630 Lane nextLane = determineNextLane(lane);
631 GTUDirectionality direction = lane.nextLanes(getGTUType()).get(nextLane);
632
633
634
635
636
637
638 if (direction.equals(GTUDirectionality.DIR_PLUS))
639 {
640 Length refPosAtLastTimestep =
641 new Length(-(lane.getLength().si - frontPosSI) - getFront().getDx().si, LengthUnit.SI);
642 enterLane(nextLane, refPosAtLastTimestep, direction);
643
644 nextLane.scheduleSensorTriggers(this, refPosAtLastTimestep.getSI(), moveSI);
645 }
646 else if (direction.equals(GTUDirectionality.DIR_MINUS))
647 {
648 Length refPosAtLastTimestep =
649 new Length(nextLane.getLength().si + (lane.getLength().si - frontPosSI) + getFront().getDx().si,
650 LengthUnit.SI);
651 enterLane(nextLane, refPosAtLastTimestep, direction);
652
653
654 nextLane.scheduleSensorTriggers(this, refPosAtLastTimestep.getSI() - moveSI, moveSI);
655 }
656 else
657 {
658 throw new NetworkException("scheduleTriggers DIR_PLUS for GTU " + toString() + ", nextLane " + nextLane
659 + ", direction not DIR_PLUS or DIR_MINUS");
660 }
661
662
663
664 frontPosSI -= lane.getLength().si;
665 nextFrontPosSI -= lane.getLength().si;
666 lane = nextLane;
667 }
668 }
669
670
671 else if (lanesCopy.get(lane).equals(GTUDirectionality.DIR_MINUS))
672 {
673 if (frontPosSI >= 0.0 && nextFrontPosSI < 0.0)
674 {
675 Lane prevLane = determinePrevLane(lane);
676 GTUDirectionality direction = lane.prevLanes(getGTUType()).get(prevLane);
677
678
679
680
681
682
683 if (direction.equals(GTUDirectionality.DIR_MINUS))
684 {
685
686 Length refPosAtLastTimestep =
687 new Length(prevLane.getLength().si + frontPosSI + getFront().getDx().si, LengthUnit.SI);
688 enterLane(prevLane, refPosAtLastTimestep, direction);
689
690 prevLane.scheduleSensorTriggers(this, refPosAtLastTimestep.getSI() - moveSI, moveSI);
691 }
692 else if (direction.equals(GTUDirectionality.DIR_PLUS))
693 {
694 Length refPosAtLastTimestep = new Length(-frontPosSI - getFront().getDx().si, LengthUnit.SI);
695 enterLane(prevLane, refPosAtLastTimestep, direction);
696
697
698 prevLane.scheduleSensorTriggers(this, refPosAtLastTimestep.getSI(), moveSI);
699 }
700 else
701 {
702 throw new NetworkException("scheduleTriggers DIR_MINUS for GTU " + toString() + ", prevLane " + prevLane
703 + ", direction not DIR_PLUS or DIR_MINUS");
704 }
705 }
706 }
707
708 else
709 {
710 throw new NetworkException(
711 "scheduleTriggers for GTU " + toString() + ", lane " + lane + ", direction not DIR_PLUS or DIR_MINUS");
712 }
713 }
714
715
716 for (Lane lane : this.lanesCurrentOperationalPlan.keySet())
717 {
718
719
720 double referenceStartSI = this.fractionalLinkPositions.get(lane.getParentLink()) * lane.getLength().getSI();
721 double sign = this.lanesCurrentOperationalPlan.get(lane).equals(GTUDirectionality.DIR_PLUS) ? 1.0 : -1.0;
722 double rearPosSI = referenceStartSI + sign * getRear().getDx().getSI();
723
724 if (this.lanesCurrentOperationalPlan.get(lane).equals(GTUDirectionality.DIR_PLUS))
725 {
726 if (rearPosSI <= lane.getLength().si && rearPosSI + moveSI > lane.getLength().si)
727 {
728 double distanceToLeave = lane.getLength().si - rearPosSI;
729 Time exitTime = getOperationalPlan().timeAtDistance(new Length(distanceToLeave, LengthUnit.SI));
730 SimEvent<OTSSimTimeDouble> event = new SimEvent<>(new OTSSimTimeDouble(exitTime), this, this, "leaveLane",
731 new Object[] { lane, new Boolean(false) });
732 getSimulator().scheduleEvent(event);
733 addTrigger(lane, event);
734
735
736
737
738 }
739 }
740 else
741 {
742 if (rearPosSI >= 0.0 && rearPosSI - moveSI < 0.0)
743 {
744 double distanceToLeave = rearPosSI;
745 Time exitTime = getOperationalPlan().timeAtDistance(new Length(distanceToLeave, LengthUnit.SI));
746 SimEvent<OTSSimTimeDouble> event = new SimEvent<>(new OTSSimTimeDouble(exitTime), this, this, "leaveLane",
747 new Object[] { lane, new Boolean(false) });
748 getSimulator().scheduleEvent(event);
749 addTrigger(lane, event);
750
751
752
753 }
754 }
755 }
756 }
757
758
759
760
761
762
763
764
765 @SuppressWarnings("checkstyle:designforextension")
766 protected Lane determineNextLane(final Lane lane) throws NetworkException, GTUException
767 {
768 Lane nextLane = null;
769 if (lane.nextLanes(getGTUType()).size() == 0)
770 {
771 throw new NetworkException(this + " - lane " + lane + " does not have a successor");
772 }
773 if (lane.nextLanes(getGTUType()).size() == 1)
774 {
775 nextLane = lane.nextLanes(getGTUType()).keySet().iterator().next();
776 }
777 else
778 {
779 if (!(getStrategicalPlanner() instanceof LaneBasedStrategicalRoutePlanner))
780 {
781 throw new GTUException(this + " reaches branch but has no route navigator");
782 }
783 Node nextNode = ((LaneBasedStrategicalRoutePlanner) getStrategicalPlanner()).nextNode(lane.getParentLink(),
784 GTUDirectionality.DIR_PLUS, getGTUType());
785 if (null == nextNode)
786 {
787 throw new GTUException(this + " reaches branch and the route returns null as nextNodeToVisit");
788 }
789 int continuingLaneCount = 0;
790 for (Lane candidateLane : lane.nextLanes(getGTUType()).keySet())
791 {
792 if (null != this.lanesCurrentOperationalPlan.get(candidateLane))
793 {
794 continue;
795 }
796
797 if (nextNode == candidateLane.getParentLink().getEndNode()
798 || nextNode == candidateLane.getParentLink().getStartNode())
799 {
800 nextLane = candidateLane;
801 continuingLaneCount++;
802 }
803 }
804 if (continuingLaneCount == 0)
805 {
806 throw new NetworkException(this + " reached branch and the route specifies a nextNodeToVisit (" + nextNode
807 + ") that is not a next node " + "at this branch at (" + lane.getParentLink().getEndNode() + ")");
808 }
809 if (continuingLaneCount > 1)
810 {
811 throw new NetworkException(
812 this + " reached branch and the route specifies multiple lanes to continue on at this branch ("
813 + lane.getParentLink().getEndNode() + "). This is not yet supported");
814 }
815 }
816 return nextLane;
817 }
818
819
820
821
822
823
824
825
826 @SuppressWarnings("checkstyle:designforextension")
827 protected Lane determinePrevLane(final Lane lane) throws NetworkException, GTUException
828 {
829 Lane prevLane = null;
830 if (lane.prevLanes(getGTUType()).size() == 0)
831 {
832 throw new NetworkException(this + " - lane " + lane + " does not have a predecessor");
833 }
834 if (lane.prevLanes(getGTUType()).size() == 1)
835 {
836 prevLane = lane.prevLanes(getGTUType()).keySet().iterator().next();
837 }
838 else
839 {
840 if (!(getStrategicalPlanner() instanceof LaneBasedStrategicalRoutePlanner))
841 {
842 throw new GTUException(this + " reaches branch but has no route navigator");
843 }
844 Node prevNode = ((LaneBasedStrategicalRoutePlanner) getStrategicalPlanner()).nextNode(lane.getParentLink(),
845 GTUDirectionality.DIR_MINUS, getGTUType());
846 if (null == prevNode)
847 {
848 throw new GTUException(this + " reaches branch and the route returns null as nextNodeToVisit");
849 }
850 int continuingLaneCount = 0;
851 for (Lane candidateLane : lane.prevLanes(getGTUType()).keySet())
852 {
853 if (null != this.lanesCurrentOperationalPlan.get(candidateLane))
854 {
855 continue;
856 }
857
858 if (prevNode == candidateLane.getParentLink().getEndNode()
859 || prevNode == candidateLane.getParentLink().getStartNode())
860 {
861 prevLane = candidateLane;
862 continuingLaneCount++;
863 }
864 }
865 if (continuingLaneCount == 0)
866 {
867 throw new NetworkException(this + " reached branch and the route specifies a nextNodeToVisit (" + prevNode
868 + ") that is not a next node " + "at this branch at (" + lane.getParentLink().getStartNode() + ")");
869 }
870 if (continuingLaneCount > 1)
871 {
872 throw new NetworkException(
873 this + " reached branch and the route specifies multiple lanes to continue on at this branch ("
874 + lane.getParentLink().getStartNode() + "). This is not yet supported");
875 }
876 }
877 return prevLane;
878 }
879
880
881 @Override
882 public final Map<Lane, Double> fractionalPositions(final RelativePosition relativePosition) throws GTUException
883 {
884 return fractionalPositions(relativePosition, getSimulator().getSimulatorTime().getTime());
885 }
886
887
888 @Override
889 public final Map<Lane, Double> fractionalPositions(final RelativePosition relativePosition, final Time when)
890 throws GTUException
891 {
892 Map<Lane, Double> positions = new LinkedHashMap<>();
893 for (Lane lane : this.lanesCurrentOperationalPlan.keySet())
894 {
895 positions.put(lane, fractionalPosition(lane, relativePosition, when));
896 }
897 return positions;
898 }
899
900
901 @Override
902 public final double fractionalPosition(final Lane lane, final RelativePosition relativePosition, final Time when)
903 throws GTUException
904 {
905 return position(lane, relativePosition, when).getSI() / lane.getLength().getSI();
906 }
907
908
909 @Override
910 public final double fractionalPosition(final Lane lane, final RelativePosition relativePosition) throws GTUException
911 {
912 return position(lane, relativePosition).getSI() / lane.getLength().getSI();
913 }
914
915
916 @Override
917 public final LaneBasedStrategicalPlanner getStrategicalPlanner()
918 {
919 return (LaneBasedStrategicalPlanner) super.getStrategicalPlanner();
920 }
921
922
923 @Override
924 public final BehavioralCharacteristics getBehavioralCharacteristics()
925 {
926 return getStrategicalPlanner().getBehavioralCharacteristics();
927 }
928
929
930 @Override
931 public final LaneBasedTacticalPlanner getTacticalPlanner()
932 {
933 return (LaneBasedTacticalPlanner) super.getTacticalPlanner();
934 }
935
936
937 @Override
938 public final void addTrigger(final Lane lane, final SimEvent<OTSSimTimeDouble> event)
939 {
940 List<SimEvent<OTSSimTimeDouble>> list = this.pendingTriggers.get(lane);
941 if (null == list)
942 {
943 list = new ArrayList<>();
944 }
945 list.add(event);
946 this.pendingTriggers.put(lane, list);
947 }
948
949
950 @Override
951 @SuppressWarnings("checkstyle:designforextension")
952 public void destroy()
953 {
954 DirectedLanePosition dlp = null;
955 try
956 {
957 dlp = getReferencePosition();
958 }
959 catch (GTUException e)
960 {
961
962 }
963 DirectedPoint location = this.getOperationalPlan() == null ? new DirectedPoint(0.0, 0.0, 0.0) : getLocation();
964
965 synchronized (this.lock)
966 {
967 Set<Lane> laneSet = new HashSet<>(this.lanesCurrentOperationalPlan.keySet());
968 for (Lane lane : laneSet)
969 {
970 try
971 {
972 leaveLane(lane, true);
973 }
974 catch (GTUException e)
975 {
976
977 }
978 }
979 }
980
981 if (dlp != null && dlp.getLane() != null)
982 {
983 Lane referenceLane = dlp.getLane();
984 fireTimedEvent(LaneBasedGTU.LANEBASED_DESTROY_EVENT,
985 new Object[] { getId(), location, getOdometer(), referenceLane, dlp.getPosition(), dlp.getGtuDirection() },
986 getSimulator().getSimulatorTime());
987 }
988 else
989 {
990 fireTimedEvent(LaneBasedGTU.LANEBASED_DESTROY_EVENT,
991 new Object[] { getId(), location, getOdometer(), null, Length.ZERO, null },
992 getSimulator().getSimulatorTime());
993 }
994
995 super.destroy();
996 }
997
998
999 @Override
1000 public final Bounds getBounds()
1001 {
1002 double dx = 0.5 * getLength().doubleValue();
1003 double dy = 0.5 * getWidth().doubleValue();
1004 return new BoundingBox(new Point3d(-dx, -dy, 0.0), new Point3d(dx, dy, 0.0));
1005 }
1006
1007
1008 @Override
1009 @SuppressWarnings("checkstyle:designforextension")
1010 public String toString()
1011 {
1012 return String.format("GTU " + getId());
1013 }
1014 }