com.griddynamics.jagger.reporting.chart.ChartHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.griddynamics.jagger.reporting.chart.ChartHelper.java

Source

/*
 * Copyright (c) 2010-2012 Grid Dynamics Consulting Services, Inc, All Rights Reserved
 * http://www.griddynamics.com
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of
 * the GNU Lesser General Public License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.griddynamics.jagger.reporting.chart;

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import com.griddynamics.jagger.util.Pair;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.*;

public class ChartHelper {
    public enum ColorTheme {
        LIGHT, DARK
    }

    public static JFreeChart createTimeSeriesChart(String title, List<TimeSeriesChartSpecification> specifications,
            String xLabel, String yLabel, ColorTheme theme) {

        TimeSeriesCollection dataCollection = new TimeSeriesCollection();
        for (TimeSeriesChartSpecification specification : specifications) {
            TimeSeries series = new TimeSeries(specification.getLabel(), Millisecond.class);
            for (Map.Entry<Date, Double> point : specification.getData().entrySet()) {
                series.add(new Millisecond(point.getKey()), point.getValue());
            }
            dataCollection.addSeries(series);
        }

        JFreeChart chart = ChartFactory.createTimeSeriesChart(title, xLabel, yLabel, dataCollection, true, false,
                false);

        formatColorTheme(chart, theme);

        XYPlot plot = chart.getXYPlot();

        Color[] colors = generateJetSpectrum(specifications.size());
        for (int i = 0; i < specifications.size(); i++) {
            plot.getRenderer().setSeriesPaint(i, colors[i]);
        }

        DateAxis axis = (DateAxis) plot.getDomainAxis();
        axis.setDateFormatOverride(new SimpleDateFormat("dd/MM hh:mm:ss"));

        return chart;
    }

    public static JFreeChart createXYChart(String title, XYDataset data, String xLabel, String yLabel,
            int pointRadius, int lineThickness, ColorTheme theme) {
        JFreeChart chart = ChartFactory.createXYLineChart(title, xLabel, yLabel, data, PlotOrientation.VERTICAL,
                true, false, false);

        formatColorTheme(chart, theme);

        XYPlot plot = (XYPlot) chart.getPlot();
        Shape icon = new Ellipse2D.Double(-pointRadius, -pointRadius, pointRadius * 2, pointRadius * 2);

        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        plot.setRenderer(renderer);

        Color[] colors = generateJetSpectrum(data.getSeriesCount());
        for (int i = 0; i < data.getSeriesCount(); i++) {
            plot.getRenderer().setSeriesStroke(i, new BasicStroke(lineThickness));
            plot.getRenderer().setSeriesShape(i, icon);
            ((XYLineAndShapeRenderer) plot.getRenderer()).setSeriesShapesVisible(i, true);
            ((XYLineAndShapeRenderer) plot.getRenderer()).setSeriesShapesFilled(i, true);
            plot.getRenderer().setSeriesPaint(i, colors[i]);
        }

        LegendTitle legend = chart.getLegend();
        Font legendFont = legend.getItemFont();
        float legendFontSize = legendFont.getSize();
        Font newLegendFont = legendFont.deriveFont(legendFontSize * 0.6f);
        legend.setItemFont(newLegendFont);

        ValueAxis domainAxis = ((XYPlot) chart.getPlot()).getDomainAxis();
        Font domainAxisLabelFont = domainAxis.getLabelFont();
        float domainAxisLabelFontSize = domainAxisLabelFont.getSize();
        domainAxis.setLabelFont(domainAxisLabelFont.deriveFont(domainAxisLabelFontSize * 0.6f));

        Font domainAxisTickLabelFont = domainAxis.getTickLabelFont();
        float domainAxisTickLabelFontSize = domainAxisTickLabelFont.getSize();
        domainAxis.setTickLabelFont(domainAxisTickLabelFont.deriveFont(domainAxisTickLabelFontSize * 0.6f));

        ValueAxis rangeAxis = ((XYPlot) chart.getPlot()).getRangeAxis();
        Font rangeAxisLabelFont = rangeAxis.getLabelFont();
        float rangeAxisLabelFontSize = rangeAxisLabelFont.getSize();
        rangeAxis.setLabelFont(rangeAxisLabelFont.deriveFont(rangeAxisLabelFontSize * 0.6f));

        Font rangeAxisTickLabelFont = rangeAxis.getTickLabelFont();
        float rangeAxisTickLabelFontSize = rangeAxisTickLabelFont.getSize();
        rangeAxis.setTickLabelFont(rangeAxisTickLabelFont.deriveFont(rangeAxisTickLabelFontSize * 0.6f));

        return chart;
    }

    public static JFreeChart createStackedBarChart(String title, List<List<Double>> bars, List<String> zoneLabels,
            String xLabel, String yLabel, ColorTheme theme) {
        DefaultCategoryDataset ds = new DefaultCategoryDataset();

        Integer barId = 0;
        int maxStack = 0;
        for (List<Double> bar : bars) {
            int i = 0;
            maxStack = Math.max(maxStack, bar.size());
            for (double value : bar) {
                ds.addValue(value, zoneLabels.get(i), barId);
                i++;
            }
            barId++;
        }

        JFreeChart chart = ChartFactory.createStackedBarChart(title, xLabel, yLabel, ds, PlotOrientation.VERTICAL,
                true, false, false);

        formatColorTheme(chart, theme);
        formatBars(chart);

        CategoryPlot plot = (CategoryPlot) chart.getPlot();
        plot.getDomainAxis().setCategoryMargin(0);
        plot.getDomainAxis().setLowerMargin(0);
        plot.getDomainAxis().setUpperMargin(0);

        Color[] colors = generateJetSpectrum(maxStack);
        for (int i = 0; i < maxStack; i++) {
            plot.getRenderer().setSeriesPaint(i, colors[i]);
            plot.getRenderer().setSeriesOutlinePaint(i, Color.white);
        }

        return chart;
    }

    public static JFreeChart createStackedAreaChart(String title, XYSeriesCollection areaData,
            XYSeriesCollection lineData, String xLabel, String yLabel, ColorTheme theme) {

        final ValueAxis xAxis = new NumberAxis(xLabel);
        final ValueAxis yAxis = new NumberAxis(yLabel);
        XYPlot mainPlot = new XYPlot();
        mainPlot.setDomainAxis(xAxis);
        mainPlot.setRangeAxis(yAxis);

        mainPlot.setForegroundAlpha(0.9f);

        //[ stacked area
        DefaultTableXYDataset areaDs = new DefaultTableXYDataset();
        for (int i = 0; i < areaData.getSeriesCount(); i++) {
            areaDs.addSeries(areaData.getSeries(i));
        }
        XYItemRenderer stackedRenderer = new StackedXYAreaRenderer2();
        mainPlot.setDataset(areaDs);
        mainPlot.setRenderer(stackedRenderer);

        Color[] colors = generateJetSpectrum(areaData.getSeriesCount());
        for (int i = 0; i < areaData.getSeriesCount(); i++) {
            stackedRenderer.setSeriesPaint(i, colors[i]);
        }
        //]

        //[ lines
        if (lineData != null) {
            XYItemRenderer lineRenderer = new StandardXYItemRenderer();
            DefaultTableXYDataset lineDs = new DefaultTableXYDataset();
            for (int i = 0; i < lineData.getSeriesCount(); i++) {
                lineDs.addSeries(lineData.getSeries(i));
            }
            mainPlot.setDataset(1, lineDs);
            mainPlot.setRenderer(1, lineRenderer);

            colors = new Color[] { Color.black, Color.red, Color.darkGray };
            for (int i = 0; i < lineData.getSeriesCount(); i++) {
                lineRenderer.setSeriesPaint(i, colors[i % colors.length]);
                lineRenderer.setSeriesStroke(i, new BasicStroke(2f));
            }
        }
        //]

        mainPlot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);

        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, mainPlot, true);

        formatColorTheme(chart, theme);

        return chart;
    }

    public static Pair<String, XYSeriesCollection> adjustTime(XYSeriesCollection chartsCollection,
            Collection<IntervalMarker> markers) {
        int maxTime = 0;
        for (int i = 0; i < chartsCollection.getSeriesCount(); i++) {
            XYSeries series = chartsCollection.getSeries(i);
            for (int j = 0; j < series.getItemCount(); j++) {
                int x = series.getX(j).intValue();
                if (x > maxTime) {
                    maxTime = x;
                }
            }
        }

        String type = "ms";
        int div = 1;

        if (maxTime > 10 * 60 * 1000) {
            div = 60 * 1000;
            type = "min";
        }

        if (maxTime > 30 * 1000) {
            div = 1000;
            type = "sec";
        }

        XYSeriesCollection result = new XYSeriesCollection();

        for (int i = 0; i < chartsCollection.getSeriesCount(); i++) {

            XYSeries old = chartsCollection.getSeries(i);
            XYSeries series = new XYSeries(old.getKey(), old.getAutoSort(), old.getAllowDuplicateXValues());
            for (int j = 0; j < old.getItemCount(); j++) {
                Number x = old.getX(j).doubleValue() / div;
                Number y = old.getY(j);
                series.add(x, y);
            }

            result.addSeries(series);
        }

        if (markers != null) {
            for (IntervalMarker marker : markers) {
                marker.setStartValue(marker.getStartValue() / div);
                marker.setEndValue(marker.getEndValue() / div);
            }
        }

        return Pair.of(type, result);
    }

    private static void formatColorTheme(JFreeChart chart, ColorTheme theme) {
        Plot rawPlot = chart.getPlot();

        Paint background = Color.BLACK;
        Paint foregroung = Color.WHITE;
        if (theme == ColorTheme.LIGHT) {
            background = Color.WHITE;
            foregroung = Color.BLACK;
        }

        chart.setBackgroundPaint(background);
        chart.getLegend().setBackgroundPaint(background);
        chart.getLegend().setItemPaint(foregroung);

        if (rawPlot instanceof XYPlot) {
            XYPlot plot = (XYPlot) rawPlot;
            plot.getDomainAxis().setLabelPaint(foregroung);
            plot.getRangeAxis().setLabelPaint(foregroung);
            plot.getDomainAxis().setTickLabelPaint(foregroung);
            plot.getDomainAxis().setTickMarkPaint(foregroung);
            plot.getRangeAxis().setTickLabelPaint(foregroung);
            plot.getRangeAxis().setTickMarkPaint(foregroung);
            plot.setBackgroundPaint(background);
            plot.setDomainGridlinePaint(foregroung);
            plot.setRangeGridlinePaint(foregroung);
        }
        if (rawPlot instanceof CategoryPlot) {
            CategoryPlot plot = (CategoryPlot) rawPlot;
            plot.getDomainAxis().setLabelPaint(foregroung);
            plot.getRangeAxis().setLabelPaint(foregroung);
            plot.getDomainAxis().setTickLabelPaint(foregroung);
            plot.getDomainAxis().setTickMarkPaint(foregroung);
            plot.getRangeAxis().setTickLabelPaint(foregroung);
            plot.getRangeAxis().setTickMarkPaint(foregroung);
            plot.setBackgroundPaint(background);
            plot.setDomainGridlinePaint(foregroung);
            plot.setRangeGridlinePaint(foregroung);
        }
    }

    private static void formatBars(JFreeChart chart) {
        if (chart != null) {
            Plot p = chart.getPlot();
            if (p instanceof CategoryPlot) {
                CategoryPlot cp = (CategoryPlot) p;
                CategoryItemRenderer cir = cp.getRenderer();
                if (cir instanceof BarRenderer) {
                    BarRenderer br = (BarRenderer) cir;
                    br.setShadowVisible(false);
                    br.setBarPainter(new StandardBarPainter());
                    br.setDrawBarOutline(true);
                }
            }
        }
    }

    private static Color[] generateJetSpectrum(int n) {
        Color[] colors = new Color[n];
        for (int i = 0; i < n; i++) {
            colors[i] = Color.getHSBColor(-(0.75f * i / n + 0.3f), 0.85f, 1.0f);
        }
        return colors;
    }

    public static class TimeSeriesChartSpecification {
        private Map<Date, Double> data;
        private String label;

        public Map<Date, Double> getData() {
            return data;
        }

        public void setData(Map<Date, Double> data) {
            this.data = data;
        }

        public String getLabel() {
            return label;
        }

        public void setLabel(String label) {
            this.label = label;
        }
    }

}