Conflict.java

  1. package org.opentrafficsim.road.network.lane.conflict;

  2. import java.rmi.RemoteException;
  3. import java.util.Iterator;
  4. import java.util.LinkedHashMap;
  5. import java.util.LinkedHashSet;
  6. import java.util.Map;
  7. import java.util.NoSuchElementException;
  8. import java.util.Set;
  9. import java.util.UUID;

  10. import org.djunits.value.vdouble.scalar.Length;
  11. import org.djunits.value.vdouble.scalar.Time;
  12. import org.djutils.draw.line.Polygon2d;
  13. import org.djutils.draw.point.Point2d;
  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.gtu.GtuException;
  22. import org.opentrafficsim.core.gtu.RelativePosition;
  23. import org.opentrafficsim.core.network.NetworkException;
  24. import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
  25. import org.opentrafficsim.road.gtu.lane.perception.AbstractPerceptionIterable;
  26. import org.opentrafficsim.road.gtu.lane.perception.AbstractPerceptionReiterable;
  27. import org.opentrafficsim.road.gtu.lane.perception.DownstreamNeighborsIterable;
  28. import org.opentrafficsim.road.gtu.lane.perception.LaneBasedObjectIterable;
  29. import org.opentrafficsim.road.gtu.lane.perception.LaneRecord;
  30. import org.opentrafficsim.road.gtu.lane.perception.LaneRecordInterface;
  31. import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
  32. import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
  33. import org.opentrafficsim.road.gtu.lane.perception.UpstreamNeighborsIterable;
  34. import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
  35. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu;
  36. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtuReal;
  37. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
  38. import org.opentrafficsim.road.network.lane.Lane;
  39. import org.opentrafficsim.road.network.lane.object.AbstractLaneBasedObject;
  40. import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;

  41. /**
  42.  * Conflicts deal with traffic on different links/roads that need to consider each other as their paths may be in conflict
  43.  * spatially. A single {@code Conflict} represents the one-sided consideration of a conflicting situation. I.e., what is
  44.  * considered <i>a single conflict in traffic theory, is represented by two {@code Conflict}s</i>, one on each of the
  45.  * conflicting {@code Lane}s.<br>
  46.  * <br>
  47.  * This class provides easy access to upstream and downstream GTUs through {@code PerceptionIterable}s using methods
  48.  * {@code getUpstreamGtus} and {@code getDownstreamGtus}. These methods are efficient in that they reuse underlying data
  49.  * structures if the GTUs are requested at the same time by another GTU.
  50.  * <p>
  51.  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  52.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  53.  * </p>
  54.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  55.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  56.  * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  57.  */
  58. public final class Conflict extends AbstractLaneBasedObject implements EventListener
  59. {

  60.     /** */
  61.     private static final long serialVersionUID = 20160915L;

  62.     /** Conflict type, i.e. crossing, merge or split. */
  63.     private final ConflictType conflictType;

  64.     /** Conflict rule, i.e. priority, give way, stop or all-stop. */
  65.     private final ConflictRule conflictRule;

  66.     /** End of conflict. */
  67.     private final ConflictEnd end;

  68.     /** Accompanying other conflict. */
  69.     private Conflict otherConflict;

  70.     /** The length of the conflict along the lane centerline. */
  71.     private final Length length;

  72.     /** Whether the conflict is a permitted conflict in traffic light control. */
  73.     private final boolean permitted;

  74.     /** Distance to upstream traffic light. */
  75.     private Length trafficLightDistance;

  76.     /** Maximum maximum search distance. */
  77.     private Length maxMaxTrafficLightDistance;

  78.     /////////////////////////////////////////////////////////////////
  79.     // Properties regarding upstream and downstream GTUs provision //
  80.     /////////////////////////////////////////////////////////////////

  81.     /** Root for GTU search. */
  82.     private final LaneRecord root;

  83.     /** Position on the root. */
  84.     private final Length rootPosition;

  85.     /** Current upstream GTUs provider. */
  86.     private AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer> upstreamGtus;

  87.     /** Upstream GTUs update time. */
  88.     private Time upstreamTime;

  89.     /** Lanes on which upstream GTUs are found. */
  90.     private Map<LaneBasedGtu, Lane> upstreamLanes;

  91.     /** Current downstream GTUs provider. */
  92.     private AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer> downstreamGtus;

  93.     /** Downstream GTUs update time. */
  94.     private Time downstreamTime;

  95.     /** Lanes on which downstream GTUs are found. */
  96.     private Map<LaneBasedGtu, Lane> downstreamLanes;

  97.     /** Headway type for the provided GTUs. */
  98.     private final HeadwayGtuType conflictGtuType = new ConflictGtuType();

  99.     /** Distance within which upstreamGTUs are provided (is automatically enlarged). */
  100.     private Length maxUpstreamVisibility = Length.ZERO;

  101.     /** Distance within which downstreamGTUs are provided (is automatically enlarged). */
  102.     private Length maxDownstreamVisibility = Length.ZERO;

  103.     /** Set of upstream GTU that invalidate the iterable when any changes lane. */
  104.     private Set<LaneBasedGtu> upstreamListening = new LinkedHashSet<>();

  105.     /** Set of upstream GTU that invalidate the iterable when any changes lane. */
  106.     private Set<LaneBasedGtu> downstreamListening = new LinkedHashSet<>();

  107.     /////////////////////////////////////////////////////////////////

  108.     /**
  109.      * Construct a new Conflict.
  110.      * @param lane Lane; lane where this conflict starts
  111.      * @param longitudinalPosition Length; position of start of conflict on lane
  112.      * @param length Length; length of the conflict along the lane centerline
  113.      * @param geometry Polygon2d; geometry of conflict
  114.      * @param conflictType ConflictType; conflict type, i.e. crossing, merge or split
  115.      * @param conflictRule ConflictRule; conflict rule, i.e. determines priority, give way, stop or all-stop
  116.      * @param permitted boolean; whether the conflict is permitted in traffic light control
  117.      * @throws NetworkException when the position on the lane is out of bounds
  118.      */
  119.     @SuppressWarnings("checkstyle:parameternumber")
  120.     private Conflict(final Lane lane, final Length longitudinalPosition, final Length length, final Polygon2d geometry,
  121.             final ConflictType conflictType, final ConflictRule conflictRule, final boolean permitted) throws NetworkException
  122.     {
  123.         super(UUID.randomUUID().toString(), lane, longitudinalPosition, geometry);
  124.         this.length = length;
  125.         this.conflictType = conflictType;
  126.         this.conflictRule = conflictRule;
  127.         this.permitted = permitted;

  128.         // Create conflict end
  129.         if (conflictType.equals(ConflictType.SPLIT) || conflictType.equals(ConflictType.MERGE))
  130.         {
  131.             Length position = conflictType.equals(ConflictType.SPLIT) ? length : lane.getLength();
  132.             try
  133.             {
  134.                 this.end = new ConflictEnd(this, lane, position);
  135.             }
  136.             catch (OtsGeometryException exception)
  137.             {
  138.                 // does not happen
  139.                 throw new RuntimeException("Could not create dummy geometry for ConflictEnd.", exception);
  140.             }
  141.         }
  142.         else
  143.         {
  144.             this.end = null;
  145.         }

  146.         // Lane record for GTU provision
  147.         this.rootPosition = longitudinalPosition;
  148.         this.root = new LaneRecord(lane, this.rootPosition.neg(), null);
  149.     }

  150.     /** {@inheritDoc} */
  151.     @Override
  152.     protected void init() throws NetworkException
  153.     {
  154.         super.init();
  155.         if (this.end != null)
  156.         {
  157.             this.end.init();
  158.         }
  159.     }

  160.     /**
  161.      * Make sure the conflict can provide the given upstream visibility.
  162.      * @param visibility Length; visibility to guarantee
  163.      */
  164.     private void provideUpstreamVisibility(final Length visibility)
  165.     {
  166.         if (visibility.gt(this.maxUpstreamVisibility))
  167.         {
  168.             this.maxUpstreamVisibility = visibility;
  169.             this.upstreamTime = null;
  170.             this.downstreamTime = null;
  171.         }
  172.     }

  173.     /**
  174.      * Make sure the conflict can provide the given downstream visibility.
  175.      * @param visibility Length; visibility to guarantee
  176.      */
  177.     private void provideDownstreamVisibility(final Length visibility)
  178.     {
  179.         if (visibility.gt(this.maxDownstreamVisibility))
  180.         {
  181.             this.maxDownstreamVisibility = visibility;
  182.             this.upstreamTime = null;
  183.             this.downstreamTime = null;
  184.         }
  185.     }

  186.     /**
  187.      * Provides the upstream GTUs.
  188.      * @param perceivingGtu LaneBasedGtu; perceiving GTU
  189.      * @param headwayGtuType HeadwayGtuType; headway GTU type to use
  190.      * @param visibility Length; distance over which GTU's are provided
  191.      * @return PerceptionIterable&lt;HeadwayGtU&gt;; iterable over the upstream GTUs
  192.      */
  193.     public PerceptionCollectable<HeadwayGtu, LaneBasedGtu> getUpstreamGtus(final LaneBasedGtu perceivingGtu,
  194.             final HeadwayGtuType headwayGtuType, final Length visibility)
  195.     {
  196.         provideUpstreamVisibility(visibility);
  197.         Time time = this.getLane().getLink().getSimulator().getSimulatorAbsTime();
  198.         if (this.upstreamTime == null || !time.eq(this.upstreamTime))
  199.         {
  200.             for (LaneBasedGtu gtu : this.upstreamListening)
  201.             {
  202.                 Try.execute(() -> gtu.removeListener(this, LaneBasedGtu.LANE_CHANGE_EVENT), "Unable to unlisten to GTU %s.",
  203.                         gtu);
  204.             }
  205.             this.upstreamListening.clear();
  206.             // setup a base iterable to provide the GTUs
  207.             this.upstreamGtus = new UpstreamNeighborsIterable(perceivingGtu, this.root, this.rootPosition,
  208.                     this.maxUpstreamVisibility, RelativePosition.REFERENCE_POSITION, this.conflictGtuType, RelativeLane.CURRENT)
  209.             {
  210.                 /** {@inheritDoc} */
  211.                 @Override
  212.                 protected AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer>.Entry getNext(
  213.                         final LaneRecordInterface<?> record, final Length position, final Integer counter) throws GtuException
  214.                 {
  215.                     AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer>.Entry entry =
  216.                             super.getNext(record, position, counter);
  217.                     if (entry != null)
  218.                     {
  219.                         Conflict.this.upstreamListening.add(entry.getObject());
  220.                         Try.execute(() -> entry.getObject().addListener(Conflict.this, LaneBasedGtu.LANE_CHANGE_EVENT),
  221.                                 "Unable to listen to GTU %s.", entry.getObject());
  222.                         Conflict.this.upstreamLanes.put(entry.getObject(), record.getLane());
  223.                     }
  224.                     return entry;
  225.                 }
  226.             };
  227.             this.upstreamTime = time;
  228.             this.upstreamLanes = new LinkedHashMap<>();
  229.         }
  230.         // return iterable that uses the base iterable
  231.         return new ConflictGtuIterable(perceivingGtu, headwayGtuType, visibility, false, this.upstreamGtus);
  232.         // PK does not think this detects GTUs changing lane INTO a lane of concern. Is that bad?
  233.     }

  234.     /**
  235.      * Provides the downstream 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 downstream GTUs
  240.      */
  241.     public PerceptionCollectable<HeadwayGtu, LaneBasedGtu> getDownstreamGtus(final LaneBasedGtu perceivingGtu,
  242.             final HeadwayGtuType headwayGtuType, final Length visibility)
  243.     {
  244.         provideDownstreamVisibility(visibility);
  245.         Time time = this.getLane().getLink().getSimulator().getSimulatorAbsTime();
  246.         if (this.downstreamTime == null || !time.eq(this.downstreamTime))
  247.         {
  248.             for (LaneBasedGtu gtu : this.downstreamListening)
  249.             {
  250.                 Try.execute(() -> gtu.removeListener(this, LaneBasedGtu.LANE_CHANGE_EVENT), "Unable to unlisten to GTU %s.",
  251.                         gtu);
  252.             }
  253.             this.downstreamListening.clear();
  254.             // setup a base iterable to provide the GTUs
  255.             boolean ignoreIfUpstream = false;
  256.             this.downstreamGtus =
  257.                     new DownstreamNeighborsIterable(null, this.root, this.rootPosition, this.maxDownstreamVisibility,
  258.                             RelativePosition.REFERENCE_POSITION, this.conflictGtuType, RelativeLane.CURRENT, ignoreIfUpstream)
  259.                     {
  260.                         /** {@inheritDoc} */
  261.                         @Override
  262.                         protected AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer>.Entry getNext(
  263.                                 final LaneRecordInterface<?> record, final Length position, final Integer counter)
  264.                                 throws GtuException
  265.                         {
  266.                             AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer>.Entry entry =
  267.                                     super.getNext(record, position, counter);
  268.                             if (entry != null)
  269.                             {
  270.                                 Conflict.this.downstreamListening.add(entry.getObject());
  271.                                 Try.execute(() -> entry.getObject().addListener(Conflict.this, LaneBasedGtu.LANE_CHANGE_EVENT),
  272.                                         "Unable to listen to GTU %s.", entry.getObject());
  273.                                 Conflict.this.downstreamLanes.put(entry.getObject(), record.getLane());
  274.                             }
  275.                             return entry;
  276.                         }
  277.                     };
  278.             this.downstreamTime = time;
  279.             this.downstreamLanes = new LinkedHashMap<>();
  280.         }
  281.         // return iterable that uses the base iterable
  282.         return new ConflictGtuIterable(perceivingGtu, new OverlapHeadway(headwayGtuType), visibility, true,
  283.                 this.downstreamGtus);
  284.         // PK does not think this detects GTUs changing lane INTO a lane of concern. Is that bad?
  285.     }

  286.     /** {@inheritDoc} */
  287.     @Override
  288.     public void notify(final Event event) throws RemoteException
  289.     {
  290.         Object[] payload = (Object[]) event.getContent();
  291.         LaneBasedGtu gtu = (LaneBasedGtu) getLane().getNetwork().getGTU((String) payload[0]);
  292.         if (this.upstreamListening.contains(gtu))
  293.         {
  294.             this.upstreamTime = null;
  295.         }
  296.         if (this.downstreamListening.contains(gtu))
  297.         {
  298.             this.downstreamTime = null;
  299.         }
  300.     }

  301.     /**
  302.      * @return conflictType.
  303.      */
  304.     public ConflictType getConflictType()
  305.     {
  306.         return this.conflictType;
  307.     }

  308.     /**
  309.      * @return conflictRule.
  310.      */
  311.     public ConflictRule getConflictRule()
  312.     {
  313.         return this.conflictRule;
  314.     }

  315.     /**
  316.      * @return conflictPriority.
  317.      */
  318.     public ConflictPriority conflictPriority()
  319.     {
  320.         return this.conflictRule.determinePriority(this);
  321.     }
  322.    
  323.     /** {@inheritDoc} */
  324.     @Override
  325.     public Polygon2d getGeometry()
  326.     {
  327.         return (Polygon2d) super.getGeometry();
  328.     }

  329.     /**
  330.      * @return length.
  331.      */
  332.     public Length getLength()
  333.     {
  334.         return this.length;
  335.     }

  336.     /**
  337.      * @return otherConflict.
  338.      */
  339.     public Conflict getOtherConflict()
  340.     {
  341.         return this.otherConflict;
  342.     }

  343.     /**
  344.      * If permitted, traffic upstream of traffic lights may not be ignored, as these can have green light.
  345.      * @return permitted.
  346.      */
  347.     public boolean isPermitted()
  348.     {
  349.         return this.permitted;
  350.     }

  351.     /**
  352.      * Returns the distance to an upstream traffic light.
  353.      * @param maxDistance Length; maximum distance of traffic light
  354.      * @return Length; distance to upstream traffic light, infinite if beyond maximum distance
  355.      */
  356.     public Length getTrafficLightDistance(final Length maxDistance)
  357.     {
  358.         if (this.trafficLightDistance == null)
  359.         {
  360.             if (this.maxMaxTrafficLightDistance == null || this.maxMaxTrafficLightDistance.lt(maxDistance))
  361.             {
  362.                 this.maxMaxTrafficLightDistance = maxDistance;
  363.                 boolean downstream = false;
  364.                 LaneBasedObjectIterable<HeadwayTrafficLight,
  365.                         TrafficLight> it = new LaneBasedObjectIterable<HeadwayTrafficLight, TrafficLight>(null,
  366.                                 TrafficLight.class, this.root, getLongitudinalPosition(), downstream, maxDistance,
  367.                                 RelativePosition.REFERENCE_POSITION, null)
  368.                         {
  369.                             /** {@inheritDoc} */
  370.                             @Override
  371.                             protected HeadwayTrafficLight perceive(final LaneBasedGtu perceivingGtu, final TrafficLight object,
  372.                                     final Length distance) throws GtuException, ParameterException
  373.                             {
  374.                                 return new HeadwayTrafficLight(object, distance, false);
  375.                             }
  376.                         };
  377.                 if (!it.isEmpty())
  378.                 {
  379.                     this.trafficLightDistance = it.first().getDistance();
  380.                 }
  381.             }
  382.         }
  383.         if (this.trafficLightDistance != null && maxDistance.ge(this.trafficLightDistance))
  384.         {
  385.             return this.trafficLightDistance;
  386.         }
  387.         return Length.POSITIVE_INFINITY;
  388.     }

  389.     /**
  390.      * Creates a pair of conflicts.
  391.      * @param conflictType ConflictType; conflict type, i.e. crossing, merge or split
  392.      * @param conflictRule ConflictRule; conflict rule
  393.      * @param permitted boolean; whether the conflict is permitted in traffic light control
  394.      * @param lane1 Lane; lane of conflict 1
  395.      * @param longitudinalPosition1 Length; longitudinal position of conflict 1
  396.      * @param length1 Length; {@code Length} of conflict 1
  397.      * @param geometry1 Polygon2d; geometry of conflict 1
  398.      * @param lane2 Lane; lane of conflict 2
  399.      * @param longitudinalPosition2 Length; longitudinal position of conflict 2
  400.      * @param length2 Length; {@code Length} of conflict 2
  401.      * @param geometry2 Polygon2d; geometry of conflict 2
  402.      * @param simulator OtsSimulatorInterface; the simulator for animation and timed events
  403.      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
  404.      */
  405.     @SuppressWarnings("checkstyle:parameternumber")
  406.     public static void generateConflictPair(final ConflictType conflictType, final ConflictRule conflictRule,
  407.             final boolean permitted, final Lane lane1, final Length longitudinalPosition1, final Length length1,
  408.             final Polygon2d geometry1, final Lane lane2, final Length longitudinalPosition2, final Length length2,
  409.             final Polygon2d geometry2, final OtsSimulatorInterface simulator) throws NetworkException
  410.     {
  411.         // lane, longitudinalPosition, length and geometry are checked in AbstractLaneBasedObject
  412.         Throw.whenNull(conflictType, "Conflict type may not be null.");

  413.         Conflict conf1 = new Conflict(lane1, longitudinalPosition1, length1, geometry1, conflictType, conflictRule, permitted);
  414.         conf1.init(); // fire events and register on lane
  415.         Conflict conf2 = new Conflict(lane2, longitudinalPosition2, length2, geometry2, conflictType, conflictRule, permitted);
  416.         conf2.init(); // fire events and register on lane
  417.         conf1.otherConflict = conf2;
  418.         conf2.otherConflict = conf1;
  419.     }

  420.     /** {@inheritDoc} */
  421.     @Override
  422.     public double getZ() throws RemoteException
  423.     {
  424.         return -0.0001;
  425.     }

  426.     /** {@inheritDoc} */
  427.     @Override
  428.     public String toString()
  429.     {
  430.         return "Conflict [conflictType=" + this.conflictType + ", conflictRule=" + this.conflictRule + "]";
  431.     }

  432.     /**
  433.      * Light-weight lane based object to indicate the end of a conflict. It is used to perceive conflicts when a GTU is on the
  434.      * conflict area, and hence the conflict lane based object is upstream.
  435.      * <p>
  436.      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  437.      * <br>
  438.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  439.      * </p>
  440.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  441.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  442.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  443.      */
  444.     public class ConflictEnd extends AbstractLaneBasedObject
  445.     {
  446.         /** */
  447.         private static final long serialVersionUID = 20161214L;

  448.         /** Conflict at start of conflict area. */
  449.         private final Conflict conflict;

  450.         /**
  451.          * Construct a new ConflictEnd object.
  452.          * @param conflict Conflict; conflict at start of conflict area
  453.          * @param lane Lane; lane
  454.          * @param longitudinalPosition Length; position along the lane of the end of the conflict
  455.          * @throws NetworkException on network exception
  456.          * @throws OtsGeometryException does not happen
  457.          */
  458.         ConflictEnd(final Conflict conflict, final Lane lane, final Length longitudinalPosition)
  459.                 throws NetworkException, OtsGeometryException
  460.         {
  461.             // FIXME: the OtsLine2d object should be shared by all ConflictEnd objects (removing OtsGeometryException)
  462.             super(conflict.getId() + "End", lane, longitudinalPosition, new Polygon2d(new Point2d(0, 0), new Point2d(1, 0)));
  463.             this.conflict = conflict;
  464.         }

  465.         /** {@inheritDoc} */
  466.         @Override
  467.         public void init() throws NetworkException
  468.         {
  469.             // override makes init accessible to conflict
  470.             super.init();
  471.         }

  472.         /**
  473.          * @return conflict
  474.          */
  475.         public final Conflict getConflict()
  476.         {
  477.             return this.conflict;
  478.         }

  479.         /** {@inheritDoc} */
  480.         @Override
  481.         public final String toString()
  482.         {
  483.             return "ConflictEnd [conflict=" + this.conflict + "]";
  484.         }
  485.     }

  486.     /**
  487.      * HeadwayGtu that is returned by base iterators for upstream and downstream GTUs. This class is used with both
  488.      * {@code UpstreamNeighborsIterable} and {@code DownstreamNeighborsIterable} which work with HeadwayGtu. The role of this
  489.      * class is however to simply provide the GTU itself such that other specific HeadwayGtu types can be created with it.
  490.      * Therefore, it extends HeadwayGtuReal which simply wraps the GTU. As the HeadwayGtuReal class has the actual GTU hidden,
  491.      * this class can provide it.
  492.      * <p>
  493.      * FIXME: why not create a getter for the gtu in the super class?
  494.      * <p>
  495.      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  496.      * <br>
  497.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  498.      * </p>
  499.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  500.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  501.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  502.      */
  503.     private class ConflictGtu extends HeadwayGtuReal
  504.     {
  505.         /** */
  506.         private static final long serialVersionUID = 20180221L;

  507.         /** Visible pointer to the GTU (which HeadwayGtuReal has not). */
  508.         private final LaneBasedGtu gtu;

  509.         /**
  510.          * Constructor.
  511.          * @param gtu LaneBasedGtu; gtu
  512.          * @param overlapFront Length; front overlap
  513.          * @param overlap Length; overlap
  514.          * @param overlapRear Length; rear overlap
  515.          * @throws GtuException on exception
  516.          */
  517.         ConflictGtu(final LaneBasedGtu gtu, final Length overlapFront, final Length overlap, final Length overlapRear)
  518.                 throws GtuException
  519.         {
  520.             super(gtu, overlapFront, overlap, overlapRear, true);
  521.             this.gtu = gtu;
  522.         }

  523.         /**
  524.          * Constructor.
  525.          * @param gtu LaneBasedGtu; gtu
  526.          * @param distance Length; distance
  527.          * @throws GtuException on exception
  528.          */
  529.         ConflictGtu(final LaneBasedGtu gtu, final Length distance) throws GtuException
  530.         {
  531.             super(gtu, distance, true);
  532.             this.gtu = gtu;
  533.         }
  534.     }

  535.     /**
  536.      * HeadwayGtuType that generates ConflictGtu's, for use within the base iterators for upstream and downstream neighbors.
  537.      * This result is used by secondary iterators (ConflictGtuIterable) to provide the requested specific HeadwatGtuType.
  538.      * <p>
  539.      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  540.      * <br>
  541.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  542.      * </p>
  543.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  544.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  545.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  546.      */
  547.     private class ConflictGtuType implements HeadwayGtuType
  548.     {
  549.         /** Constructor. */
  550.         ConflictGtuType()
  551.         {
  552.             //
  553.         }

  554.         /** {@inheritDoc} */
  555.         @Override
  556.         public ConflictGtu createHeadwayGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu,
  557.                 final Length distance, final boolean downstream) throws GtuException
  558.         {
  559.             return new ConflictGtu(perceivedGtu, distance);
  560.         }

  561.         /** {@inheritDoc} */
  562.         @Override
  563.         public HeadwayGtu createDownstreamGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu,
  564.                 final Length distance) throws GtuException, ParameterException
  565.         {
  566.             return new ConflictGtu(perceivedGtu, distance); // actually do not change it, called by iterable assuming downstream
  567.         }

  568.         /** {@inheritDoc} */
  569.         @Override
  570.         public HeadwayGtu createUpstreamGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu,
  571.                 final Length distance) throws GtuException, ParameterException
  572.         {
  573.             return new ConflictGtu(perceivedGtu, distance); // actually do not change it, called by iterable assuming upstream
  574.         }

  575.         /** {@inheritDoc} */
  576.         @Override
  577.         public ConflictGtu createParallelGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu,
  578.                 final Length overlapFront, final Length overlap, final Length overlapRear) throws GtuException
  579.         {
  580.             throw new UnsupportedOperationException("ConflictGtuType is a pass-through type, no actual perception is allowed.");
  581.         }
  582.     }

  583.     /**
  584.      * HeadwayGtuType that changes a negative headway in to an overlapping headway, by forwarding the request to a wrapped
  585.      * HeadwayGtuType. This is used for downstream GTUs of the conflict, accounting also for the length of the conflict. Hence,
  586.      * overlap information concerns the conflict and a downstream GTU (downstream of the start of the conflict).
  587.      * <p>
  588.      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  589.      * <br>
  590.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  591.      * </p>
  592.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  593.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  594.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  595.      */
  596.     private class OverlapHeadway implements HeadwayGtuType
  597.     {
  598.         /** Wrapped headway type. */
  599.         private HeadwayGtuType wrappedType;

  600.         /**
  601.          * Constructor.
  602.          * @param wrappedType HeadwayGtuType; wrapped headway type
  603.          */
  604.         OverlapHeadway(final HeadwayGtuType wrappedType)
  605.         {
  606.             this.wrappedType = wrappedType;
  607.         }

  608.         /** {@inheritDoc} */
  609.         @Override
  610.         public HeadwayGtu createHeadwayGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu, final Length dist,
  611.                 final boolean downstream) throws GtuException, ParameterException
  612.         {
  613.             if (dist.ge(getLength()))
  614.             {
  615.                 // GTU fully downstream of the conflict
  616.                 return this.wrappedType.createHeadwayGtu(perceivingGtu, perceivedGtu, dist.minus(getLength()), downstream);
  617.             }
  618.             else
  619.             {
  620.                 Length overlapRear = dist;
  621.                 Length overlap = getLength(); // start with conflict length
  622.                 Lane lane = downstream ? Conflict.this.downstreamLanes.get(perceivedGtu)
  623.                         : Conflict.this.upstreamLanes.get(perceivedGtu);
  624.                 Length overlapFront = dist.plus(perceivedGtu.getProjectedLength(lane)).minus(getLength());
  625.                 if (overlapFront.lt0())
  626.                 {
  627.                     overlap = overlap.plus(overlapFront); // subtract front being before the conflict end
  628.                 }
  629.                 if (overlapRear.gt0())
  630.                 {
  631.                     overlap = overlap.minus(overlapRear); // subtract rear being past the conflict start
  632.                 }
  633.                 return createParallelGtu(perceivingGtu, perceivedGtu, overlapFront, overlap, overlapRear);
  634.             }
  635.         }

  636.         /** {@inheritDoc} */
  637.         @Override
  638.         public HeadwayGtu createDownstreamGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu,
  639.                 final Length distance) throws GtuException, ParameterException
  640.         {
  641.             throw new UnsupportedOperationException("OverlapHeadway is a pass-through type, no actual perception is allowed.");
  642.         }

  643.         /** {@inheritDoc} */
  644.         @Override
  645.         public HeadwayGtu createUpstreamGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu,
  646.                 final Length distance) throws GtuException, ParameterException
  647.         {
  648.             throw new UnsupportedOperationException("OverlapHeadway is a pass-through type, no actual perception is allowed.");
  649.         }

  650.         /** {@inheritDoc} */
  651.         @Override
  652.         public HeadwayGtu createParallelGtu(final LaneBasedGtu perceivingGtu, final LaneBasedGtu perceivedGtu,
  653.                 final Length overlapFront, final Length overlap, final Length overlapRear) throws GtuException
  654.         {
  655.             return this.wrappedType.createParallelGtu(perceivingGtu, perceivedGtu, overlapFront, overlap, overlapRear);
  656.         }
  657.     }

  658.     /**
  659.      * Iterable for upstream and downstream GTUs of a conflict, which uses a base iterable.
  660.      * <p>
  661.      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  662.      * <br>
  663.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  664.      * </p>
  665.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  666.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  667.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  668.      */
  669.     private class ConflictGtuIterable extends AbstractPerceptionReiterable<HeadwayGtu, LaneBasedGtu>
  670.     {
  671.         /** HeadwayGtu type. */
  672.         private final HeadwayGtuType headwayGtuType;

  673.         /** Guaranteed visibility. */
  674.         private final Length visibility;

  675.         /** Downstream (or upstream) neighbors. */
  676.         private final boolean downstream;

  677.         /** Base iterator of the base iterable. */
  678.         private final Iterator<HeadwayGtu> baseIterator;

  679.         /**
  680.          * @param perceivingGtu LaneBasedGtu; perceiving GTU
  681.          * @param headwayGtuType HeadwayGtuType; HeadwayGtu type
  682.          * @param visibility Length; guaranteed visibility
  683.          * @param downstream boolean; downstream (or upstream) neighbors
  684.          * @param base AbstractPerceptionIterable&lt;HeadwayGtu, LaneBasedGtu, Integer&gt;; base iterable from the conflict
  685.          */
  686.         ConflictGtuIterable(final LaneBasedGtu perceivingGtu, final HeadwayGtuType headwayGtuType, final Length visibility,
  687.                 final boolean downstream, final AbstractPerceptionIterable<HeadwayGtu, LaneBasedGtu, Integer> base)
  688.         {
  689.             super(perceivingGtu);
  690.             this.headwayGtuType = headwayGtuType;
  691.             this.visibility = visibility;
  692.             this.downstream = downstream;
  693.             this.baseIterator = base.iterator();
  694.         }

  695.         /** {@inheritDoc} */
  696.         @Override
  697.         protected Iterator<PrimaryIteratorEntry> primaryIterator()
  698.         {
  699.             /**
  700.              * Iterator that iterates over PrimaryIteratorEntry objects.
  701.              */
  702.             class ConflictGtuIterator implements Iterator<PrimaryIteratorEntry>
  703.             {
  704.                 /** Next entry. */
  705.                 private PrimaryIteratorEntry next;

  706.                 /** {@inheritDoc} */
  707.                 @Override
  708.                 public boolean hasNext()
  709.                 {
  710.                     if (this.next == null)
  711.                     {
  712.                         if (ConflictGtuIterable.this.baseIterator.hasNext())
  713.                         {
  714.                             // ConflictGtuIterable is a private class, only used with ConflictGtuType
  715.                             ConflictGtu gtu = (ConflictGtu) ConflictGtuIterable.this.baseIterator.next();
  716.                             if (gtu.gtu.getId().equals(getGtu().getId()))
  717.                             {
  718.                                 if (ConflictGtuIterable.this.baseIterator.hasNext())
  719.                                 {
  720.                                     gtu = (ConflictGtu) ConflictGtuIterable.this.baseIterator.next();
  721.                                 }
  722.                                 else
  723.                                 {
  724.                                     return false;
  725.                                 }
  726.                             }
  727.                             if (gtu.getDistance() == null || gtu.getDistance().le(ConflictGtuIterable.this.visibility))
  728.                             {
  729.                                 this.next = new PrimaryIteratorEntry(gtu.gtu, gtu.getDistance());
  730.                             }
  731.                         }
  732.                     }
  733.                     return this.next != null;
  734.                 }

  735.                 /** {@inheritDoc} */
  736.                 @Override
  737.                 public PrimaryIteratorEntry next()
  738.                 {
  739.                     if (hasNext())
  740.                     {
  741.                         PrimaryIteratorEntry out = this.next;
  742.                         this.next = null;
  743.                         return out;
  744.                     }
  745.                     throw new NoSuchElementException();
  746.                 }
  747.             }
  748.             return new ConflictGtuIterator();
  749.         }

  750.         /** {@inheritDoc} */
  751.         @Override
  752.         protected HeadwayGtu perceive(final LaneBasedGtu perceivingGtu, final LaneBasedGtu object, final Length distance)
  753.                 throws GtuException, ParameterException
  754.         {
  755.             return this.headwayGtuType.createHeadwayGtu(perceivingGtu, object, distance, this.downstream);
  756.         }
  757.     }

  758. }