com.hmsinc.epicenter.webapp.chart.ChartService.java Source code

Java tutorial

Introduction

Here is the source code for com.hmsinc.epicenter.webapp.chart.ChartService.java

Source

/**
 * Copyright (C) 2008 University of Pittsburgh
 * 
 * 
 * This file is part of Open EpiCenter
 * 
 *     Open EpiCenter is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     Open EpiCenter is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with Open EpiCenter.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 *   
 */
package com.hmsinc.epicenter.webapp.chart;

import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Resource;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.commons.lang.Validate;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.LegendItem;
import org.jfree.chart.LegendItemCollection;
import org.jfree.chart.LegendItemSource;
import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.annotations.XYPointerAnnotation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.block.ColumnArrangement;
import org.jfree.chart.block.LineBorder;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.SeriesRenderingOrder;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.xy.XYAreaRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYDifferenceRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.Range;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.HorizontalAlignment;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.VerticalAlignment;
import org.springframework.stereotype.Service;

/**
 * Handles rendering, streaming, and caching of charts.
 * 
 * Normally, a chart is generated by passing a "TimeSeriesChart" object to the
 * getChartURL() method. This creates the chart and returns a URL that can be
 * used to access it. After the chart is accessed, it is removed from storage.
 * 
 * @author <a href="mailto:steve.kondik@hmsinc.com">Steve Kondik</a>
 * @version $Id: ChartService.java 1538 2008-04-10 21:17:55Z steve.kondik $
 */
@Service
public class ChartService {

    @Resource
    private Cache chartCache;

    private static final Font LEGEND_FONT = new Font("Arial", Font.PLAIN, 9);

    private static final Font LABEL_FONT = new Font("Arial", Font.PLAIN, 10);

    private static final Font ANNOTATION_FONT = new Font("Arial", Font.BOLD, 10);

    private static final String NO_DATA_MESSAGE = "No data to display.";

    /**
     * @param adapter
     * @return
     */
    public String getChartURL(final AbstractChart adapter) {
        return getChartURL(adapter, null);
    }

    /**
     * @param adapter
     * @return
     */
    public String getChartURL(final AbstractChart adapter, final List<XYAnnotation> annotations) {

        Validate.notNull(adapter, "No chart specified.");

        final String uuid = UUID.randomUUID().toString();

        final JFreeChart chart = createChart(adapter, annotations);
        chart.setAntiAlias(true);
        chart.setTextAntiAlias(true);

        configureRenderer(chart, adapter);
        configureTitleAndLegend(chart, adapter);

        chartCache.put(new Element(uuid, chart));
        return "chart?id=" + uuid;

    }

    /**
     * @param adapter
     * @param annotations
     * @return
     */
    private static JFreeChart createChart(final AbstractChart adapter, final List<XYAnnotation> annotations) {

        final JFreeChart chart;
        if (adapter.getItems() instanceof XYDataset) {

            chart = ChartFactory.createTimeSeriesChart(adapter.getTitle(), adapter.getXLabel(), adapter.getYLabel(),
                    (XYDataset) adapter.getItems(), true, false, false);
            final ValueAxis domainAxis = new DateAxis();
            domainAxis.setLabelFont(LABEL_FONT);
            domainAxis.setTickLabelFont(LABEL_FONT);
            domainAxis.setLowerMargin(0.0);
            domainAxis.setUpperMargin(0.0);

            chart.getXYPlot().setDomainAxis(domainAxis);
            chart.getXYPlot().getRangeAxis().setLabelFont(LABEL_FONT);
            chart.getXYPlot().getRangeAxis().setTickLabelFont(LABEL_FONT);
            chart.getXYPlot().getRangeAxis().setStandardTickUnits(adapter.getRangeTickUnits());

            if (adapter.isAlwaysScaleFromZero()) {
                final double upperBound = chart.getXYPlot().getRangeAxis().getRange().getUpperBound();
                final Range range = new Range(0, upperBound + (upperBound * 0.2));
                chart.getXYPlot().getRangeAxis().setRange(range);
            }

            for (Marker marker : adapter.getMarkers()) {
                chart.getXYPlot().addDomainMarker(marker);
            }

            if (annotations != null) {
                for (XYAnnotation annotation : annotations) {
                    if (annotation instanceof XYPointerAnnotation) {
                        ((XYPointerAnnotation) annotation).setFont(ANNOTATION_FONT);
                    }
                    chart.getXYPlot().addAnnotation(annotation);
                }
            }

            // Add any bands to the chart
            if (adapter instanceof TimeSeriesChart) {
                final TimeSeriesChart tsc = (TimeSeriesChart) adapter;
                for (int i = 0; i < tsc.getBands().size(); i++) {

                    final Color c = tsc.getBandColors().get(i);
                    final Color fill = tsc.getBandFillColors().get(i);
                    final XYDifferenceRenderer renderer = new XYDifferenceRenderer(fill, fill, false);
                    renderer.setSeriesPaint(0, c);
                    renderer.setSeriesPaint(1, c);
                    renderer.setSeriesStroke(0, LineStyle.THICK.getStroke());
                    renderer.setSeriesStroke(1, LineStyle.THICK.getStroke());

                    chart.getXYPlot().setDataset(i + 1, tsc.getBands().get(i));
                    chart.getXYPlot().setRenderer(i + 1, renderer);
                }
            }

        } else if (adapter.getItems() instanceof CategoryDataset) {

            chart = ChartFactory.createBarChart(adapter.getTitle(), adapter.getXLabel(), adapter.getYLabel(),
                    (CategoryDataset) adapter.getItems(), PlotOrientation.VERTICAL, true, false, false);
            chart.getCategoryPlot().getDomainAxis().setLabelFont(LABEL_FONT);
            chart.getCategoryPlot().getRangeAxis().setLabelFont(LABEL_FONT);
            chart.getCategoryPlot().getDomainAxis().setTickLabelFont(LABEL_FONT);
            chart.getCategoryPlot().getRangeAxis().setTickLabelFont(LABEL_FONT);

            if (adapter.isAlwaysScaleFromZero()) {
                final double upperBound = chart.getCategoryPlot().getRangeAxis().getRange().getUpperBound();
                final Range range = new Range(0, upperBound + (upperBound * 0.1));
                chart.getCategoryPlot().getRangeAxis().setRange(range);
            }

        } else {
            throw new UnsupportedOperationException("Unsupported chart type: " + adapter.getItems().getClass());
        }

        chart.getPlot().setNoDataMessage(NO_DATA_MESSAGE);

        return chart;
    }

    /**
     * @param chart
     * @return
     */
    private static void configureTitleAndLegend(final JFreeChart chart, final AbstractChart adapter) {

        final TextTitle title = chart.getTitle();
        if (title != null) {
            title.setFont(new Font("Arial", Font.BOLD, 12));
            // title.setBackgroundPaint(Color.CYAN);
            title.setTextAlignment(HorizontalAlignment.LEFT);
            title.setHorizontalAlignment(HorizontalAlignment.CENTER);
            title.setMargin(0, 4, 5, 6);
        }

        if (chart.getLegend() != null) {
            chart.removeLegend();

            final LegendTitle legend = new LegendTitle(chart.getPlot(), new SNColumnArrangement(0, 0),
                    new ColumnArrangement(HorizontalAlignment.CENTER, VerticalAlignment.CENTER, 0, 0));

            legend.setItemFont(LEGEND_FONT);
            legend.setPosition(RectangleEdge.BOTTOM);
            legend.setHorizontalAlignment(HorizontalAlignment.CENTER);
            legend.setBackgroundPaint(Color.WHITE);
            legend.setFrame(new LineBorder());
            legend.setMargin(0, 4, 5, 6);

            chart.addLegend(legend);

            // Now we'll try to remove any duplicate items from the legend..
            final Map<String, Integer> keys = new HashMap<String, Integer>();
            final LegendItemCollection items = new LegendItemCollection();

            for (LegendItemSource source : legend.getSources()) {

                for (int i = 0; i < source.getLegendItems().getItemCount(); i++) {

                    final LegendItem item = source.getLegendItems().get(i);
                    if (!keys.containsKey(item.getLabel())) {
                        keys.put(item.getLabel(), i);
                        items.add(item);
                    }
                }
            }

            legend.setSources(new LegendItemSource[] { new LegendItemSource() {

                /*
                 * (non-Javadoc)
                 * 
                 * @see org.jfree.chart.LegendItemSource#getLegendItems()
                 */
                public LegendItemCollection getLegendItems() {
                    return items;
                }

            } });
        }
    }

    /**
     * @param chart
     */
    private static void configureRenderer(final JFreeChart chart, final AbstractChart adapter) {

        if (ChartType.BAR.equals(adapter.getType())) {

            chart.getCategoryPlot().setRenderer(getBarRenderer(adapter));

        } else if (ChartType.TIMESERIES_BAR.equals(adapter.getType())) {

            chart.getXYPlot().setRenderer(getTimeSeriesBarRenderer(adapter));

        } else if (ChartType.MOUNTAIN.equals(adapter.getType())) {

            chart.getXYPlot().setRenderer(getMountainRenderer(adapter));
            chart.getXYPlot().setSeriesRenderingOrder(SeriesRenderingOrder.FORWARD);

        } else {

            final XYItemRenderer renderer = chart.getXYPlot().getRenderer();

            for (int i = 0; i < adapter.getColors().size(); i++) {

                final Color c = adapter.getColors().get(i);
                renderer.setSeriesPaint(i, c);
                renderer.setSeriesOutlinePaint(i, c.brighter());
            }

            for (int i = 0; i < adapter.getStrokes().size(); i++) {
                renderer.setSeriesStroke(i, adapter.getStrokes().get(i));
            }

            if (renderer instanceof XYLineAndShapeRenderer) {
                ((XYLineAndShapeRenderer) renderer).setDrawSeriesLineAsPath(true);
            }

        }

    }

    /**
     * @param chart
     * @return
     */
    private static XYAreaRenderer getMountainRenderer(final AbstractChart chart) {

        final XYAreaRenderer renderer = new XYAreaRenderer();
        renderer.setOutline(true);

        for (int i = 0; i < chart.getColors().size(); i++) {

            final Color c = chart.getColors().get(i);
            renderer.setSeriesPaint(i, c);
            renderer.setSeriesOutlinePaint(i, c.darker());
            renderer.setSeriesOutlineStroke(i, LineStyle.THICK.getStroke());

        }

        return renderer;

    }

    /**
     * @param chart
     * @return
     */
    private static CategoryItemRenderer getBarRenderer(final AbstractChart chart) {

        final BarRenderer renderer = new BarRenderer();
        renderer.setMaximumBarWidth(0.3);

        for (int i = 0; i < chart.getColors().size(); i++) {

            final Color c = chart.getColors().get(i);
            renderer.setSeriesPaint(i, new GradientPaint(0f, 0f, c, 0f, 0f, c.brighter().brighter()));
            renderer.setSeriesOutlinePaint(i, c);

        }

        return renderer;
    }

    /**
     * @param chart
     * @return
     */
    private static XYBarRenderer getTimeSeriesBarRenderer(final AbstractChart chart) {

        final XYBarRenderer renderer = new XYBarRenderer();
        renderer.setMargin(0.2);

        for (int i = 0; i < chart.getColors().size(); i++) {

            final Color c = chart.getColors().get(i);
            renderer.setSeriesPaint(i, new GradientPaint(0f, 0f, c, 0f, 0f, c.brighter().brighter()));
            renderer.setSeriesOutlinePaint(i, c);

        }
        return renderer;
    }

}