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.Sensor;
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 if (getGtu().getSpeed().si == 0.0 && getGtu().getOdometer().si > 100)
178 {
179 System.out.println("why you change? ");
180 }
181
182 laneBasedGTU.changeLaneInstantaneously(lcmr.getLaneChangeDirection());
183
184
185 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
186 }
187
188
189 Headway object = getPerception().getPerceptionCategory(DefaultSimplePerception.class).getForwardHeadwayObject();
190 Acceleration a = lcmr.getGfmr().getAcceleration();
191 if (object instanceof HeadwayTrafficLight)
192 {
193
194 a = Acceleration.min(a, ((GTUFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
195 getGtu().getMaximumSpeed(), Speed.ZERO, object.getDistance(), speedLimit));
196 }
197
198
199 Length dist = lanePathInfo.getPath().getLength().minus(getGtu().getFront().getDx());
200 a = Acceleration.min(a, ((GTUFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
201 getGtu().getMaximumSpeed(), Speed.ZERO, dist, speedLimit));
202
203
204 OTSLine3D path = lanePathInfo.getPath();
205 if (a.si < 1E-6 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
206 {
207 try
208 {
209 return new OperationalPlan(getGtu(), path.getLocationFraction(0.0), startTime, duration);
210 }
211 catch (OTSGeometryException exception)
212 {
213
214 throw new RuntimeException(exception);
215 }
216 }
217 List<Segment> operationalPlanSegmentList = new ArrayList<>();
218
219 if (a.si == 0.0)
220 {
221 Segment segment = new OperationalPlan.SpeedSegment(duration);
222 operationalPlanSegmentList.add(segment);
223 }
224 else
225 {
226 Segment segment = new OperationalPlan.AccelerationSegment(duration, a);
227 operationalPlanSegmentList.add(segment);
228 }
229 OperationalPlan op =
230 new OperationalPlan(getGtu(), path, startTime, getGtu().getSpeed(), operationalPlanSegmentList);
231 return op;
232 }
233 catch (ValueException exception)
234 {
235 throw new GTUException(exception);
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251 private AccelerationVector laneIncentives(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
252 throws NetworkException, ValueException, GTUException, OperationalPlanException
253 {
254 Length leftSuitability = suitability(gtu, LateralDirectionality.LEFT);
255 Length currentSuitability = suitability(gtu, null);
256 Length rightSuitability = suitability(gtu, LateralDirectionality.RIGHT);
257 System.out.println(
258 gtu.getId() + " suitability: " + leftSuitability + ", " + currentSuitability + ", " + rightSuitability);
259 if (leftSuitability == NOLANECHANGENEEDED && currentSuitability == NOLANECHANGENEEDED
260 && rightSuitability == NOLANECHANGENEEDED)
261 {
262 return checkLaneDrops(gtu, defaultLaneIncentives);
263 }
264 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
265 && currentSuitability == NOLANECHANGENEEDED
266 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
267 {
268 return checkLaneDrops(gtu,
269 new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
270 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
271 StorageType.DENSE));
272 }
273 if (currentSuitability == NOLANECHANGENEEDED)
274 {
275 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
276 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
277 StorageType.DENSE);
278 }
279 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
280 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295 private AccelerationVector checkLaneDrops(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
296 throws NetworkException, ValueException, GTUException, OperationalPlanException
297 {
298
299 Length leftSuitability = Double.isNaN(defaultLaneIncentives.get(0).si) || defaultLaneIncentives.get(0).si < -10
300 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.LEFT);
301 Length currentSuitability = laneDrop(gtu, null);
302 Length rightSuitability = Double.isNaN(defaultLaneIncentives.get(2).si) || defaultLaneIncentives.get(2).si < -10
303 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.RIGHT);
304
305 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
306 && currentSuitability == NOLANECHANGENEEDED
307 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
308 {
309 return defaultLaneIncentives;
310 }
311
312 if (currentSuitability == NOLANECHANGENEEDED)
313 {
314 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability),
315 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability) }, AccelerationUnit.SI,
316 StorageType.DENSE);
317 }
318 if (currentSuitability.le(leftSuitability))
319 {
320 return new AccelerationVector(new double[] { PREFERREDLANEINCENTIVE.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
321 GETOFFTHISLANENOW.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
322 }
323 if (currentSuitability.le(rightSuitability))
324 {
325 return new AccelerationVector(new double[] { GETOFFTHISLANENOW.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
326 PREFERREDLANEINCENTIVE.getSI() }, AccelerationUnit.SI, StorageType.DENSE);
327 }
328 return new AccelerationVector(new double[] { acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
329 acceleration(gtu, rightSuitability) }, AccelerationUnit.SI, StorageType.DENSE);
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 private Length laneDrop(final LaneBasedGTU gtu, final LateralDirectionality direction)
346 throws NetworkException, GTUException, OperationalPlanException
347 {
348 DirectedLanePosition dlp = gtu.getReferencePosition();
349 Lane lane = dlp.getLane();
350 Length longitudinalPosition = dlp.getPosition();
351 if (null != direction)
352 {
353 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
354 direction, longitudinalPosition);
355 }
356 if (null == lane)
357 {
358 return GETOFFTHISLANENOW;
359 }
360 double remainingLength = lane.getLength().getSI() - longitudinalPosition.getSI();
361 double remainingTimeSI = TIMEHORIZON.getSI() - remainingLength / lane.getSpeedLimit(gtu.getGTUType()).getSI();
362 while (remainingTimeSI >= 0)
363 {
364 for (Sensor s : lane.getSensors())
365 {
366 if (s instanceof SinkSensor)
367 {
368 return NOLANECHANGENEEDED;
369 }
370 }
371 int branching = lane.nextLanes(gtu.getGTUType()).size();
372 if (branching == 0)
373 {
374 return new Length(remainingLength, LengthUnit.SI);
375 }
376 if (branching > 1)
377 {
378 return NOLANECHANGENEEDED;
379 }
380 lane = lane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
381 remainingTimeSI -= lane.getLength().getSI() / lane.getSpeedLimit(gtu.getGTUType()).getSI();
382 remainingLength += lane.getLength().getSI();
383 }
384 return NOLANECHANGENEEDED;
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398 private Length suitability(final LaneBasedGTU gtu, final LateralDirectionality direction)
399 throws NetworkException, GTUException, OperationalPlanException
400 {
401 DirectedLanePosition dlp = gtu.getReferencePosition();
402 Lane lane = dlp.getLane();
403 Length longitudinalPosition = dlp.getPosition().plus(gtu.getFront().getDx());
404 if (null != direction)
405 {
406 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
407 direction, longitudinalPosition);
408 }
409 if (null == lane)
410 {
411 return GETOFFTHISLANENOW;
412 }
413 try
414 {
415 return suitability(lane, longitudinalPosition, gtu, TIMEHORIZON);
416
417 }
418 catch (NetworkException ne)
419 {
420 System.err.println(gtu + " has a route problem in suitability: " + ne.getMessage());
421 return NOLANECHANGENEEDED;
422 }
423 }
424
425
426
427
428
429
430
431 private double acceleration(final LaneBasedGTU gtu, final Length stopDistance)
432 {
433
434
435 double v = gtu.getSpeed().getSI();
436 double a = -v * v / 2 / stopDistance.getSI();
437 return a;
438 }
439
440
441
442
443
444
445
446
447
448
449
450 private Length suitability(final Lane lane, final Length longitudinalPosition, final LaneBasedGTU gtu,
451 final Duration timeHorizon) throws NetworkException
452 {
453 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
454 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getGTUType()).getSI();
455
456 Node nextNode = lane.getParentLink().getEndNode();
457 Link lastLink = lane.getParentLink();
458 Node nextSplitNode = null;
459 Lane currentLane = lane;
460 CrossSectionLink linkBeforeBranch = lane.getParentLink();
461 while (null != nextNode)
462 {
463 if (spareTime <= 0)
464 {
465 return NOLANECHANGENEEDED;
466 }
467 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getGTUType());
468 if (0 == laneCount)
469 {
470 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
471 }
472 if (1 == laneCount)
473 {
474 return NOLANECHANGENEEDED;
475
476 }
477 int branching = nextNode.getLinks().size();
478 if (branching > 2)
479 {
480 nextSplitNode = nextNode;
481 break;
482 }
483 else if (1 == branching)
484 {
485 return NOLANECHANGENEEDED;
486 }
487 else
488 {
489 Link nextLink = gtu.getStrategicalPlanner().nextLinkDirection(nextNode, lastLink, gtu.getGTUType()).getLink();
490 if (nextLink instanceof CrossSectionLink)
491 {
492 nextNode = nextLink.getEndNode();
493
494 remainingDistance += nextLink.getLength().getSI();
495 linkBeforeBranch = (CrossSectionLink) nextLink;
496
497 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
498 {
499
500
501 if (currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtu.getGTUType()).size() > 0)
502 {
503 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT,
504 gtu.getGTUType()))
505 {
506 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
507 {
508 currentLane = adjacentLane;
509 break;
510 }
511
512
513 }
514 }
515 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.LEFT,
516 gtu.getGTUType()))
517 {
518 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
519 {
520 currentLane = adjacentLane;
521 break;
522 }
523
524
525 }
526 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
527 {
528 throw new NetworkException(
529 "Lane ends and there is not a compatible adjacent lane that does " + "not end");
530 }
531 }
532
533 for (Lane nextLane : currentLane.nextLanes(gtu.getGTUType()).keySet())
534 {
535 if (nextLane.getLaneType().isCompatible(gtu.getGTUType()))
536 {
537 currentLane = currentLane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
538 break;
539 }
540 }
541 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtu.getGTUType()).getSI();
542 }
543 else
544 {
545
546
547 return NOLANECHANGENEEDED;
548 }
549 lastLink = nextLink;
550 }
551 }
552 if (null == nextNode)
553 {
554 throw new NetworkException("Cannot find the next branch or sink node");
555 }
556
557
558 Map<Lane, Length> suitabilityOfLanesBeforeBranch = new HashMap<Lane, Length>();
559 Link linkAfterBranch =
560 gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType()).getLink();
561 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
562 {
563 if (cse instanceof Lane)
564 {
565 Lane l = (Lane) cse;
566 if (l.getLaneType().isCompatible(gtu.getGTUType()))
567 {
568 for (Lane connectingLane : l.nextLanes(gtu.getGTUType()).keySet())
569 {
570 if (connectingLane.getParentLink() == linkAfterBranch
571 && connectingLane.getLaneType().isCompatible(gtu.getGTUType()))
572 {
573 Length currentValue = suitabilityOfLanesBeforeBranch.get(l);
574
575
576
577 Length value = suitability(connectingLane, new Length(0, LengthUnit.SI), gtu,
578 new Duration(spareTime, TimeUnit.SI));
579
580 value = value.plus(new Length(remainingDistance, LengthUnit.SI));
581
582
583 suitabilityOfLanesBeforeBranch.put(l,
584 null == currentValue || value.le(currentValue) ? value : currentValue);
585 }
586 }
587 }
588 }
589 }
590 if (suitabilityOfLanesBeforeBranch.size() == 0)
591 {
592 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
593 }
594 Length currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
595 if (null != currentLaneSuitability)
596 {
597 return currentLaneSuitability;
598 }
599
600 int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtu.getGTUType());
601 Length leftSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
602 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.LEFT, gtu.getGTUType());
603 Length rightSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
604 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.RIGHT, gtu.getGTUType());
605 if (leftSuitability.ge(rightSuitability))
606 {
607 return leftSuitability;
608 }
609 else if (rightSuitability.ge(leftSuitability))
610 {
611
612 return rightSuitability;
613 }
614 if (leftSuitability.le(GETOFFTHISLANENOW))
615 {
616 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
617 }
618 return leftSuitability;
619 }
620
621
622
623
624
625
626
627
628
629
630
631
632
633 protected final Length computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
634 final Map<Lane, Length> suitabilities, final int totalLanes, final LateralDirectionality direction,
635 final GTUType gtuType)
636 {
637
638
639
640
641
642
643
644
645
646 int laneChangesUsed = 0;
647 Lane currentLane = startLane;
648 Length currentSuitability = null;
649 while (null == currentSuitability)
650 {
651 laneChangesUsed++;
652 if (currentLane.accessibleAdjacentLanes(direction, gtuType).size() == 0)
653 {
654 return GETOFFTHISLANENOW;
655 }
656 currentLane = currentLane.accessibleAdjacentLanes(direction, gtuType).iterator().next();
657 currentSuitability = suitabilities.get(currentLane);
658 }
659 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
660 int notSuitableLaneCount = totalLanes - suitabilities.size();
661 return new Length(
662 remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction) / (notSuitableLaneCount + fraction),
663 LengthUnit.SI);
664 }
665
666
667
668
669
670
671
672
673 protected final int countCompatibleLanes(final CrossSectionLink link, final GTUType gtuType)
674 {
675 int result = 0;
676 for (CrossSectionElement cse : link.getCrossSectionElementList())
677 {
678 if (cse instanceof Lane)
679 {
680 Lane l = (Lane) cse;
681 if (l.getLaneType().isCompatible(gtuType))
682 {
683 result++;
684 }
685 }
686 }
687 return result;
688 }
689
690
691 @Override
692 public final String toString()
693 {
694 return "LaneBasedCFLCTacticalPlanner [laneChangeModel=" + this.laneChangeModel + "]";
695 }
696
697 }