1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Iterator;
6 import java.util.LinkedHashSet;
7 import java.util.List;
8 import java.util.Set;
9
10 import org.djunits.value.vdouble.scalar.Length;
11 import org.opentrafficsim.base.OtsClassUtil;
12 import org.opentrafficsim.base.parameters.ParameterTypeClass;
13 import org.opentrafficsim.base.parameters.ParameterTypeDuration;
14 import org.opentrafficsim.base.parameters.ParameterTypeLength;
15 import org.opentrafficsim.base.parameters.ParameterTypes;
16 import org.opentrafficsim.core.geometry.OtsGeometryException;
17 import org.opentrafficsim.core.geometry.OtsLine3d;
18 import org.opentrafficsim.core.gtu.GtuException;
19 import org.opentrafficsim.core.network.LateralDirectionality;
20 import org.opentrafficsim.core.network.Link;
21 import org.opentrafficsim.core.network.NetworkException;
22 import org.opentrafficsim.core.network.Node;
23 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
24 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
25 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
26 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs;
27 import org.opentrafficsim.road.network.lane.CrossSectionElement;
28 import org.opentrafficsim.road.network.lane.CrossSectionLink;
29 import org.opentrafficsim.road.network.lane.Lane;
30 import org.opentrafficsim.road.network.lane.LanePosition;
31
32
33
34
35
36
37
38
39
40
41
42
43 public abstract class AbstractLaneBasedTacticalPlanner implements LaneBasedTacticalPlanner, Serializable
44 {
45
46
47 public static final ParameterTypeClass<LaneBasedTacticalPlanner> TACTICAL_PLANNER;
48
49 static
50 {
51 Class<LaneBasedTacticalPlanner> type = LaneBasedTacticalPlanner.class;
52 TACTICAL_PLANNER = new ParameterTypeClass<>("tactical planner", "Tactical planner class.",
53 OtsClassUtil.getTypedClass(type), Lmrs.class);
54 }
55
56
57 private static final long serialVersionUID = 20151125L;
58
59
60 protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
61
62
63 protected static final ParameterTypeDuration DT = ParameterTypes.DT;
64
65
66 private CarFollowingModel carFollowingModel;
67
68
69 private final LanePerception lanePerception;
70
71
72 private final LaneBasedGtu gtu;
73
74
75
76
77
78
79
80 public AbstractLaneBasedTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGtu gtu,
81 final LanePerception lanePerception)
82 {
83 setCarFollowingModel(carFollowingModel);
84 this.gtu = gtu;
85 this.lanePerception = lanePerception;
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)
105 throws GtuException, NetworkException
106 {
107 LanePosition dlp = gtu.getReferencePosition();
108 return buildLanePathInfo(gtu, maxHeadway, dlp.getLane(), dlp.getPosition());
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public static LanePathInfo buildLanePathInfo(final LaneBasedGtu gtu, final Length maxHeadway, final Lane startLane,
125 final Length position) throws GtuException, NetworkException
126 {
127 List<Lane> laneListForward = new ArrayList<>();
128 Lane lane = startLane;
129 Length startPosition = position;
130 Lane lastLane = lane;
131 laneListForward.add(lastLane);
132 Length distanceToEndOfLane;
133 OtsLine3d path;
134 try
135 {
136 distanceToEndOfLane = lane.getLength().minus(position);
137 path = lane.getCenterLine().extract(position, lane.getLength());
138 }
139 catch (OtsGeometryException exception)
140 {
141
142
143
144
145
146
147 path = null;
148 distanceToEndOfLane = Length.ZERO;
149 laneListForward.clear();
150 startPosition = Length.ZERO;
151 }
152
153 while (distanceToEndOfLane.lt(maxHeadway))
154 {
155 Set<Lane> lanes = lane.nextLanes(gtu.getType());
156 if (lanes.size() == 0)
157 {
158
159 return new LanePathInfo(path, laneListForward, startPosition);
160 }
161 else if (lanes.size() == 1)
162 {
163
164
165 Link link = gtu.getStrategicalPlanner().nextLink(lane.getParentLink(), gtu.getType());
166 lane = lanes.iterator().next();
167 if (link != null && !lane.getParentLink().equals(link))
168 {
169
170 return new LanePathInfo(path, laneListForward, startPosition);
171 }
172 }
173 else
174 {
175
176
177 Link link;
178 try
179 {
180 link = gtu.getStrategicalPlanner().nextLink(lane.getParentLink(), gtu.getType());
181 }
182 catch (NetworkException ne)
183 {
184
185 return new LanePathInfo(path, laneListForward, startPosition);
186 }
187 Link nextLink = link;
188 Lane newLane = null;
189 for (Lane nextLane : lanes)
190 {
191 if (nextLane.getParentLink().equals(nextLink))
192 {
193 newLane = nextLane;
194 break;
195 }
196 }
197 if (newLane == null)
198 {
199
200
201 return new LanePathInfo(path, laneListForward, startPosition);
202 }
203
204 lane = newLane;
205 }
206
207
208 try
209 {
210 path = concatenateNull(path, lane.getCenterLine());
211
212 }
213 catch (OtsGeometryException exception)
214 {
215 throw new GtuException(exception);
216 }
217
218 laneListForward.add(lastLane);
219 distanceToEndOfLane = distanceToEndOfLane.plus(lastLane.getLength());
220
221 }
222 return new LanePathInfo(path, laneListForward, startPosition);
223 }
224
225
226
227
228
229
230
231
232 public static OtsLine3d concatenateNull(final OtsLine3d path, final OtsLine3d centerLine) throws OtsGeometryException
233 {
234 if (path == null)
235 {
236 return centerLine;
237 }
238 return OtsLine3d.concatenate(Lane.MARGIN.si, path, centerLine);
239 }
240
241
242
243
244
245
246
247
248
249
250
251 public static NextSplitInfo determineNextSplit(final LaneBasedGtu gtu, final Length maxHeadway)
252 throws GtuException, NetworkException
253 {
254 Node nextSplitNode = null;
255 Set<Lane> correctCurrentLanes = new LinkedHashSet<>();
256 LanePosition dlp = gtu.getReferencePosition();
257 Lane referenceLane = dlp.getLane();
258 double refFrac = dlp.getPosition().si / referenceLane.getLength().si;
259 Link lastLink = referenceLane.getParentLink();
260 Length position = dlp.getPosition();
261 Length lengthForward = referenceLane.getLength().minus(position);
262 Node lastNode = referenceLane.getParentLink().getEndNode();
263
264
265 while (lengthForward.lt(maxHeadway) && nextSplitNode == null)
266 {
267
268 Set<Link> links = lastNode.getLinks().toSet();
269 Iterator<Link> linkIterator = links.iterator();
270 while (linkIterator.hasNext())
271 {
272 Link link = linkIterator.next();
273 if (!link.getType().isCompatible(gtu.getType()) || link.getEndNode().equals(lastNode))
274 {
275 linkIterator.remove();
276 }
277 }
278
279
280 boolean laneChange = false;
281 if (links.size() == 1)
282 {
283 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
284 {
285 if (cse instanceof Lane)
286 {
287 Lane lane = (Lane) cse;
288 if (lane.nextLanes(gtu.getType()).size() == 0)
289 {
290 laneChange = true;
291 }
292 }
293 }
294 }
295
296
297 if (laneChange)
298 {
299 nextSplitNode = lastNode;
300
301
302 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
303 {
304 if (cse instanceof Lane)
305 {
306 Lane l = (Lane) cse;
307 if (noLaneDrop(gtu, maxHeadway, l, l.getLength().times(refFrac)))
308 {
309 correctCurrentLanes.add(l);
310 }
311 }
312 }
313 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
314 }
315
316
317 if (links.size() > 1)
318 {
319 nextSplitNode = lastNode;
320 Link nextLink = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
321
322
323 for (CrossSectionElement cse : referenceLane.getParentLink().getCrossSectionElementList())
324 {
325 if (cse instanceof Lane)
326 {
327 Lane l = (Lane) cse;
328 if (connectsToPath(gtu, maxHeadway, l, l.getLength().times(refFrac), nextLink))
329 {
330 correctCurrentLanes.add(l);
331 }
332 }
333 }
334 if (correctCurrentLanes.size() > 0)
335 {
336 return new NextSplitInfo(nextSplitNode, correctCurrentLanes);
337 }
338
339 Set<Lane> correctLanes = new LinkedHashSet<>();
340 Set<Lane> wrongLanes = new LinkedHashSet<>();
341 for (CrossSectionElement cse : ((CrossSectionLink) lastLink).getCrossSectionElementList())
342 {
343 if (cse instanceof Lane)
344 {
345 Lane l = (Lane) cse;
346 if (connectsToPath(gtu, maxHeadway.plus(l.getLength()), l, Length.ZERO, nextLink))
347 {
348 correctLanes.add(l);
349 }
350 else
351 {
352 wrongLanes.add(l);
353 }
354 }
355 }
356 for (Lane wrongLane : wrongLanes)
357 {
358 for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtu.getType()))
359 {
360 if (correctLanes.contains(adjLane))
361 {
362 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.LEFT);
363 }
364 }
365 for (Lane adjLane : wrongLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtu.getType()))
366 {
367 if (correctLanes.contains(adjLane))
368 {
369 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, LateralDirectionality.RIGHT);
370 }
371 }
372 }
373 return new NextSplitInfo(nextSplitNode, correctCurrentLanes, null);
374 }
375
376 if (links.size() == 0)
377 {
378 return new NextSplitInfo(null, correctCurrentLanes);
379 }
380
381
382 Link link = links.iterator().next();
383
384
385 lastNode = link.getEndNode();
386 lastLink = links.iterator().next();
387 lengthForward = lengthForward.plus(lastLink.getLength());
388 }
389
390 return new NextSplitInfo(null, correctCurrentLanes);
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404
405 protected static boolean connectsToPath(final LaneBasedGtu gtu, final Length maxHeadway, final Lane startLane,
406 final Length startLanePosition, final Link linkAfterSplit) throws GtuException, NetworkException
407 {
408 List<Lane> lanes = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition).getLaneList();
409 for (Lane lane : lanes)
410 {
411 if (lane.getParentLink().equals(linkAfterSplit))
412 {
413 return true;
414 }
415 }
416 return false;
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430 protected static boolean noLaneDrop(final LaneBasedGtu gtu, final Length maxHeadway, final Lane startLane,
431 final Length startLanePosition) throws GtuException, NetworkException
432 {
433 LanePathInfo lpi = buildLanePathInfo(gtu, maxHeadway, startLane, startLanePosition);
434 if (lpi.getPath().getLength().lt(maxHeadway))
435 {
436 return false;
437 }
438 return true;
439 }
440
441
442
443
444
445
446
447
448
449 protected static List<Link> buildLinkListForward(final LaneBasedGtu gtu, final Length maxHeadway)
450 throws GtuException, NetworkException
451 {
452 List<Link> linkList = new ArrayList<>();
453 LanePosition dlp = gtu.getReferencePosition();
454 Lane referenceLane = dlp.getLane();
455 Link lastLink = referenceLane.getParentLink();
456 linkList.add(lastLink);
457 Length position = dlp.getPosition();
458 Length lengthForward = referenceLane.getLength().minus(position);
459 Node lastNode = referenceLane.getParentLink().getEndNode();
460
461
462 while (lengthForward.lt(maxHeadway))
463 {
464
465 Set<Link> links = lastNode.getLinks().toSet();
466 Iterator<Link> linkIterator = links.iterator();
467 while (linkIterator.hasNext())
468 {
469 Link link = linkIterator.next();
470 if (link.equals(lastLink) || !link.getType().isCompatible(gtu.getType()))
471 {
472 linkIterator.remove();
473 }
474 }
475 if (links.size() == 0)
476 {
477 return linkList;
478 }
479
480 Link link;
481 if (links.size() > 1)
482 {
483 link = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
484 }
485 else
486 {
487 link = links.iterator().next();
488 }
489
490
491 lastNode = lastLink.getEndNode();
492 lastLink = link;
493 linkList.add(lastLink);
494 lengthForward = lengthForward.plus(lastLink.getLength());
495 }
496 return linkList;
497 }
498
499
500 @Override
501 public final CarFollowingModel getCarFollowingModel()
502 {
503 return this.carFollowingModel;
504 }
505
506
507
508
509
510 public final void setCarFollowingModel(final CarFollowingModel carFollowingModel)
511 {
512 this.carFollowingModel = carFollowingModel;
513 }
514
515
516 @Override
517 public final LanePerception getPerception()
518 {
519 return this.lanePerception;
520 }
521
522 }