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