1 package org.opentrafficsim.road.network.lane.conflict;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Iterator;
6 import java.util.LinkedHashMap;
7 import java.util.LinkedHashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.NoSuchElementException;
11 import java.util.Set;
12 import java.util.UUID;
13
14 import org.djunits.value.vdouble.scalar.Duration;
15 import org.djunits.value.vdouble.scalar.Length;
16 import org.djutils.draw.line.Polygon2d;
17 import org.djutils.event.Event;
18 import org.djutils.event.EventListener;
19 import org.djutils.exceptions.Throw;
20 import org.djutils.exceptions.Try;
21 import org.opentrafficsim.base.OtsRuntimeException;
22 import org.opentrafficsim.base.parameters.ParameterException;
23 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
24 import org.opentrafficsim.core.gtu.GtuException;
25 import org.opentrafficsim.core.gtu.RelativePosition;
26 import org.opentrafficsim.core.network.NetworkException;
27 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
28 import org.opentrafficsim.road.gtu.lane.perception.AbstractPerceptionReiterable;
29 import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
30 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.PerceivedGtuType;
31 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtu;
32 import org.opentrafficsim.road.gtu.lane.perception.structure.LaneRecordInterface;
33 import org.opentrafficsim.road.gtu.lane.perception.structure.NavigatingIterable;
34 import org.opentrafficsim.road.gtu.lane.perception.structure.NavigatingIterable.Entry;
35 import org.opentrafficsim.road.gtu.lane.perception.structure.SimpleLaneRecord;
36 import org.opentrafficsim.road.network.lane.Lane;
37 import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
38 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
39 import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public final class Conflict extends AbstractLaneBasedObject implements EventListener
59 {
60
61
62 private final ConflictType conflictType;
63
64
65 private final ConflictRule conflictRule;
66
67
68 private final ConflictEnd end;
69
70
71 private Conflict otherConflict;
72
73
74 private final Length length;
75
76
77 private final boolean permitted;
78
79
80 private Length trafficLightDistance;
81
82
83 private Length maxMaxTrafficLightDistance;
84
85
86
87
88
89
90 private final SimpleLaneRecord root;
91
92
93 private final Length rootPosition;
94
95
96 private Iterable<Entry<LaneBasedGtu>> upstreamGtus;
97
98
99 private Duration upstreamTime;
100
101
102 private Map<LaneBasedGtu, Lane> upstreamLanes = new LinkedHashMap<>();
103
104
105 private Iterable<Entry<LaneBasedGtu>> downstreamGtus;
106
107
108 private Duration downstreamTime;
109
110
111 private Map<LaneBasedGtu, Lane> downstreamLanes = new LinkedHashMap<>();
112
113
114 private Length maxUpstreamVisibility = Length.ZERO;
115
116
117 private Length maxDownstreamVisibility = Length.ZERO;
118
119
120 private Set<LaneBasedGtu> upstreamListening = new LinkedHashSet<>();
121
122
123 private Set<LaneBasedGtu> downstreamListening = new LinkedHashSet<>();
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 @SuppressWarnings("checkstyle:parameternumber")
139 private Conflict(final Lane lane, final Length longitudinalPosition, final Length length, final Polygon2d contour,
140 final ConflictType conflictType, final ConflictRule conflictRule, final boolean permitted) throws NetworkException
141 {
142 super(UUID.randomUUID().toString(), lane, longitudinalPosition, LaneBasedObject.makeLine(lane, longitudinalPosition),
143 contour);
144 this.length = length;
145 this.conflictType = conflictType;
146 this.conflictRule = conflictRule;
147 this.permitted = permitted;
148
149
150 if (conflictType.equals(ConflictType.SPLIT) || conflictType.equals(ConflictType.MERGE))
151 {
152 Length position = conflictType.equals(ConflictType.SPLIT) ? length : lane.getLength();
153 this.end = new ConflictEnd(this, lane, position);
154 }
155 else
156 {
157 this.end = null;
158 }
159
160
161 this.rootPosition = longitudinalPosition;
162 this.root = new SimpleLaneRecord(lane, this.rootPosition.neg(), null);
163 }
164
165 @Override
166 protected void init() throws NetworkException
167 {
168 super.init();
169 if (this.end != null)
170 {
171 this.end.init();
172 }
173 }
174
175
176
177
178
179 private void provideUpstreamVisibility(final Length visibility)
180 {
181 if (visibility.gt(this.maxUpstreamVisibility))
182 {
183 this.maxUpstreamVisibility = visibility;
184 this.upstreamTime = null;
185 clearUpstreamListening();
186 }
187 }
188
189
190
191
192
193 private void provideDownstreamVisibility(final Length visibility)
194 {
195 if (visibility.gt(this.maxDownstreamVisibility))
196 {
197 this.maxDownstreamVisibility = visibility;
198 this.downstreamTime = null;
199 clearDownstreamListening();
200 }
201 }
202
203
204
205
206 private void clearUpstreamListening()
207 {
208 for (LaneBasedGtu gtu : this.upstreamListening)
209 {
210 if (!this.downstreamListening.contains(gtu))
211 {
212 gtu.removeListener(this, LaneBasedGtu.LANE_CHANGE_EVENT);
213 }
214 }
215 this.upstreamListening.clear();
216 }
217
218
219
220
221 private void clearDownstreamListening()
222 {
223 for (LaneBasedGtu gtu : this.downstreamListening)
224 {
225 if (!this.upstreamListening.contains(gtu))
226 {
227 gtu.removeListener(this, LaneBasedGtu.LANE_CHANGE_EVENT);
228 }
229 }
230 this.downstreamListening.clear();
231 }
232
233
234
235
236
237
238
239
240 public PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getUpstreamGtus(final LaneBasedGtu perceivingGtu,
241 final PerceivedGtuType perceivedGtuType, final Length visibility)
242 {
243 provideUpstreamVisibility(visibility);
244 Duration time = this.getLane().getLink().getSimulator().getSimulatorTime();
245 if (this.upstreamTime == null || !time.eq(this.upstreamTime))
246 {
247 clearUpstreamListening();
248
249 this.upstreamLanes.clear();
250 this.upstreamGtus = new NavigatingIterable<LaneBasedGtu, SimpleLaneRecord>(LaneBasedGtu.class,
251 this.maxUpstreamVisibility, Set.of(this.root), (l) -> l.getPrev(), (l) ->
252 {
253
254 List<LaneBasedGtu> gtus = l.getLane().getGtuList().toList();
255 if (gtus.isEmpty())
256 {
257 return gtus;
258 }
259 int from = 0;
260 while (from < gtus.size() && position(gtus.get(from), l, RelativePosition.REFERENCE).lt0())
261 {
262 from++;
263 }
264 int to = gtus.size() - 1;
265 Length pos = Length.min(l.getStartDistance().neg(), l.getLength());
266 while (to >= 0 && position(gtus.get(to), l, RelativePosition.FRONT).gt(pos))
267 {
268 to--;
269 }
270 if (from > to)
271 {
272 return Collections.emptyList();
273 }
274 if (from > 0 || to < gtus.size() - 1)
275 {
276 gtus = gtus.subList(from, to + 1);
277 }
278 Collections.reverse(gtus);
279 gtus.forEach((g) ->
280 {
281 this.upstreamLanes.put(g, l.getLane());
282 g.addListener(this, LaneBasedGtu.LANE_CHANGE_EVENT);
283 this.upstreamListening.add(g);
284 });
285 return gtus;
286 }, (t, r) -> r.getStartDistance().neg().minus(position(t, r, RelativePosition.FRONT)));
287 this.upstreamTime = time;
288 }
289
290 return new ConflictGtuIterable(perceivingGtu, perceivedGtuType, visibility, false, new Reiterable(this.upstreamGtus));
291
292 }
293
294
295
296
297
298
299
300
301 public PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getDownstreamGtus(final LaneBasedGtu perceivingGtu,
302 final PerceivedGtuType perceivedGtuType, final Length visibility)
303 {
304 provideDownstreamVisibility(visibility);
305 Duration time = this.getLane().getLink().getSimulator().getSimulatorTime();
306 if (this.downstreamTime == null || !time.eq(this.downstreamTime))
307 {
308 clearDownstreamListening();
309
310 this.downstreamLanes.clear();
311 this.downstreamGtus = new Reiterable(new NavigatingIterable<LaneBasedGtu, SimpleLaneRecord>(LaneBasedGtu.class,
312 this.maxDownstreamVisibility, Set.of(this.root), (l) -> l.getNext(), (l) ->
313 {
314
315 List<LaneBasedGtu> gtus = l.getLane().getGtuList().toList();
316 if (gtus.isEmpty())
317 {
318 return gtus;
319 }
320 int from = 0;
321 Length pos = Length.max(l.getStartDistance().neg(), Length.ZERO);
322 while (from < gtus.size() && position(gtus.get(from), l, RelativePosition.FRONT).lt(pos))
323 {
324 from++;
325 }
326 int to = gtus.size() - 1;
327 while (to >= 0 && position(gtus.get(to), l, RelativePosition.REFERENCE).gt(l.getLength()))
328 {
329 to--;
330 }
331 if (from > to)
332 {
333 return Collections.emptyList();
334 }
335 if (from > 0 || to < gtus.size() - 1)
336 {
337 gtus = gtus.subList(from, to + 1);
338 }
339 gtus.forEach((g) ->
340 {
341 this.downstreamLanes.put(g, l.getLane());
342 g.addListener(this, LaneBasedGtu.LANE_CHANGE_EVENT);
343 this.downstreamListening.add(g);
344 });
345 return gtus;
346 }, (t, r) -> r.getStartDistance().plus(position(t, r, RelativePosition.REAR))));
347 this.downstreamTime = time;
348 }
349
350 return new ConflictGtuIterable(perceivingGtu, perceivedGtuType, visibility, true, this.downstreamGtus);
351
352 }
353
354
355
356
357
358
359
360
361 private Length position(final LaneBasedGtu gtu, final LaneRecordInterface<?> record,
362 final RelativePosition.Type positionType)
363 {
364 return Try.assign(() -> gtu.getPosition(record.getLane(), gtu.getRelativePositions().get(positionType)),
365 "Unable to obtain position %s of GTU.", positionType);
366 }
367
368 @Override
369 public void notify(final Event event)
370 {
371 Object[] payload = (Object[]) event.getContent();
372 LaneBasedGtu gtu = (LaneBasedGtu) getLane().getNetwork().getGTU((String) payload[0])
373 .orElseThrow(() -> new OtsRuntimeException("Lane-change event on GTU not in the network."));
374 if (this.upstreamListening.contains(gtu))
375 {
376 this.upstreamTime = null;
377 clearUpstreamListening();
378 }
379 if (this.downstreamListening.contains(gtu))
380 {
381 this.downstreamTime = null;
382 clearDownstreamListening();
383 }
384 }
385
386
387
388
389
390 public ConflictType getConflictType()
391 {
392 return this.conflictType;
393 }
394
395
396
397
398
399 public ConflictRule getConflictRule()
400 {
401 return this.conflictRule;
402 }
403
404
405
406
407
408 public ConflictPriority conflictPriority()
409 {
410 return this.conflictRule.determinePriority(this);
411 }
412
413 @Override
414 public Length getLength()
415 {
416 return this.length;
417 }
418
419
420
421
422
423 public Conflict getOtherConflict()
424 {
425 return this.otherConflict;
426 }
427
428
429
430
431
432 public boolean isPermitted()
433 {
434 return this.permitted;
435 }
436
437
438
439
440
441
442 public Length getTrafficLightDistance(final Length maxDistance)
443 {
444 if (this.trafficLightDistance == null)
445 {
446 if (this.maxMaxTrafficLightDistance == null || this.maxMaxTrafficLightDistance.lt(maxDistance))
447 {
448 this.maxMaxTrafficLightDistance = maxDistance;
449 NavigatingIterable<TrafficLight, SimpleLaneRecord> iterable =
450 new NavigatingIterable<>(TrafficLight.class, maxDistance, Set.of(this.root), (l) ->
451 {
452
453 List<LaneBasedObject> list =
454 l.getLane().getLaneBasedObjects(Length.ZERO, l.getStartDistance().neg());
455 if (list.stream().anyMatch((o) -> o instanceof TrafficLight))
456 {
457 return Collections.emptySet();
458 }
459 return l.getPrev();
460 }, (l) ->
461 {
462
463 List<LaneBasedObject> list =
464 l.getLane().getLaneBasedObjects(Length.ZERO, l.getStartDistance().neg());
465 for (int index = list.size() - 1; index >= 0; index--)
466 {
467 if (list.get(index) instanceof TrafficLight)
468 {
469 return List.of(list.get(index));
470 }
471 }
472 return Collections.emptyList();
473 }, (t, l) -> l.getStartDistance().neg().minus(t.getLongitudinalPosition()));
474 Iterator<Entry<TrafficLight>> iterator = iterable.iterator();
475 if (iterator.hasNext())
476 {
477 this.trafficLightDistance = iterator.next().distance();
478 }
479 }
480 }
481 if (this.trafficLightDistance != null && maxDistance.ge(this.trafficLightDistance))
482 {
483 return this.trafficLightDistance;
484 }
485 return Length.POSITIVE_INFINITY;
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 @SuppressWarnings("checkstyle:parameternumber")
505 public static void generateConflictPair(final ConflictType conflictType, final ConflictRule conflictRule,
506 final boolean permitted, final Lane lane1, final Length longitudinalPosition1, final Length length1,
507 final Polygon2d geometry1, final Lane lane2, final Length longitudinalPosition2, final Length length2,
508 final Polygon2d geometry2, final OtsSimulatorInterface simulator) throws NetworkException
509 {
510
511 Throw.whenNull(conflictType, "Conflict type may not be null.");
512
513 Conflict conf1 = new Conflict(lane1, longitudinalPosition1, length1, geometry1, conflictType, conflictRule, permitted);
514 conf1.init();
515 Conflict conf2 = new Conflict(lane2, longitudinalPosition2, length2, geometry2, conflictType, conflictRule, permitted);
516 conf2.init();
517 conf1.otherConflict = conf2;
518 conf2.otherConflict = conf1;
519 }
520
521 @Override
522 public String toString()
523 {
524 return "Conflict [conflictType=" + this.conflictType + ", conflictRule=" + this.conflictRule + "]";
525 }
526
527
528
529
530
531 public class ConflictEnd extends AbstractLaneBasedObject
532 {
533
534 private final Conflict conflict;
535
536
537
538
539
540
541
542
543 ConflictEnd(final Conflict conflict, final Lane lane, final Length longitudinalPosition) throws NetworkException
544 {
545 super(conflict.getId() + "End", lane, longitudinalPosition, LaneBasedObject.makeLine(lane, longitudinalPosition));
546 this.conflict = conflict;
547 }
548
549 @Override
550 public void init() throws NetworkException
551 {
552
553 super.init();
554 }
555
556
557
558
559
560 public final Conflict getConflict()
561 {
562 return this.conflict;
563 }
564
565 @Override
566 public final String toString()
567 {
568 return "ConflictEnd [conflict=" + this.conflict + "]";
569 }
570 }
571
572
573
574
575 private class ConflictGtuIterable extends AbstractPerceptionReiterable<LaneBasedGtu, PerceivedGtu, LaneBasedGtu>
576 {
577
578 private final PerceivedGtuType perceptionGtuType;
579
580
581 private final Length visibility;
582
583
584 private final boolean downstream;
585
586
587 private final Iterator<Entry<LaneBasedGtu>> base;
588
589
590
591
592
593
594
595
596 ConflictGtuIterable(final LaneBasedGtu perceivingGtu, final PerceivedGtuType perceptionGtuType, final Length visibility,
597 final boolean downstream, final Iterable<Entry<LaneBasedGtu>> base)
598 {
599 super(perceivingGtu);
600 this.perceptionGtuType = perceptionGtuType;
601 this.visibility = visibility;
602 this.downstream = downstream;
603 this.base = base.iterator();
604 }
605
606 @Override
607 protected Iterator<UnderlyingDistance<LaneBasedGtu>> primaryIterator()
608 {
609
610
611
612 class ConflictGtuIterator implements Iterator<UnderlyingDistance<LaneBasedGtu>>
613 {
614
615 private UnderlyingDistance<LaneBasedGtu> next;
616
617 @Override
618 public boolean hasNext()
619 {
620 if (this.next == null)
621 {
622 if (ConflictGtuIterable.this.base.hasNext())
623 {
624 Entry<LaneBasedGtu> gtu = ConflictGtuIterable.this.base.next();
625 if (gtu.object().getId().equals(getObject().getId()))
626 {
627 if (ConflictGtuIterable.this.base.hasNext())
628 {
629 gtu = ConflictGtuIterable.this.base.next();
630 }
631 else
632 {
633 return false;
634 }
635 }
636 if (gtu.distance() == null || gtu.distance().le(ConflictGtuIterable.this.visibility))
637 {
638 this.next = new UnderlyingDistance<>(gtu.object(), gtu.distance());
639 }
640 }
641 }
642 return this.next != null;
643 }
644
645 @Override
646 public UnderlyingDistance<LaneBasedGtu> next()
647 {
648 if (hasNext())
649 {
650 UnderlyingDistance<LaneBasedGtu> out = this.next;
651 this.next = null;
652 return out;
653 }
654 throw new NoSuchElementException();
655 }
656 }
657 return new ConflictGtuIterator();
658 }
659
660 @Override
661 protected PerceivedGtu perceive(final LaneBasedGtu object, final Length distance)
662 throws GtuException, ParameterException
663 {
664 return this.perceptionGtuType.createPerceivedGtu(getObject(), Conflict.this, object,
665 this.downstream ? distance.minus(getLength()) : distance, this.downstream);
666 }
667 }
668
669
670
671
672
673 private class Reiterable implements Iterable<Entry<LaneBasedGtu>>
674 {
675
676 private final Iterator<Entry<LaneBasedGtu>> base;
677
678
679 private final List<Entry<LaneBasedGtu>> soFar = new ArrayList<>();
680
681
682
683
684
685 Reiterable(final Iterable<Entry<LaneBasedGtu>> base)
686 {
687 this.base = base.iterator();
688 }
689
690 @Override
691 public Iterator<Entry<LaneBasedGtu>> iterator()
692 {
693 return new Iterator<Entry<LaneBasedGtu>>()
694 {
695 private int index = 0;
696
697 @Override
698 public Entry<LaneBasedGtu> next()
699 {
700 return Reiterable.this.soFar.get(this.index++);
701 }
702
703 @Override
704 public boolean hasNext()
705 {
706 if (this.index >= Reiterable.this.soFar.size() && Reiterable.this.base.hasNext())
707 {
708 Reiterable.this.soFar.add(Reiterable.this.base.next());
709 }
710 return this.index < Reiterable.this.soFar.size();
711 }
712 };
713 }
714 }
715
716 }