1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.djunits.value.vdouble.scalar.Length;
12 import org.djunits.value.vdouble.scalar.Time;
13 import org.opentrafficsim.core.geometry.OTSGeometryException;
14 import org.opentrafficsim.core.geometry.OTSLine3D;
15 import org.opentrafficsim.core.gtu.GTUDirectionality;
16 import org.opentrafficsim.core.gtu.GTUException;
17 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
18 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
19 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
20 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
21 import org.opentrafficsim.core.network.LateralDirectionality;
22 import org.opentrafficsim.core.network.Link;
23 import org.opentrafficsim.core.network.LinkDirection;
24 import org.opentrafficsim.core.network.NetworkException;
25 import org.opentrafficsim.core.network.Node;
26 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
27 import org.opentrafficsim.road.gtu.lane.perception.CategorialLanePerception;
28 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
29 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneBasedOperationalPlan;
30 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
31 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder.LaneChange;
32 import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
33 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
34 import org.opentrafficsim.road.network.lane.CrossSectionElement;
35 import org.opentrafficsim.road.network.lane.CrossSectionLink;
36 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
37 import org.opentrafficsim.road.network.lane.Lane;
38 import org.opentrafficsim.road.network.lane.LaneDirection;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public abstract class AbstractLaneBasedTacticalPlanner implements LaneBasedTacticalPlanner, Serializable
54 {
55
56 private static final long serialVersionUID = 20151125L;
57
58
59 private CarFollowingModel carFollowingModel;
60
61
62 private final LanePerception lanePerception;
63
64
65 private final LaneBasedGTU gtu;
66
67
68
69
70
71
72 public AbstractLaneBasedTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu)
73 {
74 setCarFollowingModel(carFollowingModel);
75 this.gtu = gtu;
76 this.lanePerception = new CategorialLanePerception(gtu);
77 }
78
79
80 @Override
81 public final LaneBasedGTU getGtu()
82 {
83 return this.gtu;
84 }
85
86
87
88
89
90
91
92
93
94
95 public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway)
96 throws GTUException, NetworkException
97 {
98 DirectedLanePosition dlp = gtu.getReferencePosition();
99 return buildLanePathInfo(gtu, maxHeadway, dlp.getLane(), dlp.getPosition(), dlp.getGtuDirection());
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 public static LanePathInfo buildLanePathInfo(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
117 final Length position, final GTUDirectionality startDirectionality) throws GTUException, NetworkException
118 {
119 List<LaneDirection> laneListForward = new ArrayList<>();
120 Lane lane = startLane;
121 GTUDirectionality lastGtuDir = startDirectionality;
122 Length startPosition = position;
123 Lane lastLane = lane;
124 laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
125 Length distanceToEndOfLane;
126 OTSLine3D path;
127 try
128 {
129 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
130 {
131 distanceToEndOfLane = lane.getLength().minus(position);
132 path = lane.getCenterLine().extract(position, lane.getLength());
133 }
134 else
135 {
136 distanceToEndOfLane = position;
137 path = lane.getCenterLine().extract(Length.ZERO, position).reverse();
138 }
139 }
140 catch (OTSGeometryException exception)
141 {
142 System.err.println(gtu + ": " + exception.getMessage());
143 System.err.println(lane + ", len=" + lane.getLength());
144 System.err.println(position);
145 throw new GTUException(exception);
146 }
147
148 while (distanceToEndOfLane.lt(maxHeadway))
149 {
150 Map<Lane, GTUDirectionality> lanes = lastGtuDir.equals(GTUDirectionality.DIR_PLUS)
151 ? lane.nextLanes(gtu.getGTUType()) : lane.prevLanes(gtu.getGTUType());
152 if (lanes.size() == 0)
153 {
154
155 return new LanePathInfo(path, laneListForward, startPosition);
156 }
157 else if (lanes.size() == 1)
158 {
159
160
161 LinkDirection ld = null;
162 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(), lastGtuDir, gtu.getGTUType());
163 lane = lanes.keySet().iterator().next();
164 if (ld != null && !lane.getParentLink().equals(ld.getLink()))
165 {
166
167 return new LanePathInfo(path, laneListForward, startPosition);
168 }
169 }
170 else
171 {
172
173
174 LinkDirection ld;
175 try
176 {
177 ld = gtu.getStrategicalPlanner().nextLinkDirection(lane.getParentLink(),
178 lastGtuDir, gtu.getGTUType());
179 }
180 catch (NetworkException ne)
181 {
182
183
184 return new LanePathInfo(path, laneListForward, startPosition);
185 }
186 Link nextLink = ld.getLink();
187 Lane newLane = null;
188 for (Lane nextLane : lanes.keySet())
189 {
190 if (nextLane.getParentLink().equals(nextLink))
191 {
192 newLane = nextLane;
193 break;
194 }
195 }
196 if (newLane == null)
197 {
198
199
200 return new LanePathInfo(path, laneListForward, startPosition);
201 }
202
203 lane = newLane;
204 }
205
206
207 try
208 {
209 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
210 {
211 if (lastLane.getParentLink().getEndNode().equals(lane.getParentLink().getStartNode()))
212 {
213
214 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
215 lastGtuDir = GTUDirectionality.DIR_PLUS;
216 }
217 else
218 {
219
220 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
221 lastGtuDir = GTUDirectionality.DIR_MINUS;
222 }
223 }
224 else
225 {
226 if (lastLane.getParentLink().getStartNode().equals(lane.getParentLink().getStartNode()))
227 {
228
229 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine());
230 lastGtuDir = GTUDirectionality.DIR_PLUS;
231 }
232 else
233 {
234
235 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, lane.getCenterLine().reverse());
236 lastGtuDir = GTUDirectionality.DIR_MINUS;
237 }
238 }
239 lastLane = lane;
240 }
241 catch (OTSGeometryException exception)
242 {
243 throw new GTUException(exception);
244 }
245
246 laneListForward.add(new LaneDirection(lastLane, lastGtuDir));
247 distanceToEndOfLane = distanceToEndOfLane.plus(lastLane.getLength());
248
249 }
250 return new LanePathInfo(path, laneListForward, startPosition);
251 }
252
253
254
255
256
257
258
259
260
261
262
263 public static NextSplitInfo determineNextSplit(final LaneBasedGTU gtu, final Length maxHeadway)
264 throws GTUException, NetworkException
265 {
266 Node nextSplitNode = null;
267 Set<Lane> correctCurrentLanes = new HashSet<>();
268 DirectedLanePosition dlp = gtu.getReferencePosition();
269 Lane referenceLane = dlp.getLane();
270 double refFrac = dlp.getPosition().si / referenceLane.getLength().si;
271 Link lastLink = referenceLane.getParentLink();
272 GTUDirectionality lastGtuDir = dlp.getGtuDirection();
273 GTUDirectionality referenceLaneDirectionality = lastGtuDir;
274 Length lengthForward;
275 Length position = dlp.getPosition();
276 Node lastNode;
277 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
278 {
279 lengthForward = referenceLane.getLength().minus(position);
280 lastNode = referenceLane.getParentLink().getEndNode();
281 }
282 else
283 {
284 lengthForward = gtu.position(referenceLane, gtu.getReference());
285 lastNode = referenceLane.getParentLink().getStartNode();
286 }
287
288
289 while (lengthForward.lt(maxHeadway) && nextSplitNode == null)
290 {
291
292 Set<Link> links = lastNode.getLinks().toSet();
293 Iterator<Link> linkIterator = links.iterator();
294 while (linkIterator.hasNext())
295 {
296 Link link = linkIterator.next();
297 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType()))
298 {
299 linkIterator.remove();
300 }
301 }
302
303
304 boolean laneChange = false;
305 if (links.size() == 1)
306 {
307 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
308 {
309 if (cse instanceof Lane && lastGtuDir.isPlus())
310 {
311 Lane lane = (Lane) cse;
312 if (lane.nextLanes(gtu.getGTUType()).size() == 0)
313 {
314 laneChange = true;
315 }
316 }
317 if (cse instanceof Lane && lastGtuDir.isMinus())
318 {
319 Lane lane = (Lane) cse;
320 if (lane.prevLanes(gtu.getGTUType()).size() == 0)
321 {
322 laneChange = true;
323 }
324 }
325 }
326 }
327
328
329 if (laneChange)
330 {
331 nextSplitNode = lastNode;
332
333
334 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
335 {
336 if (cse instanceof Lane)
337 {
338 Lane l = (Lane) cse;
339
340 if (noLaneDrop(gtu, maxHeadway, l, l.getLength().multiplyBy(refFrac), referenceLaneDirectionality))
341 {
342 correctCurrentLanes.add(l);
343 }
344 }
345 }
346 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
347 }
348
349
350 if (links.size() > 1)
351 {
352 nextSplitNode = lastNode;
353 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(nextSplitNode, lastLink, gtu.getGTUType());
354
355
356 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
357 {
358 if (cse instanceof Lane)
359 {
360 Lane l = (Lane) cse;
361
362 if (connectsToPath(gtu, maxHeadway, l, l.getLength().multiplyBy(refFrac), referenceLaneDirectionality,
363 ld.getLink()))
364 {
365 correctCurrentLanes.add(l);
366 }
367 }
368 }
369 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
370 }
371
372 if (links.size() == 0)
373 {
374 return new NextSplitInfo(null, correctCurrentLanes);
375 }
376
377
378 Link link = links.iterator().next();
379
380
381 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
382 {
383 if (lastLink.getEndNode().equals(link.getStartNode()))
384 {
385
386 lastGtuDir = GTUDirectionality.DIR_PLUS;
387 lastNode = lastLink.getEndNode();
388 }
389 else
390 {
391
392 lastGtuDir = GTUDirectionality.DIR_MINUS;
393 lastNode = lastLink.getEndNode();
394 }
395 }
396 else
397 {
398 if (lastLink.getStartNode().equals(link.getStartNode()))
399 {
400
401 lastNode = lastLink.getStartNode();
402 lastGtuDir = GTUDirectionality.DIR_PLUS;
403 }
404 else
405 {
406
407 lastNode = lastLink.getStartNode();
408 lastGtuDir = GTUDirectionality.DIR_MINUS;
409 }
410 }
411 lastLink = links.iterator().next();
412 lengthForward = lengthForward.plus(lastLink.getLength());
413 }
414
415 return new NextSplitInfo(null, correctCurrentLanes);
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431 protected static boolean connectsToPath(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
432 final Length startLanePosition, final GTUDirectionality startDirectionality, final Link linkAfterSplit)
433 throws GTUException, NetworkException
434 {
435 List<LaneDirection> laneDirections =
436 buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality).getLaneDirectionList();
437 for (LaneDirection laneDirection : laneDirections)
438 {
439 if (laneDirection.getLane().getParentLink().equals(linkAfterSplit))
440 {
441 return true;
442 }
443 }
444 return false;
445 }
446
447
448
449
450
451
452
453
454
455
456
457
458
459 protected static boolean noLaneDrop(final LaneBasedGTU gtu, final Length maxHeadway, final Lane startLane,
460 final Length startLanePosition, final GTUDirectionality startDirectionality) throws GTUException, NetworkException
461 {
462 LanePathInfo lpi = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition, startDirectionality);
463 if (lpi.getPath().getLength().lt(maxHeadway))
464 {
465 return false;
466 }
467 return true;
468 }
469
470
471
472
473
474
475
476
477
478 protected static List<LinkDirection> buildLinkListForward(final LaneBasedGTU gtu, final Length maxHeadway)
479 throws GTUException, NetworkException
480 {
481 List<LinkDirection> linkList = new ArrayList<>();
482 DirectedLanePosition dlp = gtu.getReferencePosition();
483 Lane referenceLane = dlp.getLane();
484 Link lastLink = referenceLane.getParentLink();
485 GTUDirectionality lastGtuDir = dlp.getGtuDirection();
486 linkList.add(new LinkDirection(lastLink, lastGtuDir));
487 Length lengthForward;
488 Length position = dlp.getPosition();
489 Node lastNode;
490 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
491 {
492 lengthForward = referenceLane.getLength().minus(position);
493 lastNode = referenceLane.getParentLink().getEndNode();
494 }
495 else
496 {
497 lengthForward = gtu.position(referenceLane, gtu.getReference());
498 lastNode = referenceLane.getParentLink().getStartNode();
499 }
500
501
502 while (lengthForward.lt(maxHeadway))
503 {
504
505 Set<Link> links = lastNode.getLinks().toSet();
506 Iterator<Link> linkIterator = links.iterator();
507 while (linkIterator.hasNext())
508 {
509 Link link = linkIterator.next();
510 if (link.equals(lastLink) || !link.getLinkType().isCompatible(gtu.getGTUType()))
511 {
512 linkIterator.remove();
513 }
514 }
515
516 if (links.size() == 0)
517 {
518 return linkList;
519 }
520
521 Link link;
522 if (links.size() > 1)
523 {
524 LinkDirection ld = gtu.getStrategicalPlanner().nextLinkDirection(lastLink, lastGtuDir, gtu.getGTUType());
525 link = ld.getLink();
526 }
527 else
528 {
529 link = links.iterator().next();
530 }
531
532
533 if (lastGtuDir.equals(GTUDirectionality.DIR_PLUS))
534 {
535 if (lastLink.getEndNode().equals(link.getStartNode()))
536 {
537
538 lastGtuDir = GTUDirectionality.DIR_PLUS;
539 lastNode = lastLink.getEndNode();
540 }
541 else
542 {
543
544 lastGtuDir = GTUDirectionality.DIR_MINUS;
545 lastNode = lastLink.getEndNode();
546 }
547 }
548 else
549 {
550 if (lastLink.getStartNode().equals(link.getStartNode()))
551 {
552
553 lastNode = lastLink.getStartNode();
554 lastGtuDir = GTUDirectionality.DIR_PLUS;
555 }
556 else
557 {
558
559 lastNode = lastLink.getStartNode();
560 lastGtuDir = GTUDirectionality.DIR_MINUS;
561 }
562 }
563 lastLink = link;
564 linkList.add(new LinkDirection(lastLink, lastGtuDir));
565 lengthForward = lengthForward.plus(lastLink.getLength());
566 }
567 return linkList;
568 }
569
570
571 @Override
572 public final CarFollowingModel getCarFollowingModel()
573 {
574 return this.carFollowingModel;
575 }
576
577
578
579
580
581 public final void setCarFollowingModel(final CarFollowingModel carFollowingModel)
582 {
583 this.carFollowingModel = carFollowingModel;
584 }
585
586
587 @Override
588 public final LanePerception getPerception()
589 {
590 return this.lanePerception;
591 }
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606 public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
607 final BehavioralCharacteristics bc, final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
608 throws ParameterException, GTUException, NetworkException, OperationalPlanException
609 {
610 Length forwardHeadway = bc.getParameter(ParameterTypes.LOOKAHEAD);
611 List<Lane> lanes = null;
612 if (!laneChange.isChangingLane())
613 {
614 lanes = buildLanePathInfo(gtu, forwardHeadway).getLanes();
615 }
616 else
617 {
618
619
620
621
622 Map<Lane, Length> positions = gtu.positions(gtu.getReference());
623 LateralDirectionality lat = laneChange.isChangingLeft() ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
624 DirectedLanePosition dlp = gtu.getReferencePosition();
625 Iterator<Lane> iterator = dlp.getLane().accessibleAdjacentLanes(lat, gtu.getGTUType()).iterator();
626 Lane adjLane = iterator.hasNext() ? iterator.next() : null;
627 if (adjLane != null && positions.containsKey(adjLane))
628 {
629
630 lanes = buildLanePathInfo(gtu, forwardHeadway).getLanes();
631 }
632 else
633 {
634
635 for (Lane lane : positions.keySet())
636 {
637 if (lane.accessibleAdjacentLanes(lat, gtu.getGTUType()).contains(dlp.getLane()))
638 {
639 lanes = buildLanePathInfo(gtu, forwardHeadway, lane, positions.get(lane), dlp.getGtuDirection())
640 .getLanes();
641 }
642 }
643 }
644 if (lanes == null)
645 {
646 throw new RuntimeException("From lane could not be determined during lane change.");
647 }
648 }
649 if ((!simplePlan.isLaneChange() && !laneChange.isChangingLane())
650 || (gtu.getSpeed().si == 0.0 && simplePlan.getAcceleration().si <= 0.0))
651 {
652 Length firstLanePosition = gtu.getReferencePosition().getPosition();
653 try
654 {
655 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, lanes, firstLanePosition, startTime,
656 gtu.getSpeed(), simplePlan.getAcceleration(), bc.getParameter(ParameterTypes.DT));
657 }
658 catch (OTSGeometryException exception)
659 {
660 throw new OperationalPlanException(exception);
661 }
662 }
663
664 try
665 {
666 return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, lanes, simplePlan.getLaneChangeDirection(),
667 gtu.getLocation(), startTime, gtu.getSpeed(), simplePlan.getAcceleration(),
668 bc.getParameter(ParameterTypes.DT), laneChange);
669 }
670 catch (OTSGeometryException exception)
671 {
672 throw new OperationalPlanException(exception);
673 }
674 }
675 }