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