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