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.DurationUnit;
11 import org.djunits.unit.LengthUnit;
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.base.parameters.ParameterException;
21 import org.opentrafficsim.base.parameters.ParameterTypeLength;
22 import org.opentrafficsim.base.parameters.ParameterTypes;
23 import org.opentrafficsim.core.geometry.OTSGeometryException;
24 import org.opentrafficsim.core.geometry.OTSLine3D;
25 import org.opentrafficsim.core.gtu.GTUDirectionality;
26 import org.opentrafficsim.core.gtu.GTUException;
27 import org.opentrafficsim.core.gtu.GTUType;
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.CategoricalLanePerception;
37 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
38 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
39 import org.opentrafficsim.road.gtu.lane.perception.categories.DirectDefaultSimplePerception;
40 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
41 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
42 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
43 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
44 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
45 import org.opentrafficsim.road.network.lane.CrossSectionElement;
46 import org.opentrafficsim.road.network.lane.CrossSectionLink;
47 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
48 import org.opentrafficsim.road.network.lane.Lane;
49 import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
50 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
51
52 import nl.tudelft.simulation.language.d3.DirectedPoint;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public class LaneBasedCFLCTacticalPlanner extends AbstractLaneBasedTacticalPlanner
68 {
69
70 private static final long serialVersionUID = 20151125L;
71
72
73 protected static final ParameterTypeLength LOOKBACKOLD = ParameterTypes.LOOKBACKOLD;
74
75
76 private static final Acceleration STAYINCURRENTLANEINCENTIVE = new Acceleration(0.1, AccelerationUnit.METER_PER_SECOND_2);
77
78
79 private static final Acceleration PREFERREDLANEINCENTIVE = new Acceleration(0.3, AccelerationUnit.METER_PER_SECOND_2);
80
81
82 private static final Acceleration NONPREFERREDLANEINCENTIVE = new Acceleration(-0.3, AccelerationUnit.METER_PER_SECOND_2);
83
84
85 public static final Length NOLANECHANGENEEDED = new Length(Double.MAX_VALUE, LengthUnit.SI);
86
87
88 public static final Length GETOFFTHISLANENOW = Length.ZERO;
89
90
91 private static final Duration TIMEHORIZON = new Duration(90, DurationUnit.SECOND);
92
93
94 private LaneChangeModel laneChangeModel;
95
96
97
98
99
100
101
102 public LaneBasedCFLCTacticalPlanner(final GTUFollowingModelOld carFollowingModel, final LaneChangeModel laneChangeModel,
103 final LaneBasedGTU gtu)
104 {
105 super(carFollowingModel, gtu, new CategoricalLanePerception(gtu));
106 this.laneChangeModel = laneChangeModel;
107 getPerception().addPerceptionCategory(new DirectDefaultSimplePerception(getPerception()));
108 }
109
110
111 @Override
112 public final OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
113 throws OperationalPlanException, NetworkException, GTUException, ParameterException
114 {
115 try
116 {
117
118 LaneBasedGTU laneBasedGTU = getGtu();
119 LanePerception perception = getPerception();
120
121
122 if (laneBasedGTU.getMaximumSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
123 {
124 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, DurationUnit.SECOND));
125 }
126
127 Length maximumForwardHeadway = laneBasedGTU.getParameters().getParameter(LOOKAHEAD);
128 DefaultSimplePerception simplePerception = perception.getPerceptionCategory(DefaultSimplePerception.class);
129 Speed speedLimit = simplePerception.getSpeedLimit();
130
131
132 Headway sameLaneLeader = simplePerception.getForwardHeadwayGTU();
133
134 Headway sameLaneFollower = simplePerception.getBackwardHeadway();
135 Collection<Headway> sameLaneTraffic = new ArrayList<>();
136 if (sameLaneLeader.getObjectType().isGtu())
137 {
138 sameLaneTraffic.add(sameLaneLeader);
139 }
140 if (sameLaneFollower.getObjectType().isGtu())
141 {
142 sameLaneTraffic.add(sameLaneFollower);
143 }
144
145
146 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
147
148
149 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, maximumForwardHeadway);
150 boolean currentLaneFine = nextSplitInfo.getCorrectCurrentLanes().contains(lanePathInfo.getReferenceLane());
151
152
153
154
155
156 Collection<Headway> leftLaneTraffic = simplePerception.getNeighboringHeadwaysLeft();
157 Collection<Headway> rightLaneTraffic = simplePerception.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());
175 if (lcmr.getLaneChangeDirection() != null)
176 {
177 laneBasedGTU.changeLaneInstantaneously(lcmr.getLaneChangeDirection());
178
179
180 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
181 }
182
183
184 Headway object = simplePerception.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
444
445 private Length suitability(final Lane lane, final Length longitudinalPosition, final LaneBasedGTU gtu,
446 final Duration timeHorizon) throws NetworkException
447 {
448 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
449 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getGTUType()).getSI();
450
451 Node nextNode = lane.getParentLink().getEndNode();
452 Link lastLink = lane.getParentLink();
453 Node nextSplitNode = null;
454 Lane currentLane = lane;
455 CrossSectionLink linkBeforeBranch = lane.getParentLink();
456 while (null != nextNode)
457 {
458 if (spareTime <= 0)
459 {
460 return NOLANECHANGENEEDED;
461 }
462 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getGTUType(), GTUDirectionality.DIR_PLUS);
463 if (0 == laneCount)
464 {
465 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
466 }
467 if (1 == laneCount)
468 {
469 return NOLANECHANGENEEDED;
470
471 }
472 int branching = nextNode.getLinks().size();
473 if (branching > 2)
474 {
475 nextSplitNode = nextNode;
476 break;
477 }
478 else if (1 == branching)
479 {
480 return NOLANECHANGENEEDED;
481 }
482 else
483 {
484 Link nextLink = gtu.getStrategicalPlanner().nextLinkDirection(nextNode, lastLink, gtu.getGTUType()).getLink();
485 if (nextLink instanceof CrossSectionLink)
486 {
487 GTUDirectionality drivingDirection =
488 nextNode.equals(nextLink.getStartNode()) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
489 nextNode = nextLink.getEndNode();
490
491 remainingDistance += nextLink.getLength().getSI();
492 linkBeforeBranch = (CrossSectionLink) nextLink;
493
494 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
495 {
496
497
498 if (currentLane
499 .accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtu.getGTUType(), drivingDirection)
500 .size() > 0)
501 {
502 for (Lane adjacentLane : currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT,
503 gtu.getGTUType(), drivingDirection))
504 {
505 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
506 {
507 currentLane = adjacentLane;
508 break;
509 }
510
511
512 }
513 }
514 for (Lane adjacentLane : currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.LEFT,
515 gtu.getGTUType(), drivingDirection))
516 {
517 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
518 {
519 currentLane = adjacentLane;
520 break;
521 }
522
523
524 }
525 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
526 {
527 throw new NetworkException(
528 "Lane ends and there is not a compatible adjacent lane that does " + "not end");
529 }
530 }
531
532 for (Lane nextLane : currentLane.nextLanes(gtu.getGTUType()).keySet())
533 {
534 if (nextLane.getLaneType().getDirectionality(gtu.getGTUType()).getDirectionalities()
535 .contains(drivingDirection))
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<>();
559 Link linkAfterBranch =
560 gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType()).getLink();
561 GTUDirectionality drivingDirectionOnNextLane =
562 linkAfterBranch.getStartNode().equals(nextSplitNode) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
563 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
564 {
565 if (cse instanceof Lane)
566 {
567 Lane l = (Lane) cse;
568 if (l.getLaneType().getDirectionality(gtu.getGTUType()).getDirectionalities()
569 .contains(GTUDirectionality.DIR_PLUS))
570 {
571 for (Lane connectingLane : l.nextLanes(gtu.getGTUType()).keySet())
572 {
573 if (connectingLane.getParentLink() == linkAfterBranch
574 && connectingLane.getLaneType().isCompatible(gtu.getGTUType(), drivingDirectionOnNextLane))
575 {
576 Length currentValue = suitabilityOfLanesBeforeBranch.get(l);
577
578
579
580 Length value = suitability(connectingLane, new Length(0, LengthUnit.SI), gtu,
581 new Duration(spareTime, DurationUnit.SI));
582
583 value = value.plus(new Length(remainingDistance, LengthUnit.SI));
584
585
586 suitabilityOfLanesBeforeBranch.put(l,
587 null == currentValue || value.le(currentValue) ? value : currentValue);
588 }
589 }
590 }
591 }
592 }
593 if (suitabilityOfLanesBeforeBranch.size() == 0)
594 {
595 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
596 }
597 Length currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
598 if (null != currentLaneSuitability)
599 {
600 return currentLaneSuitability;
601 }
602
603 int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtu.getGTUType(), GTUDirectionality.DIR_PLUS);
604 Length leftSuitability =
605 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
606 LateralDirectionality.LEFT, gtu.getGTUType(), GTUDirectionality.DIR_PLUS);
607 Length rightSuitability =
608 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
609 LateralDirectionality.RIGHT, gtu.getGTUType(), GTUDirectionality.DIR_PLUS);
610 if (leftSuitability.ge(rightSuitability))
611 {
612 return leftSuitability;
613 }
614 else if (rightSuitability.ge(leftSuitability))
615 {
616
617 return rightSuitability;
618 }
619 if (leftSuitability.le(GETOFFTHISLANENOW))
620 {
621 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
622 }
623 return leftSuitability;
624 }
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639 protected final Length computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
640 final Map<Lane, Length> suitabilities, final int totalLanes, final LateralDirectionality direction,
641 final GTUType gtuType, final GTUDirectionality drivingDirection)
642 {
643
644
645
646
647
648
649
650
651
652 int laneChangesUsed = 0;
653 Lane currentLane = startLane;
654 Length currentSuitability = null;
655 while (null == currentSuitability)
656 {
657 laneChangesUsed++;
658 if (currentLane.accessibleAdjacentLanesLegal(direction, gtuType, drivingDirection).size() == 0)
659 {
660 return GETOFFTHISLANENOW;
661 }
662 currentLane = currentLane.accessibleAdjacentLanesLegal(direction, gtuType, drivingDirection).iterator().next();
663 currentSuitability = suitabilities.get(currentLane);
664 }
665 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
666 int notSuitableLaneCount = totalLanes - suitabilities.size();
667 return new Length(
668 remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction) / (notSuitableLaneCount + fraction),
669 LengthUnit.SI);
670 }
671
672
673
674
675
676
677
678
679
680 protected final int countCompatibleLanes(final CrossSectionLink link, final GTUType gtuType,
681 final GTUDirectionality drivingDirection)
682 {
683 int result = 0;
684 for (CrossSectionElement cse : link.getCrossSectionElementList())
685 {
686 if (cse instanceof Lane)
687 {
688 Lane l = (Lane) cse;
689 if (l.getLaneType().isCompatible(gtuType, drivingDirection))
690 {
691 result++;
692 }
693 }
694 }
695 return result;
696 }
697
698
699 @Override
700 public final String toString()
701 {
702 return "LaneBasedCFLCTacticalPlanner [laneChangeModel=" + this.laneChangeModel + "]";
703 }
704
705 }