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 recordSet.add(adjacentRecord);
198 laneSet.add(lane);
199 current = adjacentRecord;
200 adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
201 }
202 }
203 try
204 {
205 this.ignoreSet.clear();
206 buildDownstreamRecursive(recordSet, gtuType, down, up, downSplit, upMerge);
207 this.ignoreSet.clear();
208 buildUpstreamRecursive(recordSet, gtuType, down, up, upMerge);
209 }
210 catch (GTUException | NetworkException exception)
211 {
212 throw new RuntimeException("Exception while building lane map.", exception);
213 }
214 }
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 private void buildDownstreamRecursive(final Set<LaneStructureRecord> recordSet, final GTUType gtuType, final Length down,
243 final Length up, final Length downSplit, final Length upMerge) throws GTUException, NetworkException
244 {
245
246 Map<Link, Set<Lane>> laneSets = new HashMap<>();
247 Map<Link, TreeMap<RelativeLane, LaneStructureRecord>> recordSets = new HashMap<>();
248 Map<Link, Length> maxStart = new HashMap<>();
249 for (LaneStructureRecord laneRecord : recordSet)
250 {
251 if (!laneRecord.isCutOffEnd())
252 {
253 for (Lane nextLane : laneRecord.getLane().nextLanes(gtuType).keySet())
254 {
255 Link nextLink = nextLane.getParentLink();
256 if (!laneSets.containsKey(nextLink))
257 {
258 laneSets.put(nextLink, new HashSet<>());
259 recordSets.put(nextLink, new TreeMap<>());
260 maxStart.put(nextLink, new Length(Double.MIN_VALUE, LengthUnit.SI));
261 }
262 laneSets.get(nextLink).add(nextLane);
263 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
264 Length start = laneRecord.getStartDistance().plus(laneRecord.getLane().getLength());
265 maxStart.put(nextLink, Length.max(maxStart.get(nextLink), start));
266 LaneStructureRecord nextRecord = constructRecord(nextLane,
267 laneRecord.getLane().nextLanes(gtuType).get(nextLane), start, relativeLane);
268 if (start.plus(nextLane.getLength()).ge(down))
269 {
270 nextRecord.setCutOffEnd(down.minus(start));
271 }
272 recordSets.get(nextLink).put(relativeLane, nextRecord);
273 laneRecord.addNext(nextRecord);
274 nextRecord.addPrev(laneRecord);
275 }
276 }
277 else
278 {
279 for (Lane nextLane : laneRecord.getLane().nextLanes(gtuType).keySet())
280 {
281 this.ignoreSet.add(nextLane);
282 }
283 }
284 }
285
286 Link currentLink = recordSet.iterator().next().getLane().getParentLink();
287 GTUDirectionality direction = recordSet.iterator().next().getDirection();
288 for (Link link : laneSets.keySet())
289 {
290 connectLaterally(recordSets.get(link), gtuType);
291 Set<LaneStructureRecord> set = new HashSet<>(recordSets.get(link).values());
292 set = extendLateral(set, gtuType, down, up, upMerge, true);
293
294 Node nextNode = direction.isPlus() ? currentLink.getEndNode() : currentLink.getStartNode();
295 Length downLimit = down;
296 Route route = getGtu().getStrategicalPlanner().getRoute();
297 if (route != null && (!route.contains(nextNode)
298 || !((LaneBasedStrategicalRoutePlanner) getGtu().getStrategicalPlanner())
299 .nextLinkDirection(nextNode, currentLink, gtuType).equals(link)))
300 {
301
302 downLimit = Length.min(downLimit, maxStart.get(link).plus(downSplit));
303 }
304 buildDownstreamRecursive(set, gtuType, downLimit, up, downSplit, upMerge);
305 }
306 }
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336 private Set<LaneStructureRecord> extendLateral(final Set<LaneStructureRecord> recordSet, final GTUType gtuType,
337 final Length down, final Length up, final Length upMerge, final boolean downstreamBuild)
338 throws GTUException, NetworkException
339 {
340 Set<Lane> laneSet = new HashSet<>();
341 for (LaneStructureRecord laneStructureRecord : recordSet)
342 {
343 laneSet.add(laneStructureRecord.getLane());
344 }
345 for (LateralDirectionality latDirection : new LateralDirectionality[] { LateralDirectionality.LEFT,
346 LateralDirectionality.RIGHT })
347 {
348 Set<LaneStructureRecord> expandSet = new HashSet<>();
349 Length startDistance = null;
350 Length endDistance = null;
351 for (LaneStructureRecord laneRecord : recordSet)
352 {
353 LaneStructureRecord current = laneRecord;
354 startDistance = current.getStartDistance();
355 endDistance = current.getStartDistance().plus(current.getLane().getLength());
356 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
357 Set<Lane> adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
358 while (!adjacentLanes.isEmpty())
359 {
360 Throw.when(adjacentLanes.size() > 1, RuntimeException.class,
361 "Multiple adjacent lanes encountered during construction of lane map.");
362 Lane laneAdjacent = adjacentLanes.iterator().next();
363 Length adjacentStart = downstreamBuild ? startDistance : endDistance.minus(laneAdjacent.getLength());
364
365 if (!laneSet.contains(laneAdjacent) && !adjacentStart.plus(laneAdjacent.getLength()).le(up)
366 && !adjacentStart.ge(down) && !this.ignoreSet.contains(laneAdjacent))
367 {
368 laneSet.add(laneAdjacent);
369 relativeLane = latDirection.isLeft() ? relativeLane.getLeft() : relativeLane.getRight();
370 LaneStructureRecord recordAdjacent =
371 constructRecord(laneAdjacent, laneRecord.getDirection(), adjacentStart, relativeLane);
372 expandSet.add(recordAdjacent);
373 if (latDirection.isLeft())
374 {
375 if (laneAdjacent.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType)
376 .contains(current.getLane()))
377 {
378 recordAdjacent.setRight(current);
379 }
380 current.setLeft(recordAdjacent);
381 }
382 else
383 {
384 if (laneAdjacent.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType)
385 .contains(current.getLane()))
386 {
387 recordAdjacent.setLeft(current);
388 }
389 current.setRight(recordAdjacent);
390 }
391 if (adjacentStart.plus(laneAdjacent.getLength()).ge(down))
392 {
393 recordAdjacent.setCutOffEnd(down.minus(adjacentStart));
394 }
395 if (adjacentStart.le(up))
396 {
397 recordAdjacent.setCutOffStart(up.minus(adjacentStart));
398 }
399 current = recordAdjacent;
400 adjacentLanes = current.getLane().accessibleAdjacentLanes(latDirection, gtuType);
401 }
402 else
403 {
404 break;
405 }
406 }
407 }
408 if (downstreamBuild & !expandSet.isEmpty())
409 {
410
411 buildUpstreamRecursive(expandSet, gtuType, down, startDistance.plus(upMerge), upMerge);
412 }
413 recordSet.addAll(expandSet);
414 }
415 return recordSet;
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429 private void buildUpstreamRecursive(final Set<LaneStructureRecord> recordSet, final GTUType gtuType, final Length down,
430 final Length up, final Length upMerge) throws GTUException, NetworkException
431 {
432
433 Map<Link, Set<Lane>> laneSets = new HashMap<>();
434 Map<Link, TreeMap<RelativeLane, LaneStructureRecord>> recordSets = new HashMap<>();
435 Map<Link, Length> minStart = new HashMap<>();
436 for (LaneStructureRecord laneRecord : recordSet)
437 {
438 if (!laneRecord.isCutOffStart())
439 {
440 for (Lane prevLane : laneRecord.getLane().prevLanes(gtuType).keySet())
441 {
442 Link prevLink = prevLane.getParentLink();
443 if (!laneSets.containsKey(prevLink))
444 {
445 laneSets.put(prevLink, new HashSet<>());
446 recordSets.put(prevLink, new TreeMap<>());
447 minStart.put(prevLink, new Length(Double.MAX_VALUE, LengthUnit.SI));
448 }
449 laneSets.get(prevLink).add(prevLane);
450 RelativeLane relativeLane = this.relativeLaneMap.get(laneRecord);
451 Length start = laneRecord.getStartDistance().minus(prevLane.getLength());
452 minStart.put(prevLink, Length.min(minStart.get(prevLink), start));
453 LaneStructureRecord prevRecord = constructRecord(prevLane,
454 laneRecord.getLane().prevLanes(gtuType).get(prevLane), start, relativeLane);
455 if (start.le(up))
456 {
457 prevRecord.setCutOffStart(up.minus(start));
458 }
459 recordSets.get(prevLink).put(relativeLane, prevRecord);
460 laneRecord.addPrev(prevRecord);
461 prevRecord.addNext(laneRecord);
462 }
463 }
464 else
465 {
466 for (Lane prevLane : laneRecord.getLane().prevLanes(gtuType).keySet())
467 {
468 this.ignoreSet.add(prevLane);
469 }
470 }
471 }
472
473 for (Link link : laneSets.keySet())
474 {
475 connectLaterally(recordSets.get(link), gtuType);
476 Set<LaneStructureRecord> set = new HashSet<>(recordSets.get(link).values());
477 set = extendLateral(set, gtuType, down, up, upMerge, false);
478 buildUpstreamRecursive(set, gtuType, down, up, upMerge);
479 }
480 }
481
482
483
484
485
486
487
488
489
490 private LaneStructureRecord constructRecord(final Lane lane, final GTUDirectionality direction, final Length startDistance,
491 final RelativeLane relativeLane)
492 {
493 LaneStructureRecord record = new LaneStructureRecord(lane, direction, startDistance);
494 this.laneStructure.addLaneStructureRecord(record, relativeLane);
495 this.relativeLaneMap.put(record, relativeLane);
496 return record;
497 }
498
499
500
501
502
503
504 private void connectLaterally(final Map<RelativeLane, LaneStructureRecord> map, final GTUType gtuType)
505 {
506 for (RelativeLane relativeLane : map.keySet())
507 {
508 if (map.containsKey(relativeLane.getRight()))
509 {
510 Lane thisLane = map.get(relativeLane).getLane();
511 Lane rightLane = map.get(relativeLane.getRight()).getLane();
512 if (thisLane.accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).contains(rightLane))
513 {
514 map.get(relativeLane).setRight(map.get(relativeLane.getRight()));
515 }
516 if (rightLane.accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType).contains(thisLane))
517 {
518 map.get(relativeLane.getRight()).setLeft(map.get(relativeLane));
519 }
520 }
521 }
522 }
523
524 }