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