DirectIntersectionPerception.java

  1. package org.opentrafficsim.road.gtu.lane.perception.categories;

  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.SortedSet;

  5. import org.djunits.value.vdouble.scalar.Length;
  6. import org.djunits.value.vdouble.scalar.Speed;
  7. import org.djutils.exceptions.Try;
  8. import org.opentrafficsim.base.parameters.ParameterException;
  9. import org.opentrafficsim.base.parameters.ParameterTypeLength;
  10. import org.opentrafficsim.base.parameters.ParameterTypes;
  11. import org.opentrafficsim.core.geometry.OtsGeometryException;
  12. import org.opentrafficsim.core.geometry.OtsLine3d;
  13. import org.opentrafficsim.core.gtu.GtuException;
  14. import org.opentrafficsim.core.gtu.RelativePosition;
  15. import org.opentrafficsim.core.network.LateralDirectionality;
  16. import org.opentrafficsim.core.network.NetworkException;
  17. import org.opentrafficsim.core.network.route.Route;
  18. import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
  19. import org.opentrafficsim.road.gtu.lane.perception.LaneBasedObjectIterable;
  20. import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
  21. import org.opentrafficsim.road.gtu.lane.perception.LaneStructure.Entry;
  22. import org.opentrafficsim.road.gtu.lane.perception.LaneStructureRecord;
  23. import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
  24. import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
  25. import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
  26. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
  27. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu;
  28. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayStopLine;
  29. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
  30. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  31. import org.opentrafficsim.road.network.lane.Lane;
  32. import org.opentrafficsim.road.network.lane.conflict.Conflict;
  33. import org.opentrafficsim.road.network.lane.conflict.Conflict.ConflictEnd;
  34. import org.opentrafficsim.road.network.lane.conflict.ConflictPriority;
  35. import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
  36. import org.opentrafficsim.road.network.lane.conflict.ConflictType;
  37. import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
  38. import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;

  39. /**
  40.  * Perceives traffic lights and intersection conflicts.
  41.  * <p>
  42.  * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  43.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  44.  * </p>
  45.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  46.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  47.  * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  48.  */
  49. public class DirectIntersectionPerception extends LaneBasedAbstractPerceptionCategory implements IntersectionPerception
  50. {

  51.     /** */
  52.     private static final long serialVersionUID = 20160811L;

  53.     /** Look ahead parameter type. */
  54.     protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;

  55.     /** Margin to find upstream conflicts who's ends are downstream, needed as the algorithm searches beyond a location. */
  56.     private static final Length MARGIN = Length.instantiateSI(0.001);

  57.     /** Headway GTU type that should be used. */
  58.     private final HeadwayGtuType headwayGtuType;

  59.     /**
  60.      * @param perception LanePerception; perception
  61.      * @param headwayGtuType HeadwayGtuType; type of headway gtu to generate
  62.      */
  63.     public DirectIntersectionPerception(final LanePerception perception, final HeadwayGtuType headwayGtuType)
  64.     {
  65.         super(perception);
  66.         this.headwayGtuType = headwayGtuType;
  67.     }

  68.     /**
  69.      * Compute traffic lights.
  70.      * @param lane RelativeLane; lane
  71.      * @return PerceptionCollectable of traffic lights
  72.      */
  73.     private PerceptionCollectable<HeadwayTrafficLight, TrafficLight> computeTrafficLights(final RelativeLane lane)
  74.     {
  75.         try
  76.         {
  77.             Route route = getPerception().getGtu().getStrategicalPlanner().getRoute();
  78.             LaneStructureRecord record = getPerception().getLaneStructure().getFirstRecord(lane);
  79.             Length pos = record.getStartDistance().neg();
  80.             pos = pos.plus(getGtu().getFront().getDx());
  81.             return new LaneBasedObjectIterable<HeadwayTrafficLight, TrafficLight>(getGtu(), TrafficLight.class, record,
  82.                     Length.max(Length.ZERO, pos), true, getGtu().getParameters().getParameter(LOOKAHEAD), getGtu().getFront(),
  83.                     route)
  84.             {
  85.                 /** {@inheritDoc} */
  86.                 @Override
  87.                 public HeadwayTrafficLight perceive(final LaneBasedGtu perceivingGtu, final TrafficLight trafficLight,
  88.                         final Length distance)
  89.                 {
  90.                     try
  91.                     {
  92.                         return new HeadwayTrafficLight(trafficLight, distance);
  93.                     }
  94.                     catch (GtuException exception)
  95.                     {
  96.                         throw new RuntimeException(exception);
  97.                     }
  98.                 }
  99.             };
  100.         }
  101.         catch (GtuException | ParameterException exception)
  102.         {
  103.             throw new RuntimeException("Unexpected exception while computing traffic lights.", exception);
  104.         }

  105.     }

  106.     /**
  107.      * Compute conflicts.
  108.      * @param lane RelativeLane; lane
  109.      * @return PerceptionCollectable of conflicts
  110.      */
  111.     private PerceptionCollectable<HeadwayConflict, Conflict> computeConflicts(final RelativeLane lane)
  112.     {
  113.         try
  114.         {
  115.             Route route = getPerception().getGtu().getStrategicalPlanner().getRoute();
  116.             LaneStructureRecord record = getPerception().getLaneStructure().getFirstRecord(lane);
  117.             Length pos = record.getStartDistance().neg().plus(getGtu().getRear().getDx());
  118.             while (pos.lt0() && !record.getPrev().isEmpty())
  119.             {
  120.                 pos = pos.plus(record.getLength());
  121.                 record = record.getPrev().get(0);
  122.             }
  123.             // find all ConflictEnd, and the most upstream relating position
  124.             List<LaneBasedObject> laneObjs;
  125.             if (record.isDownstreamBranch())
  126.             {
  127.                 laneObjs = record.getLane().getLaneBasedObjects(Length.max(Length.ZERO, pos), record.getLane().getLength());
  128.             }
  129.             else
  130.             {
  131.                 laneObjs = new ArrayList<>();
  132.             }
  133.             // TODO if conflicts span multiple lanes, this within-lane search fails
  134.             for (LaneBasedObject object : laneObjs)
  135.             {
  136.                 if (object instanceof ConflictEnd)
  137.                 {
  138.                     Conflict c = ((ConflictEnd) object).getConflict();
  139.                     Length cPos = c.getLongitudinalPosition().minus(MARGIN);
  140.                     pos = Length.min(pos, cPos);
  141.                 }
  142.             }
  143.             return new LaneBasedObjectIterable<HeadwayConflict, Conflict>(getGtu(), Conflict.class, record,
  144.                     Length.max(MARGIN.neg(), pos), true, getGtu().getParameters().getParameter(LOOKAHEAD), getGtu().getFront(),
  145.                     route)
  146.             {
  147.                 /** {@inheritDoc} */
  148.                 @SuppressWarnings("synthetic-access")
  149.                 @Override
  150.                 public HeadwayConflict perceive(final LaneBasedGtu perceivingGtu, final Conflict conflict,
  151.                         final Length distance)
  152.                 {
  153.                     Conflict otherConflict = conflict.getOtherConflict();
  154.                     ConflictType conflictType = conflict.getConflictType();
  155.                     ConflictPriority conflictPriority = conflict.conflictPriority();
  156.                     Class<? extends ConflictRule> conflictRuleType = conflict.getConflictRule().getClass();
  157.                     String id = conflict.getId();
  158.                     Length length = conflict.getLength();
  159.                     Length conflictingLength = otherConflict.getLength();
  160.                     CrossSectionLink conflictingLink = otherConflict.getLane().getParentLink();

  161.                     // TODO get from link combination (needs to be a map property on the links)
  162.                     Length lookAhead =
  163.                             Try.assign(() -> getGtu().getParameters().getParameter(LOOKAHEAD), "Parameter not present.");
  164.                     Length conflictingVisibility = lookAhead;
  165.                     Speed conflictingSpeedLimit;
  166.                     try
  167.                     {
  168.                         conflictingSpeedLimit = otherConflict.getLane().getHighestSpeedLimit();
  169.                     }
  170.                     catch (NetworkException exception)
  171.                     {
  172.                         throw new RuntimeException("GTU type not available on conflicting lane.", exception);
  173.                     }

  174.                     // TODO limit 'conflictingVisibility' to first upstream traffic light, so GTU's behind it are ignored

  175.                     HeadwayConflict headwayConflict;
  176.                     try
  177.                     {
  178.                         PerceptionCollectable<HeadwayGtu, LaneBasedGtu> upstreamConflictingGTUs = otherConflict.getUpstreamGtus(
  179.                                 getGtu(), DirectIntersectionPerception.this.headwayGtuType, conflictingVisibility);
  180.                         PerceptionCollectable<HeadwayGtu, LaneBasedGtu> downstreamConflictingGTUs =
  181.                                 otherConflict.getDownstreamGtus(getGtu(), DirectIntersectionPerception.this.headwayGtuType,
  182.                                         conflictingVisibility);
  183.                         // TODO stop lines (current models happen not to use this, but should be possible)
  184.                         HeadwayStopLine stopLine = new HeadwayStopLine("stopLineId", Length.ZERO, conflict.getLane());
  185.                         HeadwayStopLine conflictingStopLine =
  186.                                 new HeadwayStopLine("conflictingStopLineId", Length.ZERO, conflict.getLane());

  187.                         Lane thisLane = conflict.getLane();
  188.                         Lane otherLane = otherConflict.getLane();
  189.                         Length pos1a = conflict.getLongitudinalPosition();
  190.                         Length pos2a = otherConflict.getLongitudinalPosition();
  191.                         Length pos1b = Length.min(pos1a.plus(conflict.getLength()), thisLane.getLength());
  192.                         Length pos2b = Length.min(pos2a.plus(otherConflict.getLength()), otherLane.getLength());
  193.                         OtsLine3d line1 = thisLane.getCenterLine();
  194.                         OtsLine3d line2 = otherLane.getCenterLine();
  195.                         double dStart = line1.getLocation(pos1a).distance(line2.getLocation(pos2a));
  196.                         double dEnd = line1.getLocation(pos1b).distance(line2.getLocation(pos2b));
  197.                         Length startWidth = Length
  198.                                 .instantiateSI(dStart + .5 * thisLane.getWidth(pos1a).si + .5 * otherLane.getWidth(pos2a).si);
  199.                         Length endWidth = Length
  200.                                 .instantiateSI(dEnd + .5 * thisLane.getWidth(pos1b).si + .5 * otherLane.getWidth(pos2b).si);

  201.                         headwayConflict = new HeadwayConflict(conflictType, conflictPriority, conflictRuleType, id, distance,
  202.                                 length, conflictingLength, upstreamConflictingGTUs, downstreamConflictingGTUs,
  203.                                 conflictingVisibility, conflictingSpeedLimit, conflictingLink,
  204.                                 HeadwayConflict.Width.linear(startWidth, endWidth), stopLine, conflictingStopLine, thisLane);

  205.                         Length trafficLightDistance = conflict.getOtherConflict()
  206.                                 .getTrafficLightDistance(perceivingGtu.getParameters().getParameter(ParameterTypes.LOOKAHEAD));
  207.                         if (trafficLightDistance != null && trafficLightDistance.le(lookAhead))
  208.                         {
  209.                             headwayConflict.setConflictingTrafficLight(trafficLightDistance, conflict.isPermitted());
  210.                         }
  211.                     }
  212.                     catch (GtuException | OtsGeometryException | ParameterException exception)
  213.                     {
  214.                         throw new RuntimeException("Could not create headway objects.", exception);
  215.                     }
  216.                     return headwayConflict;
  217.                 }
  218.             };
  219.         }
  220.         catch (GtuException | ParameterException exception)
  221.         {
  222.             throw new RuntimeException("Unexpected exception while computing conflicts.", exception);
  223.         }
  224.     }

  225.     /**
  226.      * Compute whether there is a conflict alongside.
  227.      * @param lat LateralDirectionality; lateral directionality
  228.      * @return boolean; whether there is a conflict alongside
  229.      */
  230.     private boolean computeConflictAlongside(final LateralDirectionality lat)
  231.     {
  232.         try
  233.         {
  234.             RelativeLane lane = new RelativeLane(lat, 1);
  235.             if (getPerception().getLaneStructure().getExtendedCrossSection().contains(lane))
  236.             {
  237.                 SortedSet<Entry<Conflict>> conflictEntries = getPerception().getLaneStructure().getUpstreamObjects(lane,
  238.                         Conflict.class, getGtu(), RelativePosition.FRONT);
  239.                 if (!conflictEntries.isEmpty())
  240.                 {
  241.                     Entry<Conflict> entry = conflictEntries.first();
  242.                     return entry.getDistance().si < entry.getLaneBasedObject().getLength().si + getGtu().getLength().si;
  243.                 }
  244.             }
  245.             return false;
  246.         }
  247.         catch (GtuException | ParameterException exception)
  248.         {
  249.             throw new RuntimeException("Unexpected exception while computing conflict alongside.", exception);
  250.         }
  251.     }

  252.     /** {@inheritDoc} */
  253.     @Override
  254.     public final PerceptionCollectable<HeadwayTrafficLight, TrafficLight> getTrafficLights(final RelativeLane lane)
  255.     {
  256.         return computeIfAbsent("trafficLights", () -> computeTrafficLights(lane), lane);
  257.     }

  258.     /** {@inheritDoc} */
  259.     @Override
  260.     public final PerceptionCollectable<HeadwayConflict, Conflict> getConflicts(final RelativeLane lane)
  261.     {
  262.         return computeIfAbsent("conflicts", () -> computeConflicts(lane), lane);
  263.     }

  264.     /** {@inheritDoc} */
  265.     @Override
  266.     public final boolean isAlongsideConflictLeft()
  267.     {
  268.         return computeIfAbsent("alongside", () -> computeConflictAlongside(LateralDirectionality.LEFT),
  269.                 LateralDirectionality.LEFT);
  270.     }

  271.     /** {@inheritDoc} */
  272.     @Override
  273.     public final boolean isAlongsideConflictRight()
  274.     {
  275.         return computeIfAbsent("alongside", () -> computeConflictAlongside(LateralDirectionality.RIGHT),
  276.                 LateralDirectionality.RIGHT);
  277.     }

  278.     /** {@inheritDoc} */
  279.     @Override
  280.     public void updateAll() throws GtuException, NetworkException, ParameterException
  281.     {
  282.         // lazy evaluation
  283.     }

  284.     /** {@inheritDoc} */
  285.     @Override
  286.     public final String toString()
  287.     {
  288.         return "DirectIntersectionPerception";
  289.     }

  290. }