1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.LinkedHashMap;
6 import java.util.Map;
7
8 import org.djunits.unit.AccelerationUnit;
9 import org.djunits.unit.DurationUnit;
10 import org.djunits.unit.LengthUnit;
11 import org.djunits.value.ValueRuntimeException;
12 import org.djunits.value.storage.StorageType;
13 import org.djunits.value.vdouble.scalar.Acceleration;
14 import org.djunits.value.vdouble.scalar.Duration;
15 import org.djunits.value.vdouble.scalar.Length;
16 import org.djunits.value.vdouble.scalar.Speed;
17 import org.djunits.value.vdouble.scalar.Time;
18 import org.djunits.value.vdouble.vector.AccelerationVector;
19 import org.djutils.draw.point.OrientedPoint2d;
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.OtsLine2d;
24 import org.opentrafficsim.core.gtu.GtuException;
25 import org.opentrafficsim.core.gtu.GtuType;
26 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
27 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
28 import org.opentrafficsim.core.gtu.plan.operational.Segments;
29 import org.opentrafficsim.core.network.LateralDirectionality;
30 import org.opentrafficsim.core.network.Link;
31 import org.opentrafficsim.core.network.NetworkException;
32 import org.opentrafficsim.core.network.Node;
33 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
34 import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
35 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
36 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
37 import org.opentrafficsim.road.gtu.lane.perception.categories.DirectDefaultSimplePerception;
38 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
39 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
40 import org.opentrafficsim.road.gtu.lane.tactical.following.GtuFollowingModelOld;
41 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
42 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
43 import org.opentrafficsim.road.network.lane.CrossSectionElement;
44 import org.opentrafficsim.road.network.lane.CrossSectionLink;
45 import org.opentrafficsim.road.network.lane.Lane;
46 import org.opentrafficsim.road.network.lane.LanePosition;
47 import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
48 import org.opentrafficsim.road.network.lane.object.detector.SinkDetector;
49
50
51
52
53
54
55
56
57
58
59
60
61 public class LaneBasedCfLcTacticalPlanner extends AbstractLaneBasedTacticalPlanner
62 {
63
64 private static final long serialVersionUID = 20151125L;
65
66
67 protected static final ParameterTypeLength LOOKBACKOLD = ParameterTypes.LOOKBACKOLD;
68
69
70 private static final Acceleration STAYINCURRENTLANEINCENTIVE = 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 = new Acceleration(-0.3, AccelerationUnit.METER_PER_SECOND_2);
77
78
79 public static final Length NOLANECHANGENEEDED = new Length(Double.MAX_VALUE, LengthUnit.SI);
80
81
82 public static final Length GETOFFTHISLANENOW = Length.ZERO;
83
84
85 private static final Duration TIMEHORIZON = new Duration(90, DurationUnit.SECOND);
86
87
88 private LaneChangeModel laneChangeModel;
89
90
91
92
93
94
95
96 public LaneBasedCfLcTacticalPlanner(final GtuFollowingModelOld carFollowingModel, final LaneChangeModel laneChangeModel,
97 final LaneBasedGtu gtu)
98 {
99 super(carFollowingModel, gtu, new CategoricalLanePerception(gtu));
100 this.laneChangeModel = laneChangeModel;
101 getPerception().addPerceptionCategory(new DirectDefaultSimplePerception(getPerception()));
102 }
103
104
105 @Override
106 public final OperationalPlan generateOperationalPlan(final Time startTime, final OrientedPoint2d 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 OperationalPlan.standStill(getGtu(), getGtu().getLocation(), startTime, Duration.ONE);
119 }
120
121 Length maximumForwardHeadway = laneBasedGTU.getParameters().getParameter(LOOKAHEAD);
122 DefaultSimplePerception simplePerception = perception.getPerceptionCategory(DefaultSimplePerception.class);
123 simplePerception.updateSpeedLimit();
124 simplePerception.updateForwardHeadwayGtu();
125 simplePerception.updateForwardHeadwayObject();
126 simplePerception.updateBackwardHeadway();
127 simplePerception.updateNeighboringHeadwaysLeft();
128 simplePerception.updateNeighboringHeadwaysRight();
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.correctCurrentLanes().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 = new AccelerationVector(new double[] {defaultLeftLaneIncentive.getSI(),
167 STAYINCURRENTLANEINCENTIVE.getSI(), defaultRightLaneIncentive.getSI()}, AccelerationUnit.SI);
168 AccelerationVector laneIncentives = laneIncentives(laneBasedGTU, defaultLaneIncentives);
169 LaneMovementStep lcmr = this.laneChangeModel.computeLaneChangeAndAcceleration(laneBasedGTU, sameLaneTraffic,
170 rightLaneTraffic, leftLaneTraffic, speedLimit,
171 new Acceleration(laneIncentives.get(preferred.isRight() ? 2 : 0)), new Acceleration(laneIncentives.get(1)),
172 new Acceleration(laneIncentives.get(preferred.isRight() ? 0 : 2)));
173 Duration duration = lcmr.getGfmr().getValidUntil().minus(getGtu().getSimulator().getSimulatorAbsTime());
174 if (lcmr.getLaneChangeDirection() != null)
175 {
176 laneBasedGTU.changeLaneInstantaneously(lcmr.getLaneChangeDirection());
177
178
179 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
180 }
181
182
183 Headway object = simplePerception.getForwardHeadwayObject();
184 Acceleration a = lcmr.getGfmr().getAcceleration();
185 if (object instanceof HeadwayTrafficLight)
186 {
187
188 a = Acceleration.min(a, ((GtuFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
189 getGtu().getMaximumSpeed(), Speed.ZERO, object.getDistance(), speedLimit));
190 }
191
192
193 Length dist = lanePathInfo.path().getLength().minus(getGtu().getFront().dx());
194 a = Acceleration.min(a, ((GtuFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
195 getGtu().getMaximumSpeed(), Speed.ZERO, dist, speedLimit));
196
197
198 if (a.si < 1E-6 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
199 {
200 return OperationalPlan.standStill(getGtu(), getGtu().getLocation(), startTime, Duration.ONE);
201 }
202 OtsLine2d path = lanePathInfo.path();
203 OperationalPlan op = new OperationalPlan(getGtu(), path, startTime, Segments.off(getGtu().getSpeed(), duration, a));
204 return op;
205 }
206 catch (ValueRuntimeException exception)
207 {
208 throw new GtuException(exception);
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224 private AccelerationVector laneIncentives(final LaneBasedGtu gtu, final AccelerationVector defaultLaneIncentives)
225 throws NetworkException, ValueRuntimeException, GtuException, OperationalPlanException
226 {
227 Length leftSuitability = suitability(gtu, LateralDirectionality.LEFT);
228 Length currentSuitability = suitability(gtu, null);
229 Length rightSuitability = suitability(gtu, LateralDirectionality.RIGHT);
230 if (leftSuitability == NOLANECHANGENEEDED && currentSuitability == NOLANECHANGENEEDED
231 && rightSuitability == NOLANECHANGENEEDED)
232 {
233 return checkLaneDrops(gtu, defaultLaneIncentives);
234 }
235 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
236 && currentSuitability == NOLANECHANGENEEDED
237 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
238 {
239 return checkLaneDrops(gtu, new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
240 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI));
241 }
242 if (currentSuitability == NOLANECHANGENEEDED)
243 {
244 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
245 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
246 }
247 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
248 acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
249 }
250
251
252
253
254
255
256
257
258
259
260
261
262
263 private AccelerationVector checkLaneDrops(final LaneBasedGtu gtu, final AccelerationVector defaultLaneIncentives)
264 throws NetworkException, ValueRuntimeException, GtuException, OperationalPlanException
265 {
266
267 Length leftSuitability = Double.isNaN(defaultLaneIncentives.get(0).si) || defaultLaneIncentives.get(0).si < -10
268 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.LEFT);
269 Length currentSuitability = laneDrop(gtu, null);
270 Length rightSuitability = Double.isNaN(defaultLaneIncentives.get(2).si) || defaultLaneIncentives.get(2).si < -10
271 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.RIGHT);
272
273 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
274 && currentSuitability == NOLANECHANGENEEDED
275 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
276 {
277 return defaultLaneIncentives;
278 }
279
280 if (currentSuitability == NOLANECHANGENEEDED)
281 {
282 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
283 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
284 }
285 if (currentSuitability.le(leftSuitability))
286 {
287 return new AccelerationVector(
288 new double[] {PREFERREDLANEINCENTIVE.getSI(), NONPREFERREDLANEINCENTIVE.getSI(), GETOFFTHISLANENOW.getSI()},
289 AccelerationUnit.SI);
290 }
291 if (currentSuitability.le(rightSuitability))
292 {
293 return new AccelerationVector(
294 new double[] {GETOFFTHISLANENOW.getSI(), NONPREFERREDLANEINCENTIVE.getSI(), PREFERREDLANEINCENTIVE.getSI()},
295 AccelerationUnit.SI, StorageType.DENSE);
296 }
297 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
298 acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314 private Length laneDrop(final LaneBasedGtu gtu, final LateralDirectionality direction)
315 throws NetworkException, GtuException, OperationalPlanException
316 {
317 LanePosition dlp = gtu.getReferencePosition();
318 Lane lane = dlp.lane();
319 Length longitudinalPosition = dlp.position();
320 if (null != direction)
321 {
322 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
323 direction, longitudinalPosition);
324 }
325 if (null == lane)
326 {
327 return GETOFFTHISLANENOW;
328 }
329 double remainingLength = lane.getLength().getSI() - longitudinalPosition.getSI();
330 double remainingTimeSI = TIMEHORIZON.getSI() - remainingLength / lane.getSpeedLimit(gtu.getType()).getSI();
331 while (remainingTimeSI >= 0)
332 {
333 for (LaneDetector detector : lane.getDetectors())
334 {
335 if (detector instanceof SinkDetector)
336 {
337 return NOLANECHANGENEEDED;
338 }
339 }
340 int branching = lane.nextLanes(gtu.getType()).size();
341 if (branching == 0)
342 {
343 return new Length(remainingLength, LengthUnit.SI);
344 }
345 if (branching > 1)
346 {
347 return NOLANECHANGENEEDED;
348 }
349 lane = lane.nextLanes(gtu.getType()).iterator().next();
350 remainingTimeSI -= lane.getLength().getSI() / lane.getSpeedLimit(gtu.getType()).getSI();
351 remainingLength += lane.getLength().getSI();
352 }
353 return NOLANECHANGENEEDED;
354 }
355
356
357
358
359
360
361
362
363
364
365
366
367 private Length suitability(final LaneBasedGtu gtu, final LateralDirectionality direction)
368 throws NetworkException, GtuException, OperationalPlanException
369 {
370 LanePosition dlp = gtu.getReferencePosition();
371 Lane lane = dlp.lane();
372 Length longitudinalPosition = dlp.position().plus(gtu.getFront().dx());
373 if (null != direction)
374 {
375 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
376 direction, longitudinalPosition);
377 }
378 if (null == lane)
379 {
380 return GETOFFTHISLANENOW;
381 }
382 try
383 {
384 return suitability(lane, longitudinalPosition, gtu, TIMEHORIZON);
385
386 }
387 catch (NetworkException ne)
388 {
389 System.err.println(gtu + " has a route problem in suitability: " + ne.getMessage());
390 return NOLANECHANGENEEDED;
391 }
392 }
393
394
395
396
397
398
399
400 private double acceleration(final LaneBasedGtu gtu, final Length stopDistance)
401 {
402
403
404 double v = gtu.getSpeed().getSI();
405 double a = -v * v / 2 / stopDistance.getSI();
406 return a;
407 }
408
409
410
411
412
413
414
415
416
417
418
419
420 private Length suitability(final Lane lane, final Length longitudinalPosition, final LaneBasedGtu gtu,
421 final Duration timeHorizon) throws NetworkException
422 {
423 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
424 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getType()).getSI();
425
426 Node nextNode = lane.getLink().getEndNode();
427 Link lastLink = lane.getLink();
428 Node nextSplitNode = null;
429 Lane currentLane = lane;
430 CrossSectionLink linkBeforeBranch = lane.getLink();
431 while (null != nextNode)
432 {
433 if (spareTime <= 0)
434 {
435 return NOLANECHANGENEEDED;
436 }
437 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getType());
438 if (0 == laneCount)
439 {
440 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
441 }
442 if (1 == laneCount)
443 {
444 return NOLANECHANGENEEDED;
445
446 }
447 int branching = nextNode.getLinks().size();
448 if (branching > 2)
449 {
450 nextSplitNode = nextNode;
451 break;
452 }
453 else if (1 == branching)
454 {
455 return NOLANECHANGENEEDED;
456 }
457 else
458 {
459 Link nextLink = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
460 if (nextLink instanceof CrossSectionLink)
461 {
462 nextNode = nextLink.getEndNode();
463
464 remainingDistance += nextLink.getLength().getSI();
465 linkBeforeBranch = (CrossSectionLink) nextLink;
466
467 if (currentLane.nextLanes(gtu.getType()).size() == 0)
468 {
469
470
471 if (currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtu.getType()).size() > 0)
472 {
473 for (Lane adjacentLane : currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT,
474 gtu.getType()))
475 {
476 if (adjacentLane.nextLanes(gtu.getType()).size() > 0)
477 {
478 currentLane = adjacentLane;
479 break;
480 }
481
482
483 }
484 }
485 for (Lane adjacentLane : currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.LEFT,
486 gtu.getType()))
487 {
488 if (adjacentLane.nextLanes(gtu.getType()).size() > 0)
489 {
490 currentLane = adjacentLane;
491 break;
492 }
493
494
495 }
496 if (currentLane.nextLanes(gtu.getType()).size() == 0)
497 {
498 throw new NetworkException(
499 "Lane ends and there is not a compatible adjacent lane that does " + "not end");
500 }
501 }
502
503 for (Lane nextLane : currentLane.nextLanes(gtu.getType()))
504 {
505 currentLane = currentLane.nextLanes(gtu.getType()).iterator().next();
506 break;
507 }
508 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtu.getType()).getSI();
509 }
510 else
511 {
512
513
514 return NOLANECHANGENEEDED;
515 }
516 lastLink = nextLink;
517 }
518 }
519 if (null == nextNode)
520 {
521 throw new NetworkException("Cannot find the next branch or sink node");
522 }
523
524
525 Map<Lane, Length> suitabilityOfLanesBeforeBranch = new LinkedHashMap<>();
526 Link linkAfterBranch = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
527 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
528 {
529 if (cse instanceof Lane)
530 {
531 Lane l = (Lane) cse;
532 if (l.getType().isCompatible(gtu.getType()))
533 {
534 for (Lane connectingLane : l.nextLanes(gtu.getType()))
535 {
536 if (connectingLane.getLink() == linkAfterBranch && connectingLane.getType().isCompatible(gtu.getType()))
537 {
538 Length currentValue = suitabilityOfLanesBeforeBranch.get(l);
539
540
541
542 Length value = suitability(connectingLane, new Length(0, LengthUnit.SI), gtu,
543 new Duration(spareTime, DurationUnit.SI));
544
545 value = value.plus(new Length(remainingDistance, LengthUnit.SI));
546
547
548 suitabilityOfLanesBeforeBranch.put(l,
549 null == currentValue || value.le(currentValue) ? value : currentValue);
550 }
551 }
552 }
553 }
554 }
555 if (suitabilityOfLanesBeforeBranch.size() == 0)
556 {
557 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
558 }
559 Length currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
560 if (null != currentLaneSuitability)
561 {
562 return currentLaneSuitability;
563 }
564
565 int totalLanes = countCompatibleLanes(currentLane.getLink(), gtu.getType());
566 Length leftSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
567 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.LEFT, gtu.getType());
568 Length rightSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
569 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.RIGHT, gtu.getType());
570 if (leftSuitability.ge(rightSuitability))
571 {
572 return leftSuitability;
573 }
574 else if (rightSuitability.ge(leftSuitability))
575 {
576
577 return rightSuitability;
578 }
579 if (leftSuitability.le(GETOFFTHISLANENOW))
580 {
581 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
582 }
583 return leftSuitability;
584 }
585
586
587
588
589
590
591
592
593
594
595
596
597
598 protected final Length computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
599 final Map<Lane, Length> suitabilities, final int totalLanes, final LateralDirectionality direction,
600 final GtuType gtuType)
601 {
602
603
604
605
606
607
608
609
610
611 int laneChangesUsed = 0;
612 Lane currentLane = startLane;
613 Length currentSuitability = null;
614 while (null == currentSuitability)
615 {
616 laneChangesUsed++;
617 if (currentLane.accessibleAdjacentLanesLegal(direction, gtuType).size() == 0)
618 {
619 return GETOFFTHISLANENOW;
620 }
621 currentLane = currentLane.accessibleAdjacentLanesLegal(direction, gtuType).iterator().next();
622 currentSuitability = suitabilities.get(currentLane);
623 }
624 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
625 int notSuitableLaneCount = totalLanes - suitabilities.size();
626 return new Length(
627 remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction) / (notSuitableLaneCount + fraction),
628 LengthUnit.SI);
629 }
630
631
632
633
634
635
636
637
638 protected final int countCompatibleLanes(final CrossSectionLink link, final GtuType gtuType)
639 {
640 int result = 0;
641 for (CrossSectionElement cse : link.getCrossSectionElementList())
642 {
643 if (cse instanceof Lane)
644 {
645 Lane l = (Lane) cse;
646 if (l.getType().isCompatible(gtuType))
647 {
648 result++;
649 }
650 }
651 }
652 return result;
653 }
654
655
656 @Override
657 public final String toString()
658 {
659 return "LaneBasedCFLCTacticalPlanner [laneChangeModel=" + this.laneChangeModel + "]";
660 }
661
662 }