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