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