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