1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.io.Serializable;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.SortedSet;
10 import java.util.TreeMap;
11 import java.util.TreeSet;
12
13 import org.djunits.unit.LengthUnit;
14 import org.djunits.value.vdouble.scalar.Length;
15 import org.opentrafficsim.core.gtu.GTU;
16 import org.opentrafficsim.core.gtu.GTUException;
17 import org.opentrafficsim.core.gtu.GTUType;
18 import org.opentrafficsim.core.gtu.RelativePosition;
19 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
20 import org.opentrafficsim.core.network.LateralDirectionality;
21 import org.opentrafficsim.core.network.NetworkException;
22 import org.opentrafficsim.core.network.route.Route;
23 import org.opentrafficsim.road.gtu.lane.Break;
24 import org.opentrafficsim.road.network.lane.CrossSectionLink;
25 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
26
27 import nl.tudelft.simulation.language.Throw;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public class LaneStructure implements Serializable
95 {
96
97 private static final long serialVersionUID = 20160400L;
98
99
100 private final LaneStructureRecord rootLSR;
101
102
103 private Length lookAhead;
104
105
106 private TreeMap<RelativeLane, LaneStructureRecord> crossSectionRecords = new TreeMap<>();
107
108
109 private final Map<RelativeLane, Set<LaneStructureRecord>> relativeLaneMap = new HashMap<>();
110
111
112
113
114
115 public LaneStructure(final LaneStructureRecord rootLSR, final Length lookAhead)
116 {
117 this.rootLSR = rootLSR;
118 this.lookAhead = lookAhead;
119 }
120
121
122
123
124 public final LaneStructureRecord getRootLSR()
125 {
126 return this.rootLSR;
127 }
128
129
130
131
132
133 public final SortedSet<RelativeLane> getCrossSection()
134 {
135 return this.crossSectionRecords.navigableKeySet();
136 }
137
138
139
140
141
142
143 public final LaneStructureRecord getLaneLSR(final RelativeLane lane) throws GTUException
144 {
145 Throw.when(!this.crossSectionRecords.containsKey(lane), GTUException.class,
146 "The requested lane %s is not in the most recent cross section.", lane);
147 return this.crossSectionRecords.get(lane);
148 }
149
150
151
152
153
154 public final void removeInvalidMappings(final Map<RelativeLane, ?> map)
155 {
156 Iterator<RelativeLane> iterator = map.keySet().iterator();
157 while (iterator.hasNext())
158 {
159 RelativeLane lane = iterator.next();
160 if (!this.crossSectionRecords.containsKey(lane))
161 {
162 iterator.remove();
163 }
164 }
165 }
166
167
168
169
170
171
172
173
174 public final void addLaneStructureRecord(final LaneStructureRecord lsr, final RelativeLane relativeLane)
175 {
176 if (!this.relativeLaneMap.containsKey(relativeLane))
177 {
178 this.relativeLaneMap.put(relativeLane, new HashSet<>());
179 }
180 this.relativeLaneMap.get(relativeLane).add(lsr);
181 if (lsr.getStartDistance().le0() && lsr.getStartDistance().plus(lsr.getLane().getLength()).gt0())
182 {
183 this.crossSectionRecords.put(relativeLane, lsr);
184 }
185 if (lsr.getStartDistance().gt0() && (!this.crossSectionRecords.containsKey(relativeLane)
186 || this.crossSectionRecords.get(relativeLane).getStartDistance().gt(lsr.getStartDistance())))
187 {
188 this.crossSectionRecords.put(relativeLane, lsr);
189 }
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203 @SuppressWarnings("unchecked")
204 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjects(final RelativeLane lane,
205 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
206 {
207 LaneStructureRecord record = null;
208 if (this.crossSectionRecords.containsKey(lane))
209 {
210 record = this.getLaneLSR(lane);
211 }
212 else
213 {
214 Length minLength = new Length(Double.MAX_VALUE, LengthUnit.SI);
215
216 for (LaneStructureRecord rec : this.relativeLaneMap.get(lane))
217 {
218 if (rec.getStartDistance().ge0() && rec.getStartDistance().lt(minLength))
219 {
220 record = rec;
221 minLength = rec.getStartDistance();
222 }
223 }
224 }
225 Throw.when(record == null, GTUException.class, "Trying to get objects on %s, but that lane is not in the structure.",
226 lane);
227 Length ds = gtu.getRelativePositions().get(pos).getDx().minus(gtu.getReference().getDx());
228
229 Length minimumPosition;
230 Length maximumPosition;
231 if (record.getDirection().isPlus())
232 {
233 minimumPosition = ds.minus(record.getStartDistance());
234 maximumPosition = record.getLane().getLength();
235 }
236 else
237 {
238 minimumPosition = Length.ZERO;
239 maximumPosition = record.getLane().getLength().plus(record.getStartDistance()).minus(ds);
240 }
241 SortedSet<Entry<T>> set = new TreeSet<>();
242 Length distance;
243 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
244 {
245 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
246 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead)
247 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
248 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
249 {
250
251 set.add(new Entry<>(distance, (T) object));
252 }
253 }
254 getDownstreamObjectsRecursive(set, record, clazz, ds);
255 return set;
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269
270 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getDownstreamObjectsOnRoute(final RelativeLane lane,
271 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos, final Route route) throws GTUException
272 {
273 SortedSet<Entry<T>> set = getDownstreamObjects(lane, clazz, gtu, pos);
274 if (route != null)
275 {
276 Iterator<Entry<T>> iterator = set.iterator();
277 while (iterator.hasNext())
278 {
279 Entry<T> entry = iterator.next();
280 CrossSectionLink link = entry.getLaneBasedObject().getLane().getParentLink();
281 if (!route.contains(link.getStartNode()) || !route.contains(link.getEndNode())
282 || Math.abs(route.indexOf(link.getStartNode()) - route.indexOf(link.getEndNode())) != 1)
283 {
284 iterator.remove();
285 }
286 }
287 }
288 return set;
289 }
290
291
292
293
294
295
296
297
298
299 @SuppressWarnings("unchecked")
300 private <T extends LaneBasedObject> void getDownstreamObjectsRecursive(final SortedSet<Entry<T>> set,
301 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
302 {
303 if (record.getNext().isEmpty() || record.getNext().get(0).getStartDistance().gt(this.lookAhead))
304 {
305 return;
306 }
307 for (LaneStructureRecord next : record.getNext())
308 {
309 Length distance;
310 for (LaneBasedObject object : next.getLane().getLaneBasedObjects())
311 {
312 distance = next.getDistanceToPosition(object.getLongitudinalPosition()).minus(ds);
313 if (clazz.isAssignableFrom(object.getClass()) && distance.le(this.lookAhead)
314 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
315 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
316 {
317
318 set.add(new Entry<>(distance, (T) object));
319 }
320 }
321 getDownstreamObjectsRecursive(set, next, clazz, ds);
322 }
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336 public <T extends LaneBasedObject> Map<RelativeLane, SortedSet<Entry<T>>> getDownstreamObjectsOnRoute(final Class<T> clazz,
337 final GTU gtu, final RelativePosition.TYPE pos, final Route route) throws GTUException
338 {
339 Map<RelativeLane, SortedSet<Entry<T>>> out = new HashMap<>();
340 for (RelativeLane relativeLane : this.relativeLaneMap.keySet())
341 {
342 out.put(relativeLane, getDownstreamObjectsOnRoute(relativeLane, clazz, gtu, pos, route));
343 }
344 return out;
345 }
346
347
348
349
350
351
352
353
354
355
356
357
358 @SuppressWarnings("unchecked")
359 public final <T extends LaneBasedObject> SortedSet<Entry<T>> getUpstreamObjects(final RelativeLane lane,
360 final Class<T> clazz, final GTU gtu, final RelativePosition.TYPE pos) throws GTUException
361 {
362 LaneStructureRecord record = this.getLaneLSR(lane);
363 Length ds = gtu.getReference().getDx().minus(gtu.getRelativePositions().get(pos).getDx());
364
365 Length minimumPosition;
366 Length maximumPosition;
367 if (record.getDirection().isPlus())
368 {
369 minimumPosition = Length.ZERO;
370 maximumPosition = record.getStartDistance().neg().minus(ds);
371 }
372 else
373 {
374 minimumPosition = record.getLane().getLength().plus(record.getStartDistance()).plus(ds);
375 maximumPosition = record.getLane().getLength();
376 }
377 SortedSet<Entry<T>> set = new TreeSet<>();
378 Length distance;
379 for (LaneBasedObject object : record.getLane().getLaneBasedObjects(minimumPosition, maximumPosition))
380 {
381 if (clazz.isAssignableFrom(object.getClass())
382 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
383 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
384 {
385 distance = record.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
386
387 set.add(new Entry<>(distance, (T) object));
388 }
389 }
390 getUpstreamObjectsRecursive(set, record, clazz, ds);
391 return set;
392 }
393
394
395
396
397
398
399
400
401
402 @SuppressWarnings("unchecked")
403 private <T extends LaneBasedObject> void getUpstreamObjectsRecursive(final SortedSet<Entry<T>> set,
404 final LaneStructureRecord record, final Class<T> clazz, final Length ds)
405 {
406 for (LaneStructureRecord prev : record.getPrev())
407 {
408 Length distance;
409 for (LaneBasedObject object : prev.getLane().getLaneBasedObjects())
410 {
411 if (clazz.isAssignableFrom(object.getClass())
412 && ((record.getDirection().isPlus() && object.getDirection().isForwardOrBoth())
413 || (record.getDirection().isMinus() && object.getDirection().isBackwardOrBoth())))
414 {
415 distance = prev.getDistanceToPosition(object.getLongitudinalPosition()).neg().minus(ds);
416
417 set.add(new Entry<>(distance, (T) object));
418 }
419 }
420 getUpstreamObjectsRecursive(set, prev, clazz, ds);
421 }
422 }
423
424
425
426
427
428
429
430
431
432
433
434 public boolean canChange(final LateralDirectionality lat, final LanePerception perception) throws OperationalPlanException
435 {
436
437
438 if ((lat.isLeft() && this.rootLSR.getLeft() == null) || (lat.isRight() && this.rootLSR.getRight() == null))
439 {
440 return false;
441 }
442
443
444 try
445 {
446 Length nose = perception.getGtu().getFront().getDx();
447
448 if (!this.rootLSR.getNext().isEmpty() && this.rootLSR.getNext().get(0).getStartDistance().lt(nose))
449 {
450 Route route = perception.getGtu().getStrategicalPlanner().getRoute();
451 GTUType gtuType = perception.getGtu().getGTUType();
452 boolean noseOk = false;
453 for (LaneStructureRecord next : this.rootLSR.getNext())
454 {
455 if ((lat.isLeft() && next.getLeft() != null && next.getLeft().allowsRoute(route, gtuType))
456 || (lat.isRight() && next.getRight() != null && next.getRight().allowsRoute(route, gtuType)))
457 {
458 noseOk = true;
459 }
460 }
461 if (!noseOk)
462 {
463 return false;
464 }
465 }
466 }
467 catch (GTUException | NetworkException exception)
468 {
469 throw new OperationalPlanException("Could not obtain GTU or lane to check nose for lane change.", exception);
470 }
471
472
473 try
474 {
475 Length tail = perception.getGtu().getRear().getDx();
476
477 if (!this.rootLSR.getPrev().isEmpty() && this.rootLSR.getStartDistance().gt(tail))
478 {
479 Route route = perception.getGtu().getStrategicalPlanner().getRoute();
480 GTUType gtuType = perception.getGtu().getGTUType();
481 boolean tailOk = false;
482 for (LaneStructureRecord prev : this.rootLSR.getPrev())
483 {
484 if ((lat.isLeft() && prev.getLeft() != null && prev.getLeft().allowsRoute(route, gtuType))
485 || (lat.isRight() && prev.getRight() != null && prev.getRight().allowsRoute(route, gtuType)))
486 {
487 tailOk = true;
488 }
489 }
490 if (!tailOk)
491 {
492 return false;
493 }
494 }
495 }
496 catch (GTUException | NetworkException exception)
497 {
498 throw new OperationalPlanException("Could not obtain GTU or lane to check nose for lane change.", exception);
499 }
500
501 return true;
502 }
503
504
505 @Override
506 public final String toString()
507 {
508 return "LaneStructure [rootLSR=" + this.rootLSR + "]";
509 }
510
511
512
513
514
515
516
517
518
519
520
521
522
523 public static class Entry<T extends LaneBasedObject> implements Comparable<Entry<T>>
524 {
525
526
527 private final Length distance;
528
529
530 private final T laneBasedObject;
531
532
533
534
535
536 public Entry(final Length distance, final T laneBasedObject)
537 {
538 this.distance = distance;
539 this.laneBasedObject = laneBasedObject;
540 }
541
542
543
544
545 public final Length getDistance()
546 {
547 return this.distance;
548 }
549
550
551
552
553 public final T getLaneBasedObject()
554 {
555 return this.laneBasedObject;
556 }
557
558
559 @Override
560 public final int hashCode()
561 {
562 final int prime = 31;
563 int result = 1;
564 result = prime * result + ((this.distance == null) ? 0 : this.distance.hashCode());
565 result = prime * result + ((this.laneBasedObject == null) ? 0 : this.laneBasedObject.hashCode());
566 return result;
567 }
568
569
570 @Override
571 public final boolean equals(final Object obj)
572 {
573 if (this == obj)
574 {
575 return true;
576 }
577 if (obj == null)
578 {
579 return false;
580 }
581 if (getClass() != obj.getClass())
582 {
583 return false;
584 }
585 Entry<?> other = (Entry<?>) obj;
586 if (this.distance == null)
587 {
588 if (other.distance != null)
589 {
590 return false;
591 }
592 }
593 else if (!this.distance.equals(other.distance))
594 {
595 return false;
596 }
597 if (this.laneBasedObject == null)
598 {
599 if (other.laneBasedObject != null)
600 {
601 return false;
602 }
603 }
604
605 else if (!this.laneBasedObject.equals(other.laneBasedObject))
606 {
607 return false;
608 }
609 return true;
610 }
611
612
613 @Override
614 public final int compareTo(final Entry<T> arg)
615 {
616 int d = this.distance.compareTo(arg.distance);
617 if (d != 0 || this.laneBasedObject.equals(arg.laneBasedObject))
618 {
619 return d;
620 }
621 return 1;
622 }
623
624
625 @Override
626 public final String toString()
627 {
628 return "LaneStructure.Entry [distance=" + this.distance + ", laneBasedObject=" + this.laneBasedObject + "]";
629 }
630
631 }
632
633 }