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