1 package org.opentrafficsim.animation.graphs;
2
3 import java.util.Optional;
4
5 import org.djunits.value.vdouble.scalar.Duration;
6 import org.djutils.exceptions.Throw;
7 import org.jfree.chart.JFreeChart;
8 import org.jfree.chart.axis.NumberAxis;
9 import org.jfree.chart.plot.XYPlot;
10 import org.jfree.chart.renderer.xy.XYBarRenderer;
11 import org.jfree.data.DomainOrder;
12 import org.jfree.data.xy.IntervalXYDataset;
13 import org.opentrafficsim.animation.graphs.AbstractPlot.PaintState;
14 import org.opentrafficsim.animation.graphs.DistributionPlotExtendedData.DistributionPaintState;
15 import org.opentrafficsim.animation.graphs.GraphPath.Section;
16 import org.opentrafficsim.kpi.interfaces.GtuData;
17 import org.opentrafficsim.kpi.interfaces.LaneData;
18 import org.opentrafficsim.kpi.sampling.SamplerData;
19 import org.opentrafficsim.kpi.sampling.Trajectory;
20 import org.opentrafficsim.kpi.sampling.TrajectoryGroup;
21 import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
22
23
24
25
26
27
28
29
30
31
32
33
34 public class DistributionPlotExtendedData<G extends GtuData, Z extends Number> extends AbstractPlot<DistributionPaintState>
35 implements IntervalXYDataset
36 {
37
38
39 private final SamplerData<G> samplerData;
40
41
42 private final GraphPath<? extends LaneData<?>> path;
43
44
45 private final ExtendedDataType<Z, ?, ?, ?> dataType;
46
47
48 private double lastUpdateTime = Double.NEGATIVE_INFINITY;
49
50
51 private final double[] x;
52
53
54 private final double dx;
55
56
57 private final long[] y;
58
59
60
61
62
63
64
65
66
67 public DistributionPlotExtendedData(final SamplerData<G> samplerData, final GraphPath<? extends LaneData<?>> path,
68 final ExtendedDataType<Z, ?, ?, G> dataType, final PlotScheduler plotScheduler, final LabelData<Z> labelData)
69 {
70 super(plotScheduler, labelData.caption(), Duration.ofSI(10.0), Duration.ZERO);
71 int n = 1 + (int) Math.round(
72 (labelData.maximum().doubleValue() - labelData.minimum().doubleValue()) / labelData.step().doubleValue());
73 this.x = new double[n];
74 for (int i = 0; i < n; i++)
75 {
76 this.x[i] = labelData.minimum().doubleValue() + i * labelData.step().doubleValue();
77 }
78 this.dx = labelData.step().doubleValue();
79 this.y = new long[n];
80 this.samplerData = Throw.whenNull(samplerData, "samplerData");
81 this.path = Throw.whenNull(path, "path");
82 this.dataType = Throw.whenNull(dataType, "dataType");
83 setChart(createChart(labelData.xLabel()));
84 }
85
86
87
88
89
90
91 private JFreeChart createChart(final String xLabel)
92 {
93 NumberAxis xAxis = new NumberAxis(xLabel);
94 xAxis.setRange(this.x[0], this.x[this.x.length - 1]);
95 NumberAxis yAxis = new NumberAxis("Count [-]");
96 yAxis.setAutoRangeIncludesZero(true);
97 XYBarRenderer renderer = new XYBarRenderer();
98 XYPlot plot = new XYPlot(this, xAxis, yAxis, renderer);
99 return new JFreeChart(getCaption(), JFreeChart.DEFAULT_TITLE_FONT, plot, false);
100 }
101
102 @Override
103 public int getSeriesCount()
104 {
105 return 1;
106 }
107
108 @SuppressWarnings("rawtypes")
109 @Override
110 public Comparable getSeriesKey(final int series)
111 {
112 return Integer.valueOf(1);
113 }
114
115 @SuppressWarnings("rawtypes")
116 @Override
117 public int indexOf(final Comparable seriesKey)
118 {
119 return 0;
120 }
121
122 @Override
123 public DomainOrder getDomainOrder()
124 {
125 return DomainOrder.ASCENDING;
126 }
127
128 @Override
129 public int getItemCount(final int series)
130 {
131 return this.x.length - 1;
132 }
133
134 @Override
135 public Number getX(final int series, final int item)
136 {
137 return this.x[item];
138 }
139
140 @Override
141 public double getXValue(final int series, final int item)
142 {
143 return this.x[item];
144 }
145
146 @Override
147 public Number getY(final int series, final int item)
148 {
149 return this.y[item];
150 }
151
152 @Override
153 public double getYValue(final int series, final int item)
154 {
155 return this.y[item];
156 }
157
158 @Override
159 public Number getStartX(final int series, final int item)
160 {
161 return this.x[item];
162 }
163
164 @Override
165 public double getStartXValue(final int series, final int item)
166 {
167 return this.x[item];
168 }
169
170 @Override
171 public Number getEndX(final int series, final int item)
172 {
173 return this.x[item + 1];
174 }
175
176 @Override
177 public double getEndXValue(final int series, final int item)
178 {
179 return this.x[item + 1];
180 }
181
182 @Override
183 public Number getStartY(final int series, final int item)
184 {
185 return getYValue(series, item);
186 }
187
188 @Override
189 public double getStartYValue(final int series, final int item)
190 {
191 return getYValue(series, item);
192 }
193
194 @Override
195 public Number getEndY(final int series, final int item)
196 {
197 return getYValue(series, item);
198 }
199
200 @Override
201 public double getEndYValue(final int series, final int item)
202 {
203 return getYValue(series, item);
204 }
205
206 @Override
207 public GraphType getGraphType()
208 {
209 return GraphType.OTHER;
210 }
211
212 @Override
213 public String getStatusLabel(final double domainValue, final double rangeValue)
214 {
215 return " ";
216 }
217
218 @Override
219 protected DistributionPaintState emptyPaintState()
220 {
221 return new DistributionPaintState(Duration.ZERO);
222 }
223
224 @Override
225 protected void calculatePaintState(final Duration time)
226 {
227 for (Section<? extends LaneData<?>> section : this.path.getSections())
228 {
229 for (LaneData<?> lane : section.sections())
230 {
231 processLane(lane);
232 }
233 }
234 this.lastUpdateTime = time.si;
235 offerPaintState(new DistributionPaintState(time));
236 }
237
238
239
240
241
242 private void processLane(final LaneData<?> lane)
243 {
244 Optional<TrajectoryGroup<G>> trajectories = this.samplerData.getTrajectoryGroup(lane);
245 if (trajectories.isPresent())
246 {
247 for (Trajectory<G> trajectory : trajectories.get())
248 {
249 int n = trajectory.size() - 1;
250 while (n >= 0 && trajectory.getT(n) > this.lastUpdateTime)
251 {
252 Z z = trajectory.getExtendedData(this.dataType, n);
253 double value = z == null ? Double.NaN : z.doubleValue();
254 if (!Double.isNaN(value) && value >= this.x[0] && value < this.x[this.x.length - 1])
255 {
256 int index = (int) Math.floor((value - this.x[0]) / this.dx);
257 this.y[index]++;
258 }
259 n--;
260 }
261 }
262 }
263 }
264
265 @Override
266 public void setAutoBoundDomain(final XYPlot plot)
267 {
268
269 }
270
271 @Override
272 public void setAutoBoundRange(final XYPlot plot)
273 {
274
275 }
276
277
278
279
280
281
282
283
284
285
286 public record LabelData<Z extends Number>(String caption, String xLabel, Z minimum, Z step, Z maximum)
287 {
288
289
290
291
292
293
294
295
296
297 public LabelData
298 {
299 Throw.when(maximum.doubleValue() <= minimum.doubleValue(), IllegalArgumentException.class,
300 "maximum must be greater than minimum");
301 Throw.when(step.doubleValue() <= 0.0, IllegalArgumentException.class, "step must be greater than 0");
302 }
303
304 }
305
306
307
308
309
310 public record DistributionPaintState(Duration getAvailableTime) implements PaintState
311 {
312 }
313
314 }