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