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.HashSet;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import nl.tudelft.simulation.language.d3.DirectedPoint;
12
13 import org.djunits.unit.AccelerationUnit;
14 import org.djunits.unit.LengthUnit;
15 import org.djunits.unit.TimeUnit;
16 import org.djunits.value.StorageType;
17 import org.djunits.value.ValueException;
18 import org.djunits.value.vdouble.scalar.Acceleration;
19 import org.djunits.value.vdouble.scalar.Duration;
20 import org.djunits.value.vdouble.scalar.Length;
21 import org.djunits.value.vdouble.scalar.Speed;
22 import org.djunits.value.vdouble.scalar.Time;
23 import org.djunits.value.vdouble.vector.AccelerationVector;
24 import org.opentrafficsim.core.geometry.OTSLine3D;
25 import org.opentrafficsim.core.gtu.GTUException;
26 import org.opentrafficsim.core.gtu.GTUType;
27 import org.opentrafficsim.core.gtu.RelativePosition;
28 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
29 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
30 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
31 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
32 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
33 import org.opentrafficsim.core.network.LateralDirectionality;
34 import org.opentrafficsim.core.network.Link;
35 import org.opentrafficsim.core.network.NetworkException;
36 import org.opentrafficsim.core.network.Node;
37 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
38 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
39 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultAlexander;
40 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
41 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
42 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
43 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
44 import org.opentrafficsim.road.network.lane.CrossSectionElement;
45 import org.opentrafficsim.road.network.lane.CrossSectionLink;
46 import org.opentrafficsim.road.network.lane.Lane;
47 import org.opentrafficsim.road.network.lane.Sensor;
48 import org.opentrafficsim.road.network.lane.SinkSensor;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class LaneBasedCFLCTacticalPlanner extends AbstractLaneBasedTacticalPlanner
64 {
65
66 private static final long serialVersionUID = 20151125L;
67
68
69 private static final Acceleration STAYINCURRENTLANEINCENTIVE =
70 new Acceleration(0.1, AccelerationUnit.METER_PER_SECOND_2);
71
72
73 private static final Acceleration PREFERREDLANEINCENTIVE = new Acceleration(0.3, AccelerationUnit.METER_PER_SECOND_2);
74
75
76 private static final Acceleration NONPREFERREDLANEINCENTIVE =
77 new Acceleration(-0.3, AccelerationUnit.METER_PER_SECOND_2);
78
79
80 public static final Length NOLANECHANGENEEDED = new Length(Double.MAX_VALUE, LengthUnit.SI);
81
82
83 public static final Length GETOFFTHISLANENOW = Length.ZERO;
84
85
86 private static final Duration TIMEHORIZON = new Duration(90, TimeUnit.SECOND);
87
88
89 private LaneChangeModel laneChangeModel;
90
91
92
93
94
95
96
97 public LaneBasedCFLCTacticalPlanner(final GTUFollowingModelOld carFollowingModel, final LaneChangeModel laneChangeModel,
98 final LaneBasedGTU gtu)
99 {
100 super(carFollowingModel, gtu);
101 this.laneChangeModel = laneChangeModel;
102 }
103
104
105 @Override
106 public final OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
107 throws OperationalPlanException, NetworkException, GTUException, ParameterException
108 {
109 try
110 {
111
112 LaneBasedGTU laneBasedGTU = getGtu();
113 LanePerception perception = getPerception();
114
115
116 if (laneBasedGTU.getMaximumSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
117 {
118 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, TimeUnit.SECOND));
119 }
120
121
122
123 perception.perceive();
124
125 Length maximumForwardHeadway =
126 laneBasedGTU.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
127 Length maximumReverseHeadway =
128 laneBasedGTU.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACKOLD);
129 Time now = getGtu().getSimulator().getSimulatorTime().getTime();
130 Speed speedLimit = perception.getPerceptionCategory(DefaultAlexander.class).getSpeedLimit();
131
132
133 Headway sameLaneLeader = perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway();
134 Headway sameLaneFollower = perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway();
135 Collection<Headway> sameLaneTraffic = new ArrayList<Headway>();
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 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, maximumForwardHeadway);
148 boolean currentLaneFine = nextSplitInfo.getCorrectCurrentLanes().contains(lanePathInfo.getReferenceLane());
149
150
151
152
153
154 Collection<Headway> leftLaneTraffic =
155 perception.getPerceptionCategory(DefaultAlexander.class).getNeighboringHeadwaysLeft();
156 Collection<Headway> rightLaneTraffic =
157 perception.getPerceptionCategory(DefaultAlexander.class).getNeighboringHeadwaysRight();
158
159
160 final LateralDirectionality preferred = LateralDirectionality.RIGHT;
161 final Acceleration defaultLeftLaneIncentive =
162 preferred.isLeft() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
163 final Acceleration defaultRightLaneIncentive =
164 preferred.isRight() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
165
166 AccelerationVector defaultLaneIncentives =
167 new AccelerationVector(new double[] {defaultLeftLaneIncentive.getSI(), STAYINCURRENTLANEINCENTIVE.getSI(),
168 defaultRightLaneIncentive.getSI()}, AccelerationUnit.SI, StorageType.DENSE);
169 AccelerationVector laneIncentives = laneIncentives(laneBasedGTU, defaultLaneIncentives);
170 LaneMovementStep lcmr =
171 this.laneChangeModel.computeLaneChangeAndAcceleration(laneBasedGTU, sameLaneTraffic, rightLaneTraffic,
172 leftLaneTraffic, speedLimit, new Acceleration(laneIncentives.get(preferred.isRight() ? 2 : 0)),
173 new Acceleration(laneIncentives.get(1)), new Acceleration(laneIncentives
174 .get(preferred.isRight() ? 0 : 2)));
175 Duration duration = lcmr.getGfmr().getValidUntil().minus(getGtu().getSimulator().getSimulatorTime().getTime());
176
177
178
179
180 if (lcmr.getLaneChange() != null)
181 {
182 Collection<Lane> oldLaneSet = new HashSet<Lane>(laneBasedGTU.getLanes().keySet());
183 Collection<Lane> newLaneSet = new HashSet<Lane>(2);
184 Map<Lane, Double> oldFractionalPositions = new LinkedHashMap<Lane, Double>();
185 for (Lane l : laneBasedGTU.getLanes().keySet())
186 {
187 oldFractionalPositions.put(l, laneBasedGTU.fractionalPosition(l, getGtu().getReference(), now));
188 if (lcmr.getLaneChange().isLeft())
189 {
190 newLaneSet.addAll(getPerception().getPerceptionCategory(DefaultAlexander.class)
191 .getAccessibleAdjacentLanesLeft().get(l));
192 }
193 else if (lcmr.getLaneChange().isRight())
194 {
195 newLaneSet.addAll(getPerception().getPerceptionCategory(DefaultAlexander.class)
196 .getAccessibleAdjacentLanesRight().get(l));
197 }
198 else
199 {
200 throw new GTUException("Lane change is neither left nor right");
201 }
202 }
203
204
205 for (Lane newLane : newLaneSet)
206 {
207 Double fractionalPosition = null;
208
209 Lane foundOldLane = null;
210 for (Lane oldLane : oldLaneSet)
211 {
212 if ((lcmr.getLaneChange().isLeft() && getPerception().getPerceptionCategory(DefaultAlexander.class)
213 .getAccessibleAdjacentLanesLeft().get(oldLane).contains(newLane))
214 || (lcmr.getLaneChange().isRight() && getPerception().getPerceptionCategory(
215 DefaultAlexander.class).getAccessibleAdjacentLanesRight().get(oldLane).contains(newLane)))
216 {
217 fractionalPosition = oldFractionalPositions.get(oldLane);
218 foundOldLane = oldLane;
219 break;
220 }
221 }
222 if (null == fractionalPosition)
223 {
224 throw new Error("Program error: Cannot find an oldLane that has newLane " + newLane + " as "
225 + lcmr.getLaneChange() + " neighbor");
226 }
227 laneBasedGTU.enterLane(newLane, newLane.getLength().multiplyBy(fractionalPosition), laneBasedGTU
228 .getLanes().get(foundOldLane));
229 }
230
231
232
233
234
235
236
237
238
239
240 System.out.println(getGtu() + " changes lane from " + oldLaneSet + " to " + newLaneSet + " at time "
241 + getGtu().getSimulator().getSimulatorTime().get());
242
243
244 for (Lane l : oldFractionalPositions.keySet())
245 {
246 laneBasedGTU.leaveLane(l);
247 }
248
249 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
250
251
252
253 if (lcmr.getGfmr().getAcceleration().si < 1E-6
254 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
255 {
256
257 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, duration);
258 }
259
260
261 OTSLine3D path = lanePathInfo.getPath();
262 List<Segment> operationalPlanSegmentList = new ArrayList<>();
263 if (lcmr.getGfmr().getAcceleration().si == 0.0)
264 {
265 Segment segment = new OperationalPlan.SpeedSegment(duration);
266 operationalPlanSegmentList.add(segment);
267 }
268 else
269 {
270 Segment segment = new OperationalPlan.AccelerationSegment(duration, lcmr.getGfmr().getAcceleration());
271 operationalPlanSegmentList.add(segment);
272 }
273 OperationalPlan op =
274 new OperationalPlan(getGtu(), path, startTime, getGtu().getSpeed(), operationalPlanSegmentList);
275 return op;
276 }
277 else
278
279 {
280
281 if (lcmr.getGfmr().getAcceleration().si < 1E-6
282 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
283 {
284 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, duration);
285 }
286
287 OTSLine3D path = lanePathInfo.getPath();
288 List<Segment> operationalPlanSegmentList = new ArrayList<>();
289 if (lcmr.getGfmr().getAcceleration().si == 0.0)
290 {
291 Segment segment = new OperationalPlan.SpeedSegment(duration);
292 operationalPlanSegmentList.add(segment);
293 }
294 else
295 {
296 Segment segment = new OperationalPlan.AccelerationSegment(duration, lcmr.getGfmr().getAcceleration());
297 operationalPlanSegmentList.add(segment);
298 }
299 OperationalPlan op =
300 new OperationalPlan(getGtu(), path, startTime, getGtu().getSpeed(), operationalPlanSegmentList);
301 return op;
302 }
303 }
304 catch (ValueException exception)
305 {
306 throw new GTUException(exception);
307 }
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321
322 private AccelerationVector laneIncentives(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
323 throws NetworkException, ValueException, GTUException, OperationalPlanException
324 {
325 Length leftSuitability = suitability(gtu, LateralDirectionality.LEFT);
326 Length currentSuitability = suitability(gtu, null);
327 Length rightSuitability = suitability(gtu, LateralDirectionality.RIGHT);
328 if (leftSuitability == NOLANECHANGENEEDED && currentSuitability == NOLANECHANGENEEDED
329 && rightSuitability == NOLANECHANGENEEDED)
330 {
331 return checkLaneDrops(gtu, defaultLaneIncentives);
332 }
333 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
334 && currentSuitability == NOLANECHANGENEEDED
335 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
336 {
337 return checkLaneDrops(gtu, new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
338 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI,
339 StorageType.DENSE));
340 }
341 if (currentSuitability == NOLANECHANGENEEDED)
342 {
343 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
344 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI,
345 StorageType.DENSE);
346 }
347 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
348 acceleration(gtu, currentSuitability), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI,
349 StorageType.DENSE);
350 }
351
352
353
354
355
356
357
358
359
360
361
362
363
364 private AccelerationVector checkLaneDrops(final LaneBasedGTU gtu, final AccelerationVector defaultLaneIncentives)
365 throws NetworkException, ValueException, GTUException, OperationalPlanException
366 {
367
368 Length leftSuitability =
369 Double.isNaN(defaultLaneIncentives.get(0).si) || defaultLaneIncentives.get(0).si < -10 ? GETOFFTHISLANENOW
370 : laneDrop(gtu, LateralDirectionality.LEFT);
371 Length currentSuitability = laneDrop(gtu, null);
372 Length rightSuitability =
373 Double.isNaN(defaultLaneIncentives.get(2).si) || defaultLaneIncentives.get(2).si < -10 ? GETOFFTHISLANENOW
374 : laneDrop(gtu, LateralDirectionality.RIGHT);
375
376 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
377 && currentSuitability == NOLANECHANGENEEDED
378 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
379 {
380 return defaultLaneIncentives;
381 }
382
383 if (currentSuitability == NOLANECHANGENEEDED)
384 {
385 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
386 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI,
387 StorageType.DENSE);
388 }
389 if (currentSuitability.le(leftSuitability))
390 {
391 return new AccelerationVector(new double[] {PREFERREDLANEINCENTIVE.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
392 GETOFFTHISLANENOW.getSI()}, AccelerationUnit.SI, StorageType.DENSE);
393 }
394 if (currentSuitability.le(rightSuitability))
395 {
396 return new AccelerationVector(new double[] {GETOFFTHISLANENOW.getSI(), NONPREFERREDLANEINCENTIVE.getSI(),
397 PREFERREDLANEINCENTIVE.getSI()}, AccelerationUnit.SI, StorageType.DENSE);
398 }
399 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
400 acceleration(gtu, currentSuitability), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI,
401 StorageType.DENSE);
402 }
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417 private Length laneDrop(final LaneBasedGTU gtu, final LateralDirectionality direction) throws NetworkException,
418 GTUException, OperationalPlanException
419 {
420 Lane lane = null;
421 Length longitudinalPosition = null;
422 Map<Lane, Length> positions = gtu.positions(RelativePosition.REFERENCE_POSITION);
423 if (null == direction)
424 {
425 for (Lane l : gtu.getLanes().keySet())
426 {
427 if (l.getLaneType().isCompatible(gtu.getGTUType()))
428 {
429 lane = l;
430 }
431 }
432 if (null == lane)
433 {
434 throw new NetworkException(this + " is not on any compatible lane");
435 }
436 longitudinalPosition = positions.get(lane);
437 }
438 else
439 {
440 lane = positions.keySet().iterator().next();
441 longitudinalPosition = positions.get(lane);
442 lane =
443 getPerception().getPerceptionCategory(DefaultAlexander.class).bestAccessibleAdjacentLane(lane, direction,
444 longitudinalPosition);
445
446 }
447 if (null == lane)
448 {
449 return GETOFFTHISLANENOW;
450 }
451 double remainingLength = lane.getLength().getSI() - longitudinalPosition.getSI();
452 double remainingTimeSI = TIMEHORIZON.getSI() - remainingLength / lane.getSpeedLimit(gtu.getGTUType()).getSI();
453 while (remainingTimeSI >= 0)
454 {
455 for (Sensor s : lane.getSensors())
456 {
457 if (s instanceof SinkSensor)
458 {
459 return NOLANECHANGENEEDED;
460 }
461 }
462 int branching = lane.nextLanes(gtu.getGTUType()).size();
463 if (branching == 0)
464 {
465 return new Length(remainingLength, LengthUnit.SI);
466 }
467 if (branching > 1)
468 {
469 return NOLANECHANGENEEDED;
470 }
471 lane = lane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
472 remainingTimeSI -= lane.getLength().getSI() / lane.getSpeedLimit(gtu.getGTUType()).getSI();
473 remainingLength += lane.getLength().getSI();
474 }
475 return NOLANECHANGENEEDED;
476 }
477
478
479
480
481
482
483
484
485
486
487
488
489 private Length suitability(final LaneBasedGTU gtu, final LateralDirectionality direction) throws NetworkException,
490 GTUException, OperationalPlanException
491 {
492 Lane lane = null;
493 Length longitudinalPosition = null;
494 Map<Lane, Length> positions = gtu.positions(RelativePosition.REFERENCE_POSITION);
495 if (null == direction)
496 {
497 for (Lane l : gtu.getLanes().keySet())
498 {
499 if (l.getLaneType().isCompatible(gtu.getGTUType()))
500 {
501 lane = l;
502 }
503 }
504 if (null == lane)
505 {
506 throw new NetworkException(this + " is not on any compatible lane");
507 }
508 longitudinalPosition = positions.get(lane);
509 }
510 else
511 {
512 lane = positions.keySet().iterator().next();
513 longitudinalPosition = positions.get(lane);
514
515
516
517
518
519 lane =
520 getPerception().getPerceptionCategory(DefaultAlexander.class).bestAccessibleAdjacentLane(lane, direction,
521 longitudinalPosition);
522
523
524 if (null != lane)
525 {
526
527 if ((!canChangeLane(gtu.positions(gtu.getRear()), positions, gtu, direction))
528 || (!canChangeLane(gtu.positions(gtu.getFront()), positions, gtu, direction)))
529 {
530
531 lane = null;
532 }
533 }
534 }
535 if (null == lane)
536 {
537 return GETOFFTHISLANENOW;
538 }
539 try
540 {
541 return suitability(lane, longitudinalPosition, gtu, TIMEHORIZON);
542 }
543 catch (NetworkException ne)
544 {
545 System.err.println(gtu + " has a route problem in suitability: " + ne.getMessage());
546 return NOLANECHANGENEEDED;
547 }
548 }
549
550
551
552
553
554
555
556
557
558
559
560 private boolean canChangeLane(final Map<Lane, Length> laneMap, final Map<Lane, Length> positions,
561 final LaneBasedGTU gtu, final LateralDirectionality direction) throws OperationalPlanException
562 {
563 for (Lane lane : laneMap.keySet())
564 {
565 Length positionInLane = laneMap.get(lane);
566 if (positionInLane.si < 0 || positionInLane.gt(lane.getLength()))
567 {
568 continue;
569 }
570 if (null == getPerception().getPerceptionCategory(DefaultAlexander.class).bestAccessibleAdjacentLane(lane,
571 direction, positions.get(lane)))
572 {
573
574 return false;
575 }
576 }
577 return true;
578 }
579
580
581
582
583
584
585
586 private double acceleration(final LaneBasedGTU gtu, final Length stopDistance)
587 {
588
589
590 double v = gtu.getSpeed().getSI();
591 double a = -v * v / 2 / stopDistance.getSI();
592 return a;
593 }
594
595
596
597
598
599
600
601
602
603
604
605 private Length suitability(final Lane lane, final Length longitudinalPosition, final LaneBasedGTU gtu,
606 final Duration timeHorizon) throws NetworkException
607 {
608 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
609 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getGTUType()).getSI();
610
611 Node nextNode = lane.getParentLink().getEndNode();
612 Link lastLink = lane.getParentLink();
613 Node nextSplitNode = null;
614 Lane currentLane = lane;
615 CrossSectionLink linkBeforeBranch = lane.getParentLink();
616 while (null != nextNode)
617 {
618 if (spareTime <= 0)
619 {
620 return NOLANECHANGENEEDED;
621 }
622 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getGTUType());
623 if (0 == laneCount)
624 {
625 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
626 }
627 if (1 == laneCount)
628 {
629 return NOLANECHANGENEEDED;
630
631 }
632 int branching = nextNode.getLinks().size();
633 if (branching > 2)
634 {
635 nextSplitNode = nextNode;
636 break;
637 }
638 else if (1 == branching)
639 {
640 return NOLANECHANGENEEDED;
641 }
642 else
643 {
644 Link nextLink =
645 gtu.getStrategicalPlanner().nextLinkDirection(nextNode, lastLink, gtu.getGTUType()).getLink();
646 if (nextLink instanceof CrossSectionLink)
647 {
648 nextNode = nextLink.getEndNode();
649
650 remainingDistance += nextLink.getLength().getSI();
651 linkBeforeBranch = (CrossSectionLink) nextLink;
652
653 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
654 {
655
656
657 if (currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtu.getGTUType()).size() > 0)
658 {
659 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtu
660 .getGTUType()))
661 {
662 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
663 {
664 currentLane = adjacentLane;
665 break;
666 }
667
668
669 }
670 }
671 for (Lane adjacentLane : currentLane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtu
672 .getGTUType()))
673 {
674 if (adjacentLane.nextLanes(gtu.getGTUType()).size() > 0)
675 {
676 currentLane = adjacentLane;
677 break;
678 }
679
680
681 }
682 if (currentLane.nextLanes(gtu.getGTUType()).size() == 0)
683 {
684 throw new NetworkException("Lane ends and there is not a compatible adjacent lane that does "
685 + "not end");
686 }
687 }
688
689 for (Lane nextLane : currentLane.nextLanes(gtu.getGTUType()).keySet())
690 {
691 if (nextLane.getLaneType().isCompatible(gtu.getGTUType()))
692 {
693 currentLane = currentLane.nextLanes(gtu.getGTUType()).keySet().iterator().next();
694 break;
695 }
696 }
697 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtu.getGTUType()).getSI();
698 }
699 else
700 {
701
702
703 return NOLANECHANGENEEDED;
704 }
705 lastLink = nextLink;
706 }
707 }
708 if (null == nextNode)
709 {
710 throw new NetworkException("Cannot find the next branch or sink node");
711 }
712
713
714 Map<Lane, Length> suitabilityOfLanesBeforeBranch = new HashMap<Lane, Length>();
715 Link linkAfterBranch =
716 gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType()).getLink();
717 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
718 {
719 if (cse instanceof Lane)
720 {
721 Lane l = (Lane) cse;
722 if (l.getLaneType().isCompatible(gtu.getGTUType()))
723 {
724 for (Lane connectingLane : l.nextLanes(gtu.getGTUType()).keySet())
725 {
726 if (connectingLane.getParentLink() == linkAfterBranch
727 && connectingLane.getLaneType().isCompatible(gtu.getGTUType()))
728 {
729 Length currentValue = suitabilityOfLanesBeforeBranch.get(l);
730
731
732
733 Length value =
734 suitability(connectingLane, new Length(0, LengthUnit.SI), gtu, new Duration(spareTime,
735 TimeUnit.SI));
736
737
738 suitabilityOfLanesBeforeBranch.put(l, null == currentValue || value.le(currentValue) ? value
739 : currentValue);
740 }
741 }
742 }
743 }
744 }
745 if (suitabilityOfLanesBeforeBranch.size() == 0)
746 {
747 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
748 }
749 Length currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
750 if (null != currentLaneSuitability)
751 {
752 return currentLaneSuitability;
753 }
754
755 int totalLanes = countCompatibleLanes(currentLane.getParentLink(), gtu.getGTUType());
756 Length leftSuitability =
757 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
758 LateralDirectionality.LEFT, gtu.getGTUType());
759 Length rightSuitability =
760 computeSuitabilityWithLaneChanges(currentLane, remainingDistance, suitabilityOfLanesBeforeBranch, totalLanes,
761 LateralDirectionality.RIGHT, gtu.getGTUType());
762 if (leftSuitability.ge(rightSuitability))
763 {
764 return leftSuitability;
765 }
766 else if (rightSuitability.ge(leftSuitability))
767 {
768 return rightSuitability;
769 }
770 if (leftSuitability.le(GETOFFTHISLANENOW))
771 {
772 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
773 }
774 return leftSuitability;
775 }
776
777
778
779
780
781
782
783
784
785
786
787
788
789 protected final Length computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
790 final Map<Lane, Length> suitabilities, final int totalLanes, final LateralDirectionality direction,
791 final GTUType gtuType)
792 {
793
794
795
796
797
798
799
800
801
802 int laneChangesUsed = 0;
803 Lane currentLane = startLane;
804 Length currentSuitability = null;
805 while (null == currentSuitability)
806 {
807 laneChangesUsed++;
808 if (currentLane.accessibleAdjacentLanes(direction, gtuType).size() == 0)
809 {
810 return GETOFFTHISLANENOW;
811 }
812 currentLane = currentLane.accessibleAdjacentLanes(direction, gtuType).iterator().next();
813 currentSuitability = suitabilities.get(currentLane);
814 }
815 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
816 int notSuitableLaneCount = totalLanes - suitabilities.size();
817 return new Length(remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction)
818 / (notSuitableLaneCount + fraction), LengthUnit.SI);
819 }
820
821
822
823
824
825
826
827
828 protected final int countCompatibleLanes(final CrossSectionLink link, final GTUType gtuType)
829 {
830 int result = 0;
831 for (CrossSectionElement cse : link.getCrossSectionElementList())
832 {
833 if (cse instanceof Lane)
834 {
835 Lane l = (Lane) cse;
836 if (l.getLaneType().isCompatible(gtuType))
837 {
838 result++;
839 }
840 }
841 }
842 return result;
843 }
844
845
846 @Override
847 public final String toString()
848 {
849 return "LaneBasedCFLCTacticalPlanner [laneChangeModel=" + this.laneChangeModel + "]";
850 }
851
852 }