package org.cloudgraph.web.component; import java.awt.Color; import java.awt.Font; import java.io.IOException; import java.util.List; import javax.faces.component.UIComponent; import javax.faces.context.ResponseWriter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.PiePlot3D; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.title.TextTitle; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.IntervalCategoryDataset; import org.jfree.data.general.Dataset; import org.jfree.data.general.PieDataset; import org.jfree.data.statistics.BoxAndWhiskerXYDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.OHLCDataset; import org.jfree.data.xy.WindDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** */ public class ChartUtils { private static Log log =LogFactory.getLog(ChartUtils.class); private static String passthruImgAttributes[] = { "alt", "styleClass", "onclick", "ondblclick", "onmousedown", "onmouseup", "onmouseover", "onmousemove", "onmouseout", "onkeypress", "onkeydown", "onkeyup", "usemap", }; // MyFaces html.HtmlResponseWriterImpl will not write an 'img' tag with // content nested in it. So wew gotta bypass using write() public static void renderPassThruImgAttributes(ResponseWriter writer, UIComponent component) throws IOException{ for(int i = 0 ; i < passthruImgAttributes.length ; i++) { Object value = component.getAttributes().get(passthruImgAttributes[i]); if(value != null) { writer.write(passthruImgAttributes[i] + "=\"" + value + "\""); } } //title attribute overlaps with the chart title so renamed to imgTitle to define img tag's title if(component.getAttributes().get("imgTitle") != null) writer.write("title=\"" + component.getAttributes().get("imgTitle") + "\""); } public static PlotOrientation getPlotOrientation(String orientation) { if (orientation.equalsIgnoreCase("horizontal")) { return PlotOrientation.HORIZONTAL; } else if (orientation.equalsIgnoreCase("vertical")){ return PlotOrientation.VERTICAL; } else { throw new RuntimeException("Unsupported plot orientation:" + orientation); } } public static Color getColor(String color) { // HTML colors (#FFFFFF format) if (color.startsWith("#")) { return new Color(Integer.parseInt(color.substring(1), 16)); } else { // Colors by name if (color.equalsIgnoreCase("black")) return Color.black; if (color.equalsIgnoreCase("grey")) return Color.gray; if (color.equalsIgnoreCase("yellow")) return Color.yellow; if (color.equalsIgnoreCase("green")) return Color.green; if (color.equalsIgnoreCase("blue")) return Color.blue; if (color.equalsIgnoreCase("red")) return Color.red; if (color.equalsIgnoreCase("orange")) return Color.orange; if (color.equalsIgnoreCase("cyan")) return Color.cyan; if (color.equalsIgnoreCase("magenta")) return Color.magenta; if (color.equalsIgnoreCase("darkgray")) return Color.darkGray; if (color.equalsIgnoreCase("lightgray")) return Color.lightGray; if (color.equalsIgnoreCase("pink")) return Color.pink; if (color.equalsIgnoreCase("white")) return Color.white; throw new RuntimeException("Unsupported chart color:" + color); } } public static String resolveContentType(String output) { if(output.equalsIgnoreCase("png")) return "img/png"; else if(output.equalsIgnoreCase("jpeg")) return "img/jpeg"; else throw new RuntimeException("Unsupported output format:" + output); } // Creates the chart with the given chart data public static JFreeChart createChartWithType(ChartData chartData) { JFreeChart chart = null; Object datasource = chartData.getDatasource(); if (datasource instanceof PieDataset) { chart = createChartWithPieDataSet(chartData, (PieDataset)datasource); } else if (datasource instanceof CategoryDataset) { chart = createChartWithCategoryDataSet(chartData, (CategoryDataset)datasource); } else if (datasource instanceof XYDataset) { chart = createChartWithXYDataSet(chartData, (XYDataset)datasource); } else { throw new RuntimeException("Unsupported dataset type, " + datasource.getClass().getName()); } return chart; } // Creates the chart with the given chart data public static JFreeChart createChartWithType(ChartData chartData, Dataset dataset) { JFreeChart chart = null; if (dataset instanceof PieDataset) { chart = createChartWithPieDataSet(chartData, (PieDataset)dataset); } else if (dataset instanceof CategoryDataset) { chart = createChartWithCategoryDataSet(chartData, (CategoryDataset)dataset); } else if (dataset instanceof XYDataset) { chart = createChartWithXYDataSet(chartData, (XYDataset)dataset); } else { throw new RuntimeException("Unsupported dataset type, " + dataset.getClass().getName()); } return chart; } public static void setGeneralChartProperties(JFreeChart chart, ChartData chartData) { chart.setBackgroundPaint(ChartUtils.getColor(chartData.getBackground())); chart.getPlot().setBackgroundPaint(ChartUtils.getColor(chartData.getForeground())); chart.setTitle(chartData.getTitle()); chart.setAntiAlias(chartData.isAntialias()); TextTitle textTitle = new TextTitle(); textTitle.setText(chartData.getTitle()); Font font = new Font("Verdana", Font.BOLD, 14); // FIXME: mo' tag attributes ? textTitle.setFont(font); chart.setTitle(textTitle); // Alpha transparency (100% means opaque) if (chartData.getAlpha() < 100) { chart.getPlot().setForegroundAlpha((float) chartData.getAlpha() / 100); } } public static JFreeChart createChartWithCategoryDataSet(ChartData chartData, CategoryDataset dataset) { JFreeChart chart = null; PlotOrientation plotOrientation = ChartUtils.getPlotOrientation(chartData.getOrientation()); String type = chartData.getType(); String xAxis = chartData.getXlabel(); String yAxis = chartData.getYlabel(); boolean is3d = chartData.isChart3d(); boolean legend = chartData.isLegend(); if (type.equalsIgnoreCase("bar")) { if (is3d == true) { chart = ChartFactory.createBarChart3D("", xAxis, yAxis,dataset, plotOrientation, legend, true, true); } else { chart = ChartFactory.createBarChart("", xAxis, yAxis, dataset, plotOrientation, legend, true, true); } setBarOutline(chart, chartData); } else if (type.equalsIgnoreCase("stackedbar")) { if (is3d == true) { chart = ChartFactory.createStackedBarChart3D("", xAxis, yAxis,dataset, plotOrientation, legend, true, false); } else { chart = ChartFactory.createStackedBarChart("", xAxis, yAxis,dataset, plotOrientation, legend, true, false); } setBarOutline(chart, chartData); } else if (type.equalsIgnoreCase("line")) { if (is3d == true) chart = ChartFactory.createLineChart3D("", xAxis, yAxis,dataset, plotOrientation, legend, true, false); else chart = ChartFactory.createLineChart("", xAxis, yAxis, dataset,plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("area")) { chart = ChartFactory.createAreaChart("", xAxis, yAxis, dataset,plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("stackedarea")) { chart = ChartFactory.createStackedAreaChart("", xAxis, yAxis,dataset, plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("waterfall")) { chart = ChartFactory.createWaterfallChart("", xAxis, yAxis,dataset, plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("gantt")) { chart = ChartFactory.createGanttChart("", xAxis, yAxis,(IntervalCategoryDataset) dataset, legend, true, false); } setCategorySeriesColors(chart, chartData); return chart; } public static JFreeChart createChartWithPieDataSet(ChartData chartData, PieDataset dataset) { String type = chartData.getType(); boolean legend = chartData.isLegend(); JFreeChart chart = null; if (type.equalsIgnoreCase("pie")) { if (chartData.isChart3d()) { chart = ChartFactory.createPieChart3D("", dataset, legend,true, false); PiePlot3D plot = (PiePlot3D) chart.getPlot(); plot.setDepthFactor((float) chartData.getDepth() / 100); } else { chart = ChartFactory.createPieChart("", dataset, legend, true,false); } } else if (type.equalsIgnoreCase("ring")) { chart = ChartFactory.createRingChart("", dataset, legend, true,false); } PiePlot plot = (PiePlot) chart.getPlot(); plot.setStartAngle((float) chartData.getStartAngle()); setPieSectionColors(chart, chartData); return chart; } public static JFreeChart createChartWithXYDataSet(ChartData chartData, XYDataset dataset) { String type = chartData.getType(); String xAxis = chartData.getXlabel(); String yAxis = chartData.getYlabel(); boolean legend = chartData.isLegend(); JFreeChart chart = null; PlotOrientation plotOrientation = ChartUtils.getPlotOrientation(chartData.getOrientation()); if (type.equalsIgnoreCase("bar")) { if (!(dataset instanceof IntervalXYDataset)) throw new RuntimeException("expected instance of IntervalXYDataset"); chart = ChartFactory.createXYBarChart("", xAxis, false, yAxis, (IntervalXYDataset)dataset, plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("timeseries")) { chart = ChartFactory.createTimeSeriesChart("", xAxis, yAxis,dataset, legend, true, false); } else if (type.equalsIgnoreCase("xyline")) { chart = ChartFactory.createXYLineChart("", xAxis, yAxis, dataset,plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("polar")) { chart = ChartFactory.createPolarChart("", dataset, legend, true,false); } else if (type.equalsIgnoreCase("scatter")) { chart = ChartFactory.createScatterPlot("", xAxis, yAxis, dataset,plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("xyarea")) { chart = ChartFactory.createXYAreaChart("", xAxis, yAxis, dataset,plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("xysteparea")) { chart = ChartFactory.createXYStepAreaChart("", xAxis, yAxis,dataset, plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("xystep")) { chart = ChartFactory.createXYStepChart("", xAxis, yAxis, dataset,plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("bubble")) { chart = ChartFactory.createBubbleChart("", xAxis, yAxis,(XYZDataset) dataset, plotOrientation, legend, true, false); } else if (type.equalsIgnoreCase("candlestick")) { chart = ChartFactory.createCandlestickChart("", xAxis, yAxis,(OHLCDataset) dataset, legend); } else if (type.equalsIgnoreCase("boxandwhisker")) { chart = ChartFactory.createBoxAndWhiskerChart("", xAxis, yAxis,(BoxAndWhiskerXYDataset) dataset, legend); } else if (type.equalsIgnoreCase("highlow")) { chart = ChartFactory.createHighLowChart("", xAxis, yAxis,(OHLCDataset) dataset, legend); } else if (type.equalsIgnoreCase("histogram")) { chart = ChartFactory.createHistogram("", xAxis, yAxis,(IntervalXYDataset) dataset, plotOrientation, legend, true,false); //} else if (type.equalsIgnoreCase("signal")) { // chart = ChartFactory.createSignalChart("", xAxis, yAxis,(SignalsDataset) dataset, legend); } else if (type.equalsIgnoreCase("wind")) { chart = ChartFactory.createWindPlot("", xAxis, yAxis,(WindDataset) dataset, legend, true, false); } setXYSeriesColors(chart, chartData); return chart; } /** * Series coloring * Plot has no getRenderer so two methods for each plot type(categoryplot and xyplot) */ public static void setCategorySeriesColors(JFreeChart chart, ChartData chartData) { //KeyedValues2D dataValues = (KeyedValues2D)chartData.getDatasource(); if(chart.getPlot() instanceof CategoryPlot) { CategoryPlot plot = (CategoryPlot) chart.getPlot(); if (chartData.getColors() != null) { ChartDataSource dataSource = (ChartDataSource)chartData.getDatasource(); List seriesKeys = ((CategoryDataset)dataSource.getDataSet()).getRowKeys(); String[] colors = chartData.getColors().split(","); for (int i = 0; i < colors.length; i++) { // if it's a color mapping of a series name to a color int idx = colors[i].indexOf(":"); if (idx >= 0) { String seriesKey = colors[i].substring(0, idx).trim(); String color = colors[i].substring(idx+1).trim(); // find the series for (int j = 0; j < seriesKeys.size(); j++) if (seriesKeys.get(j).equals(seriesKey)) plot.getRenderer().setSeriesPaint(j, ChartUtils.getColor(color)); } else // it's a static color set which assumes the number of colors matches number of series plot.getRenderer().setSeriesPaint(i, ChartUtils.getColor(colors[i].trim())); } } } } public static void setXYSeriesColors(JFreeChart chart, ChartData chartData) { if(chart.getPlot() instanceof XYPlot && chartData.getColors() != null) { XYPlot plot = (XYPlot) chart.getPlot(); String[] colors = chartData.getColors().split(","); for (int i = 0; i < colors.length; i++) { plot.getRenderer().setSeriesPaint(i, ChartUtils.getColor(colors[i].trim())); } } } public static void setPieSectionColors(JFreeChart chart, ChartData chartData) { if(chart.getPlot() instanceof PiePlot && chartData.getColors() != null) { String[] colors = chartData.getColors().split(","); ChartDataSource dataSource = (ChartDataSource)chartData.getDatasource(); PieDataset dataSet = (PieDataset)dataSource.getDataSet(); for (int i = 0; i < colors.length; i++) { // if it's a color mapping of a series name to a color int idx = colors[i].indexOf(":"); if (idx >= 0) { String key = colors[i].substring(0, idx).trim(); String color = colors[i].substring(idx+1).trim(); // find the series int keyIndex = dataSet.getIndex(key); if (keyIndex >= 0) ((PiePlot)chart.getPlot()).setSectionPaint(keyIndex, ChartUtils.getColor(color)); } else // it's a static color set which assumes the number of colors matches number of series ((PiePlot)chart.getPlot()).setSectionPaint(i, ChartUtils.getColor(colors[i].trim())); } } } /** * Sets the outline of the bars */ public static void setBarOutline(JFreeChart chart, ChartData chartData) { CategoryPlot plot = (CategoryPlot) chart.getPlot(); BarRenderer barrenderer = (BarRenderer) plot.getRenderer(); barrenderer.setDrawBarOutline(chartData.isOutline()); } }