View Javadoc
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   * Conflicts deal with traffic on different links/roads that need to consider each other as their paths may be in conflict
46   * spatially. A single {@code Conflict} represents the one-sided consideration of a conflicting situation. I.e., what is
47   * considered <i>a single conflict in traffic theory, is represented by two {@code Conflict}s</i>, one on each of the
48   * conflicting {@code Lane}s.<br>
49   * <br>
50   * This class provides easy access to upstream and downstream GTUs through {@code PerceptionIterable}s using methods
51   * {@code getUpstreamGtus} and {@code getDownstreamGtus}. These methods are efficient in that they reuse underlying data
52   * structures if the GTUs are requested at the same time by another GTU.
53   * <p>
54   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
55   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
56   * </p>
57   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
58   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
59   * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
60   */
61  public final class Conflict extends AbstractLaneBasedObject implements EventListener
62  {
63  
64      /** */
65      private static final long serialVersionUID = 20160915L;
66  
67      /** Conflict type, i.e. crossing, merge or split. */
68      private final ConflictType conflictType;
69  
70      /** Conflict rule, i.e. priority, give way, stop or all-stop. */
71      private final ConflictRule conflictRule;
72  
73      /** Accompanying other conflict. */
74      private Conflict otherConflict;
75  
76      /** The length of the conflict along the lane centerline. */
77      private final Length length;
78  
79      /** Whether the conflict is a permitted conflict in traffic light control. */
80      private final boolean permitted;
81  
82      /** Distance to upstream traffic light. */
83      private Length trafficLightDistance;
84  
85      /** Maximum maximum search distance. */
86      private Length maxMaxTrafficLightDistance;
87  
88      /////////////////////////////////////////////////////////////////
89      // Properties regarding upstream and downstream GTUs provision //
90      /////////////////////////////////////////////////////////////////
91  
92      /** Root for GTU search. */
93      private final LaneRecord root;
94  
95      /** Position on the root. */
96      private final Length rootPosition;
97  
98      /** Current upstream GTUs provider. */
99      private AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer> upstreamGtus;
100 
101     /** Upstream GTUs update time. */
102     private Time upstreamTime;
103 
104     /** Lanes on which upstream GTUs are found. */
105     private Map<LaneBasedGtu, Lane> upstreamLanes;
106 
107     /** Current downstream GTUs provider. */
108     private AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer> downstreamGtus;
109 
110     /** Downstream GTUs update time. */
111     private Time downstreamTime;
112 
113     /** Lanes on which downstream GTUs are found. */
114     private Map<LaneBasedGtu, Lane> downstreamLanes;
115 
116     /** Headway type for the provided GTUs. */
117     private final HeadwayGtuType conflictGtuType = new ConflictGtuType();
118 
119     /** Distance within which upstreamGTUs are provided (is automatically enlarged). */
120     private Length maxUpstreamVisibility = Length.ZERO;
121 
122     /** Distance within which downstreamGTUs are provided (is automatically enlarged). */
123     private Length maxDownstreamVisibility = Length.ZERO;
124 
125     /** Set of upstream GTU that invalidate the iterable when any changes lane. */
126     private Set<LaneBasedGtu> upstreamListening = new LinkedHashSet<>();
127 
128     /** Set of upstream GTU that invalidate the iterable when any changes lane. */
129     private Set<LaneBasedGtu> downstreamListening = new LinkedHashSet<>();
130 
131     /////////////////////////////////////////////////////////////////
132 
133     /**
134      * Construct a new Conflict.
135      * @param lane Lane; lane where this conflict starts
136      * @param longitudinalPosition Length; position of start of conflict on lane
137      * @param length Length; length of the conflict along the lane centerline
138      * @param geometry OtsLine3d; geometry of conflict
139      * @param conflictType ConflictType; conflict type, i.e. crossing, merge or split
140      * @param conflictRule ConflictRule; conflict rule, i.e. determines priority, give way, stop or all-stop
141      * @param permitted boolean; whether the conflict is permitted in traffic light control
142      * @throws NetworkException when the position on the lane is out of bounds
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         // Create conflict end
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                 // does not happen
165                 throw new RuntimeException("Could not create dummy geometry for ConflictEnd.", exception);
166             }
167         }
168 
169         // Lane record for GTU provision
170         this.rootPosition = longitudinalPosition;
171         this.root = new LaneRecord(lane, this.rootPosition.neg(), null);
172     }
173 
174     /**
175      * Make sure the conflict can provide the given upstream visibility.
176      * @param visibility Length; visibility to guarantee
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      * Make sure the conflict can provide the given downstream visibility.
190      * @param visibility Length; visibility to guarantee
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      * Provides the upstream GTUs.
204      * @param perceivingGtu LaneBasedGtu; perceiving GTU
205      * @param headwayGtuType HeadwayGtuType; headway GTU type to use
206      * @param visibility Length; distance over which GTU's are provided
207      * @return PerceptionIterable&lt;HeadwayGtU&gt;; iterable over the upstream GTUs
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             // setup a base iterable to provide the GTUs
223             this.upstreamGtus = new UpstreamNeighborsIterable(perceivingGtu, this.root, this.rootPosition,
224                     this.maxUpstreamVisibility, RelativePosition.REFERENCE_POSITION, this.conflictGtuType, RelativeLane.CURRENT)
225             {
226                 /** {@inheritDoc} */
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         // return iterable that uses the base iterable
247         return new ConflictGtuIterable(perceivingGtu, headwayGtuType, visibility, false, this.upstreamGtus);
248         // PK does not think this detects GTUs changing lane INTO a lane of concern. Is that bad?
249     }
250 
251     /**
252      * Provides the downstream GTUs.
253      * @param perceivingGtu LaneBasedGtu; perceiving GTU
254      * @param headwayGtuType HeadwayGtuType; headway GTU type to use
255      * @param visibility Length; distance over which GTU's are provided
256      * @return PerceptionIterable&lt;HeadwayGtU&gt;; iterable over the downstream GTUs
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             // setup a base iterable to provide the GTUs
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                         /** {@inheritDoc} */
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         // return iterable that uses the base iterable
299         return new ConflictGtuIterable(perceivingGtu, new OverlapHeadway(headwayGtuType), visibility, true,
300                 this.downstreamGtus);
301         // PK does not think this detects GTUs changing lane INTO a lane of concern. Is that bad?
302     }
303 
304     /** {@inheritDoc} */
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      * @return conflictType.
322      */
323     public ConflictType getConflictType()
324     {
325         return this.conflictType;
326     }
327 
328     /**
329      * @return conflictRule.
330      */
331     public ConflictRule getConflictRule()
332     {
333         return this.conflictRule;
334     }
335 
336     /**
337      * @return conflictPriority.
338      */
339     public ConflictPriority conflictPriority()
340     {
341         return this.conflictRule.determinePriority(this);
342     }
343 
344     /**
345      * @return length.
346      */
347     public Length getLength()
348     {
349         return this.length;
350     }
351 
352     /**
353      * @return otherConflict.
354      */
355     public Conflict getOtherConflict()
356     {
357         return this.otherConflict;
358     }
359 
360     /**
361      * If permitted, traffic upstream of traffic lights may not be ignored, as these can have green light.
362      * @return permitted.
363      */
364     public boolean isPermitted()
365     {
366         return this.permitted;
367     }
368 
369     /**
370      * Returns the distance to an upstream traffic light.
371      * @param maxDistance Length; maximum distance of traffic light
372      * @return Length; distance to upstream traffic light, infinite if beyond maximum distance
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                             /** {@inheritDoc} */
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      * Creates a pair of conflicts.
410      * @param conflictType ConflictType; conflict type, i.e. crossing, merge or split
411      * @param conflictRule ConflictRule; conflict rule
412      * @param permitted boolean; whether the conflict is permitted in traffic light control
413      * @param lane1 Lane; lane of conflict 1
414      * @param longitudinalPosition1 Length; longitudinal position of conflict 1
415      * @param length1 Length; {@code Length} of conflict 1
416      * @param geometry1 OtsLine3d; geometry of conflict 1
417      * @param lane2 Lane; lane of conflict 2
418      * @param longitudinalPosition2 Length; longitudinal position of conflict 2
419      * @param length2 Length; {@code Length} of conflict 2
420      * @param geometry2 OtsLine3d; geometry of conflict 2
421      * @param simulator OtsSimulatorInterface; the simulator for animation and timed events
422      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
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         // lane, longitudinalPosition, length and geometry are checked in AbstractLaneBasedObject
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(); // fire events and register on lane
435         Conflict conf2 = new Conflict(lane2, longitudinalPosition2, length2, geometry2, conflictType, conflictRule, permitted);
436         conf2.init(); // fire events and register on lane
437         conf1.otherConflict = conf2;
438         conf2.otherConflict = conf1;
439     }
440     
441     /** {@inheritDoc} */
442     @Override
443     public double getZ() throws RemoteException
444     {
445         return -0.0001;
446     }
447 
448     /** {@inheritDoc} */
449     @Override
450     public String toString()
451     {
452         return "Conflict [conflictType=" + this.conflictType + ", conflictRule=" + this.conflictRule + "]";
453     }
454 
455     /**
456      * Light-weight lane based object to indicate the end of a conflict. It is used to perceive conflicts when a GTU is on the
457      * conflict area, and hence the conflict lane based object is upstream.
458      * <p>
459      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
460      * <br>
461      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
462      * </p>
463      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
464      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
465      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
466      */
467     public class ConflictEnd extends AbstractLaneBasedObject
468     {
469         /** */
470         private static final long serialVersionUID = 20161214L;
471 
472         /** Conflict at start of conflict area. */
473         private final Conflict conflict;
474 
475         /**
476          * Construct a new ConflictEnd object.
477          * @param conflict Conflict; conflict at start of conflict area
478          * @param lane Lane; lane
479          * @param longitudinalPosition Length; position along the lane of the end of the conflict
480          * @throws NetworkException on network exception
481          * @throws OtsGeometryException does not happen
482          */
483         ConflictEnd(final Conflict conflict, final Lane lane, final Length longitudinalPosition)
484                 throws NetworkException, OtsGeometryException
485         {
486             // FIXME: the OtsLine3d object should be shared by all ConflictEnd objects (removing OtsGeometryException)
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          * @return conflict
494          */
495         public final Conflict getConflict()
496         {
497             return this.conflict;
498         }
499 
500         /** {@inheritDoc} */
501         @Override
502         public final String toString()
503         {
504             return "ConflictEnd [conflict=" + this.conflict + "]";
505         }
506     }
507 
508     /**
509      * HeadwayGtu that is returned by base iterators for upstream and downstream GTUs. This class is used with both
510      * {@code UpstreamNeighborsIterable} and {@code DownstreamNeighborsIterable} which work with HeadwayGtu. The role of this
511      * class is however to simply provide the GTU itself such that other specific HeadwayGtu types can be created with it.
512      * Therefore, it extends HeadwayGtuReal which simply wraps the GTU. As the HeadwayGtuReal class has the actual GTU hidden,
513      * this class can provide it.
514      * <p>
515      * FIXME: why not create a getter for the gtu in the super class?
516      * <p>
517      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
518      * <br>
519      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
520      * </p>
521      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
522      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
523      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
524      */
525     private class ConflictGtu extends HeadwayGtuReal
526     {
527         /** */
528         private static final long serialVersionUID = 20180221L;
529 
530         /** Visible pointer to the GTU (which HeadwayGtuReal has not). */
531         private final LaneBasedGtu gtu;
532 
533         /**
534          * Constructor.
535          * @param gtu LaneBasedGtu; gtu
536          * @param overlapFront Length; front overlap
537          * @param overlap Length; overlap
538          * @param overlapRear Length; rear overlap
539          * @throws GtuException on exception
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          * Constructor.
550          * @param gtu LaneBasedGtu; gtu
551          * @param distance Length; distance
552          * @throws GtuException on exception
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      * HeadwayGtuType that generates ConflictGtu's, for use within the base iterators for upstream and downstream neighbors.
563      * This result is used by secondary iterators (ConflictGtuIterable) to provide the requested specific HeadwatGtuType.
564      * <p>
565      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
566      * <br>
567      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
568      * </p>
569      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
570      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
571      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
572      */
573     private class ConflictGtuType implements HeadwayGtuType
574     {
575         /** Constructor. */
576         ConflictGtuType()
577         {
578             //
579         }
580 
581         /** {@inheritDoc} */
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         /** {@inheritDoc} */
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); // actually do not change it, called by iterable assuming downstream
595         }
596 
597         /** {@inheritDoc} */
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); // actually do not change it, called by iterable assuming upstream
603         }
604 
605         /** {@inheritDoc} */
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      * HeadwayGtuType that changes a negative headway in to an overlapping headway, by forwarding the request to a wrapped
616      * HeadwayGtuType. This is used for downstream GTUs of the conflict, accounting also for the length of the conflict. Hence,
617      * overlap information concerns the conflict and a downstream GTU (downstream of the start of the conflict).
618      * <p>
619      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
620      * <br>
621      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
622      * </p>
623      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
624      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
625      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
626      */
627     private class OverlapHeadway implements HeadwayGtuType
628     {
629         /** Wrapped headway type. */
630         private HeadwayGtuType wrappedType;
631 
632         /**
633          * Constructor.
634          * @param wrappedType HeadwayGtuType; wrapped headway type
635          */
636         OverlapHeadway(final HeadwayGtuType wrappedType)
637         {
638             this.wrappedType = wrappedType;
639         }
640 
641         /** {@inheritDoc} */
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                 // GTU fully downstream of the conflict
649                 return this.wrappedType.createHeadwayGtu(perceivingGtu, perceivedGtu, dist.minus(getLength()), downstream);
650             }
651             else
652             {
653                 Length overlapRear = dist;
654                 Length overlap = getLength(); // start with conflict length
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); // subtract front being before the conflict end
661                 }
662                 if (overlapRear.gt0())
663                 {
664                     overlap = overlap.minus(overlapRear); // subtract rear being past the conflict start
665                 }
666                 return createParallelGtu(perceivingGtu, perceivedGtu, overlapFront, overlap, overlapRear);
667             }
668         }
669 
670         /** {@inheritDoc} */
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         /** {@inheritDoc} */
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         /** {@inheritDoc} */
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      * Iterable for upstream and downstream GTUs of a conflict, which uses a base iterable.
697      * <p>
698      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
699      * <br>
700      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
701      * </p>
702      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
703      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
704      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
705      */
706     private class ConflictGtuIterable extends AbstractPerceptionReiterable<HeadwayGtu, LaneBasedGtu>
707     {
708         /** HeadwayGtu type. */
709         private final HeadwayGtuType headwayGtuType;
710 
711         /** Guaranteed visibility. */
712         private final Length visibility;
713 
714         /** Downstream (or upstream) neighbors. */
715         private final boolean downstream;
716 
717         /** Base iterator of the base iterable. */
718         private final Iterator<HeadwayGtu> baseIterator;
719 
720         /**
721          * @param perceivingGtu LaneBasedGtu; perceiving GTU
722          * @param headwayGtuType HeadwayGtuType; HeadwayGtu type
723          * @param visibility Length; guaranteed visibility
724          * @param downstream boolean; downstream (or upstream) neighbors
725          * @param base AbstractPerceptionIterable&lt;HeadwayGtu, LaneBasedGtu, Integer&gt;; base iterable from the conflict
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         /** {@inheritDoc} */
738         @Override
739         protected Iterator<PrimaryIteratorEntry> primaryIterator()
740         {
741             /**
742              * Iterator that iterates over PrimaryIteratorEntry objects.
743              */
744             class ConflictGtuIterator implements Iterator<PrimaryIteratorEntry>
745             {
746                 /** Next entry. */
747                 private PrimaryIteratorEntry next;
748 
749                 /** {@inheritDoc} */
750                 @Override
751                 public boolean hasNext()
752                 {
753                     if (this.next == null)
754                     {
755                         if (ConflictGtuIterable.this.baseIterator.hasNext())
756                         {
757                             // ConflictGtuIterable is a private class, only used with ConflictGtuType
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                 /** {@inheritDoc} */
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         /** {@inheritDoc} */
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 }