DefaultConflictRule.java

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

  2. import java.util.LinkedHashMap;
  3. import java.util.Map;

  4. import org.djunits.value.vdouble.scalar.Length;
  5. import org.djutils.draw.point.OrientedPoint2d;
  6. import org.djutils.exceptions.Throw;
  7. import org.opentrafficsim.core.geometry.OtsGeometryException;
  8. import org.opentrafficsim.road.network.lane.CrossSectionLink.Priority;
  9. import org.opentrafficsim.road.network.lane.Lane;

  10. /**
  11.  * Default determination of priority based on link priority, or right-hand traffic. Note that this class is stateful as the
  12.  * priorities are cached. So each conflict pair should receive a separate {@code DefaultConflictRule}. This rule is only for use
  13.  * on merge and crossing conflicts. For split conflicts there is a separate rule {@code SplitConflictRule}.
  14.  * <p>
  15.  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  16.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  17.  * </p>
  18.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  19.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  20.  * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  21.  */
  22. public class DefaultConflictRule implements ConflictRule
  23. {

  24.     /** Priority per conflict. */
  25.     private Map<String, ConflictPriority> map = null;

  26.     // Throw.whenNull(priority1, "Conflict rule may not be null.");
  27.     // Throw.whenNull(priority2, "Conflict rule may not be null.");
  28.     // if (priority1.equals(ConflictPriority.SPLIT) || priority2.equals(ConflictPriority.SPLIT))
  29.     // {
  30.     // // Split with split (on split)
  31.     // Throw.when(!priority1.equals(ConflictPriority.SPLIT) || !priority2.equals(ConflictPriority.SPLIT),
  32.     // NetworkException.class, "Both conflict rules should be split for conflict type split.");
  33.     // }
  34.     // else
  35.     // {
  36.     // // Priority with give-way/stop
  37.     // boolean check1 = priority1.equals(ConflictPriority.PRIORITY) && !priority2.equals(ConflictPriority.GIVE_WAY)
  38.     // && !priority2.equals(ConflictPriority.STOP);
  39.     // boolean check2 = priority2.equals(ConflictPriority.PRIORITY) && !priority1.equals(ConflictPriority.GIVE_WAY)
  40.     // && !priority1.equals(ConflictPriority.STOP);
  41.     // boolean check3 =
  42.     // priority1.equals(ConflictPriority.GIVE_WAY) && !priority2.equals(ConflictPriority.PRIORITY);
  43.     // boolean check4 =
  44.     // priority2.equals(ConflictPriority.GIVE_WAY) && !priority1.equals(ConflictPriority.PRIORITY);
  45.     // boolean check5 = priority1.equals(ConflictPriority.STOP) && !priority2.equals(ConflictPriority.PRIORITY);
  46.     // boolean check6 = priority2.equals(ConflictPriority.STOP) && !priority1.equals(ConflictPriority.PRIORITY);
  47.     // Throw.when(check1 || check2 || check3 || check4 || check5 || check6, NetworkException.class,
  48.     // "Conflict rules need to be a combination of 'PRIORITY' and 'GIVE_WAY' or 'STOP', "
  49.     // + "if any of these types is used.");
  50.     // // All-stop with all-stop
  51.     // boolean check7 =
  52.     // priority1.equals(ConflictPriority.ALL_STOP) && !priority2.equals(ConflictPriority.ALL_STOP);
  53.     // boolean check8 =
  54.     // priority2.equals(ConflictPriority.ALL_STOP) && !priority1.equals(ConflictPriority.ALL_STOP);
  55.     // Throw.when(check7 || check8, NetworkException.class,
  56.     // "Conflict rule 'ALL_STOP' can only be combined with a conflict rule 'ALL_STOP'.");
  57.     // // No split
  58.     // Throw.when(priority1.equals(ConflictPriority.SPLIT) || priority2.equals(ConflictPriority.SPLIT),
  59.     // NetworkException.class, "Conflict rule 'SPLIT' may only be used on conflicts of type SPLIT.");
  60.     // }

  61.     /** {@inheritDoc} */
  62.     @Override
  63.     public ConflictPriority determinePriority(final Conflict conflict)
  64.     {
  65.         if (this.map == null)
  66.         {
  67.             ConflictPriority[] conflictPriorities = getConflictRules(conflict.getLane(), conflict.getLongitudinalPosition(),
  68.                     conflict.getOtherConflict().getLane(), conflict.getOtherConflict().getLongitudinalPosition(),
  69.                     conflict.getConflictType());
  70.             this.map = new LinkedHashMap<>();
  71.             this.map.put(conflict.getId(), conflictPriorities[0]);
  72.             this.map.put(conflict.getOtherConflict().getId(), conflictPriorities[1]);
  73.         }
  74.         ConflictPriority out = this.map.get(conflict.getId());
  75.         Throw.when(out == null, IllegalArgumentException.class,
  76.                 "Conflict %s is not related to a conflict that was used before in the same conflict rule.", conflict);
  77.         return out;
  78.     }

  79.     /**
  80.      * Determine conflict rules.
  81.      * @param lane1 Lane; lane 1
  82.      * @param longitudinalPosition1 Length; position 1
  83.      * @param lane2 Lane; lane 2
  84.      * @param longitudinalPosition2 Length; position 2
  85.      * @param conflictType ConflictType; conflict type
  86.      * @return conflict rule 1 and 2
  87.      */
  88.     private static ConflictPriority[] getConflictRules(final Lane lane1, final Length longitudinalPosition1, final Lane lane2,
  89.             final Length longitudinalPosition2, final ConflictType conflictType)
  90.     {
  91.         Throw.when(conflictType.equals(ConflictType.SPLIT), UnsupportedOperationException.class,
  92.                 "DefaultConflictRule is not for use on a split conflict. Use SplitConflictRule instead.");
  93.         ConflictPriority[] conflictRules = new ConflictPriority[2];
  94.         Priority priority1 = lane1.getLink().getPriority();
  95.         Priority priority2 = lane2.getLink().getPriority();
  96.         if (priority1.isAllStop() && priority2.isAllStop())
  97.         {
  98.             conflictRules[0] = ConflictPriority.ALL_STOP;
  99.             conflictRules[1] = ConflictPriority.ALL_STOP;
  100.         }
  101.         else if (priority1.equals(priority2) || (priority1.isYield() && priority2.isStop())
  102.                 || (priority2.isYield() && priority1.isStop()))
  103.         {
  104.             Throw.when(priority1.isBusStop(), IllegalArgumentException.class,
  105.                     "Both priorities are 'bus stop', which is not allowed. Use BusStopConflictRule for bus stops.");
  106.             // Based on right- or left-hand traffic
  107.             OrientedPoint2d p1;
  108.             OrientedPoint2d p2;
  109.             try
  110.             {
  111.                 p1 = lane1.getCenterLine().getLocation(longitudinalPosition1);
  112.                 p2 = lane2.getCenterLine().getLocation(longitudinalPosition2);
  113.             }
  114.             catch (OtsGeometryException exception)
  115.             {
  116.                 throw new RuntimeException("Conflict position is not on its lane.", exception);
  117.             }
  118.             double diff = p2.getDirZ() - p1.getDirZ();
  119.             while (diff > Math.PI)
  120.             {
  121.                 diff -= 2 * Math.PI;
  122.             }
  123.             while (diff < -Math.PI)
  124.             {
  125.                 diff += 2 * Math.PI;
  126.             }
  127.             if (diff > 0.0)
  128.             {
  129.                 // 2 comes from the right
  130.                 conflictRules[0] = priority1.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
  131.                 conflictRules[1] = ConflictPriority.PRIORITY;
  132.             }
  133.             else
  134.             {
  135.                 // 1 comes from the right
  136.                 conflictRules[0] = ConflictPriority.PRIORITY;
  137.                 conflictRules[1] = priority2.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
  138.             }
  139.         }
  140.         else if ((priority1.isPriority() || priority1.isNone()) // note, both NONE already captured
  141.                 && (priority2.isNone() || priority2.isYield() || priority2.isStop()))
  142.         {
  143.             conflictRules[0] = ConflictPriority.PRIORITY;
  144.             conflictRules[1] = priority2.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
  145.         }
  146.         else if ((priority2.isPriority() || priority2.isNone())
  147.                 && (priority1.isNone() || priority1.isYield() || priority1.isStop()))
  148.         {
  149.             conflictRules[0] = priority1.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
  150.             conflictRules[1] = ConflictPriority.PRIORITY;
  151.         }
  152.         else
  153.         {
  154.             throw new IllegalArgumentException(
  155.                     "Could not sort out conflict priority from link priorities " + priority1 + " and " + priority2);
  156.         }
  157.         return conflictRules;
  158.     }

  159.     /** {@inheritDoc} */
  160.     @Override
  161.     public final String toString()
  162.     {
  163.         return "DefaultConflictRule";
  164.     }

  165. }