• 在iReport中通过JFreeChart生成雷达图(蜘蛛图)

    日期:2010-07-13 | 分类:技术

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://gang613.blogbus.com/logs/69142477.html

        在iReport中提供了丰富的chart组件,包括:Pie、Pie 3D、Bar、YX Bar、Stacked Bar、Stacked Bar 3D、Line、XY Line、Area、YX Area、Stacked Area、Scatter、Bubble、Time Series、High Low、Candlestick、Gantt、Meter、Thermometer、Multi Axis 等。iReport为以上组件提供了相应的wizard,可以快速地在报表中输出chart。

        在我们的项目中用户需要在报表中生成雷达图(Spider Chart)——又名蜘蛛图,而这种chart在iReport中并没有相应的组件。我们发现通过jFreeChart能够生成Spider Chart,然后通过iReport对jFreeChart的调用完成在报表中输出Spider Chart 。
    1)在报表中创建一个Image对象,如下图所示:

    该Image的类型为:“java.awt.Image”,表达式为“$F{spiderimage}”其xml属性如下图:

    字段spiderimage的类型为“java.awt.image.BufferedImage”

    2)jFreeChart创建Spider Chart的代码如下:

     

    /**
     * 扩展了SpiderWebPlot,用于在显示刻度和说明。
     * 来源于:http://javacrazyer.javaeye.com/blog/623741
     */

    import java.awt.AlphaComposite;
    import java.awt.Composite;
    import java.awt.Graphics2D;
    import java.awt.font.FontRenderContext;
    import java.awt.font.LineMetrics;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Arc2D;
    import java.awt.geom.Line2D;
    import java.awt.geom.Point2D;
    import java.awt.geom.Rectangle2D;
    import java.text.NumberFormat;

    import org.jfree.chart.plot.SpiderWebPlot;
    import org.jfree.data.category.CategoryDataset;
    public class MySpiderWebPlot extends SpiderWebPlot {
        private static final long serialVersionUID = 4005814203754627127L;
        private int ticks = DEFAULT_TICKS;
        private static final int DEFAULT_TICKS = 5;
        private NumberFormat format = NumberFormat.getInstance();
        private static final double PERPENDICULAR = 90;
        private static final double TICK_SCALE = 0.015;
        private int valueLabelGap = DEFAULT_GAP;
        private static final int DEFAULT_GAP = 10;
        private static final double THRESHOLD = 15;

        MySpiderWebPlot(CategoryDataset createCategoryDataset) {
            super(createCategoryDataset);
        }

        protected void drawLabel(final Graphics2D g2, final Rectangle2D plotArea,
                final double value, final int cat, final double startAngle,
                final double extent) {
            super.drawLabel(g2, plotArea, value, cat, startAngle, extent);
            final FontRenderContext frc = g2.getFontRenderContext();
            final double[] transformed = new double[2];
            final double[] transformer = new double[2];
            final Arc2D arc1 = new Arc2D.Double(plotArea, startAngle, 0, Arc2D.OPEN);
            for (int i = 1; i <= ticks; i++) {
                final Point2D point1 = arc1.getEndPoint();
                final double deltaX = plotArea.getCenterX();
                final double deltaY = plotArea.getCenterY();
                double labelX = point1.getX() - deltaX;
                double labelY = point1.getY() - deltaY;
                final double scale = ((double) i / (double) ticks);
                final AffineTransform tx = AffineTransform.getScaleInstance(scale,
                        scale);
                final AffineTransform pointTrans = AffineTransform
                        .getScaleInstance(scale + TICK_SCALE, scale + TICK_SCALE);
                transformer[0] = labelX;
                transformer[1] = labelY;
                pointTrans.transform(transformer, 0, transformed, 0, 1);
                final double pointX = transformed[0] + deltaX;
                final double pointY = transformed[1] + deltaY;
                tx.transform(transformer, 0, transformed, 0, 1);
                labelX = transformed[0] + deltaX;
                labelY = transformed[1] + deltaY;
                double rotated = (PERPENDICULAR);
                AffineTransform rotateTrans = AffineTransform.getRotateInstance(
                        Math.toRadians(rotated), labelX, labelY);
                transformer[0] = pointX;
                transformer[1] = pointY;
                rotateTrans.transform(transformer, 0, transformed, 0, 1);
                final double x1 = transformed[0];
                final double y1 = transformed[1];
                rotated = (-PERPENDICULAR);
                rotateTrans = AffineTransform.getRotateInstance(Math
                        .toRadians(rotated), labelX, labelY);
                rotateTrans.transform(transformer, 0, transformed, 0, 1);
                final Composite saveComposite = g2.getComposite();
                g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                        1.0f));
                g2.draw(new Line2D.Double(transformed[0], transformed[1], x1, y1));
                if (startAngle == this.getStartAngle()) {
                    final String label = format
                            .format(((double) i / (double) ticks)
                                    * this.getMaxValue());
                    final LineMetrics lm = getLabelFont()
                            .getLineMetrics(label, frc);
                    final double ascent = lm.getAscent();
                    if (Math.abs(labelX - plotArea.getCenterX()) < THRESHOLD) {
                        labelX += valueLabelGap;
                        labelY += ascent / (float) 2;
                    } else if (Math.abs(labelY - plotArea.getCenterY()) < THRESHOLD) {
                        labelY += valueLabelGap;
                    } else if (labelX >= plotArea.getCenterX()) {
                        if (labelY < plotArea.getCenterY()) {
                            labelX += valueLabelGap;
                            labelY += valueLabelGap;
                        } else {
                            labelX -= valueLabelGap;
                            labelY += valueLabelGap;
                        }
                    } else {
                        if (labelY > plotArea.getCenterY()) {
                            labelX -= valueLabelGap;
                            labelY -= valueLabelGap;
                        } else {
                            labelX += valueLabelGap;
                            labelY -= valueLabelGap;
                        }
                    }
                    g2.setPaint(getLabelPaint());
                    g2.setFont(getLabelFont());
                    g2.drawString(label, (float) labelX, (float) labelY);
                }
                g2.setComposite(saveComposite);
            }
        }
    }

    /**
     * 通过构造DefaultCategoryDataset创建Spider Chart。
     *
    */
    public class MySpriderWebPlotFactory {
        public static JFreeChart createChart() {
            MySpiderWebPlot spiderwebplot = new MySpiderWebPlot(createDataset());
            JFreeChart jfreechart = new JFreeChart("前三个季度水果销售报告",
                    TextTitle.DEFAULT_FONT, spiderwebplot, false);
            LegendTitle legendtitle = new LegendTitle(spiderwebplot);
            legendtitle.setPosition(RectangleEdge.RIGHT);
            jfreechart.addSubtitle(legendtitle);
            return jfreechart;
        }

        public static DefaultCategoryDataset createDataset() {
            DefaultCategoryDataset dataset = new DefaultCategoryDataset();
            String group1 = "苹果";
           
            dataset.addValue(50, group1, "一月份");
            dataset.addValue(60, group1, "二月份");
            dataset.addValue(40, group1, "三月份");
            dataset.addValue(20, group1, "四月份");
            // dataset.addValue(50, group1, "五月份");
            dataset.addValue(53, group1, "六月份");
            dataset.addValue(52, group1, "七月份");
            // dataset.addValue(80, group1, "八月份");

            String group2 = "橙子";
            dataset.addValue(31, group2, "一月份");
            dataset.addValue(34, group2, "二月份");
            dataset.addValue(45, group2, "三月份");
            dataset.addValue(74, group2, "四月份");
            // dataset.addValue(46, group2, "五月份");
            dataset.addValue(59, group2, "六月份");
            dataset.addValue(33, group2, "七月份");
            // dataset.addValue(31, group2, "八月份");

            String group3 = "香蕉";
            dataset.addValue(47, group3, "一月份");
            dataset.addValue(58, group3, "二月份");
            dataset.addValue(20, group3, "三月份");
            dataset.addValue(50, group3, "四月份");
            // dataset.addValue(65, group3, "五月份");
            dataset.addValue(67, group3, "六月份");
            dataset.addValue(44, group3, "七月份");
            // dataset.addValue(49, group3, "八月份");
            return dataset;
        }
    }

    3)将jFreeChart转换为BufferedImage

    public BufferedImage extractImage(JFreeChart chart, int width, int height) {
        BufferedImage img = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = img.createGraphics();
        chart.draw(g2, new Rectangle2D.Double(0, 0, width, height));
        g2.dispose();
        return img;
    }

    4)定义ChartBean,用于封装数据。

    public class ChartBean {
        java.awt.image.BufferedImage spiderimage;
        public ChartBean() {
        }
        public BufferedImage getSpiderimage(){
            return this.spiderimage;
        }
        public void setSpiderimage(BufferedImage spiderimage) {
            this.spiderimage = spiderimage;
        }
    }

    5)完成调用过程如下:


    List charts = new ArrayList();
    JFreeChart jfreechart = MySpiderWebPlotFactory.createChart();
    BufferedImage bi = extractImage(jfreechart, 800, 600);
    ChartBean chartBean = new ChartBean();
    chartBean.setSpiderimage(bi);
    charts.add(chartBean);
    JRBeanCollectionDataSource  ds = new JRBeanCollectionDataSource(charts);

    try {
        JasperPrint jasperPrint = JasperFillManager.fillReport(rep,
                new HashMap(), ds);
    } catch (JRException jre) {
        pw.println("生成JasperPrint错误!");
        jre.printStackTrace();
    }

    最终生成效果图如下:

    参考资料:

    1)Using Dynamically Generated JFreeChart charts and JasperReports

    2)使用JFreeChart生成带刻度的雷达图(蜘蛛网图)


    收藏到:Del.icio.us