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.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 HashMap<>();
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 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 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 }