Java tutorial
/** * 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; } }