View Javadoc
1   package org.opentrafficsim.road.network.lane.conflict;
2   
3   import java.util.LinkedHashMap;
4   import java.util.Map;
5   
6   import org.djunits.value.vdouble.scalar.Length;
7   import org.djutils.draw.point.OrientedPoint2d;
8   import org.djutils.exceptions.Throw;
9   import org.opentrafficsim.core.geometry.OtsGeometryException;
10  import org.opentrafficsim.road.network.lane.CrossSectionLink.Priority;
11  import org.opentrafficsim.road.network.lane.Lane;
12  
13  /**
14   * Default determination of priority based on link priority, or right-hand traffic. Note that this class is stateful as the
15   * priorities are cached. So each conflict pair should receive a separate {@code DefaultConflictRule}. This rule is only for use
16   * on merge and crossing conflicts. For split conflicts there is a separate rule {@code SplitConflictRule}.
17   * <p>
18   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
19   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
20   * </p>
21   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
22   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
23   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
24   */
25  public class DefaultConflictRule implements ConflictRule
26  {
27  
28      /** Priority per conflict. */
29      private Map<String, ConflictPriority> map = null;
30  
31      // Throw.whenNull(priority1, "Conflict rule may not be null.");
32      // Throw.whenNull(priority2, "Conflict rule may not be null.");
33      // if (priority1.equals(ConflictPriority.SPLIT) || priority2.equals(ConflictPriority.SPLIT))
34      // {
35      // // Split with split (on split)
36      // Throw.when(!priority1.equals(ConflictPriority.SPLIT) || !priority2.equals(ConflictPriority.SPLIT),
37      // NetworkException.class, "Both conflict rules should be split for conflict type split.");
38      // }
39      // else
40      // {
41      // // Priority with give-way/stop
42      // boolean check1 = priority1.equals(ConflictPriority.PRIORITY) && !priority2.equals(ConflictPriority.GIVE_WAY)
43      // && !priority2.equals(ConflictPriority.STOP);
44      // boolean check2 = priority2.equals(ConflictPriority.PRIORITY) && !priority1.equals(ConflictPriority.GIVE_WAY)
45      // && !priority1.equals(ConflictPriority.STOP);
46      // boolean check3 =
47      // priority1.equals(ConflictPriority.GIVE_WAY) && !priority2.equals(ConflictPriority.PRIORITY);
48      // boolean check4 =
49      // priority2.equals(ConflictPriority.GIVE_WAY) && !priority1.equals(ConflictPriority.PRIORITY);
50      // boolean check5 = priority1.equals(ConflictPriority.STOP) && !priority2.equals(ConflictPriority.PRIORITY);
51      // boolean check6 = priority2.equals(ConflictPriority.STOP) && !priority1.equals(ConflictPriority.PRIORITY);
52      // Throw.when(check1 || check2 || check3 || check4 || check5 || check6, NetworkException.class,
53      // "Conflict rules need to be a combination of 'PRIORITY' and 'GIVE_WAY' or 'STOP', "
54      // + "if any of these types is used.");
55      // // All-stop with all-stop
56      // boolean check7 =
57      // priority1.equals(ConflictPriority.ALL_STOP) && !priority2.equals(ConflictPriority.ALL_STOP);
58      // boolean check8 =
59      // priority2.equals(ConflictPriority.ALL_STOP) && !priority1.equals(ConflictPriority.ALL_STOP);
60      // Throw.when(check7 || check8, NetworkException.class,
61      // "Conflict rule 'ALL_STOP' can only be combined with a conflict rule 'ALL_STOP'.");
62      // // No split
63      // Throw.when(priority1.equals(ConflictPriority.SPLIT) || priority2.equals(ConflictPriority.SPLIT),
64      // NetworkException.class, "Conflict rule 'SPLIT' may only be used on conflicts of type SPLIT.");
65      // }
66  
67      /** {@inheritDoc} */
68      @Override
69      public ConflictPriority determinePriority(final Conflict conflict)
70      {
71          if (this.map == null)
72          {
73              ConflictPriority[] conflictPriorities = getConflictRules(conflict.getLane(), conflict.getLongitudinalPosition(),
74                      conflict.getOtherConflict().getLane(), conflict.getOtherConflict().getLongitudinalPosition(),
75                      conflict.getConflictType());
76              this.map = new LinkedHashMap<>();
77              this.map.put(conflict.getId(), conflictPriorities[0]);
78              this.map.put(conflict.getOtherConflict().getId(), conflictPriorities[1]);
79          }
80          ConflictPriority out = this.map.get(conflict.getId());
81          Throw.when(out == null, IllegalArgumentException.class,
82                  "Conflict %s is not related to a conflict that was used before in the same conflict rule.", conflict);
83          return out;
84      }
85  
86      /**
87       * Determine conflict rules.
88       * @param lane1 Lane; lane 1
89       * @param longitudinalPosition1 Length; position 1
90       * @param lane2 Lane; lane 2
91       * @param longitudinalPosition2 Length; position 2
92       * @param conflictType ConflictType; conflict type
93       * @return conflict rule 1 and 2
94       */
95      private static ConflictPriority[] getConflictRules(final Lane lane1, final Length longitudinalPosition1, final Lane lane2,
96              final Length longitudinalPosition2, final ConflictType conflictType)
97      {
98          Throw.when(conflictType.equals(ConflictType.SPLIT), UnsupportedOperationException.class,
99                  "DefaultConflictRule is not for use on a split conflict. Use SplitConflictRule instead.");
100         ConflictPriority[] conflictRules = new ConflictPriority[2];
101         Priority priority1 = lane1.getLink().getPriority();
102         Priority priority2 = lane2.getLink().getPriority();
103         if (priority1.isAllStop() && priority2.isAllStop())
104         {
105             conflictRules[0] = ConflictPriority.ALL_STOP;
106             conflictRules[1] = ConflictPriority.ALL_STOP;
107         }
108         else if (priority1.equals(priority2) || (priority1.isYield() && priority2.isStop())
109                 || (priority2.isYield() && priority1.isStop()))
110         {
111             Throw.when(priority1.isBusStop(), IllegalArgumentException.class,
112                     "Both priorities are 'bus stop', which is not allowed. Use BusStopConflictRule for bus stops.");
113             // Based on right- or left-hand traffic
114             OrientedPoint2d p1;
115             OrientedPoint2d p2;
116             try
117             {
118                 p1 = lane1.getCenterLine().getLocation(longitudinalPosition1);
119                 p2 = lane2.getCenterLine().getLocation(longitudinalPosition2);
120             }
121             catch (OtsGeometryException exception)
122             {
123                 throw new RuntimeException("Conflict position is not on its lane.", exception);
124             }
125             double diff = p2.getDirZ() - p1.getDirZ();
126             while (diff > Math.PI)
127             {
128                 diff -= 2 * Math.PI;
129             }
130             while (diff < -Math.PI)
131             {
132                 diff += 2 * Math.PI;
133             }
134             if (diff > 0.0)
135             {
136                 // 2 comes from the right
137                 conflictRules[0] = priority1.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
138                 conflictRules[1] = ConflictPriority.PRIORITY;
139             }
140             else
141             {
142                 // 1 comes from the right
143                 conflictRules[0] = ConflictPriority.PRIORITY;
144                 conflictRules[1] = priority2.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
145             }
146         }
147         else if ((priority1.isPriority() || priority1.isNone()) // note, both NONE already captured
148                 && (priority2.isNone() || priority2.isYield() || priority2.isStop()))
149         {
150             conflictRules[0] = ConflictPriority.PRIORITY;
151             conflictRules[1] = priority2.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
152         }
153         else if ((priority2.isPriority() || priority2.isNone())
154                 && (priority1.isNone() || priority1.isYield() || priority1.isStop()))
155         {
156             conflictRules[0] = priority1.isStop() ? ConflictPriority.STOP : ConflictPriority.YIELD;
157             conflictRules[1] = ConflictPriority.PRIORITY;
158         }
159         else
160         {
161             throw new IllegalArgumentException(
162                     "Could not sort out conflict priority from link priorities " + priority1 + " and " + priority2);
163         }
164         return conflictRules;
165     }
166 
167     /** {@inheritDoc} */
168     @Override
169     public final String toString()
170     {
171         return "DefaultConflictRule";
172     }
173 
174 }