001 package org.expasy.jpl.commons.collection.render;
002
003
004 import java.awt.Color;
005 import java.awt.Dimension;
006 import java.awt.Paint;
007 import java.awt.image.BufferedImage;
008 import java.io.File;
009 import java.io.IOException;
010 import java.util.Arrays;
011 import org.apache.commons.math.stat.StatUtils;
012 import org.apache.commons.math.util.MathUtils;
013 import org.expasy.jpl.commons.base.render.ChartRenderer;
014 import org.expasy.jpl.commons.base.render.ImageFormat;
015 import org.expasy.jpl.commons.collection.Interval;
016 import org.expasy.jpl.commons.collection.stat.HistogramDataSet;
017 import org.expasy.jpl.commons.collection.stat.HistogramDataSetExporter;
018 import org.expasy.jpl.commons.collection.stat.StatisticalCategory;
019 import org.jfree.chart.ChartRenderingInfo;
020 import org.jfree.chart.ChartUtilities;
021 import org.jfree.chart.JFreeChart;
022 import org.jfree.chart.axis.AxisLocation;
023 import org.jfree.chart.axis.NumberAxis;
024 import org.jfree.chart.plot.DatasetRenderingOrder;
025 import org.jfree.chart.plot.XYPlot;
026 import org.jfree.chart.renderer.xy.StandardXYBarPainter;
027 import org.jfree.chart.renderer.xy.XYBarRenderer;
028 import org.jfree.data.statistics.SimpleHistogramBin;
029 import org.jfree.data.statistics.SimpleHistogramDataset;
030 import org.jfree.data.xy.IntervalXYDataset;
031 import org.jfree.ui.RectangleInsets;
032
033
034 /**
035 * This object renders {@code HistogramDataSet} objects.
036 *
037 * @author nikitin
038 *
039 * @version 1.0
040 *
041 */
042 public final class HistogramDataSetRenderer implements ChartRenderer {
043
044 private static final XYBarRenderer DEFAULT_RENDERER;
045 private static final HistogramDataSetExporter HISTO_EXPORTER =
046 HistogramDataSetExporter.newInstance();
047
048 /** dimension of rendered image */
049 private Dimension dimension;
050
051 /** image format */
052 private ImageFormat format;
053
054 private XYPlot plot;
055 private IntervalXYDataset dataset;
056
057 /** render the graph */
058 private XYBarRenderer renderer;
059
060 /** rendering info for interactive graphs **/
061 private ChartRenderingInfo renderingInfo;
062
063 /**
064 * if true histogram centers of all bins will be added for rendering else
065 * all values will be rendered (slow if lots of data)
066 */
067 private boolean isBinData;
068
069 static {
070 XYBarRenderer.setDefaultShadowsVisible(false);
071 XYBarRenderer.setDefaultBarPainter(new StandardXYBarPainter());
072
073 DEFAULT_RENDERER = new XYBarRenderer();
074 DEFAULT_RENDERER.setMargin(0.0);
075 }
076
077 private HistogramDataSetRenderer(ImageFormat format, Dimension dim) {
078 this.format = format;
079 this.dimension = dim;
080 init();
081 }
082
083 public static HistogramDataSetRenderer newInstance() {
084 return new HistogramDataSetRenderer(ImageFormat.PNG, new Dimension(600,
085 400));
086 }
087
088 /**
089 * The values of all bins will be added for rendering (quick rendering).
090 */
091 public void enableBinRenderMode() {
092 isBinData = true;
093 }
094
095 /**
096 * All the values will be rendered (slow down rendering if lots of data).
097 */
098 public void disableBinRenderMode() {
099 isBinData = false;
100 }
101
102 /**
103 * Set plot background color.
104 *
105 * @param color the background color.
106 */
107 public void setBackgroundColor(Paint color) {
108 plot.setBackgroundPaint(color);
109 }
110
111 /**
112 * Set image dimension.
113 *
114 * @param dim the image dimension.
115 */
116 public void setDimension(Dimension dim) {
117 this.dimension = dim;
118 }
119
120 /**
121 * Set image format.
122 *
123 * @param format the image format (png or jpeg).
124 */
125 public void setImageFormat(ImageFormat format) {
126 this.format = format;
127 }
128
129 /**
130 * Get image format.
131 *
132 * @return format the image format (png or jpeg).
133 */
134 public ImageFormat getImageFormat() {
135 return format;
136 }
137
138 private void init() {
139 plot = new XYPlot();
140 plot.setDomainAxis(new NumberAxis("x"));
141 plot.setRangeAxis(new NumberAxis("frequency"));
142 plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
143 plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
144
145 plot.setDatasetRenderingOrder(DatasetRenderingOrder.REVERSE);
146 renderer = DEFAULT_RENDERER;
147 plot.setRenderer(renderer);
148
149 renderingInfo = new ChartRenderingInfo();
150 }
151
152 /**
153 * Set the legend for the var axis.
154 *
155 * @param legend the x-axis legend.
156 */
157 public void setXAxisLegend(String legend) {
158 plot.setDomainAxis(new NumberAxis(legend));
159 }
160
161 /**
162 * Set the legend for the frequency axis.
163 *
164 * @param legend the y-axis legend.
165 */
166 public void setYAxisLegend(String legend) {
167 plot.setRangeAxis(new NumberAxis(legend));
168 }
169
170 /**
171 * Empty data set.
172 */
173 public void clearDataSet() {
174 dataset = null;
175 }
176
177 /**
178 * Add the statistical data set (default colors: 1st serie (blue), 2nd serie
179 * (red)).
180 *
181 * @param statSerie the statistical serie to render.
182 * @param title the title associated to the data set.
183 */
184 public void addDataSet(HistogramDataSet histo, String title) {
185 addDataSet(histo, title, null);
186 }
187
188 /**
189 * Add the statistical data set.
190 *
191 * @param statSerie the statistical serie to render.
192 * @param title the title associated to the data set.
193 * @param color the color of the serie.
194 */
195 public void addDataSet(HistogramDataSet histo, String title, Color color) {
196 if (!isBinData && histo.getValues() != null
197 && histo.getValues().length > 10000) {
198 System.err
199 .println("Warning: "
200 + histo.getValues().length
201 + " data to load for rendering !\n"
202 + "Please switch to the bin render mode (see enableBinRenderMode() method).");
203 }
204 HISTO_EXPORTER.exportBinStatus(isBinData);
205 dataset =
206 HistogramDataSetRenderer.toXYDataset(HISTO_EXPORTER, title, histo);
207 }
208
209 /**
210 * @return a new chart that wrap data set.
211 */
212 private JFreeChart flushIntoChart() {
213 plot.setDataset(dataset);
214
215 return new JFreeChart(plot);
216 }
217
218 /**
219 * Create an image corresponding to the given MS spectrum.
220 *
221 * @param pl the peak list to render.
222 * @param title the image title.
223 * @return a new buffered image.
224 */
225 public BufferedImage render(HistogramDataSet histo, String title) {
226 addDataSet(histo, title);
227
228 JFreeChart chart = flushIntoChart();
229
230 return chart.createBufferedImage((int) dimension.getWidth(),
231 (int) dimension.getHeight(), renderingInfo);
232 }
233
234 /**
235 * Retrieve rendering info (for interactive graphs).
236 *
237 * @return the rendering information associated to the chart.
238 */
239 public ChartRenderingInfo getRenderingInfo() {
240 return renderingInfo;
241 }
242
243 /**
244 * Export as image file.
245 *
246 * @param statSerie the statistical serie to render and export.
247 * @param dir the directory to put new file in.
248 * @param title the file name.
249 */
250 public void exportChart(HistogramDataSet histo, String title,
251 String filename) {
252 addDataSet(histo, title);
253 exportChart(filename);
254 }
255
256 /**
257 * Create an image (prerequisite: data sets have already been entered via
258 * {@code addDataSet} method).
259 *
260 * @return a new buffered image.
261 */
262 public BufferedImage render() {
263 JFreeChart chart = flushIntoChart();
264 return chart.createBufferedImage((int) dimension.getWidth(),
265 (int) dimension.getHeight(), renderingInfo);
266 }
267
268 /**
269 * Export a chart (prerequisite: data sets have already been entered via
270 * {@code addDataSet} method).
271 *
272 * @param filename the image file name without extension (defined in {@code
273 * ImageFormat}).
274 */
275 public void exportChart(String filename) {
276 JFreeChart chart = flushIntoChart();
277
278 try {
279 switch (format) {
280 case JPG:
281 ChartUtilities.saveChartAsJPEG(new File(filename + ".jpg"),
282 chart, (int) dimension.getWidth(), (int) dimension
283 .getHeight());
284 break;
285 case PNG:
286 ChartUtilities.saveChartAsPNG(new File(filename + ".png"),
287 chart, (int) dimension.getWidth(), (int) dimension
288 .getHeight());
289 }
290 } catch (IOException e) {
291 System.err.println("Problem occurred creating chart.");
292 }
293 }
294
295 public void setChartColor(int chartIndex, Paint color) {
296 renderer.setSeriesPaint(chartIndex, color);
297 }
298
299 public Paint getChartColor(int chartIndex) {
300 return renderer.getSeriesPaint(chartIndex);
301 }
302
303 /**
304 * Convert the histogram for jfreechart display (add values and weights).
305 *
306 * @param title the title of the histogram.
307 * @param isLoadBinData true if load bin centers instead of data.
308 *
309 * @return an instance of {@code IntervalXYDataset}.
310 */
311 public static IntervalXYDataset toXYDataset(
312 HistogramDataSetExporter exporter, String title, HistogramDataSet histo) {
313
314 StringBuilder note = new StringBuilder();
315
316 double[] vs = histo.getValues();
317 double[] ws = histo.getWeights();
318 double[] bs = histo.getBins();
319
320 // if no data, render bins
321 if (vs == null || vs.length == 0 || ws == null || ws.length == 0) {
322 exporter.setBinExport(true);
323 }
324
325 if (!exporter.isBinExport()) {
326 note.append(vs.length + " data, ");
327 } else {
328 note.append(bs.length + " bins, bin width=" + histo.getBinWidth()
329 + ", ");
330 }
331
332 if (histo.isDataNormalized()) {
333 if (histo.isNormalizedSum()) {
334 note.append("norm sum=" + histo.getNormalizedValue() + ", ");
335 } else {
336 note.append("max value (+norm)=" + histo.getNormalizedValue()
337 + ", ");
338 }
339 } else {
340 double mean = 0;
341 if (exporter.isBinExport()) {
342 mean = StatUtils.mean(bs);
343
344 if (mean < 1 || mean > 1000) {
345 bs =
346 MathUtils
347 .normalizeArray(bs, histo.getNormalizedValue());
348 note
349 .append("norm sum=" + histo.getNormalizedValue() + ", ");
350 }
351 } else {
352 mean = StatUtils.mean(ws);
353
354 if (mean < 1 || mean > 1000) {
355 ws =
356 MathUtils
357 .normalizeArray(ws, histo.getNormalizedValue());
358 note
359 .append("norm sum=" + histo.getNormalizedValue() + ", ");
360 }
361 }
362 }
363
364 if (note.length() > 0) {
365 note.delete(note.length() - 2, note.length());
366 title += " [" + note + "]";
367 }
368
369 SimpleHistogramDataset dataset = new SimpleHistogramDataset(title);
370
371 for (int i = 0; i < bs.length; i++) {
372 Interval interval = histo.getIntervalBinAt(i);
373
374 SimpleHistogramBin bin =
375 new SimpleHistogramBin(interval.getLowerBound(), interval
376 .getUpperBound(), interval.isLowerBoundIncluded(), interval
377 .isUpperBoundIncluded());
378
379 dataset.addBin(bin);
380
381 // new bin data
382 if (exporter.isBinExport()) {
383 StatisticalCategory catego = histo.toCategory(i);
384
385 int freq = (int) Math.round(bs[i]);
386 double binVal = 0;
387
388 if (exporter.isLoadCenterBin()) {
389 binVal = catego.getCenter();
390 } else {
391 binVal = catego.getInterval().getLowerBound();
392 }
393
394 if (freq == 1) {
395 dataset.addObservation(binVal);
396 } else {
397 double[] obs = new double[freq];
398 Arrays.fill(obs, binVal);
399
400 dataset.addObservations(obs);
401 }
402 }
403 }
404
405 // add data into bins
406 if (!exporter.isBinExport()) {
407 if (ws.length == 0) {
408 dataset.addObservations(vs);
409 } else {
410 for (int i = 0; i < vs.length; i++) {
411 if (histo.getIntervalOfDefinition().contains(vs[i])) {
412
413 if (ws[i] > 0) {
414 double[] obs = new double[(int) Math.round(ws[i])];
415 Arrays.fill(obs, vs[i]);
416
417 dataset.addObservations(obs);
418 }
419 }
420 }
421 }
422 }
423 return dataset;
424 }
425
426 @Override
427 public void setXAxisDefaultInterval() {
428 // TODO Auto-generated method stub
429
430 }
431
432 @Override
433 public void setXAxisInterval(double from, double to) {
434 // TODO Auto-generated method stub
435
436 }
437 }