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.Duration;
9 import org.djunits.value.vdouble.scalar.Length;
10 import org.djunits.value.vdouble.scalar.Speed;
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.math.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
22
23
24
25
26
27
28
29
30
31
32
33 public class GraphPath<S> extends AbstractGraphSpace<S>
34 {
35
36
37 private final List<Section<S>> sections;
38
39
40 private final List<Length> startDistances = new ArrayList<>();
41
42
43 private final Length totalLength;
44
45
46 private final Speed speedLimit;
47
48
49 private boolean circular = false;
50
51
52
53
54
55
56 public GraphPath(final String name, final List<Section<S>> sections)
57 {
58 this(new ArrayList<String>()
59 {
60
61 private static final long serialVersionUID = 20181020L;
62 {
63 add(name);
64 }
65 }, sections);
66 }
67
68
69
70
71
72
73 public GraphPath(final List<String> seriesNames, final List<Section<S>> sections)
74 {
75 super(seriesNames);
76 this.sections = sections;
77 Length cumulativeLength = Length.ZERO;
78 for (Section<S> section : sections)
79 {
80 this.startDistances.add(cumulativeLength);
81 cumulativeLength = cumulativeLength.plus(section.length());
82 }
83 this.totalLength = cumulativeLength;
84 ArithmeticMean<Double, Double> mean = new ArithmeticMean<>();
85 for (Section<S> section : sections)
86 {
87 mean.add(section.speedLimit().si, section.length().si);
88 }
89 this.speedLimit = Speed.ofSI(mean.getMean());
90 }
91
92
93
94
95
96
97 @SuppressWarnings("hiddenfield")
98 public GraphPath<S> setCircular(final boolean circular)
99 {
100 this.circular = circular;
101 return this;
102 }
103
104
105
106
107
108 public boolean isCircular()
109 {
110 return this.circular;
111 }
112
113
114
115
116
117
118 public Length getStartDistance(final Section<?> section)
119 {
120 int index = this.sections.indexOf(section);
121 Throw.when(index == -1, IllegalArgumentException.class, "Section is not part of the path.");
122 return this.startDistances.get(index);
123 }
124
125
126
127
128
129 public Length getTotalLength()
130 {
131 return this.totalLength;
132 }
133
134
135
136
137
138 public Speed getSpeedLimit()
139 {
140 return this.speedLimit;
141 }
142
143
144
145
146
147
148 public Section<S> get(final int index)
149 {
150 return this.sections.get(index);
151 }
152
153 @Override
154 public Iterator<S> iterator()
155 {
156 return new Iterator<S>()
157 {
158
159
160 private Iterator<Section<S>> sectionIterator = getSections().iterator();
161
162
163 private Iterator<S> sourceIterator = this.sectionIterator.hasNext() ? this.sectionIterator.next().iterator() : null;
164
165 @Override
166 public boolean hasNext()
167 {
168 if (this.sourceIterator != null && this.sourceIterator.hasNext())
169 {
170 return true;
171 }
172 while (this.sectionIterator.hasNext())
173 {
174 Iterator<S> it = this.sectionIterator.next().iterator();
175 if (it.hasNext())
176 {
177 this.sourceIterator = it;
178 return true;
179 }
180 }
181 this.sourceIterator = null;
182 return false;
183 }
184
185 @Override
186 public S next()
187 {
188 Throw.when(!hasNext(), NoSuchElementException.class, "No more element left.");
189 return this.sourceIterator.next();
190 }
191 };
192 }
193
194 @Override
195 public Iterator<S> iterator(final int series)
196 {
197 List<S> list = new ArrayList<>();
198 for (Section<S> section : this.sections)
199 {
200 list.add(section.getSource(series));
201 }
202 return new ImmutableArrayList<>(list, Immutable.WRAP).iterator();
203 }
204
205
206
207
208
209 public ImmutableList<Section<S>> getSections()
210 {
211 return new ImmutableArrayList<>(this.sections, Immutable.WRAP);
212 }
213
214 @Override
215 public String toString()
216 {
217 return "GraphPath [sections=" + this.sections + ", startDistances=" + this.startDistances + ", totalLength="
218 + this.totalLength + ", speedLimit=" + this.speedLimit + "]";
219 }
220
221
222
223
224
225
226
227 public static <L extends LaneData<L>> void initRecording(final Sampler<?, L> sampler, final GraphPath<L> path)
228 {
229 for (Section<L> section : path.getSections())
230 {
231 for (L lane : section)
232 {
233 sampler.registerSpaceTimeRegion(new SpaceTimeRegion<>(lane, Length.ZERO, lane.getLength(), Duration.ZERO,
234 Duration.ofSI(Double.MAX_VALUE)));
235 }
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 public record Section<L>(Length length, Speed speedLimit, List<L> sections) implements Iterable<L>
255 {
256
257
258
259
260
261 public L getSource(final int series)
262 {
263 return this.sections.get(series);
264 }
265
266 @Override
267 public Iterator<L> iterator()
268 {
269 return this.sections.iterator();
270 }
271 }
272
273 }