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