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.List;
7 import java.util.Map;
8
9 import org.djunits.unit.AccelerationUnit;
10 import org.djunits.unit.LengthUnit;
11 import org.djunits.unit.TimeUnit;
12 import org.djunits.value.StorageType;
13 import org.djunits.value.ValueException;
14 import org.djunits.value.vdouble.scalar.Acceleration;
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.djunits.value.vdouble.vector.AccelerationVector;
20 import org.opentrafficsim.core.geometry.OTSGeometryException;
21 import org.opentrafficsim.core.geometry.OTSLine3D;
22 import org.opentrafficsim.core.gtu.GTUException;
23 import org.opentrafficsim.core.gtu.GTUType;
24 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
25 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
26 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
27 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
28 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
29 import org.opentrafficsim.core.network.LateralDirectionality;
30 import org.opentrafficsim.core.network.Link;
31 import org.opentrafficsim.core.network.NetworkException;
32 import org.opentrafficsim.core.network.Node;
33 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
34 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
35 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
36 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
37 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
38 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
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.DirectedLanePosition;
44 import org.opentrafficsim.road.network.lane.Lane;
45 import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
46 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
47
48 import nl.tudelft.simulation.language.d3.DirectedPoint;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class LaneBasedCFLCTacticalPlanner extends AbstractLaneBasedTacticalPlanner
64 {
65
66 private static final long serialVersionUID = 20151125L;
67
68
69 private static final Acceleration STAYINCURRENTLANEINCENTIVE = new Acceleration(0.1, AccelerationUnit.METER_PER_SECOND_2);
70
71
72 private static final Acceleration PREFERREDLANEINCENTIVE = new Acceleration(0.3, AccelerationUnit.METER_PER_SECOND_2);
73
74
75 private static final Acceleration NONPREFERREDLANEINCENTIVE = new Acceleration(-0.3, AccelerationUnit.METER_PER_SECOND_2);
76
77
78 public static final Length NOLANECHANGENEEDED = new Length(Double.MAX_VALUE, LengthUnit.SI);
79
80
81 public static final Length GETOFFTHISLANENOW = Length.ZERO;
82
83
84 private static final Duration TIMEHORIZON = new Duration(90, TimeUnit.SECOND);
85
86
87 private LaneChangeModel laneChangeModel;
88
89
90
91
92
93
94
95 public LaneBasedCFLCTacticalPlanner(final GTUFollowingModelOld carFollowingModel, final LaneChangeModel laneChangeModel,
96 final LaneBasedGTU gtu)
97 {
98 super(carFollowingModel, gtu);
99 this.laneChangeModel = laneChangeModel;
100 getPerception().addPerceptionCategory(new DefaultSimplePerception(getPerception()));
101 }
102
103
104 @Override
105 public final OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
106 throws OperationalPlanException, NetworkException, GTUException, ParameterException
107 {
108 try
109 {
110
111 LaneBasedGTU laneBasedGTU = getGtu();
112 LanePerception perception = getPerception();
113
114
115 if (laneBasedGTU.getMaximumSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
116 {
117 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, TimeUnit.SECOND));
118 }
119
120
121
122 perception.perceive();
123
124 Length maximumForwardHeadway = laneBasedGTU.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
125 Length maximumReverseHeadway = laneBasedGTU.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACKOLD);
126 Time now = getGtu().getSimulator().getSimulatorTime().getTime();
127 Speed speedLimit = perception.getPerceptionCategory(DefaultSimplePerception.class).getSpeedLimit();
128
129
130 Headway sameLaneLeader = perception.getPerceptionCategory(DefaultSimplePerception.class).getForwardHeadwayGTU();
131
132 Headway sameLaneFollower = perception.getPerceptionCategory(DefaultSimplePerception.class).getBackwardHeadway();
133 Collection<Headway> sameLaneTraffic = new ArrayList<Headway>();
134 if (sameLaneLeader.getObjectType().isGtu())
135 {
136 sameLaneTraffic.add(sameLaneLeader);
137 }
138 if (sameLaneFollower.getObjectType().isGtu())
139 {
140 sameLaneTraffic.add(sameLaneFollower);
141 }
142
143
144 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
145
146
147 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, maximumForwardHeadway);
148 boolean currentLaneFine = nextSplitInfo.getCorrectCurrentLanes().contains(lanePathInfo.getReferenceLane());
149
150
151
152
153
154 Collection<Headway> leftLaneTraffic =
155 perception.getPerceptionCategory(DefaultSimplePerception.class).getNeighboringHeadwaysLeft();
156 Collection<Headway> rightLaneTraffic =
157 perception.getPerceptionCategory(DefaultSimplePerception.class).getNeighboringHeadwaysRight();
158
159
160 final LateralDirectionality preferred = LateralDirectionality.RIGHT;
161 final Acceleration defaultLeftLaneIncentive =
162 preferred.isLeft() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
163 final Acceleration defaultRightLaneIncentive =
164 preferred.isRight() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
165
166 AccelerationVector defaultLaneIncentives =
167 new AccelerationVector(new double[] { defaultLeftLaneIncentive.getSI(), STAYINCURRENTLANEINCENTIVE.getSI(),
168 defaultRightLaneIncentive.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
169 AccelerationVector laneIncentives = laneIncentives(laneBasedGTU, defaultLaneIncentives);
170 LaneMovementStep lcmr = this.laneChangeModel.computeLaneChangeAndAcceleration(laneBasedGTU, sameLaneTraffic,
171 rightLaneTraffic, leftLaneTraffic, speedLimit,
172 new Acceleration(laneIncentives.get(preferred.isRight() ? 2 : 0)), new Acceleration(laneIncentives.get(1)),
173 new Acceleration(laneIncentives.get(preferred.isRight() ? 0 : 2)));
174 Duration duration = lcmr.getGfmr().getValidUntil().minus(getGtu().getSimulator().getSimulatorTime().getTime());
175 if (lcmr.getLaneChangeDirection() != null)
176 {
177 laneBasedGTU.changeLaneInstantaneously(lcmr.getLaneChangeDirection());
178
179
180 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
181 }
182
183
184 Headway object = getPerception().getPerceptionCategory(DefaultSimplePerception.class).getForwardHeadwayObject();
185 Acceleration a = lcmr.getGfmr().getAcceleration();
186 if (object instanceof HeadwayTrafficLight)
187 {
188
189 a = Acceleration.min(a, ((GTUFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
190 getGtu().getMaximumSpeed(), Speed.ZERO, object.getDistance(), speedLimit));
191 }
192
193
194 Length dist = lanePathInfo.getPath().getLength().minus(getGtu().getFront().getDx());
195 a = Acceleration.min(a, ((GTUFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
196 getGtu().getMaximumSpeed(), Speed.ZERO, dist, speedLimit));
197
198
199 OTSLine3D path = lanePathInfo.getPath();
200 if (a.si < 1E-6 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
201 {
202 try
203 {
204 return new OperationalPlan(getGtu(), path.getLocationFraction(0.0), startTime, duration);
205 }
206 catch (OTSGeometryException exception)
207 {
208
209 throw new RuntimeException(exception);
210 }
211 }
212 List<Segment> operationalPlanSegmentList = new ArrayList<>();
213
214 if (a.si == 0.0)
215 {
216 Segment segment = new OperationalPlan.SpeedSegment(duration);
217 operationalPlanSegmentList.add(segment);
218 }
219 else
220 {
221 Segment segment = new OperationalPlan.AccelerationSegment(duration, a);
222 operationalPlanSegmentList.add(segment);
223 }
224 OperationalPlan op =
225 new OperationalPlan(getGtu(), path, startTime, getGtu().getSpeed(), operationalPlanSegmentList);
226 return op;
227 }
228 catch (ValueException exception)
229 {
230 throw new GTUException(exception);
231 }
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245
246 private AccelerationVector laneIncentives(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
247 throws NetworkException, ValueException, GTUException, OperationalPlanException
248 {
249 Length leftSuitability = suitability(gtu, LateralDirectionality.LEFT);
250 Length currentSuitability = suitability(gtu, null);
251 Length rightSuitability = suitability(gtu, LateralDirectionality.RIGHT);
252 if (leftSuitability == NOLANECHANGENEEDED && currentSuitability == NOLANECHANGENEEDED
253 && rightSuitability == NOLANECHANGENEEDED)
254 {
255 return checkLaneDrops(gtu, defaultLaneIncentives);
256 }
257 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
258 && currentSuitability == NOLANECHANGENEEDED
259 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
260 {
261 return checkLaneDrops(gtu,
262 new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
263 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
264 StorageType.DENSE));
265 }
266 if (currentSuitability == NOLANECHANGENEEDED)
267 {
268 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
269 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
270 StorageType.DENSE);
271 }
272 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
273 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288 private AccelerationVector checkLaneDrops(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
289 throws NetworkException, ValueException, GTUException, OperationalPlanException
290 {
291
292 Length leftSuitability = Double.isNaN(defaultLaneIncentives.get(0).si) || defaultLaneIncentives.get(0).si < -10
293 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.LEFT);
294 Length currentSuitability = laneDrop(gtu, null);
295 Length rightSuitability = Double.isNaN(defaultLaneIncentives.get(2).si) || defaultLaneIncentives.get(2).si < -10
296 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.RIGHT);
297
298 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
299 && currentSuitability == NOLANECHANGENEEDED
300 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
301 {
302 return defaultLaneIncentives;
303 }
304
305 if (currentSuitability == NOLANECHANGENEEDED)
306 {
307 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
308 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
309 StorageType.DENSE);
310 }
311 if (currentSuitability.le(leftSuitability))
312 {
313 return new AccelerationVector(new double[] { PREFERREDLANEINCENTIVE.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
314 GETOFFTHISLANENOW.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
315 }
316 if (currentSuitability.le(rightSuitability))
317 {
318 return new AccelerationVector(new double[] { GETOFFTHISLANENOW.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
319 PREFERREDLANEINCENTIVE.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
320 }
321 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
322 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338 private Length laneDrop(final LaneBasedGTU gtu, final LateralDirectionality direction)
339 throws NetworkException, GTUException, OperationalPlanException
340 {
341 DirectedLanePosition dlp = gtu.getReferencePosition();
342 Lane lane = dlp.getLane();
343 Length longitudinalPosition = dlp.getPosition();
344 if (null != direction)
345 {
346 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
347 direction, longitudinalPosition);
348 }
349 if (null == lane)
350 {
351 return GETOFFTHISLANENOW;
352 }
353 double remainingLength = lane.getLength().getSI() - longitudinalPosition.getSI();
354 double remainingTimeSI = TIMEHORIZON.getSI() - remainingLength / lane.getSpeedLimit(gtu.getGTUType()).getSI();
355 while (remainingTimeSI >= 0)
356 {
357 for (SingleSensor s : lane.getSensors())
358 {
359 if (s instanceof SinkSensor)
360 {
361 return NOLANECHANGENEEDED;
362 }
363 }
364 int branching = lane.nextLanes(gtu.getGTUType()).size();
365 if (branching == 0)
366 {
367 return new Length(remainingLength, LengthUnit.SI);
368 }
369 if (branching > 1)
370 {
371 return NOLANECHANGENEEDED;
372 }
373 lane = lane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
374 remainingTimeSI -= lane.getLength().getSI() / lane.getSpeedLimit(gtu.getGTUType()).getSI();
375 remainingLength += lane.getLength().getSI();
376 }
377 return NOLANECHANGENEEDED;
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391 private Length suitability(final LaneBasedGTU gtu, final LateralDirectionality direction)
392 throws NetworkException, GTUException, OperationalPlanException
393 {
394 DirectedLanePosition dlp = gtu.getReferencePosition();
395 Lane lane = dlp.getLane();
396 Length longitudinalPosition = dlp.getPosition().plus(gtu.getFront().getDx());
397 if (null != direction)
398 {
399 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
400 direction, longitudinalPosition);
401 }
402 if (null == lane)
403 {
404 return GETOFFTHISLANENOW;
405 }
406 try
407 {
408 return suitability(lane, longitudinalPosition, gtu, TIMEHORIZON);
409
410 }
411 catch (NetworkException ne)
412 {
413 System.err.println(gtu + " has a route problem in suitability: " + ne.getMessage());
414 return NOLANECHANGENEEDED;
415 }
416 }
417
418
419
420
421
422
423
424 private double acceleration(final LaneBasedGTU gtu, final Length stopDistance)
425 {
426
427
428 double v = gtu.getSpeed().getSI();
429 double a = -v * v / 2 / stopDistance.getSI();
430 return a;
431 }
432
433
434
435
436
437
438
439
440
441
442
443 private Length suitability(final Lane lane, final Length longitudinalPosition, final LaneBasedGTU gtu,
444 final Duration timeHorizon) throws NetworkException
445 {
446 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
447 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getGTUType()).getSI();
448
449 Node nextNode = lane.getParentLink().getEndNode();
450 Link lastLink = lane.getParentLink();
451 Node nextSplitNode = null;
452 Lane currentLane = lane;
453 CrossSectionLink linkBeforeBranch = lane.getParentLink();
454 while (null != nextNode)
455 {
456 if (spareTime <= 0)
457 {
458 return NOLANECHANGENEEDED;
459 }
460 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getGTUType());
461 if (0 == laneCount)
462 {
463 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
464 }
465 if (1 == laneCount)
466 {
467 return NOLANECHANGENEEDED;
468
469 }
470 int branching = nextNode.getLinks().size();
471 if (branching > 2)
472 {
473 nextSplitNode = nextNode;
474 break;
475 }
476 else if (1 == branching)
477 {
478 return NOLANECHANGENEEDED;
479 }
480 else
481 {
482 Link nextLink = gtu.getStrategicalPlanner().nextLinkDirection(nextNode, lastLink, gtu.getGTUType()).getLink();
483 if (nextLink instanceof CrossSectionLink)
484 {
485 nextNode = nextLink.getEndNode();
486
487 remainingDistance += nextLink.getLength().getSI();
488 linkBeforeBranch = (CrossSectionLink) nextLink;
489
490 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
491 {
492
493
494 if (currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtu.getGTUType()).size() > 0)
495 {
496 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT,
497 gtu.getGTUType()))
498 {
499 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
500 {
501 currentLane = adjacentLane;
502 break;
503 }
504
505
506 }
507 }
508 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.LEFT,
509 gtu.getGTUType()))
510 {
511 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
512 {
513 currentLane = adjacentLane;
514 break;
515 }
516
517
518 }
519 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
520 {
521 throw new NetworkException(
522 "Lane ends and there is not a compatible adjacent lane that does " + "not end");
523 }
524 }
525
526 for (Lane nextLane : currentLane.nextLanes(gtu.getGTUType()).keySet())
527 {
528 if (nextLane.getLaneType().isCompatible(gtu.getGTUType()))
529 {
530 currentLane = currentLane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
531 break;
532 }
533 }
534 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtu.getGTUType()).getSI();
535 }
536 else
537 {
538
539
540 return NOLANECHANGENEEDED;
541 }
542 lastLink = nextLink;
543 }
544 }
545 if (null == nextNode)
546 {
547 throw new NetworkException("Cannot find the next branch or sink node");
548 }
549
550
551 Map<Lane, Length> suitabilityOfLanesBeforeBranch = new HashMap<Lane, Length>();
552 Link linkAfterBranch =
553 gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType()).getLink();
554 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
555 {
556 if (cse instanceof Lane)
557 {
558 Lane l = (Lane) cse;
559 if (l.getLaneType().isCompatible(gtu.getGTUType()))
560 {
561 for (Lane connectingLane : l.nextLanes(gtu.getGTUType()).keySet())
562 {
563 if (connectingLane.getParentLink() == linkAfterBranch
564 && connectingLane.getLaneType().isCompatible(gtu.getGTUType()))
565 {
566 Length currentValue = suitabilityOfLanesBeforeBranch.get(l);
567
568
569
570 Length value = suitability(connectingLane, new Length(0, LengthUnit.SI), gtu,
571 new Duration(spareTime, TimeUnit.SI));
572
573 value = value.plus(new Length(remainingDistance, LengthUnit.SI));
574
575
576 suitabilityOfLanesBeforeBranch.put(l,
577 null == currentValue || value.le(currentValue) ? value : currentValue);
578 }
579 }
580 }
581 }
582 }
583 if (suitabilityOfLanesBeforeBranch.size() == 0)
584 {
585 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
586 }
587 Length currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
588 if (null != currentLaneSuitability)
589 {
590 return currentLaneSuitability;
591 }
592
593 int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtu.getGTUType());
594 Length leftSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
595 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.LEFT, gtu.getGTUType());
596 Length rightSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
597 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.RIGHT, gtu.getGTUType());
598 if (leftSuitability.ge(rightSuitability))
599 {
600 return leftSuitability;
601 }
602 else if (rightSuitability.ge(leftSuitability))
603 {
604
605 return rightSuitability;
606 }
607 if (leftSuitability.le(GETOFFTHISLANENOW))
608 {
609 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
610 }
611 return leftSuitability;
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625
626 protected final Length computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
627 final Map<Lane, Length> suitabilities, final int totalLanes, final LateralDirectionality direction,
628 final GTUType gtuType)
629 {
630
631
632
633
634
635
636
637
638
639 int laneChangesUsed = 0;
640 Lane currentLane = startLane;
641 Length currentSuitability = null;
642 while (null == currentSuitability)
643 {
644 laneChangesUsed++;
645 if (currentLane.accessibleAdjacentLanes(direction, gtuType).size() == 0)
646 {
647 return GETOFFTHISLANENOW;
648 }
649 currentLane = currentLane.accessibleAdjacentLanes(direction, gtuType).iterator().next();
650 currentSuitability = suitabilities.get(currentLane);
651 }
652 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
653 int notSuitableLaneCount = totalLanes - suitabilities.size();
654 return new Length(
655 remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction) / (notSuitableLaneCount + fraction),
656 LengthUnit.SI);
657 }
658
659
660
661
662
663
664
665
666 protected final int countCompatibleLanes(final CrossSectionLink link, final GTUType gtuType)
667 {
668 int result = 0;
669 for (CrossSectionElement cse : link.getCrossSectionElementList())
670 {
671 if (cse instanceof Lane)
672 {
673 Lane l = (Lane) cse;
674 if (l.getLaneType().isCompatible(gtuType))
675 {
676 result++;
677 }
678 }
679 }
680 return result;
681 }
682
683
684 @Override
685 public final String toString()
686 {
687 return "LaneBasedCFLCTacticalPlanner [laneChangeModel=" + this.laneChangeModel + "]";
688 }
689
690 }