View Javadoc
1   package org.opentrafficsim.road.network.lane;
2   
3   import java.math.BigInteger;
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.value.vdouble.scalar.Length;
13  import org.djutils.exceptions.Throw;
14  import org.opentrafficsim.base.OtsRuntimeException;
15  import org.opentrafficsim.base.StripeElement;
16  import org.opentrafficsim.base.StripeElement.StripeLateralSync;
17  import org.opentrafficsim.core.gtu.GtuType;
18  import org.opentrafficsim.core.network.LateralDirectionality;
19  
20  /**
21   * Container for data about stripes, independent from the link and curvature.
22   * <p>
23   * Copyright (c) 2024-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
24   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
25   * </p>
26   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
27   */
28  public class StripeData
29  {
30  
31      /** Stripe elements. */
32      private List<StripeElement> elements;
33  
34      /** Left permeability. */
35      private final boolean left;
36  
37      /** Right permeability. */
38      private final boolean right;
39  
40      /** Lateral permeability per GTU type and direction. */
41      private final Map<GtuType, Set<LateralDirectionality>> permeabilityMap = new LinkedHashMap<>();
42  
43      /** Lateral synchronization. */
44      private StripeLateralSync lateralSync = StripeLateralSync.LINK;
45  
46      /** Phase synchronization. */
47      private StripePhaseSync phaseSync = StripePhaseSync.NONE;
48  
49      /** Period based on all stripe elements. */
50      private Double period;
51  
52      /**
53       * Constructor.
54       * @param elements list of stripe elements
55       * @param left left overall permeability
56       * @param right right overall permeability
57       */
58      public StripeData(final List<StripeElement> elements, final boolean left, final boolean right)
59      {
60          this.elements = elements;
61          this.left = left;
62          this.right = right;
63      }
64  
65      /**
66       * Returns the elements.
67       * @return elements
68       */
69      public List<StripeElement> getElements()
70      {
71          return this.elements;
72      }
73  
74      /**
75       * Sets the elements.
76       * @param elements elements
77       */
78      public void setElements(final List<StripeElement> elements)
79      {
80          this.elements = elements;
81          this.period = null;
82      }
83  
84      /**
85       * Add lateral permeability for a GTU type. This overrules overall stripe permeability. Add NONE to prevent lane changes.
86       * Add both LEFT and RIGHT in two calls, to enable lane changes. Add LEFT or RIGHT to enable one direction while prohibiting
87       * the other.
88       * @param gtuType GTU type to add permeability for
89       * @param lateralDirection direction to add compared to the direction of the design line
90       */
91      public void addPermeability(final GtuType gtuType, final LateralDirectionality lateralDirection)
92      {
93          if (!this.permeabilityMap.containsKey(gtuType))
94          {
95              this.permeabilityMap.put(gtuType, new LinkedHashSet<LateralDirectionality>(2));
96          }
97          this.permeabilityMap.get(gtuType).add(lateralDirection);
98      }
99  
100     /**
101      * Returns whether the given GTU type is allowed to cross the line in the given lateral direction.
102      * @param gtuType GTU type to look for.
103      * @param lateralDirection direction to look for (LEFT or RIGHT) compared to the direction of the design line.
104      * @return whether the road marker is permeable for the GTU type.
105      */
106     public final boolean isPermeable(final GtuType gtuType, final LateralDirectionality lateralDirection)
107     {
108         Throw.when(lateralDirection.isNone(), OtsRuntimeException.class,
109                 "May not request NONE lateral direction for permeability.");
110         for (GtuType testGtuType = gtuType; testGtuType != null; testGtuType = testGtuType.getParent().orElse(null))
111         {
112             Set<LateralDirectionality> set = this.permeabilityMap.get(testGtuType);
113             if (null != set)
114             {
115                 return set.contains(lateralDirection);
116             }
117         }
118         return lateralDirection.isLeft() ? this.left : this.right;
119     }
120 
121     /**
122      * Sets the lateral synchronization.
123      * @param lateralSync lateral synchronization
124      */
125     public void setLateralSync(final StripeLateralSync lateralSync)
126     {
127         this.lateralSync = lateralSync;
128     }
129 
130     /**
131      * Returns the lateral synchronization.
132      * @return lateral synchronization
133      */
134     public StripeLateralSync getLateralSync()
135     {
136         return this.lateralSync;
137     }
138 
139     /**
140      * Sets the phase synchronization.
141      * @param phaseSync phase synchronization
142      */
143     public void setPhaseSync(final StripePhaseSync phaseSync)
144     {
145         this.phaseSync = phaseSync;
146     }
147 
148     /**
149      * Returns the phase synchronization.
150      * @return phase synchronization
151      */
152     public StripePhaseSync getPhaseSync()
153     {
154         return this.phaseSync;
155     }
156 
157     /**
158      * Returns the period of the common dash pattern.
159      * @return period of the common dash pattern
160      */
161     public double getPeriod()
162     {
163         if (this.period == null)
164         {
165             this.period = getPeriod(this.elements);
166         }
167         return this.period;
168     }
169 
170     /**
171      * Returns the period after which the given line gap-dash patterns repeat as a whole. Lengths are rounded to a precision of
172      * 0.0001 to find the greatest common divisor.
173      * @param elements elements
174      * @return period
175      */
176     public static double getPeriod(final List<StripeElement> elements)
177     {
178         List<Double> lineLengths = new ArrayList<>();
179         for (StripeElement element : elements)
180         {
181             if (element.dashes() != null)
182             {
183                 double length = 0.0;
184                 for (Length gapDash : element.dashes())
185                 {
186                     length += gapDash.si;
187                 }
188                 lineLengths.add(length);
189             }
190         }
191         return getPeriod(lineLengths);
192     }
193 
194     /**
195      * Returns the period after which the given line gap-dash patterns repeat as a whole. Lengths are rounded to a precision of
196      * 0.0001 to find the greatest common divisor.
197      * @param lineLengths gap-dash pattern lengths
198      * @return period
199      */
200     private static double getPeriod(final Collection<Double> lineLengths)
201     {
202         Set<Double> set = new LinkedHashSet<>(lineLengths);
203         if (lineLengths.isEmpty())
204         {
205             return -1.0;
206         }
207         else if (set.size() == 1)
208         {
209             return ((long) (lineLengths.iterator().next() * 10000)) / 10000.0;
210         }
211         long gcd = 1L;
212         for (double length : set)
213         {
214             gcd = BigInteger.valueOf(gcd).gcd(BigInteger.valueOf((long) (length * 10000))).longValue();
215         }
216         return gcd / 10000.0;
217     }
218 
219     /**
220      * Returns the width, which is the sum of stripe elements.
221      * @return width
222      */
223     public Length getWidth()
224     {
225         Length width = Length.ZERO;
226         for (StripeElement element : getElements())
227         {
228             width = width.plus(element.width());
229         }
230         return width;
231     }
232 
233     /**
234      * Method of stripe phase synchronization.
235      */
236     public enum StripePhaseSync
237     {
238         /** Do not synchronize. */
239         NONE(false),
240 
241         /** Synchronize phase to upstream stripe. */
242         UPSTREAM(true),
243 
244         /** Synchronize phase to downstream stripe. */
245         DOWNSTREAM(true);
246 
247         /** Whether synchronization should be applied. */
248         private final boolean sync;
249 
250         /**
251          * Constructor.
252          * @param sync whether synchronization should be applied
253          */
254         StripePhaseSync(final boolean sync)
255         {
256             this.sync = sync;
257         }
258 
259         /**
260          * Returns whether synchronization should be applied.
261          * @return whether synchronization should be applied
262          */
263         public boolean isSync()
264         {
265             return this.sync;
266         }
267     }
268 
269 }