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