View Javadoc
1   package org.opentrafficsim.draw.graphs;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.List;
6   import java.util.NoSuchElementException;
7   
8   import org.djunits.value.vdouble.scalar.Length;
9   import org.djunits.value.vdouble.scalar.Speed;
10  import org.djunits.value.vdouble.scalar.Time;
11  import org.djutils.exceptions.Throw;
12  import org.djutils.immutablecollections.Immutable;
13  import org.djutils.immutablecollections.ImmutableArrayList;
14  import org.djutils.immutablecollections.ImmutableList;
15  import org.djutils.means.ArithmeticMean;
16  import org.opentrafficsim.kpi.interfaces.LaneData;
17  import org.opentrafficsim.kpi.sampling.Sampler;
18  import org.opentrafficsim.kpi.sampling.SpaceTimeRegion;
19  
20  /**
21   * A {@code GraphPath} defines the spatial dimension of graphs. It has a number of sections, each of which may have one or more
22   * source objects depending on the number of series. For example, a 3-lane road may result in a few sections each having 3
23   * series. Graphs can aggregate the series, or show multiple series.
24   * <p>
25   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
27   * </p>
28   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
29   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
30   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
31   * @param <S> underlying type of path sections
32   */
33  public class GraphPath<S> extends AbstractGraphSpace<S>
34  {
35  
36      /** Sections. */
37      private final List<Section<S>> sections;
38  
39      /** Start distance per section. */
40      private final List<Length> startDistances = new ArrayList<>();
41  
42      /** Total path length. */
43      private final Length totalLength;
44  
45      /** Mean speed limit over the entire path. */
46      private final Speed speedLimit;
47  
48      /**
49       * Constructor for a one-series path.
50       * @param name String; name
51       * @param sections List&lt;Section&lt;S&gt;&gt;; sections
52       */
53      public GraphPath(final String name, final List<Section<S>> sections)
54      {
55          this(new ArrayList<String>()
56          {
57              /** */
58              private static final long serialVersionUID = 20181020L;
59              {
60                  add(name);
61              }
62          }, sections);
63      }
64  
65      /**
66       * Constructor.
67       * @param seriesNames List&lt;String&gt;; names of series
68       * @param sections List&lt;Section&lt;S&gt;&gt;; sections
69       */
70      public GraphPath(final List<String> seriesNames, final List<Section<S>> sections)
71      {
72          super(seriesNames);
73          this.sections = sections;
74          Length cumulativeLength = Length.ZERO;
75          for (Section<S> section : sections)
76          {
77              this.startDistances.add(cumulativeLength);
78              cumulativeLength = cumulativeLength.plus(section.length());
79          }
80          this.totalLength = cumulativeLength;
81          ArithmeticMean<Double, Double> mean = new ArithmeticMean<>();
82          for (Section<S> section : sections)
83          {
84              mean.add(section.speedLimit().si, section.length().si);
85          }
86          this.speedLimit = Speed.instantiateSI(mean.getMean());
87      }
88  
89      /**
90       * Returns the start distance of the section.
91       * @param section Section&lt;?&gt; section
92       * @return Length; start distance of the section
93       */
94      public Length getStartDistance(final Section<?> section)
95      {
96          int index = this.sections.indexOf(section);
97          Throw.when(index == -1, IllegalArgumentException.class, "Section is not part of the path.");
98          return this.startDistances.get(index);
99      }
100 
101     /**
102      * Returns the total path length.
103      * @return Length; total path length
104      */
105     public Length getTotalLength()
106     {
107         return this.totalLength;
108     }
109 
110     /**
111      * Returns the mean speed over the entire section.
112      * @return Speed; mean speed over the entire section
113      */
114     public Speed getSpeedLimit()
115     {
116         return this.speedLimit;
117     }
118 
119     /**
120      * Returns a section.
121      * @param index int; index of section
122      * @return Section&lt;S&gt;; section
123      */
124     public Section<S> get(final int index)
125     {
126         return this.sections.get(index);
127     }
128 
129     /** {@inheritDoc} */
130     @Override
131     public Iterator<S> iterator()
132     {
133         return new Iterator<S>()
134         {
135 
136             /** Section iterator. */
137             private Iterator<Section<S>> sectionIterator = getSections().iterator();
138 
139             /** Source object iterator per section. */
140             private Iterator<S> sourceIterator = this.sectionIterator.hasNext() ? this.sectionIterator.next().iterator() : null;
141 
142             /** {@inheritDoc} */
143             @Override
144             public boolean hasNext()
145             {
146                 if (this.sourceIterator != null && this.sourceIterator.hasNext())
147                 {
148                     return true;
149                 }
150                 while (this.sectionIterator.hasNext())
151                 {
152                     Iterator<S> it = this.sectionIterator.next().iterator();
153                     if (it.hasNext())
154                     {
155                         this.sourceIterator = it;
156                         return true;
157                     }
158                 }
159                 this.sourceIterator = null;
160                 return false;
161             }
162 
163             /** {@inheritDoc} */
164             @Override
165             public S next()
166             {
167                 Throw.when(!hasNext(), NoSuchElementException.class, "No more element left.");
168                 return this.sourceIterator.next();
169             }
170         };
171     }
172 
173     /** {@inheritDoc} */
174     @Override
175     public Iterator<S> iterator(final int series)
176     {
177         List<S> list = new ArrayList<>();
178         for (Section<S> section : this.sections)
179         {
180             list.add(section.getSource(series));
181         }
182         return new ImmutableArrayList<>(list, Immutable.WRAP).iterator();
183     }
184 
185     /**
186      * Returns an immutable list of the sections.
187      * @return ImmutableList&lt;Section&lt;S&gt;&gt;; sections
188      */
189     public ImmutableList<Section<S>> getSections()
190     {
191         return new ImmutableArrayList<>(this.sections, Immutable.WRAP);
192     }
193 
194     /** {@inheritDoc} */
195     @Override
196     public String toString()
197     {
198         return "GraphPath [sections=" + this.sections + ", startDistances=" + this.startDistances + ", totalLength="
199                 + this.totalLength + ", speedLimit=" + this.speedLimit + "]";
200     }
201 
202     /**
203      * Start recording along path.
204      * @param sampler Sampler&lt;?, L&gt;; sampler
205      * @param path GraphPath&lt;L&gt;; path
206      * @param <L> lane data type
207      */
208     public static <L extends LaneData<L>> void initRecording(final Sampler<?, L> sampler, final GraphPath<L> path)
209     {
210         for (Section<L> section : path.getSections())
211         {
212             for (L lane : section)
213             {
214                 sampler.registerSpaceTimeRegion(new SpaceTimeRegion<>(lane, Length.ZERO, lane.getLength(), Time.ZERO,
215                         Time.instantiateSI(Double.MAX_VALUE)));
216             }
217         }
218     }
219 
220     /**
221      * Class for sections.
222      * <p>
223      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
224      * <br>
225      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
226      * </p>
227      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
228      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
229      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
230      * @param <L> underlying type
231      * @param length Length; length of the section
232      * @param speedLimit Speed; speed limit on the section
233      * @param sections List&lt;S&gt;; list of underlying objects
234      */
235     public static record Section<L>(Length length, Speed speedLimit, List<L> sections) implements Iterable<L>
236     {
237         /**
238          * Returns the source object.
239          * @param series int; number
240          * @return S; underlying object of the series
241          */
242         public L getSource(final int series)
243         {
244             return this.sections.get(series);
245         }
246 
247         /** {@inheritDoc} */
248         @Override
249         public Iterator<L> iterator()
250         {
251             return this.sections.iterator();
252         }
253     }
254 
255 }