1 package org.opentrafficsim.draw.graphs;
2
3 import java.awt.Color;
4 import java.awt.Font;
5 import java.awt.Graphics2D;
6 import java.awt.geom.AffineTransform;
7 import java.awt.geom.Rectangle2D;
8 import java.awt.image.BufferedImage;
9 import java.io.IOException;
10 import java.util.LinkedHashSet;
11 import java.util.Set;
12 import java.util.UUID;
13
14 import org.djunits.value.vdouble.scalar.Duration;
15 import org.djunits.value.vdouble.scalar.Time;
16 import org.djutils.base.Identifiable;
17 import org.djutils.event.EventType;
18 import org.djutils.metadata.MetaData;
19 import org.djutils.metadata.ObjectDescriptor;
20 import org.jfree.chart.ChartUtils;
21 import org.jfree.chart.JFreeChart;
22 import org.jfree.chart.plot.XYPlot;
23 import org.jfree.chart.title.TextTitle;
24 import org.jfree.data.general.Dataset;
25 import org.jfree.data.general.DatasetChangeEvent;
26 import org.jfree.data.general.DatasetChangeListener;
27 import org.jfree.data.general.DatasetGroup;
28
29
30
31
32
33
34
35
36
37
38
39
40 public abstract class AbstractPlot implements Identifiable, Dataset
41 {
42
43
44
45
46
47 public static final EventType GRAPH_ADD_EVENT = new EventType("GRAPH.ADD",
48 new MetaData("Graph add", "Graph added", new ObjectDescriptor("Graph id", "Id of the graph", String.class)));
49
50
51
52
53
54 public static final EventType GRAPH_REMOVE_EVENT = new EventType("GRAPH.REMOVE",
55 new MetaData("Graph remove", "Graph removed", new ObjectDescriptor("Graph id", "Id of the graph", String.class)));
56
57
58 public static final Time DEFAULT_INITIAL_UPPER_TIME_BOUND = Time.ofSI(300.0);
59
60
61 private final PlotScheduler scheduler;
62
63
64 private final String id = UUID.randomUUID().toString();
65
66
67 private final String caption;
68
69
70 private JFreeChart chart;
71
72
73 private Set<DatasetChangeListener> listeners = new LinkedHashSet<>();
74
75
76 private final Duration delay;
77
78
79 private Duration updateTime;
80
81
82 private int updates = 0;
83
84
85 private Duration updateInterval;
86
87
88 private boolean inUpdate = false;
89
90
91 private Duration offeredUpdateInterval;
92
93
94
95
96
97
98
99
100 public AbstractPlot(final PlotScheduler scheduler, final String caption, final Duration updateInterval,
101 final Duration delay)
102 {
103 this.scheduler = scheduler;
104 this.caption = caption;
105 this.updateInterval = updateInterval;
106 this.delay = delay;
107 this.updates = (int) (scheduler.getTime().si / updateInterval.si);
108 update();
109 }
110
111
112
113
114
115 @SuppressWarnings("methodlength")
116 protected void setChart(final JFreeChart chart)
117 {
118 this.chart = chart;
119
120
121 chart.setTitle(new TextTitle(chart.getTitle().getText(), new Font("SansSerif", java.awt.Font.BOLD, 16)));
122
123
124 chart.getPlot().setBackgroundPaint(Color.LIGHT_GRAY);
125 chart.setBackgroundPaint(Color.WHITE);
126 if (chart.getPlot() instanceof XYPlot)
127 {
128 chart.getXYPlot().setDomainGridlinePaint(Color.WHITE);
129 chart.getXYPlot().setRangeGridlinePaint(Color.WHITE);
130 }
131
132 }
133
134
135
136
137
138
139
140
141
142 public byte[] encodeAsPng(final int width, final int height, final double fontSize) throws IOException
143 {
144
145
146 double baseWidth = width / (fontSize / 16);
147 double baseHeight = height / (fontSize / 16);
148
149 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
150 Graphics2D g2 = image.createGraphics();
151
152 AffineTransform saved = g2.getTransform();
153 g2.transform(AffineTransform.getScaleInstance(width / baseWidth, height / baseHeight));
154 getChart().draw(g2, new Rectangle2D.Double(0, 0, baseWidth, baseHeight), null, null);
155 g2.setTransform(saved);
156 g2.dispose();
157 return ChartUtils.encodeAsPNG(image);
158 }
159
160 @Override
161 public final DatasetGroup getGroup()
162 {
163 return null;
164 }
165
166 @Override
167 public final void setGroup(final DatasetGroup group)
168 {
169
170 }
171
172
173
174
175
176
177
178
179 public void setAutoBoundDomain(final XYPlot plot)
180 {
181
182 }
183
184
185
186
187
188
189
190
191 public void setAutoBoundRange(final XYPlot plot)
192 {
193
194 }
195
196
197
198
199
200 public abstract GraphType getGraphType();
201
202
203
204
205
206
207
208 public abstract String getStatusLabel(double domainValue, double rangeValue);
209
210
211
212
213
214 protected abstract void increaseTime(Duration time);
215
216
217
218
219 public final void notifyPlotChange()
220 {
221 DatasetChangeEvent event = new DatasetChangeEvent(this, this);
222 for (DatasetChangeListener dcl : this.listeners)
223 {
224 dcl.datasetChanged(event);
225 }
226 }
227
228
229
230
231
232 public final JFreeChart getChart()
233 {
234 return this.chart;
235 }
236
237 @Override
238 public final String getId()
239 {
240 return this.id;
241 }
242
243 @Override
244 public final void addChangeListener(final DatasetChangeListener listener)
245 {
246 this.listeners.add(listener);
247 }
248
249 @Override
250 public final void removeChangeListener(final DatasetChangeListener listener)
251 {
252 this.listeners.remove(listener);
253 }
254
255
256
257
258
259 public final synchronized void setUpdateInterval(final Duration interval)
260 {
261 this.scheduler.cancelEvent(this);
262 this.updates = (int) (this.scheduler.getTime().si / interval.si);
263 this.updateInterval = interval;
264 this.updateTime = Duration.ofSI(this.updates * this.updateInterval.si);
265 scheduleNextUpdateEvent();
266 }
267
268
269
270
271
272
273 public void offerUpdateInterval(final Duration interval)
274 {
275 if (this.inUpdate)
276 {
277
278 this.offeredUpdateInterval = interval;
279 }
280 else
281 {
282
283 setUpdateInterval(interval);
284 }
285 }
286
287
288
289
290
291 public final Duration getUpdateTime()
292 {
293 return this.updateTime;
294 }
295
296
297
298
299 public synchronized void update()
300 {
301 this.inUpdate = true;
302 this.updateTime = this.scheduler.getTime();
303 increaseTime(this.updateTime.minus(this.delay));
304 notifyPlotChange();
305 scheduleNextUpdateEvent();
306 this.inUpdate = false;
307 }
308
309
310
311
312 private void scheduleNextUpdateEvent()
313 {
314 if (this.offeredUpdateInterval != null)
315 {
316 this.scheduler.cancelEvent(this);
317 this.updates = (int) (this.scheduler.getTime().si / this.offeredUpdateInterval.si);
318 this.updateInterval = this.offeredUpdateInterval;
319 this.updateTime = Duration.ofSI(this.updates * this.updateInterval.si);
320 this.offeredUpdateInterval = null;
321 }
322 this.updates++;
323
324 this.scheduler.scheduleUpdate(Duration.ofSI(this.updateInterval.si * this.updates + this.delay.si), this);
325 }
326
327
328
329
330
331 public String getCaption()
332 {
333 return this.caption;
334 }
335
336 }