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://github.com/peter-knoppers">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 name
51       * @param sections 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 names of series
68       * @param sections 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 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 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 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 index of section
122      * @return section
123      */
124     public Section<S> get(final int index)
125     {
126         return this.sections.get(index);
127     }
128 
129     @Override
130     public Iterator<S> iterator()
131     {
132         return new Iterator<S>()
133         {
134 
135             /** Section iterator. */
136             private Iterator<Section<S>> sectionIterator = getSections().iterator();
137 
138             /** Source object iterator per section. */
139             private Iterator<S> sourceIterator = this.sectionIterator.hasNext() ? this.sectionIterator.next().iterator() : null;
140 
141             @Override
142             public boolean hasNext()
143             {
144                 if (this.sourceIterator != null && this.sourceIterator.hasNext())
145                 {
146                     return true;
147                 }
148                 while (this.sectionIterator.hasNext())
149                 {
150                     Iterator<S> it = this.sectionIterator.next().iterator();
151                     if (it.hasNext())
152                     {
153                         this.sourceIterator = it;
154                         return true;
155                     }
156                 }
157                 this.sourceIterator = null;
158                 return false;
159             }
160 
161             @Override
162             public S next()
163             {
164                 Throw.when(!hasNext(), NoSuchElementException.class, "No more element left.");
165                 return this.sourceIterator.next();
166             }
167         };
168     }
169 
170     @Override
171     public Iterator<S> iterator(final int series)
172     {
173         List<S> list = new ArrayList<>();
174         for (Section<S> section : this.sections)
175         {
176             list.add(section.getSource(series));
177         }
178         return new ImmutableArrayList<>(list, Immutable.WRAP).iterator();
179     }
180 
181     /**
182      * Returns an immutable list of the sections.
183      * @return sections
184      */
185     public ImmutableList<Section<S>> getSections()
186     {
187         return new ImmutableArrayList<>(this.sections, Immutable.WRAP);
188     }
189 
190     @Override
191     public String toString()
192     {
193         return "GraphPath [sections=" + this.sections + ", startDistances=" + this.startDistances + ", totalLength="
194                 + this.totalLength + ", speedLimit=" + this.speedLimit + "]";
195     }
196 
197     /**
198      * Start recording along path.
199      * @param sampler sampler
200      * @param path path
201      * @param <L> lane data type
202      */
203     public static <L extends LaneData<L>> void initRecording(final Sampler<?, L> sampler, final GraphPath<L> path)
204     {
205         for (Section<L> section : path.getSections())
206         {
207             for (L lane : section)
208             {
209                 sampler.registerSpaceTimeRegion(new SpaceTimeRegion<>(lane, Length.ZERO, lane.getLength(), Time.ZERO,
210                         Time.instantiateSI(Double.MAX_VALUE)));
211             }
212         }
213     }
214 
215     /**
216      * Class for sections.
217      * <p>
218      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
219      * <br>
220      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
221      * </p>
222      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
223      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
224      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
225      * @param <L> underlying type
226      * @param length length of the section
227      * @param speedLimit speed limit on the section
228      * @param sections list of underlying objects
229      */
230     public static record Section<L>(Length length, Speed speedLimit, List<L> sections) implements Iterable<L>
231     {
232         /**
233          * Returns the source object.
234          * @param series number
235          * @return underlying object of the series
236          */
237         public L getSource(final int series)
238         {
239             return this.sections.get(series);
240         }
241 
242         @Override
243         public Iterator<L> iterator()
244         {
245             return this.sections.iterator();
246         }
247     }
248 
249 }