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<Section<S>>; 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<String>; names of series
69 * @param sections List<Section<S>>; 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<S>; Section<S> 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<S>; 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<Section<S>>; 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<?>; sampler
241 * @param path GraphPath<KpiLaneDirection>; 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 }