1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
6 import java.util.HashSet;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import nl.tudelft.simulation.language.d3.DirectedPoint;
12
13 import org.djunits.unit.AccelerationUnit;
14 import org.djunits.unit.LengthUnit;
15 import org.djunits.unit.TimeUnit;
16 import org.djunits.value.StorageType;
17 import org.djunits.value.ValueException;
18 import org.djunits.value.vdouble.scalar.Acceleration;
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djunits.value.vdouble.scalar.Speed;
21 import org.djunits.value.vdouble.scalar.Time;
22 import org.djunits.value.vdouble.vector.AccelerationVector;
23 import org.opentrafficsim.core.geometry.OTSLine3D;
24 import org.opentrafficsim.core.gtu.GTU;
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.plan.operational.OperationalPlan;
29 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
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.road.gtu.lane.LaneBasedGTU;
36 import org.opentrafficsim.road.gtu.lane.perception.LanePerceptionFull;
37 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
38 import org.opentrafficsim.road.gtu.lane.tactical.following.HeadwayGTU;
39 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
40 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
41 import org.opentrafficsim.road.network.lane.CrossSectionElement;
42 import org.opentrafficsim.road.network.lane.CrossSectionLink;
43 import org.opentrafficsim.road.network.lane.Lane;
44 import org.opentrafficsim.road.network.lane.Sensor;
45 import org.opentrafficsim.road.network.lane.SinkSensor;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class LaneBasedCFLCTacticalPlanner extends AbstractLaneBasedTacticalPlanner
61 {
62
63 private static final long serialVersionUID = 20151125L;
64
65
66 private static final Acceleration STAYINCURRENTLANEINCENTIVE = new Acceleration(0.1, AccelerationUnit.METER_PER_SECOND_2);
67
68
69 private static final Acceleration PREFERREDLANEINCENTIVE = new Acceleration(0.3, AccelerationUnit.METER_PER_SECOND_2);
70
71
72 private static final Acceleration NONPREFERREDLANEINCENTIVE = new Acceleration(-0.3, AccelerationUnit.METER_PER_SECOND_2);
73
74
75 public static final Length.Rel NOLANECHANGENEEDED = new Length.Rel(Double.MAX_VALUE, LengthUnit.SI);
76
77
78 public static final Length.Rel GETOFFTHISLANENOW = Length.Rel.ZERO;
79
80
81 private static final Time.Rel TIMEHORIZON = new Time.Rel(90, TimeUnit.SECOND);
82
83
84
85
86 public LaneBasedCFLCTacticalPlanner()
87 {
88 super();
89 }
90
91
92 @Override
93 public OperationalPlan generateOperationalPlan(final GTU gtu, final Time.Abs startTime,
94 final DirectedPoint locationAtStartTime) throws OperationalPlanException, NetworkException, GTUException
95 {
96 try
97 {
98
99 LaneBasedGTU laneBasedGTU = (LaneBasedGTU) gtu;
100 LanePerceptionFull perception = laneBasedGTU.getPerception();
101
102
103 if (laneBasedGTU.getMaximumVelocity().si < OperationalPlan.DRIFTING_SPEED_SI)
104 {
105 return new OperationalPlan(gtu, locationAtStartTime, startTime, new Time.Rel(1.0, TimeUnit.SECOND));
106 }
107
108
109
110 perception.perceive();
111
112 Length.Rel maximumForwardHeadway = laneBasedGTU.getBehavioralCharacteristics().getForwardHeadwayDistance();
113 Length.Rel maximumReverseHeadway = laneBasedGTU.getBehavioralCharacteristics().getBackwardHeadwayDistance();
114 Time.Abs now = gtu.getSimulator().getSimulatorTime().getTime();
115 Speed speedLimit = perception.getSpeedLimit();
116
117
118 GTUFollowingModelOld gtuFollowingModel =
119 laneBasedGTU.getStrategicalPlanner().getDrivingCharacteristics().getGTUFollowingModel();
120 LaneChangeModel laneChangeModel =
121 laneBasedGTU.getStrategicalPlanner().getDrivingCharacteristics().getLaneChangeModel();
122
123
124 HeadwayGTU sameLaneLeader = perception.getForwardHeadwayGTU();
125 HeadwayGTU sameLaneFollower = perception.getBackwardHeadwayGTU();
126 Collection<HeadwayGTU> sameLaneTraffic = new ArrayList<HeadwayGTU>();
127 if (null != sameLaneLeader.getGtuId())
128 {
129 sameLaneTraffic.add(sameLaneLeader);
130 }
131 if (null != sameLaneFollower.getGtuId())
132 {
133 sameLaneTraffic.add(new HeadwayGTU(sameLaneFollower.getGtuId(), sameLaneFollower.getGtuSpeed(),
134 sameLaneFollower.getDistance().si, sameLaneFollower.getGtuType()));
135 }
136
137
138 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
139 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, maximumForwardHeadway);
140 boolean currentLaneFine = nextSplitInfo.getCorrectCurrentLanes().contains(lanePathInfo.getReferenceLane());
141
142
143
144
145
146 Collection<HeadwayGTU> leftLaneTraffic = perception.getNeighboringGTUsLeft();
147 Collection<HeadwayGTU> rightLaneTraffic = perception.getNeighboringGTUsRight();
148
149
150 final LateralDirectionality preferred = LateralDirectionality.RIGHT;
151 final Acceleration defaultLeftLaneIncentive =
152 LateralDirectionality.LEFT == preferred ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
153 final Acceleration defaultRightLaneIncentive =
154 LateralDirectionality.RIGHT == preferred ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
155
156 AccelerationVector defaultLaneIncentives =
157 new AccelerationVector(new double[] { defaultLeftLaneIncentive.getSI(), STAYINCURRENTLANEINCENTIVE.getSI(),
158 defaultRightLaneIncentive.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
159 AccelerationVector laneIncentives = laneIncentives(laneBasedGTU, defaultLaneIncentives);
160 LaneMovementStep lcmr =
161 laneChangeModel.computeLaneChangeAndAcceleration(laneBasedGTU, sameLaneTraffic, rightLaneTraffic,
162 leftLaneTraffic, speedLimit,
163 new Acceleration(laneIncentives.get(preferred == LateralDirectionality.RIGHT ? 2 : 0)),
164 new Acceleration(laneIncentives.get(1)),
165 new Acceleration(laneIncentives.get(preferred == LateralDirectionality.RIGHT ? 0 : 2)));
166 Time.Rel duration = lcmr.getGfmr().getValidUntil().minus(gtu.getSimulator().getSimulatorTime().getTime());
167
168
169
170
171 if (lcmr.getLaneChange() != null)
172 {
173 Collection<Lane> oldLaneSet = new HashSet<Lane>(laneBasedGTU.getLanes().keySet());
174 Collection<Lane> newLaneSet = new HashSet<Lane>(2);
175 Map<Lane, Double> oldFractionalPositions = new LinkedHashMap<Lane, Double>();
176 for (Lane l : laneBasedGTU.getLanes().keySet())
177 {
178 oldFractionalPositions.put(l, laneBasedGTU.fractionalPosition(l, gtu.getReference(), now));
179 if (lcmr.getLaneChange().equals(LateralDirectionality.LEFT))
180 {
181 newLaneSet.addAll(laneBasedGTU.getPerception().getAccessibleAdjacentLanesLeft().get(l));
182 }
183 else
184 {
185 newLaneSet.addAll(laneBasedGTU.getPerception().getAccessibleAdjacentLanesRight().get(l));
186 }
187 }
188
189
190 for (Lane newLane : newLaneSet)
191 {
192 Double fractionalPosition = null;
193
194 Lane foundOldLane = null;
195 for (Lane oldLane : oldLaneSet)
196 {
197 if ((lcmr.getLaneChange().equals(LateralDirectionality.LEFT) && laneBasedGTU.getPerception()
198 .getAccessibleAdjacentLanesLeft().get(oldLane).contains(newLane))
199 || (lcmr.getLaneChange().equals(LateralDirectionality.RIGHT) && laneBasedGTU.getPerception()
200 .getAccessibleAdjacentLanesRight().get(oldLane).contains(newLane)))
201 {
202 fractionalPosition = oldFractionalPositions.get(oldLane);
203 foundOldLane = oldLane;
204 break;
205 }
206 }
207 if (null == fractionalPosition)
208 {
209 throw new Error("Program error: Cannot find an oldLane that has newLane " + newLane + " as "
210 + lcmr.getLaneChange() + " neighbor");
211 }
212 laneBasedGTU.enterLane(newLane, newLane.getLength().multiplyBy(fractionalPosition), laneBasedGTU.getLanes()
213 .get(foundOldLane));
214 }
215
216
217
218
219
220
221
222
223
224
225 System.out.println(gtu + " changes lane from " + oldLaneSet + " to " + newLaneSet + " at time "
226 + gtu.getSimulator().getSimulatorTime().get());
227
228
229 for (Lane l : oldFractionalPositions.keySet())
230 {
231 laneBasedGTU.leaveLane(l);
232 }
233
234 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
235
236
237
238 if (lcmr.getGfmr().getAcceleration().si < 1E-6
239 && laneBasedGTU.getVelocity().si < OperationalPlan.DRIFTING_SPEED_SI)
240 {
241
242 return new OperationalPlan(gtu, locationAtStartTime, startTime, duration);
243 }
244
245
246 OTSLine3D path = lanePathInfo.getPath();
247 List<Segment> operationalPlanSegmentList = new ArrayList<>();
248 if (lcmr.getGfmr().getAcceleration().si == 0.0)
249 {
250 Segment segment = new OperationalPlan.SpeedSegment(duration);
251 operationalPlanSegmentList.add(segment);
252 }
253 else
254 {
255 Segment segment = new OperationalPlan.AccelerationSegment(duration, lcmr.getGfmr().getAcceleration());
256 operationalPlanSegmentList.add(segment);
257 }
258 OperationalPlan op = new OperationalPlan(gtu, path, startTime, gtu.getVelocity(), operationalPlanSegmentList);
259 return op;
260 }
261 else
262
263 {
264
265 if (lcmr.getGfmr().getAcceleration().si < 1E-6
266 && laneBasedGTU.getVelocity().si < OperationalPlan.DRIFTING_SPEED_SI)
267 {
268 return new OperationalPlan(gtu, locationAtStartTime, startTime, duration);
269 }
270
271 OTSLine3D path = lanePathInfo.getPath();
272 List<Segment> operationalPlanSegmentList = new ArrayList<>();
273 if (lcmr.getGfmr().getAcceleration().si == 0.0)
274 {
275 Segment segment = new OperationalPlan.SpeedSegment(duration);
276 operationalPlanSegmentList.add(segment);
277 }
278 else
279 {
280 Segment segment = new OperationalPlan.AccelerationSegment(duration, lcmr.getGfmr().getAcceleration());
281 operationalPlanSegmentList.add(segment);
282 }
283 OperationalPlan op = new OperationalPlan(gtu, path, startTime, gtu.getVelocity(), operationalPlanSegmentList);
284 return op;
285 }
286 }
287 catch (ValueException exception)
288 {
289 throw new GTUException(exception);
290 }
291 }
292
293
294
295
296
297
298
299
300
301
302
303
304 private AccelerationVector laneIncentives(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
305 throws NetworkException, ValueException, GTUException
306 {
307 Length.Rel leftSuitability = suitability(gtu, LateralDirectionality.LEFT);
308 Length.Rel currentSuitability = suitability(gtu, null);
309 Length.Rel rightSuitability = suitability(gtu, LateralDirectionality.RIGHT);
310 if (leftSuitability == NOLANECHANGENEEDED && currentSuitability == NOLANECHANGENEEDED
311 && rightSuitability == NOLANECHANGENEEDED)
312 {
313 return checkLaneDrops(gtu, defaultLaneIncentives);
314 }
315 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
316 && currentSuitability == NOLANECHANGENEEDED
317 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
318 {
319 return checkLaneDrops(gtu, new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
320 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
321 StorageType.DENSE));
322 }
323 if (currentSuitability == NOLANECHANGENEEDED)
324 {
325 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
326 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
327 StorageType.DENSE);
328 }
329 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
330 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344 private AccelerationVector checkLaneDrops(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
345 throws NetworkException, ValueException, GTUException
346 {
347
348 Length.Rel leftSuitability =
349 Double.isNaN(defaultLaneIncentives.get(0).si) || defaultLaneIncentives.get(0).si < -10 ? GETOFFTHISLANENOW
350 : laneDrop(gtu, LateralDirectionality.LEFT);
351 Length.Rel currentSuitability = laneDrop(gtu, null);
352 Length.Rel rightSuitability =
353 Double.isNaN(defaultLaneIncentives.get(2).si) || defaultLaneIncentives.get(2).si < -10 ? GETOFFTHISLANENOW
354 : laneDrop(gtu, LateralDirectionality.RIGHT);
355
356 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
357 && currentSuitability == NOLANECHANGENEEDED
358 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
359 {
360 return defaultLaneIncentives;
361 }
362
363 if (currentSuitability == NOLANECHANGENEEDED)
364 {
365 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
366 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
367 StorageType.DENSE);
368 }
369 if (currentSuitability.le(leftSuitability))
370 {
371 return new AccelerationVector(new double[] { PREFERREDLANEINCENTIVE.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
372 GETOFFTHISLANENOW.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
373 }
374 if (currentSuitability.le(rightSuitability))
375 {
376 return new AccelerationVector(new double[] { GETOFFTHISLANENOW.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
377 PREFERREDLANEINCENTIVE.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
378 }
379 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
380 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
381 }
382
383
384
385
386
387
388
389
390
391
392
393
394
395 private Length.Rel laneDrop(final LaneBasedGTU gtu, final LateralDirectionality direction) throws NetworkException,
396 GTUException
397 {
398 Lane lane = null;
399 Length.Rel longitudinalPosition = null;
400 Map<Lane, Length.Rel> positions = gtu.positions(RelativePosition.REFERENCE_POSITION);
401 if (null == direction)
402 {
403 for (Lane l : gtu.getLanes().keySet())
404 {
405 if (l.getLaneType().isCompatible(gtu.getGTUType()))
406 {
407 lane = l;
408 }
409 }
410 if (null == lane)
411 {
412 throw new NetworkException(this + " is not on any compatible lane");
413 }
414 longitudinalPosition = positions.get(lane);
415 }
416 else
417 {
418 lane = positions.keySet().iterator().next();
419 longitudinalPosition = positions.get(lane);
420 lane = gtu.getPerception().bestAccessibleAdjacentLane(lane, direction, longitudinalPosition);
421 }
422 if (null == lane)
423 {
424 return GETOFFTHISLANENOW;
425 }
426 double remainingLength = lane.getLength().getSI() - longitudinalPosition.getSI();
427 double remainingTimeSI = TIMEHORIZON.getSI() - remainingLength / lane.getSpeedLimit(gtu.getGTUType()).getSI();
428 while (remainingTimeSI >= 0)
429 {
430 for (Sensor s : lane.getSensors())
431 {
432 if (s instanceof SinkSensor)
433 {
434 return NOLANECHANGENEEDED;
435 }
436 }
437 int branching = lane.nextLanes(gtu.getGTUType()).size();
438 if (branching == 0)
439 {
440 return new Length.Rel(remainingLength, LengthUnit.SI);
441 }
442 if (branching > 1)
443 {
444 return NOLANECHANGENEEDED;
445 }
446 lane = lane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
447 remainingTimeSI -= lane.getLength().getSI() / lane.getSpeedLimit(gtu.getGTUType()).getSI();
448 remainingLength += lane.getLength().getSI();
449 }
450 return NOLANECHANGENEEDED;
451 }
452
453
454
455
456
457
458
459
460
461
462
463 private Length.Rel suitability(final LaneBasedGTU gtu, final LateralDirectionality direction) throws NetworkException,
464 GTUException
465 {
466 Lane lane = null;
467 Length.Rel longitudinalPosition = null;
468 Map<Lane, Length.Rel> positions = gtu.positions(RelativePosition.REFERENCE_POSITION);
469 if (null == direction)
470 {
471 for (Lane l : gtu.getLanes().keySet())
472 {
473 if (l.getLaneType().isCompatible(gtu.getGTUType()))
474 {
475 lane = l;
476 }
477 }
478 if (null == lane)
479 {
480 throw new NetworkException(this + " is not on any compatible lane");
481 }
482 longitudinalPosition = positions.get(lane);
483 }
484 else
485 {
486 lane = positions.keySet().iterator().next();
487 longitudinalPosition = positions.get(lane);
488
489
490
491
492
493 lane = gtu.getPerception().bestAccessibleAdjacentLane(lane, direction, longitudinalPosition);
494
495 if (null != lane)
496 {
497
498 if ((!canChangeLane(gtu.positions(gtu.getRear()), positions, gtu, direction))
499 || (!canChangeLane(gtu.positions(gtu.getFront()), positions, gtu, direction)))
500 {
501
502 lane = null;
503 }
504 }
505 }
506 if (null == lane)
507 {
508 return GETOFFTHISLANENOW;
509 }
510 try
511 {
512 return suitability(lane, longitudinalPosition, gtu, TIMEHORIZON);
513 }
514 catch (NetworkException ne)
515 {
516 System.err.println(gtu + " has a route problem in suitability: " + ne.getMessage());
517 return NOLANECHANGENEEDED;
518 }
519 }
520
521
522
523
524
525
526
527
528
529
530 private boolean canChangeLane(final Map<Lane, Length.Rel> laneMap, final Map<Lane, Length.Rel> positions,
531 final LaneBasedGTU gtu, final LateralDirectionality direction)
532 {
533 for (Lane lane : laneMap.keySet())
534 {
535 Length.Rel positionInLane = laneMap.get(lane);
536 if (positionInLane.si < 0 || positionInLane.gt(lane.getLength()))
537 {
538 continue;
539 }
540 if (null == gtu.getPerception().bestAccessibleAdjacentLane(lane, direction, positions.get(lane)))
541 {
542
543 return false;
544 }
545 }
546 return true;
547 }
548
549
550
551
552
553
554
555 private double acceleration(final LaneBasedGTU gtu, final Length.Rel stopDistance)
556 {
557
558
559 double v = gtu.getVelocity().getSI();
560 double a = -v * v / 2 / stopDistance.getSI();
561 return a;
562 }
563
564
565
566
567
568
569
570
571
572
573
574 private Length.Rel suitability(final Lane lane, final Length.Rel longitudinalPosition, final LaneBasedGTU gtu,
575 final Time.Rel timeHorizon) throws NetworkException
576 {
577 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
578 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getGTUType()).getSI();
579
580 Node nextNode = lane.getParentLink().getEndNode();
581 Link lastLink = lane.getParentLink();
582 Node nextSplitNode = null;
583 Lane currentLane = lane;
584 CrossSectionLink linkBeforeBranch = lane.getParentLink();
585 while (null != nextNode)
586 {
587 if (spareTime <= 0)
588 {
589 return NOLANECHANGENEEDED;
590 }
591 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getGTUType());
592 if (0 == laneCount)
593 {
594 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
595 }
596 if (1 == laneCount)
597 {
598 return NOLANECHANGENEEDED;
599
600 }
601 int branching = nextNode.getLinks().size();
602 if (branching > 2)
603 {
604 nextSplitNode = nextNode;
605 break;
606 }
607 else if (1 == branching)
608 {
609 return NOLANECHANGENEEDED;
610 }
611 else
612 {
613 Link nextLink = gtu.getStrategicalPlanner().nextLinkDirection(nextNode, lastLink, gtu.getGTUType()).getLink();
614 if (nextLink instanceof CrossSectionLink)
615 {
616 nextNode = nextLink.getEndNode();
617
618 remainingDistance += nextLink.getLength().getSI();
619 linkBeforeBranch = (CrossSectionLink) nextLink;
620
621 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
622 {
623
624
625 if (currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtu.getGTUType()).size() > 0)
626 {
627 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT,
628 gtu.getGTUType()))
629 {
630 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
631 {
632 currentLane = adjacentLane;
633 break;
634 }
635
636
637 }
638 }
639 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.LEFT,
640 gtu.getGTUType()))
641 {
642 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
643 {
644 currentLane = adjacentLane;
645 break;
646 }
647
648
649 }
650 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
651 {
652 throw new NetworkException("Lane ends and there is not a compatible adjacent lane that does "
653 + "not end");
654 }
655 }
656
657 for (Lane nextLane : currentLane.nextLanes(gtu.getGTUType()).keySet())
658 {
659 if (nextLane.getLaneType().isCompatible(gtu.getGTUType()))
660 {
661 currentLane = currentLane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
662 break;
663 }
664 }
665 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtu.getGTUType()).getSI();
666 }
667 else
668 {
669
670
671 return NOLANECHANGENEEDED;
672 }
673 lastLink = nextLink;
674 }
675 }
676 if (null == nextNode)
677 {
678 throw new NetworkException("Cannot find the next branch or sink node");
679 }
680
681
682 Map<Lane, Length.Rel> suitabilityOfLanesBeforeBranch = new HashMap<Lane, Length.Rel>();
683 Link linkAfterBranch =
684 gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType()).getLink();
685 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
686 {
687 if (cse instanceof Lane)
688 {
689 Lane l = (Lane) cse;
690 if (l.getLaneType().isCompatible(gtu.getGTUType()))
691 {
692 for (Lane connectingLane : l.nextLanes(gtu.getGTUType()).keySet())
693 {
694 if (connectingLane.getParentLink() == linkAfterBranch
695 && connectingLane.getLaneType().isCompatible(gtu.getGTUType()))
696 {
697 Length.Rel currentValue = suitabilityOfLanesBeforeBranch.get(l);
698
699
700
701 Length.Rel value =
702 suitability(connectingLane, new Length.Rel(0, LengthUnit.SI), gtu, new Time.Rel(spareTime,
703 TimeUnit.SI));
704
705
706 suitabilityOfLanesBeforeBranch.put(l, null == currentValue || value.le(currentValue) ? value
707 : currentValue);
708 }
709 }
710 }
711 }
712 }
713 if (suitabilityOfLanesBeforeBranch.size() == 0)
714 {
715 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
716 }
717 Length.Rel currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
718 if (null != currentLaneSuitability)
719 {
720 return currentLaneSuitability;
721 }
722
723 int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtu.getGTUType());
724 Length.Rel leftSuitability =
725 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
726 LateralDirectionality.LEFT, gtu.getGTUType());
727 Length.Rel rightSuitability =
728 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
729 LateralDirectionality.RIGHT, gtu.getGTUType());
730 if (leftSuitability.ge(rightSuitability))
731 {
732 return leftSuitability;
733 }
734 else if (rightSuitability.ge(leftSuitability))
735 {
736 return rightSuitability;
737 }
738 if (leftSuitability.le(GETOFFTHISLANENOW))
739 {
740 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
741 }
742 return leftSuitability;
743 }
744
745
746
747
748
749
750
751
752
753
754
755
756
757 protected final Length.Rel computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
758 final Map<Lane, Length.Rel> suitabilities, final int totalLanes, final LateralDirectionality direction,
759 final GTUType gtuType)
760 {
761
762
763
764
765
766
767
768
769
770 int laneChangesUsed = 0;
771 Lane currentLane = startLane;
772 Length.Rel currentSuitability = null;
773 while (null == currentSuitability)
774 {
775 laneChangesUsed++;
776 if (currentLane.accessibleAdjacentLanes(direction, gtuType).size() == 0)
777 {
778 return GETOFFTHISLANENOW;
779 }
780 currentLane = currentLane.accessibleAdjacentLanes(direction, gtuType).iterator().next();
781 currentSuitability = suitabilities.get(currentLane);
782 }
783 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
784 int notSuitableLaneCount = totalLanes - suitabilities.size();
785 return new Length.Rel(remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction)
786 / (notSuitableLaneCount + fraction), LengthUnit.SI);
787 }
788
789
790
791
792
793
794
795
796 protected final int countCompatibleLanes(final CrossSectionLink link, final GTUType gtuType)
797 {
798 int result = 0;
799 for (CrossSectionElement cse : link.getCrossSectionElementList())
800 {
801 if (cse instanceof Lane)
802 {
803 Lane l = (Lane) cse;
804 if (l.getLaneType().isCompatible(gtuType))
805 {
806 result++;
807 }
808 }
809 }
810 return result;
811 }
812 }