1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.Map;
6 import java.util.Set;
7 import java.util.TreeMap;
8
9 import org.djunits.unit.LengthUnit;
10 import org.djunits.value.vdouble.scalar.Length;
11 import org.djunits.value.vdouble.scalar.Time;
12 import org.opentrafficsim.core.gtu.GTUDirectionality;
13 import org.opentrafficsim.core.gtu.GTUException;
14 import org.opentrafficsim.core.gtu.GTUType;
15 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
16 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
17 import org.opentrafficsim.core.gtu.perception.AbstractPerception;
18 import org.opentrafficsim.core.network.LateralDirectionality;
19 import org.opentrafficsim.core.network.Link;
20 import org.opentrafficsim.core.network.NetworkException;
21 import org.opentrafficsim.core.network.Node;
22 import org.opentrafficsim.core.network.route.Route;
23 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
24 import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
25 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
26 import org.opentrafficsim.road.network.lane.Lane;
27
28 import nl.tudelft.simulation.language.Throw;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public abstract class AbstractLanePerception extends AbstractPerception implements LanePerception
50 {
51
52
53 private static final long serialVersionUID = 20151128L;
54
55
56 private LaneStructure laneStructure = null;
57
58
59 private Time updateTime = null;
60
61
62
63
64
65
66
67 public AbstractLanePerception(final LaneBasedGTU gtu)
68 {
69 super(gtu);
70 }
71
72
73 @Override
74 public final LaneBasedGTU getGtu()
75 {
76 return (LaneBasedGTU) super.getGtu();
77 }
78
79
80 @Override
81 public final LaneStructure getLaneStructure() throws ParameterException
82 {
83 if (this.laneStructure == null || this.updateTime.lt(getGtu().getSimulator().getSimulatorTime().getTime()))
84 {
85
86 Length down = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.PERCEPTION);
87
88 Length up = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACK);
89
90 Length downSplit = getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
91
92 Length upMerge = Length.max(up, downSplit);
93
94 up = up.neg();
95 upMerge = upMerge.neg();
96
97 DirectedLanePosition dlp;
98 try
99 {
100 dlp = getGtu().getReferencePosition();
101 }
102 catch (GTUException exception)
103 {
104
105 throw new RuntimeException("Could not get fraction on root lane.", exception);
106 }
107 Lane rootLane = dlp.getLane();
108 GTUDirectionality direction = dlp.getGtuDirection();
109 double fraction = dlp.getPosition().si / rootLane.getLength().si;
110 LaneStructureRecord rootLSR =
111 new LaneStructureRecord(rootLane, direction, rootLane.getLength().multiplyBy(-fraction));
112 this.laneStructure = new LaneStructure(rootLSR, downSplit);
113 this.laneStructure.addLaneStructureRecord(rootLSR, RelativeLane.CURRENT);
114 this.relativeLaneMap.clear();
115 this.relativeLaneMap.put(rootLSR, RelativeLane.CURRENT);
116 startBuild(rootLSR, fraction, getGtu().getGTUType(), down, downSplit, up, upMerge);
117
118
119
120 this.updateTime = getGtu().getSimulator().getSimulatorTime().getTime();
121 }
122 return this.laneStructure;
123 }
124
125
126
127
128 private final Map<LaneStructureRecord, RelativeLane> relativeLaneMap = new HashMap<>();
129
130
131 private final Set<Lane> ignoreSet = new HashSet<>();
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 private void startBuild(final LaneStructureRecord rootLSR, final double fraction, final GTUType gtuType, final Length down,
160 final Length downSplit, final Length up, final Length upMerge)
161 {
162
163 Set<LaneStructureRecord> recordSet = new HashSet<>();
164 Set<Lane> laneSet = new HashSet<>();
165 recordSet.add(rootLSR);
166 laneSet.add(rootLSR.getLane());
167 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
168 LateralDirectionality.RIGHT })
169 {
170 LaneStructureRecord current = rootLSR;
171 RelativeLane relativeLane = RelativeLane.CURRENT;
172 Set<Lane> adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
173 while (!adjacentLanes.isEmpty())
174 {
175 Throw.when(adjacentLanes.size() > 1, RuntimeException.class,
176 "Multiple adjacent lanes encountered during construction of lane map.");
177 relativeLane = latDirection.isLeft() ? relativeLane.getLeft() : relativeLane.getRight();
178 Lane lane = adjacentLanes.iterator().next();
179 LaneStructureRecord adjacentRecord =
180 constructRecord(lane, current.getDirection(), lane.getLength().multiplyBy(-fraction), relativeLane);
181 if (latDirection.isLeft())
182 {
183 if (lane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).contains(current.getLane()))
184 {
185 adjacentRecord.setRight(current);
186 }
187 current.setLeft(adjacentRecord);
188 }
189 else
190 {
191 if (lane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType).contains(current.getLane()))
192 {
193 adjacentRecord.setLeft(current);
194 }
195 current.setRight(adjacentRecord);
196 }
197
198 recordSet.add(adjacentRecord);
199 laneSet.add(lane);
200 current = adjacentRecord;
201 adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
202 }
203 }
204 try
205 {
206 for (LaneStructureRecord record : recordSet)
207 {
208 if (record.getStartDistance().plus(record.getLane().getLength()).ge(down))
209 {
210 record.setCutOffEnd(down.minus(record.getStartDistance()));
211 }
212 if (record.getStartDistance().le(up))
213 {
214 record.setCutOffStart(up.minus(record.getStartDistance()));
215 }
216 }
217 this.ignoreSet.clear();
218 buildDownstreamRecursive(recordSet, gtuType, down, up, downSplit, upMerge);
219 this.ignoreSet.clear();
220 buildUpstreamRecursive(recordSet, gtuType, down, up, upMerge);
221 }
222 catch (GTUException | NetworkException exception)
223 {
224 throw new RuntimeException("Exception while building lane map.", exception);
225 }
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 private void buildDownstreamRecursive(final Set<LaneStructureRecord> recordSet, final GTUType gtuType, final Length down,
255 final Length up, final Length downSplit, final Length upMerge) throws GTUException, NetworkException
256 {
257
258 Map<Link, Set<Lane>> laneSets = new HashMap<>();
259 Map<Link, TreeMap<RelativeLane, LaneStructureRecord>> recordSets = new HashMap<>();
260 Map<Link, Length> maxStart = new HashMap<>();
261 for (LaneStructureRecord laneRecord : recordSet)
262 {
263 if (!laneRecord.isCutOffEnd())
264 {
265 for (Lane nextLane : laneRecord.getLane().downstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
266 {
267 Link nextLink = nextLane.getParentLink();
268 if (!laneSets.containsKey(nextLink))
269 {
270 laneSets.put(nextLink, new HashSet<>());
271 recordSets.put(nextLink, new TreeMap<>());
272 maxStart.put(nextLink, new Length(Double.MIN_VALUE, LengthUnit.SI));
273 }
274 laneSets.get(nextLink).add(nextLane);
275 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
276 Length start = laneRecord.getStartDistance().plus(laneRecord.getLane().getLength());
277 maxStart.put(nextLink, Length.max(maxStart.get(nextLink), start));
278 LaneStructureRecord nextRecord = constructRecord(nextLane,
279 laneRecord.getLane().downstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).get(nextLane), start,
280 relativeLane);
281 if (start.plus(nextLane.getLength()).ge(down))
282 {
283 nextRecord.setCutOffEnd(down.minus(start));
284 }
285 recordSets.get(nextLink).put(relativeLane, nextRecord);
286 laneRecord.addNext(nextRecord);
287 nextRecord.addPrev(laneRecord);
288 }
289 }
290 else
291 {
292 for (Lane nextLane : laneRecord.getLane().downstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
293 {
294 this.ignoreSet.add(nextLane);
295 }
296 }
297 }
298
299 Link currentLink = recordSet.iterator().next().getLane().getParentLink();
300 GTUDirectionality direction = recordSet.iterator().next().getDirection();
301 Node nextNode = direction.isPlus() ? currentLink.getEndNode() : currentLink.getStartNode();
302 Route route = getGtu().getStrategicalPlanner().getRoute();
303 for (Link link : laneSets.keySet())
304 {
305 connectLaterally(recordSets.get(link), gtuType);
306 Set<LaneStructureRecord> set = new HashSet<>(recordSets.get(link).values());
307
308 Length downLimit = down;
309 if (route != null && (!route.contains(nextNode)
310 || !((LaneBasedStrategicalRoutePlanner) getGtu().getStrategicalPlanner())
311 .nextLinkDirection(nextNode, currentLink, gtuType).getLink().equals(link)))
312 {
313
314 downLimit = Length.min(downLimit, maxStart.get(link).plus(downSplit));
315 }
316 else
317 {
318 set = extendLateral(set, gtuType, down, up, upMerge, true);
319 }
320 buildDownstreamRecursive(set, gtuType, downLimit, up, downSplit, upMerge);
321 }
322 }
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352 private Set<LaneStructureRecord> extendLateral(final Set<LaneStructureRecord> recordSet, final GTUType gtuType,
353 final Length down, final Length up, final Length upMerge, final boolean downstreamBuild)
354 throws GTUException, NetworkException
355 {
356 Set<Lane> laneSet = new HashSet<>();
357 for (LaneStructureRecord laneStructureRecord : recordSet)
358 {
359 laneSet.add(laneStructureRecord.getLane());
360 }
361 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
362 LateralDirectionality.RIGHT })
363 {
364 Set<LaneStructureRecord> expandSet = new HashSet<>();
365 Length startDistance = null;
366 Length endDistance = null;
367 for (LaneStructureRecord laneRecord : recordSet)
368 {
369 LaneStructureRecord current = laneRecord;
370 startDistance = current.getStartDistance();
371 endDistance = current.getStartDistance().plus(current.getLane().getLength());
372 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
373 Set<Lane> adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
374 while (!adjacentLanes.isEmpty())
375 {
376 Throw.when(adjacentLanes.size() > 1, RuntimeException.class,
377 "Multiple adjacent lanes encountered during construction of lane map.");
378 Lane laneAdjacent = adjacentLanes.iterator().next();
379 Length adjacentStart = downstreamBuild ? startDistance : endDistance.minus(laneAdjacent.getLength());
380
381 if (!laneSet.contains(laneAdjacent) && !adjacentStart.plus(laneAdjacent.getLength()).le(up)
382 && !adjacentStart.ge(down) && !this.ignoreSet.contains(laneAdjacent))
383 {
384 laneSet.add(laneAdjacent);
385 relativeLane = latDirection.isLeft() ? relativeLane.getLeft() : relativeLane.getRight();
386 LaneStructureRecord recordAdjacent =
387 constructRecord(laneAdjacent, laneRecord.getDirection(), adjacentStart, relativeLane);
388 expandSet.add(recordAdjacent);
389 if (latDirection.isLeft())
390 {
391 if (laneAdjacent.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType)
392 .contains(current.getLane()))
393 {
394 recordAdjacent.setRight(current);
395 }
396 current.setLeft(recordAdjacent);
397 }
398 else
399 {
400 if (laneAdjacent.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType)
401 .contains(current.getLane()))
402 {
403 recordAdjacent.setLeft(current);
404 }
405 current.setRight(recordAdjacent);
406 }
407 if (adjacentStart.plus(laneAdjacent.getLength()).ge(down))
408 {
409 recordAdjacent.setCutOffEnd(down.minus(adjacentStart));
410 }
411 if (adjacentStart.le(up))
412 {
413 recordAdjacent.setCutOffStart(up.minus(adjacentStart));
414 }
415 current = recordAdjacent;
416 adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
417 }
418 else
419 {
420 break;
421 }
422 }
423 }
424 if (downstreamBuild & !expandSet.isEmpty())
425 {
426
427 buildUpstreamRecursive(expandSet, gtuType, down, startDistance.plus(upMerge), upMerge);
428 }
429 recordSet.addAll(expandSet);
430 }
431 return recordSet;
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445 private void buildUpstreamRecursive(final Set<LaneStructureRecord> recordSet, final GTUType gtuType, final Length down,
446 final Length up, final Length upMerge) throws GTUException, NetworkException
447 {
448
449 Map<Link, Set<Lane>> laneSets = new HashMap<>();
450 Map<Link, TreeMap<RelativeLane, LaneStructureRecord>> recordSets = new HashMap<>();
451 Map<Link, Length> minStart = new HashMap<>();
452 for (LaneStructureRecord laneRecord : recordSet)
453 {
454 if (!laneRecord.isCutOffStart())
455 {
456 for (Lane prevLane : laneRecord.getLane().upstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
457 {
458 Link prevLink = prevLane.getParentLink();
459 if (!laneSets.containsKey(prevLink))
460 {
461 laneSets.put(prevLink, new HashSet<>());
462 recordSets.put(prevLink, new TreeMap<>());
463 minStart.put(prevLink, new Length(Double.MAX_VALUE, LengthUnit.SI));
464 }
465 laneSets.get(prevLink).add(prevLane);
466 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
467 Length start = laneRecord.getStartDistance().minus(prevLane.getLength());
468 minStart.put(prevLink, Length.min(minStart.get(prevLink), start));
469 LaneStructureRecord prevRecord = constructRecord(prevLane,
470 laneRecord.getLane().upstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).get(prevLane), start,
471 relativeLane);
472 if (start.le(up))
473 {
474 prevRecord.setCutOffStart(up.minus(start));
475 }
476 recordSets.get(prevLink).put(relativeLane, prevRecord);
477 laneRecord.addPrev(prevRecord);
478 prevRecord.addNext(laneRecord);
479 }
480 }
481 else
482 {
483 for (Lane prevLane : laneRecord.getLane().upstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
484 {
485 this.ignoreSet.add(prevLane);
486 }
487 }
488 }
489
490 for (Link link : laneSets.keySet())
491 {
492 connectLaterally(recordSets.get(link), gtuType);
493 Set<LaneStructureRecord> set = new HashSet<>(recordSets.get(link).values());
494 buildUpstreamRecursive(set, gtuType, down, up, upMerge);
495 }
496 }
497
498
499
500
501
502
503
504
505
506 private LaneStructureRecord constructRecord(final Lane lane, final GTUDirectionality direction, final Length startDistance,
507 final RelativeLane relativeLane)
508 {
509 LaneStructureRecord record = new LaneStructureRecord(lane, direction, startDistance);
510 this.laneStructure.addLaneStructureRecord(record, relativeLane);
511 this.relativeLaneMap.put(record, relativeLane);
512 return record;
513 }
514
515
516
517
518
519
520 private void connectLaterally(final Map<RelativeLane, LaneStructureRecord> map, final GTUType gtuType)
521 {
522 for (RelativeLane relativeLane : map.keySet())
523 {
524 if (map.containsKey(relativeLane.getRight()))
525 {
526 Lane thisLane = map.get(relativeLane).getLane();
527 Lane rightLane = map.get(relativeLane.getRight()).getLane();
528 if (thisLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).contains(rightLane))
529 {
530 map.get(relativeLane).setRight(map.get(relativeLane.getRight()));
531 }
532 if (rightLane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType).contains(thisLane))
533 {
534 map.get(relativeLane.getRight()).setLeft(map.get(relativeLane));
535 }
536 }
537 }
538 }
539
540 }