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