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.EventInterface;
15  import org.djutils.event.EventListenerInterface;
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.GTUDirectionality;
24  import org.opentrafficsim.core.gtu.GTUException;
25  import org.opentrafficsim.core.gtu.GTUType;
26  import org.opentrafficsim.core.gtu.RelativePosition;
27  import org.opentrafficsim.core.network.LongitudinalDirectionality;
28  import org.opentrafficsim.core.network.NetworkException;
29  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
30  import org.opentrafficsim.road.gtu.lane.perception.AbstractPerceptionIterable;
31  import org.opentrafficsim.road.gtu.lane.perception.AbstractPerceptionReiterable;
32  import org.opentrafficsim.road.gtu.lane.perception.DownstreamNeighborsIterable;
33  import org.opentrafficsim.road.gtu.lane.perception.LaneBasedObjectIterable;
34  import org.opentrafficsim.road.gtu.lane.perception.LaneDirectionRecord;
35  import org.opentrafficsim.road.gtu.lane.perception.LaneRecord;
36  import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
37  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
38  import org.opentrafficsim.road.gtu.lane.perception.UpstreamNeighborsIterable;
39  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
40  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
41  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
42  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
43  import org.opentrafficsim.road.network.lane.CrossSectionElement;
44  import org.opentrafficsim.road.network.lane.Lane;
45  import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
46  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
47  
48  /**
49   * Conflicts deal with traffic on different links/roads that need to consider each other as their paths may be in conflict
50   * spatially. A single {@code Conflict} represents the one-sided consideration of a conflicting situation. I.e., what is
51   * considered <i>a single conflict in traffic theory, is represented by two {@code Conflict}s</i>, one on each of the
52   * conflicting {@code Lane}s.<br>
53   * <br>
54   * This class provides easy access to upstream and downstream GTUs through {@code PerceptionIterable}s using methods
55   * {@code getUpstreamGtus} and {@code getDownstreamGtus}. These methods are efficient in that they reuse underlying data
56   * structures if the GTUs are requested at the same time by another GTU.
57   * <p>
58   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
59   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
60   * <p>
61   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 7, 2016 <br>
62   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
63   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
64   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
65   */
66  public final class Conflict extends AbstractLaneBasedObject implements EventListenerInterface
67  {
68  
69      /** */
70      private static final long serialVersionUID = 20160915L;
71  
72      /** Conflict type, i.e. crossing, merge or split. */
73      private final ConflictType conflictType;
74  
75      /** Conflict rule, i.e. priority, give way, stop or all-stop. */
76      private final ConflictRule conflictRule;
77  
78      /** Accompanying other conflict. */
79      private Conflict otherConflict;
80  
81      /** The length of the conflict along the lane centerline. */
82      private final Length length;
83  
84      /** GTU direction. */
85      private final GTUDirectionality direction;
86  
87      /** Simulator for animation and timed events. */
88      private final OTSSimulatorInterface simulator;
89  
90      /** GTU type. */
91      private final GTUType gtuType;
92  
93      /** Whether the conflict is a permitted conflict in traffic light control. */
94      private final boolean permitted;
95  
96      /** Distance to upstream traffic light. */
97      private Length trafficLightDistance;
98  
99      /** Maximum maximum search distance. */
100     private Length maxMaxTrafficLightDistance;
101 
102     /** Lock object for cloning a pair of conflicts. */
103     private final Object cloneLock;
104 
105     /////////////////////////////////////////////////////////////////
106     // Properties regarding upstream and downstream GTUs provision //
107     /////////////////////////////////////////////////////////////////
108 
109     /** Root for GTU search. */
110     private final LaneDirectionRecord root;
111 
112     /** Position on the root. */
113     private final Length rootPosition;
114 
115     /** Current upstream GTUs provider. */
116     private AbstractPerceptionIterable<HeadwayGTU, LaneBasedGTU, Integer> upstreamGtus;
117 
118     /** Upstream GTUs update time. */
119     private Time upstreamTime;
120 
121     /** Lanes on which upstream GTUs are found. */
122     private Map<LaneBasedGTU, Lane> upstreamLanes;
123 
124     /** Current downstream GTUs provider. */
125     private AbstractPerceptionIterable<HeadwayGTU, LaneBasedGTU, Integer> downstreamGtus;
126 
127     /** Downstream GTUs update time. */
128     private Time downstreamTime;
129 
130     /** Lanes on which downstream GTUs are found. */
131     private Map<LaneBasedGTU, Lane> downstreamLanes;
132 
133     /** Headway type for the provided GTUs. */
134     private final HeadwayGtuType conflictGtuType = new ConflictGtuType();
135 
136     /** Distance within which upstreamGTUs are provided (is automatically enlarged). */
137     private Length maxUpstreamVisibility = Length.ZERO;
138 
139     /** Distance within which downstreamGTUs are provided (is automatically enlarged). */
140     private Length maxDownstreamVisibility = Length.ZERO;
141 
142     /** Set of upstream GTU that invalidate the iterable when any changes lane. */
143     private Set<LaneBasedGTU> upstreamListening = new LinkedHashSet<>();
144 
145     /** Set of upstream GTU that invalidate the iterable when any changes lane. */
146     private Set<LaneBasedGTU> downstreamListening = new LinkedHashSet<>();
147 
148     /////////////////////////////////////////////////////////////////
149 
150     /**
151      * Construct a new Conflict.
152      * @param lane Lane; lane where this conflict starts
153      * @param longitudinalPosition Length; position of start of conflict on lane
154      * @param length Length; length of the conflict along the lane centerline
155      * @param direction GTUDirectionality; GTU direction
156      * @param geometry OTSLine3D; geometry of conflict
157      * @param conflictRule ConflictRule; conflict rule, i.e. determines priority, give way, stop or all-stop
158      * @param conflictType ConflictType; conflict type, i.e. crossing, merge or split
159      * @param simulator OTSSimulatorInterface; the simulator for animation and timed events
160      * @param permitted boolean; whether the conflict is permitted in traffic light control
161      * @param gtuType GTUType; GTU type
162      * @param cloneLock Object; lock object for cloning a pair of conflicts
163      * @throws NetworkException when the position on the lane is out of bounds
164      */
165     @SuppressWarnings("checkstyle:parameternumber")
166     private Conflict(final Lane lane, final Length longitudinalPosition, final Length length, final GTUDirectionality direction,
167             final OTSLine3D geometry, final ConflictType conflictType, final ConflictRule conflictRule,
168             final OTSSimulatorInterface simulator, final GTUType gtuType, final boolean permitted, final Object cloneLock)
169             throws NetworkException
170     {
171         super(UUID.randomUUID().toString(), lane, Throw.whenNull(direction, "Direction may not be null.").isPlus()
172                 ? LongitudinalDirectionality.DIR_PLUS : LongitudinalDirectionality.DIR_MINUS, longitudinalPosition, geometry);
173         this.length = length;
174         this.direction = direction;
175         this.conflictType = conflictType;
176         this.conflictRule = conflictRule;
177         this.simulator = simulator;
178         this.gtuType = gtuType;
179         this.permitted = permitted;
180         this.cloneLock = cloneLock;
181 
182         // Create conflict end
183         if (conflictType.equals(ConflictType.SPLIT) || conflictType.equals(ConflictType.MERGE))
184         {
185             Length position =
186                     conflictType.equals(ConflictType.SPLIT) ? (direction.isPlus() ? length : lane.getLength().minus(length))
187                             : (direction.isPlus() ? lane.getLength() : Length.ZERO);
188             try
189             {
190                 new ConflictEnd(this, lane,
191                         direction.isPlus() ? LongitudinalDirectionality.DIR_PLUS : LongitudinalDirectionality.DIR_MINUS,
192                         position);
193             }
194             catch (OTSGeometryException exception)
195             {
196                 // does not happen
197                 throw new RuntimeException("Could not create dummy geometry for ConflictEnd.", exception);
198             }
199         }
200 
201         // Lane record for GTU provision
202         this.rootPosition = direction.isPlus() ? longitudinalPosition : lane.getLength().minus(longitudinalPosition);
203         this.root = new LaneDirectionRecord(lane, direction, this.rootPosition.neg(), gtuType);
204     }
205 
206     /**
207      * Make sure the conflict can provide the given upstream visibility.
208      * @param visibility Length; visibility to guarantee
209      */
210     private void provideUpstreamVisibility(final Length visibility)
211     {
212         if (visibility.gt(this.maxUpstreamVisibility))
213         {
214             this.maxUpstreamVisibility = visibility;
215             this.upstreamTime = null;
216             this.downstreamTime = null;
217         }
218     }
219 
220     /**
221      * Make sure the conflict can provide the given downstream visibility.
222      * @param visibility Length; visibility to guarantee
223      */
224     private void provideDownstreamVisibility(final Length visibility)
225     {
226         if (visibility.gt(this.maxDownstreamVisibility))
227         {
228             this.maxDownstreamVisibility = visibility;
229             this.upstreamTime = null;
230             this.downstreamTime = null;
231         }
232     }
233 
234     /**
235      * Provides the upstream GTUs.
236      * @param perceivingGtu LaneBasedGTU; perceiving GTU
237      * @param headwayGtuType HeadwayGtuType; headway GTU type to use
238      * @param visibility Length; distance over which GTU's are provided
239      * @return PerceptionIterable&lt;HeadwayGtU&gt;; iterable over the upstream GTUs
240      */
241     public PerceptionCollectable<HeadwayGTU, LaneBasedGTU> getUpstreamGtus(final LaneBasedGTU perceivingGtu,
242             final HeadwayGtuType headwayGtuType, final Length visibility)
243     {
244         provideUpstreamVisibility(visibility);
245         Time time = this.getLane().getParentLink().getSimulator().getSimulatorAbsTime();
246         if (this.upstreamTime == null || !time.eq(this.upstreamTime))
247         {
248             for (LaneBasedGTU gtu : this.upstreamListening)
249             {
250                 Try.execute(() -> gtu.removeListener(this, LaneBasedGTU.LANE_CHANGE_EVENT), "Unable to unlisten to GTU %s.",
251                         gtu);
252             }
253             this.upstreamListening.clear();
254             // setup a base iterable to provide the GTUs
255             this.upstreamGtus = new UpstreamNeighborsIterable(perceivingGtu, this.root, this.rootPosition,
256                     this.maxUpstreamVisibility, RelativePosition.REFERENCE_POSITION, this.conflictGtuType, RelativeLane.CURRENT)
257             {
258                 /** {@inheritDoc} */
259                 @SuppressWarnings("synthetic-access")
260                 @Override
261                 protected AbstractPerceptionIterable<HeadwayGTU, LaneBasedGTU, Integer>.Entry getNext(
262                         final LaneRecord<?> record, final Length position, final Integer counter) throws GTUException
263                 {
264                     AbstractPerceptionIterable<HeadwayGTU, LaneBasedGTU, Integer>.Entry entry =
265                             super.getNext(record, position, counter);
266                     if (entry != null)
267                     {
268                         Conflict.this.upstreamListening.add(entry.getObject());
269                         Try.execute(() -> entry.getObject().addListener(Conflict.this, LaneBasedGTU.LANE_CHANGE_EVENT),
270                                 "Unable to listen to GTU %s.", entry.getObject());
271                         Conflict.this.upstreamLanes.put(entry.getObject(), record.getLane());
272                     }
273                     return entry;
274                 }
275             };
276             this.upstreamTime = time;
277             this.upstreamLanes = new LinkedHashMap<>();
278         }
279         // return iterable that uses the base iterable
280         return new ConflictGtuIterable(perceivingGtu, headwayGtuType, visibility, false, this.upstreamGtus);
281         // PK does not think this detects GTUs changing lane INTO a lane of concern. Is that bad?
282     }
283 
284     /**
285      * Provides the downstream GTUs.
286      * @param perceivingGtu LaneBasedGTU; perceiving GTU
287      * @param headwayGtuType HeadwayGtuType; headway GTU type to use
288      * @param visibility Length; distance over which GTU's are provided
289      * @return PerceptionIterable&lt;HeadwayGtU&gt;; iterable over the downstream GTUs
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().getSimulatorAbsTime();
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             // setup a base iterable to provide the GTUs
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                         /** {@inheritDoc} */
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         // return iterable that uses the base iterable
332         return new ConflictGtuIterable(perceivingGtu, new OverlapHeadway(headwayGtuType), visibility, true,
333                 this.downstreamGtus);
334         // PK does not think this detects GTUs changing lane INTO a lane of concern. Is that bad?
335     }
336 
337     /** {@inheritDoc} */
338     @Override
339     public void notify(final EventInterface event) throws RemoteException
340     {
341         LaneBasedGTU gtu = (LaneBasedGTU) event.getSourceId();
342         if (this.upstreamListening.contains(gtu))
343         {
344             this.upstreamTime = null;
345         }
346         if (this.downstreamListening.contains(gtu))
347         {
348             this.downstreamTime = null;
349         }
350     }
351 
352     /**
353      * @return conflictType.
354      */
355     public ConflictType getConflictType()
356     {
357         return this.conflictType;
358     }
359 
360     /**
361      * @return conflictRule.
362      */
363     public ConflictRule getConflictRule()
364     {
365         return this.conflictRule;
366     }
367 
368     /**
369      * @return conflictPriority.
370      */
371     public ConflictPriority conflictPriority()
372     {
373         return this.conflictRule.determinePriority(this);
374     }
375 
376     /**
377      * @return length.
378      */
379     public Length getLength()
380     {
381         return this.length;
382     }
383 
384     /**
385      * @return otherConflict.
386      */
387     public Conflict getOtherConflict()
388     {
389         return this.otherConflict;
390     }
391 
392     /**
393      * @return gtuType.
394      */
395     public GTUType getGtuType()
396     {
397         return this.gtuType;
398     }
399 
400     /**
401      * If permitted, traffic upstream of traffic lights may not be ignored, as these can have green light.
402      * @return permitted.
403      */
404     public boolean isPermitted()
405     {
406         return this.permitted;
407     }
408 
409     /**
410      * Returns the distance to an upstream traffic light.
411      * @param maxDistance Length; maximum distance of traffic light
412      * @return Length; distance to upstream traffic light, infinite if beyond maximum distance
413      */
414     public Length getTrafficLightDistance(final Length maxDistance)
415     {
416         if (this.trafficLightDistance == null)
417         {
418             if (this.maxMaxTrafficLightDistance == null || this.maxMaxTrafficLightDistance.lt(maxDistance))
419             {
420                 this.maxMaxTrafficLightDistance = maxDistance;
421                 boolean downstream = false;
422                 LaneBasedObjectIterable<HeadwayTrafficLight,
423                         TrafficLight> it = new LaneBasedObjectIterable<HeadwayTrafficLight, TrafficLight>(null,
424                                 TrafficLight.class, this.root, getLongitudinalPosition(), downstream, maxDistance,
425                                 RelativePosition.REFERENCE_POSITION, null)
426                         {
427                             /** {@inheritDoc} */
428                             @Override
429                             protected HeadwayTrafficLight perceive(final LaneBasedGTU perceivingGtu, final TrafficLight object,
430                                     final Length distance) throws GTUException, ParameterException
431                             {
432                                 return new HeadwayTrafficLight(object, distance);
433                             }
434                         };
435                 if (!it.isEmpty())
436                 {
437                     this.trafficLightDistance = it.first().getDistance();
438                 }
439             }
440         }
441         if (this.trafficLightDistance != null && maxDistance.ge(this.trafficLightDistance))
442         {
443             return this.trafficLightDistance;
444         }
445         return Length.POSITIVE_INFINITY;
446     }
447 
448     /**
449      * Creates a pair of conflicts.
450      * @param conflictType ConflictType; conflict type, i.e. crossing, merge or split
451      * @param conflictRule ConflictRule; conflict rule
452      * @param permitted boolean; whether the conflict is permitted in traffic light control
453      * @param lane1 Lane; lane of conflict 1
454      * @param longitudinalPosition1 Length; longitudinal position of conflict 1
455      * @param length1 Length; {@code Length} of conflict 1
456      * @param direction1 GTUDirectionality; GTU direction of conflict 1
457      * @param geometry1 OTSLine3D; geometry of conflict 1
458      * @param gtuType1 GTUType; gtu type of conflict 1
459      * @param lane2 Lane; lane of conflict 2
460      * @param longitudinalPosition2 Length; longitudinal position of conflict 2
461      * @param length2 Length; {@code Length} of conflict 2
462      * @param direction2 GTUDirectionality; GTU direction of conflict 2
463      * @param geometry2 OTSLine3D; geometry of conflict 2
464      * @param gtuType2 GTUType; gtu type of conflict 2
465      * @param simulator OTSSimulatorInterface; the simulator for animation and timed events
466      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
467      */
468     @SuppressWarnings("checkstyle:parameternumber")
469     public static void generateConflictPair(final ConflictType conflictType, final ConflictRule conflictRule,
470             final boolean permitted, final Lane lane1, final Length longitudinalPosition1, final Length length1,
471             final GTUDirectionality direction1, final OTSLine3D geometry1, final GTUType gtuType1, final Lane lane2,
472             final Length longitudinalPosition2, final Length length2, final GTUDirectionality direction2,
473             final OTSLine3D geometry2, final GTUType gtuType2, final OTSSimulatorInterface simulator) throws NetworkException
474     {
475         // lane, longitudinalPosition, length and geometry are checked in AbstractLaneBasedObject
476         Throw.whenNull(conflictType, "Conflict type may not be null.");
477 
478         Object cloneLock = new Object();
479         Conflict conf1 = new Conflict(lane1, longitudinalPosition1, length1, direction1, geometry1, conflictType, conflictRule,
480                 simulator, gtuType1, permitted, cloneLock);
481         conf1.init(); // fire events and register on lane
482         Conflict conf2 = new Conflict(lane2, longitudinalPosition2, length2, direction2, geometry2, conflictType, conflictRule,
483                 simulator, gtuType2, permitted, cloneLock);
484         conf2.init(); // fire events and register on lane
485         conf1.otherConflict = conf2;
486         conf2.otherConflict = conf1;
487     }
488 
489     /** {@inheritDoc} */
490     @Override
491     public String toString()
492     {
493         return "Conflict [conflictType=" + this.conflictType + ", conflictRule=" + this.conflictRule + "]";
494     }
495 
496     /**
497      * Clone of other conflict.
498      */
499     private Conflict otherClone;
500 
501     /** {@inheritDoc} */
502     @Override
503     public Conflict clone(final CrossSectionElement newCSE, final OTSSimulatorInterface newSimulator) throws NetworkException
504     {
505         Throw.when(!(newCSE instanceof Lane), NetworkException.class, "sensors can only be cloned for Lanes");
506         Throw.when(!(newSimulator instanceof OTSSimulatorInterface), NetworkException.class,
507                 "simulator should be a DEVSSimulator");
508         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             // couple both clones
515             if (this.otherClone == null || this.otherClone.simulator != newSimulator)
516             {
517                 // other clone will do it
518                 this.otherConflict.otherClone = out;
519             }
520             else
521             {
522                 out.otherConflict = this.otherClone;
523                 this.otherClone.otherConflict = out;
524             }
525             // reset successful clone of pair, or remove otherClone from other simulator (or was already null)
526             this.otherClone = null;
527         }
528         return out;
529     }
530 
531     /**
532      * Light-weight lane based object to indicate the end of a conflict. It is used to perceive conflicts when a GTU is on the
533      * conflict area, and hence the conflict lane based object is upstream.
534      * <p>
535      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
536      * <br>
537      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
538      * <p>
539      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 14 dec. 2016 <br>
540      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
541      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
542      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
543      */
544     public class ConflictEnd extends AbstractLaneBasedObject
545     {
546         /** */
547         private static final long serialVersionUID = 20161214L;
548 
549         /** Conflict at start of conflict area. */
550         private final Conflict conflict;
551 
552         /**
553          * Construct a new ConflictEnd object.
554          * @param conflict Conflict; conflict at start of conflict area
555          * @param lane Lane; lane
556          * @param direction LongitudinalDirectionality; driving direction (from the conflict to the new ConflictEnd)
557          * @param longitudinalPosition Length; position along the lane of the end of the conflict
558          * @throws NetworkException on network exception
559          * @throws OTSGeometryException does not happen
560          */
561         ConflictEnd(final Conflict conflict, final Lane lane, final LongitudinalDirectionality direction,
562                 final Length longitudinalPosition) throws NetworkException, OTSGeometryException
563         {
564             // FIXME: the OTSLine3D object should be shared by all ConflictEnd objects (removing OTSGeometryException)
565             super(conflict.getId() + "End", lane, direction, longitudinalPosition,
566                     new OTSLine3D(new OTSPoint3D(0, 0, 0), new OTSPoint3D(1, 0, 0)));
567             this.conflict = conflict;
568         }
569 
570         /**
571          * @return conflict
572          */
573         public final Conflict getConflict()
574         {
575             return this.conflict;
576         }
577 
578         /** {@inheritDoc} */
579         @Override
580         public final AbstractLaneBasedObject clone(final CrossSectionElement newCSE, final OTSSimulatorInterface newSimulator)
581                 throws NetworkException
582         {
583             // Constructor of Conflict creates these.
584             return null;
585         }
586 
587         /** {@inheritDoc} */
588         @Override
589         public final String toString()
590         {
591             return "ConflictEnd [conflict=" + this.conflict + "]";
592         }
593     }
594 
595     /**
596      * HeadwayGTU that is returned by base iterators for upstream and downstream GTUs. This class is used with both
597      * {@code UpstreamNeighborsIterable} and {@code DownstreamNeighborsIterable} which work with HeadwayGTU. The role of this
598      * class is however to simply provide the GTU itself such that other specific HeadwayGTU types can be created with it.
599      * Therefore, it extends HeadwayGTUReal which simply wraps the GTU. As the HeadwayGTUReal class has the actual GTU hidden,
600      * this class can provide it.
601      * <p>
602      * FIXME: why not create a getter for the gtu in the super class?
603      * <p>
604      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
605      * <br>
606      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
607      * <p>
608      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 21 feb. 2018 <br>
609      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
610      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
611      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
612      */
613     private class ConflictGtu extends HeadwayGTUReal
614     {
615         /** */
616         private static final long serialVersionUID = 20180221L;
617 
618         /** Visible pointer to the GTU (which HeadwayGTUReal has not). */
619         private final LaneBasedGTU gtu;
620 
621         /**
622          * Constructor.
623          * @param gtu LaneBasedGTU; gtu
624          * @param overlapFront Length; front overlap
625          * @param overlap Length; overlap
626          * @param overlapRear Length; rear overlap
627          * @throws GTUException on exception
628          */
629         ConflictGtu(final LaneBasedGTU gtu, final Length overlapFront, final Length overlap, final Length overlapRear)
630                 throws GTUException
631         {
632             super(gtu, overlapFront, overlap, overlapRear, true);
633             this.gtu = gtu;
634         }
635 
636         /**
637          * Constructor.
638          * @param gtu LaneBasedGTU; gtu
639          * @param distance Length; distance
640          * @throws GTUException on exception
641          */
642         ConflictGtu(final LaneBasedGTU gtu, final Length distance) throws GTUException
643         {
644             super(gtu, distance, true);
645             this.gtu = gtu;
646         }
647     }
648 
649     /**
650      * HeadwayGtuType that generates ConflictGtu's, for use within the base iterators for upstream and downstream neighbors.
651      * This result is used by secondary iterators (ConflictGtuIterable) to provide the requested specific HeadwatGtuType.
652      * <p>
653      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
654      * <br>
655      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
656      * <p>
657      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 21 feb. 2018 <br>
658      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
659      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
660      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
661      */
662     private class ConflictGtuType implements HeadwayGtuType
663     {
664         /** Constructor. */
665         ConflictGtuType()
666         {
667             //
668         }
669 
670         /** {@inheritDoc} */
671         @Override
672         public ConflictGtu createHeadwayGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu,
673                 final Length distance, final boolean downstream) throws GTUException
674         {
675             return new ConflictGtu(perceivedGtu, distance);
676         }
677 
678         /** {@inheritDoc} */
679         @Override
680         public HeadwayGTU createDownstreamGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu,
681                 final Length distance) throws GTUException, ParameterException
682         {
683             return new ConflictGtu(perceivedGtu, distance); // actually do not change it, called by iterable assuming downstream
684         }
685 
686         /** {@inheritDoc} */
687         @Override
688         public HeadwayGTU createUpstreamGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu,
689                 final Length distance) throws GTUException, ParameterException
690         {
691             return new ConflictGtu(perceivedGtu, distance); // actually do not change it, called by iterable assuming upstream
692         }
693 
694         /** {@inheritDoc} */
695         @Override
696         public ConflictGtu createParallelGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu,
697                 final Length overlapFront, final Length overlap, final Length overlapRear) throws GTUException
698         {
699             throw new UnsupportedOperationException("ConflictGtuType is a pass-through type, no actual perception is allowed.");
700         }
701     }
702 
703     /**
704      * HeadwayGtuType that changes a negative headway in to an overlapping headway, by forwarding the request to a wrapped
705      * HeadwayGtuType. This is used for downstream GTUs of the conflict, accounting also for the length of the conflict. Hence,
706      * overlap information concerns the conflict and a downstream GTU (downstream of the start of the conflict).
707      * <p>
708      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
709      * <br>
710      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
711      * <p>
712      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 21 feb. 2018 <br>
713      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
714      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
715      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
716      */
717     private class OverlapHeadway implements HeadwayGtuType
718     {
719         /** Wrapped headway type. */
720         private HeadwayGtuType wrappedType;
721 
722         /**
723          * Constructor.
724          * @param wrappedType HeadwayGtuType; wrapped headway type
725          */
726         OverlapHeadway(final HeadwayGtuType wrappedType)
727         {
728             this.wrappedType = wrappedType;
729         }
730 
731         /** {@inheritDoc} */
732         @Override
733         public HeadwayGTU createHeadwayGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu, final Length dist,
734                 final boolean downstream) throws GTUException, ParameterException
735         {
736             if (dist.ge(getLength()))
737             {
738                 // GTU fully downstream of the conflict
739                 return this.wrappedType.createHeadwayGtu(perceivingGtu, perceivedGtu, dist.minus(getLength()), downstream);
740             }
741             else
742             {
743                 Length overlapRear = dist;
744                 Length overlap = getLength(); // start with conflict length
745                 @SuppressWarnings("synthetic-access")
746                 Lane lane = downstream ? Conflict.this.downstreamLanes.get(perceivedGtu)
747                         : Conflict.this.upstreamLanes.get(perceivedGtu);
748                 Length overlapFront = dist.plus(perceivedGtu.getProjectedLength(lane)).minus(getLength());
749                 if (overlapFront.lt0())
750                 {
751                     overlap = overlap.plus(overlapFront); // subtract front being before the conflict end
752                 }
753                 if (overlapRear.gt0())
754                 {
755                     overlap = overlap.minus(overlapRear); // subtract rear being past the conflict start
756                 }
757                 return createParallelGtu(perceivingGtu, perceivedGtu, overlapFront, overlap, overlapRear);
758             }
759         }
760 
761         /** {@inheritDoc} */
762         @Override
763         public HeadwayGTU createDownstreamGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu,
764                 final Length distance) throws GTUException, ParameterException
765         {
766             throw new UnsupportedOperationException("OverlapHeadway is a pass-through type, no actual perception is allowed.");
767         }
768 
769         /** {@inheritDoc} */
770         @Override
771         public HeadwayGTU createUpstreamGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu,
772                 final Length distance) throws GTUException, ParameterException
773         {
774             throw new UnsupportedOperationException("OverlapHeadway is a pass-through type, no actual perception is allowed.");
775         }
776 
777         /** {@inheritDoc} */
778         @Override
779         public HeadwayGTU createParallelGtu(final LaneBasedGTU perceivingGtu, final LaneBasedGTU perceivedGtu,
780                 final Length overlapFront, final Length overlap, final Length overlapRear) throws GTUException
781         {
782             return this.wrappedType.createParallelGtu(perceivingGtu, perceivedGtu, overlapFront, overlap, overlapRear);
783         }
784     }
785 
786     /**
787      * Iterable for upstream and downstream GTUs of a conflict, which uses a base iterable.
788      * <p>
789      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
790      * <br>
791      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
792      * <p>
793      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 21 feb. 2018 <br>
794      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
795      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
796      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
797      */
798     private class ConflictGtuIterable extends AbstractPerceptionReiterable<HeadwayGTU, LaneBasedGTU>
799     {
800         /** HeadwayGTU type. */
801         private final HeadwayGtuType headwayGtuType;
802 
803         /** Guaranteed visibility. */
804         private final Length visibility;
805 
806         /** Downstream (or upstream) neighbors. */
807         private final boolean downstream;
808 
809         /** Base iterator of the base iterable. */
810         private final Iterator<HeadwayGTU> baseIterator;
811 
812         /**
813          * @param perceivingGtu LaneBasedGTU; perceiving GTU
814          * @param headwayGtuType HeadwayGtuType; HeadwayGTU type
815          * @param visibility Length; guaranteed visibility
816          * @param downstream boolean; downstream (or upstream) neighbors
817          * @param base AbstractPerceptionIterable&lt;HeadwayGTU, LaneBasedGTU, Integer&gt;; base iterable from the conflict
818          */
819         ConflictGtuIterable(final LaneBasedGTU perceivingGtu, final HeadwayGtuType headwayGtuType, final Length visibility,
820                 final boolean downstream, final AbstractPerceptionIterable<HeadwayGTU, LaneBasedGTU, Integer> base)
821         {
822             super(perceivingGtu);
823             this.headwayGtuType = headwayGtuType;
824             this.visibility = visibility;
825             this.downstream = downstream;
826             this.baseIterator = base.iterator();
827         }
828 
829         /** {@inheritDoc} */
830         @Override
831         protected Iterator<PrimaryIteratorEntry> primaryIterator()
832         {
833             /**
834              * Iterator that iterates over PrimaryIteratorEntry objects.
835              */
836             class ConflictGtuIterator implements Iterator<PrimaryIteratorEntry>
837             {
838                 /** Next entry. */
839                 private PrimaryIteratorEntry next;
840 
841                 /** {@inheritDoc} */
842                 @SuppressWarnings("synthetic-access")
843                 @Override
844                 public boolean hasNext()
845                 {
846                     if (this.next == null)
847                     {
848                         if (ConflictGtuIterable.this.baseIterator.hasNext())
849                         {
850                             // ConflictGtuIterable is a private class, only used with ConflictGtuType
851                             ConflictGtu gtu = (ConflictGtu) ConflictGtuIterable.this.baseIterator.next();
852                             if (gtu.gtu.getId().equals(getGtu().getId()))
853                             {
854                                 if (ConflictGtuIterable.this.baseIterator.hasNext())
855                                 {
856                                     gtu = (ConflictGtu) ConflictGtuIterable.this.baseIterator.next();
857                                 }
858                                 else
859                                 {
860                                     return false;
861                                 }
862                             }
863                             if (gtu.getDistance() == null || gtu.getDistance().le(ConflictGtuIterable.this.visibility))
864                             {
865                                 this.next = new PrimaryIteratorEntry(gtu.gtu, gtu.getDistance());
866                             }
867                         }
868                     }
869                     return this.next != null;
870                 }
871 
872                 /** {@inheritDoc} */
873                 @Override
874                 public PrimaryIteratorEntry next()
875                 {
876                     if (hasNext())
877                     {
878                         PrimaryIteratorEntry out = this.next;
879                         this.next = null;
880                         return out;
881                     }
882                     throw new NoSuchElementException();
883                 }
884             }
885             return new ConflictGtuIterator();
886         }
887 
888         /** {@inheritDoc} */
889         @Override
890         protected HeadwayGTU perceive(final LaneBasedGTU perceivingGtu, final LaneBasedGTU object, final Length distance)
891                 throws GTUException, ParameterException
892         {
893             return this.headwayGtuType.createHeadwayGtu(perceivingGtu, object, distance, this.downstream);
894         }
895     }
896 
897 }