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