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