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