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
164 Set<LaneStructureRecord> recordSet = new HashSet<>();
165 Set<Lane> laneSet = new HashSet<>();
166 recordSet.add(rootLSR);
167 laneSet.add(rootLSR.getLane());
168 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
169 LateralDirectionality.RIGHT })
170 {
171 LaneStructureRecord current = rootLSR;
172 RelativeLane relativeLane = RelativeLane.CURRENT;
173 Set<Lane> adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
174 while (!adjacentLanes.isEmpty())
175 {
176 Throw.when(adjacentLanes.size() > 1, RuntimeException.class,
177 "Multiple adjacent lanes encountered during construction of lane map.");
178 relativeLane = latDirection.isLeft() ? relativeLane.getLeft() : relativeLane.getRight();
179 Lane lane = adjacentLanes.iterator().next();
180 LaneStructureRecord adjacentRecord =
181 constructRecord(lane, current.getDirection(), lane.getLength().multiplyBy(-fraction), relativeLane);
182 if (latDirection.isLeft())
183 {
184 if (lane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).contains(current.getLane()))
185 {
186 adjacentRecord.setRight(current);
187 }
188 current.setLeft(adjacentRecord);
189 }
190 else
191 {
192 if (lane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType).contains(current.getLane()))
193 {
194 adjacentRecord.setLeft(current);
195 }
196 current.setRight(adjacentRecord);
197 }
198
199 recordSet.add(adjacentRecord);
200 laneSet.add(lane);
201 current = adjacentRecord;
202 adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
203 }
204 }
205 try
206 {
207 for (LaneStructureRecord record : recordSet)
208 {
209 if (record.getStartDistance().plus(record.getLane().getLength()).ge(down))
210 {
211 record.setCutOffEnd(down.minus(record.getStartDistance()));
212 }
213 if (record.getStartDistance().le(up))
214 {
215 record.setCutOffStart(up.minus(record.getStartDistance()));
216 }
217 }
218 this.ignoreSet.clear();
219 buildDownstreamRecursive(recordSet, gtuType, down, up, downSplit, upMerge);
220 this.ignoreSet.clear();
221 buildUpstreamRecursive(recordSet, gtuType, down, up, upMerge);
222 }
223 catch (GTUException | NetworkException exception)
224 {
225 throw new RuntimeException("Exception while building lane map.", exception);
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
255 private void buildDownstreamRecursive(final Set<LaneStructureRecord> recordSet, final GTUType gtuType, final Length down,
256 final Length up, final Length downSplit, final Length upMerge) throws GTUException, NetworkException
257 {
258
259 Map<Link, Set<Lane>> laneSets = new HashMap<>();
260 Map<Link, TreeMap<RelativeLane, LaneStructureRecord>> recordSets = new HashMap<>();
261 Map<Link, Length> maxStart = new HashMap<>();
262 for (LaneStructureRecord laneRecord : recordSet)
263 {
264 if (!laneRecord.isCutOffEnd())
265 {
266 for (Lane nextLane : laneRecord.getLane().downstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
267 {
268 Link nextLink = nextLane.getParentLink();
269 if (!laneSets.containsKey(nextLink))
270 {
271 laneSets.put(nextLink, new HashSet<>());
272 recordSets.put(nextLink, new TreeMap<>());
273 maxStart.put(nextLink, new Length(Double.MIN_VALUE, LengthUnit.SI));
274 }
275 laneSets.get(nextLink).add(nextLane);
276 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
277 Length start = laneRecord.getStartDistance().plus(laneRecord.getLane().getLength());
278 maxStart.put(nextLink, Length.max(maxStart.get(nextLink), start));
279 LaneStructureRecord nextRecord = constructRecord(nextLane,
280 laneRecord.getLane().downstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).get(nextLane), start,
281 relativeLane);
282 if (start.plus(nextLane.getLength()).ge(down))
283 {
284 nextRecord.setCutOffEnd(down.minus(start));
285 }
286 recordSets.get(nextLink).put(relativeLane, nextRecord);
287 laneRecord.addNext(nextRecord);
288 nextRecord.addPrev(laneRecord);
289 }
290 }
291 else
292 {
293 for (Lane nextLane : laneRecord.getLane().downstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
294 {
295 this.ignoreSet.add(nextLane);
296 }
297 }
298 }
299
300 Link currentLink = recordSet.iterator().next().getLane().getParentLink();
301 GTUDirectionality direction = recordSet.iterator().next().getDirection();
302 Node nextNode = direction.isPlus() ? currentLink.getEndNode() : currentLink.getStartNode();
303 Route route = getGtu().getStrategicalPlanner().getRoute();
304 for (Link link : laneSets.keySet())
305 {
306 connectLaterally(recordSets.get(link), gtuType);
307 Set<LaneStructureRecord> set = new HashSet<>(recordSets.get(link).values());
308
309 Length downLimit = down;
310 if (route != null && (!route.contains(nextNode)
311 || !((LaneBasedStrategicalRoutePlanner) getGtu().getStrategicalPlanner())
312 .nextLinkDirection(nextNode, currentLink, gtuType).getLink().equals(link)))
313 {
314
315 downLimit = Length.min(downLimit, maxStart.get(link).plus(downSplit));
316 }
317 else
318 {
319 set = extendLateral(set, gtuType, down, up, upMerge, true);
320 }
321 buildDownstreamRecursive(set, gtuType, downLimit, up, downSplit, upMerge);
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
353 private Set<LaneStructureRecord> extendLateral(final Set<LaneStructureRecord> recordSet, final GTUType gtuType,
354 final Length down, final Length up, final Length upMerge, final boolean downstreamBuild)
355 throws GTUException, NetworkException
356 {
357 Set<Lane> laneSet = new HashSet<>();
358 for (LaneStructureRecord laneStructureRecord : recordSet)
359 {
360 laneSet.add(laneStructureRecord.getLane());
361 }
362 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
363 LateralDirectionality.RIGHT })
364 {
365 Set<LaneStructureRecord> expandSet = new HashSet<>();
366 Length startDistance = null;
367 Length endDistance = null;
368 for (LaneStructureRecord laneRecord : recordSet)
369 {
370 LaneStructureRecord current = laneRecord;
371 startDistance = current.getStartDistance();
372 endDistance = current.getStartDistance().plus(current.getLane().getLength());
373 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
374 Set<Lane> adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
375 while (!adjacentLanes.isEmpty())
376 {
377 Throw.when(adjacentLanes.size() > 1, RuntimeException.class,
378 "Multiple adjacent lanes encountered during construction of lane map.");
379 Lane laneAdjacent = adjacentLanes.iterator().next();
380 Length adjacentStart = downstreamBuild ? startDistance : endDistance.minus(laneAdjacent.getLength());
381
382 if (!laneSet.contains(laneAdjacent) && !adjacentStart.plus(laneAdjacent.getLength()).le(up)
383 && !adjacentStart.ge(down) && !this.ignoreSet.contains(laneAdjacent))
384 {
385 laneSet.add(laneAdjacent);
386 relativeLane = latDirection.isLeft() ? relativeLane.getLeft() : relativeLane.getRight();
387 LaneStructureRecord recordAdjacent =
388 constructRecord(laneAdjacent, laneRecord.getDirection(), adjacentStart, relativeLane);
389 expandSet.add(recordAdjacent);
390 if (latDirection.isLeft())
391 {
392 if (laneAdjacent.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType)
393 .contains(current.getLane()))
394 {
395 recordAdjacent.setRight(current);
396 }
397 current.setLeft(recordAdjacent);
398 }
399 else
400 {
401 if (laneAdjacent.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType)
402 .contains(current.getLane()))
403 {
404 recordAdjacent.setLeft(current);
405 }
406 current.setRight(recordAdjacent);
407 }
408 if (adjacentStart.plus(laneAdjacent.getLength()).ge(down))
409 {
410 recordAdjacent.setCutOffEnd(down.minus(adjacentStart));
411 }
412 if (adjacentStart.le(up))
413 {
414 recordAdjacent.setCutOffStart(up.minus(adjacentStart));
415 }
416 current = recordAdjacent;
417 adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
418 }
419 else
420 {
421 break;
422 }
423 }
424 }
425 if (downstreamBuild & !expandSet.isEmpty())
426 {
427
428 buildUpstreamRecursive(expandSet, gtuType, down, startDistance.plus(upMerge), upMerge);
429 }
430 recordSet.addAll(expandSet);
431 }
432 return recordSet;
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446 private void buildUpstreamRecursive(final Set<LaneStructureRecord> recordSet, final GTUType gtuType, final Length down,
447 final Length up, final Length upMerge) throws GTUException, NetworkException
448 {
449
450 Map<Link, Set<Lane>> laneSets = new HashMap<>();
451 Map<Link, TreeMap<RelativeLane, LaneStructureRecord>> recordSets = new HashMap<>();
452 Map<Link, Length> minStart = new HashMap<>();
453 for (LaneStructureRecord laneRecord : recordSet)
454 {
455 if (!laneRecord.isCutOffStart())
456 {
457 for (Lane prevLane : laneRecord.getLane().upstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
458 {
459 Link prevLink = prevLane.getParentLink();
460 if (!laneSets.containsKey(prevLink))
461 {
462 laneSets.put(prevLink, new HashSet<>());
463 recordSets.put(prevLink, new TreeMap<>());
464 minStart.put(prevLink, new Length(Double.MAX_VALUE, LengthUnit.SI));
465 }
466 laneSets.get(prevLink).add(prevLane);
467 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
468 Length start = laneRecord.getStartDistance().minus(prevLane.getLength());
469 minStart.put(prevLink, Length.min(minStart.get(prevLink), start));
470 LaneStructureRecord prevRecord = constructRecord(prevLane,
471 laneRecord.getLane().upstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).get(prevLane), start,
472 relativeLane);
473 if (start.le(up))
474 {
475 prevRecord.setCutOffStart(up.minus(start));
476 }
477 recordSets.get(prevLink).put(relativeLane, prevRecord);
478 laneRecord.addPrev(prevRecord);
479 prevRecord.addNext(laneRecord);
480 }
481 }
482 else
483 {
484 for (Lane prevLane : laneRecord.getLane().upstreamLanes(GTUDirectionality.DIR_PLUS, gtuType).keySet())
485 {
486 this.ignoreSet.add(prevLane);
487 }
488 }
489 }
490
491 for (Link link : laneSets.keySet())
492 {
493 connectLaterally(recordSets.get(link), gtuType);
494 Set<LaneStructureRecord> set = new HashSet<>(recordSets.get(link).values());
495
496 buildUpstreamRecursive(set, gtuType, down, up, upMerge);
497 }
498 }
499
500
501
502
503
504
505
506
507
508 private LaneStructureRecord constructRecord(final Lane lane, final GTUDirectionality direction, final Length startDistance,
509 final RelativeLane relativeLane)
510 {
511
512 LaneStructureRecord record = new LaneStructureRecord(lane, direction, startDistance);
513 this.laneStructure.addLaneStructureRecord(record, relativeLane);
514 this.relativeLaneMap.put(record, relativeLane);
515 return record;
516 }
517
518
519
520
521
522
523 private void connectLaterally(final Map<RelativeLane, LaneStructureRecord> map, final GTUType gtuType)
524 {
525 for (RelativeLane relativeLane : map.keySet())
526 {
527 if (map.containsKey(relativeLane.getRight()))
528 {
529 Lane thisLane = map.get(relativeLane).getLane();
530 Lane rightLane = map.get(relativeLane.getRight()).getLane();
531 if (thisLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).contains(rightLane))
532 {
533 map.get(relativeLane).setRight(map.get(relativeLane.getRight()));
534 }
535 if (rightLane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType).contains(thisLane))
536 {
537 map.get(relativeLane.getRight()).setLeft(map.get(relativeLane));
538 }
539 }
540 }
541 }
542
543 }