View Javadoc
1   /*
2    * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this program; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17   *
18   * Additionally licensed with:
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *      http://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
31   */
32  package org.spf4j.perf.impl.chart;
33  
34  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
35  import java.awt.Color;
36  import java.awt.Graphics;
37  import java.awt.image.BufferedImage;
38  import org.jfree.chart.ChartFactory;
39  import org.jfree.chart.JFreeChart;
40  import org.jfree.chart.axis.DateAxis;
41  import org.jfree.chart.axis.NumberAxis;
42  import org.jfree.chart.labels.StandardXYSeriesLabelGenerator;
43  import org.jfree.chart.plot.XYPlot;
44  import org.jfree.chart.renderer.PaintScale;
45  import org.jfree.chart.renderer.xy.XYBlockRenderer;
46  import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
47  import org.jfree.chart.title.PaintScaleLegend;
48  import org.jfree.data.time.FixedMillisecond;
49  import org.jfree.data.time.TimeSeries;
50  import org.jfree.data.time.TimeSeriesCollection;
51  import org.jfree.data.xy.XYDataset;
52  import org.spf4j.base.Arrays;
53  import org.spf4j.base.Pair;
54  
55  /**
56   *
57   * @author zoly
58   */
59  public final class Charts {
60  
61    private Charts() {
62    }
63  
64    @SuppressFBWarnings("CE_CLASS_ENVY")
65    public static JFreeChart createHeatJFreeChart(final String[] dsNames, final double[][] values,
66            final long startTimeMillis, final long stepMillis, final String uom, final String chartName) {
67      final QuantizedXYZDatasetImpl dataSet = new QuantizedXYZDatasetImpl(dsNames, values,
68              startTimeMillis, stepMillis);
69      NumberAxis xAxis = new NumberAxis("Time");
70      xAxis.setStandardTickUnits(dataSet.createXTickUnits());
71      xAxis.setLowerMargin(0);
72      xAxis.setUpperMargin(0);
73      xAxis.setVerticalTickLabels(true);
74      NumberAxis yAxis = new NumberAxis(uom);
75      yAxis.setStandardTickUnits(dataSet.createYTickUnits());
76      yAxis.setLowerMargin(0);
77      yAxis.setUpperMargin(0);
78      XYBlockRenderer renderer = new XYBlockRenderer();
79      PaintScale scale;
80      if (dataSet.getMinValue() >= dataSet.getMaxValue()) {
81        if (dataSet.getMinValue() == Double.POSITIVE_INFINITY) {
82          scale = new InverseGrayScale(0, 1);
83        } else {
84          scale = new InverseGrayScale(dataSet.getMinValue(), dataSet.getMaxValue() + 1);
85        }
86      } else {
87        scale = new InverseGrayScale(dataSet.getMinValue(), dataSet.getMaxValue());
88      }
89      renderer.setPaintScale(scale);
90      renderer.setBlockWidth(1);
91      renderer.setBlockHeight(1);
92      XYPlot plot = new XYPlot(dataSet, xAxis, yAxis, renderer);
93      plot.setBackgroundPaint(Color.white);
94      plot.setDomainGridlinesVisible(false);
95      plot.setRangeGridlinesVisible(false);
96      plot.setRangeMinorGridlinesVisible(false);
97      JFreeChart chart = new JFreeChart(chartName, plot);
98      PaintScaleLegend legend = new PaintScaleLegend(scale, new NumberAxis("Count"));
99      legend.setMargin(0, 5, 0, 5);
100     chart.addSubtitle(legend);
101     chart.removeLegend();
102     chart.setBackgroundPaint(Color.white);
103     return chart;
104   }
105 
106   private static JFreeChart createJFreeChart(final String chartName, final String uom,
107           final XYDataset timeseriescollection) {
108     JFreeChart jfreechart = ChartFactory.createTimeSeriesChart(chartName,
109             "Time", uom, timeseriescollection, true, true, false);
110     XYPlot xyplot = (XYPlot) jfreechart.getPlot();
111     DateAxis dateaxis = (DateAxis) xyplot.getDomainAxis();
112     dateaxis.setVerticalTickLabels(true);
113     XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer) xyplot.getRenderer();
114     xylineandshaperenderer.setDefaultShapesVisible(true);
115     xylineandshaperenderer.setUseFillPaint(true);
116     xylineandshaperenderer.setLegendItemToolTipGenerator(new StandardXYSeriesLabelGenerator("Tooltip {0}"));
117     return jfreechart;
118   }
119 
120   public static BufferedImage createMinMaxAvgCountImg(final String chartName, final long[] timestamps,
121           final double[] min, final double[] max, final double[] total, final double[] count,
122           final String uom, final int width, final int height) {
123 
124     BufferedImage bi = Charts.createTimeSeriesJFreeChart(chartName, timestamps,
125             new String[]{"min", "max", "avg"}, uom,
126             new double[][]{min, max, Arrays.divide(total, count)}).createBufferedImage(width, height - height / 3);
127     BufferedImage bi2 = Charts.createTimeSeriesJFreeChart(null, timestamps,
128             new String[]{"count"}, "count", new double[][]{count}).createBufferedImage(width, height / 3);
129     BufferedImage combined = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
130     final Graphics graphics = combined.getGraphics();
131     try {
132       graphics.drawImage(bi, 0, 0, null);
133       graphics.drawImage(bi2, 0, height - height / 3, null);
134     } finally {
135       graphics.dispose();
136     }
137     return combined;
138   }
139 
140   public static BufferedImage generateCountTotalChart(final String groupName, final long[][] timestamps,
141           final String[] measurementNames, final String uom1, final double[][] measurements, final int width,
142           final int height, final String[] measurementNames2, final String uom2, final double[][] measurements2) {
143     BufferedImage count = Charts.createTimeSeriesJFreeChart("Measurements for "
144             + groupName + " generated by spf4j",
145             timestamps, measurementNames, uom1, measurements).createBufferedImage(width, height / 2);
146     BufferedImage total = Charts.createTimeSeriesJFreeChart(null,
147             timestamps, measurementNames2, uom2, measurements2).createBufferedImage(width, height / 2);
148     BufferedImage combined = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
149     final Graphics graphics = combined.getGraphics();
150     try {
151       graphics.drawImage(count, 0, 0, null);
152       graphics.drawImage(total, 0, height / 2, null);
153     } finally {
154       graphics.dispose();
155     }
156     return combined;
157   }
158 
159   private static TimeSeriesCollection createTimeSeriesCollection(final String[] measurementNames,
160           final long[] timestamps, final double[][] measurements) {
161     TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
162     for (int i = 0; i < measurementNames.length; i++) {
163       TimeSeries tseries = new TimeSeries(measurementNames[i]);
164       for (int j = 0; j < timestamps.length; j++) {
165         FixedMillisecond ts = new FixedMillisecond(timestamps[j]);
166         tseries.add(ts, measurements[i][j]);
167       }
168       timeseriescollection.addSeries(tseries);
169     }
170     return timeseriescollection;
171   }
172 
173   public static JFreeChart createTimeSeriesJFreeChart(final String chartName, final long[] timestamps,
174           final String[] measurementNames, final String uom, final double[][] measurements) {
175     TimeSeriesCollection timeseriescollection
176             = createTimeSeriesCollection(measurementNames, timestamps, measurements);
177     return createJFreeChart(chartName, uom, timeseriescollection);
178   }
179 
180   private static TimeSeriesCollection createTimeSeriesCollection(final String[] measurementNames,
181           final long[][] timestamps, final double[][] measurements) {
182     TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
183     for (int i = 0; i < measurementNames.length; i++) {
184       TimeSeries tseries = new TimeSeries(measurementNames[i]);
185       for (int j = 0; j < timestamps[i].length; j++) {
186         FixedMillisecond ts = new FixedMillisecond(timestamps[i][j]);
187         tseries.add(ts, measurements[i][j]);
188       }
189       timeseriescollection.addSeries(tseries);
190     }
191     return timeseriescollection;
192   }
193 
194   public static JFreeChart createTimeSeriesJFreeChart(final String chartName, final long[][] timestamps,
195           final String[] measurementNames, final String uom, final double[][] measurements) {
196     TimeSeriesCollection timeseriescollection
197             = createTimeSeriesCollection(measurementNames, timestamps, measurements);
198     return createJFreeChart(chartName, uom, timeseriescollection);
199   }
200 
201   public static Pair<long[], double[][]> fillGaps(final long[] timestamps,
202           final long[][] data, final int sampleTime, final int nrColumns) {
203     long startTime = timestamps[0];
204     int nrSamples = (int) ((timestamps[timestamps.length - 1] - startTime) / sampleTime) + 1;
205     long[] lts = new long[nrSamples];
206     double[][] dr = new double[nrSamples][];
207     long nextTime = startTime;
208     int j = 0;
209     int maxDeviation = sampleTime / 2;
210     double[] nodata = new double[nrColumns];
211     for (int i = 0; i < nrColumns; i++) {
212       nodata[i] = Double.NaN;
213     }
214     for (int i = 0; i < nrSamples; i++) {
215       lts[i] = nextTime;
216 
217       if (Math.abs(timestamps[j] - nextTime) < maxDeviation) {
218         dr[i] = Arrays.toDoubleArray(data[j]);
219         j++;
220       } else {
221         dr[i] = nodata;
222       }
223       nextTime += sampleTime;
224     }
225     return Pair.of(lts, dr);
226   }
227 
228 }