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