【问题标题】:JFreeChart: customizing BoxAndWhisker chartJFreeChart:自定义 BoxAndWhisker 图表
【发布时间】:2016-10-26 14:12:04
【问题描述】:

我开始使用JFreeChart 进行一些绘图。 我希望我的图表如下图所示:

我使用BoxAndWhisker 图表非常接近:

但是,我仍然想更改某些内容。有没有办法去掉盒子所以我只有中间线?以及如何向条形添加标签?同样由于某种原因,y轴的最后一个数字(图像右下角)被切断。

这是我的示例代码:

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.text.DecimalFormat;
import javax.swing.Icon;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.MinMaxCategoryRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.RefineryUtilities;

public class MinMaxCategoryPlotTest extends ApplicationFrame {

    public MinMaxCategoryPlotTest(String title) {

        super(title);

        DefaultCategoryDataset dataset = new DefaultCategoryDataset();

        dataset.addValue(1604, "1", "PESAII");
        dataset.addValue(1704, "2", "PESAII");
        dataset.addValue(1804, "3", "PESAII");

        dataset.addValue(1512, "1", "NSGAII");
        dataset.addValue(1612, "2", "NSGAII");
        dataset.addValue(1712, "3", "NSGAII");

        dataset.addValue(1436, "1", "SPEA2");
        dataset.addValue(1536, "2", "SPEA2");
        dataset.addValue(1636, "3", "SPEA2");

        dataset.addValue(1363, "1", "IBEA");
        dataset.addValue(1463, "2", "IBEA");
        dataset.addValue(1563, "3", "IBEA");

        dataset.addValue(1186, "1", "MOEA/D");
        dataset.addValue(1286, "2", "MOEA/D");
        dataset.addValue(1386, "3", "MOEA/D");

        final CategoryAxis xAxis = new CategoryAxis("");
        xAxis.setTickLabelsVisible(false);

        final NumberAxis yAxis = new NumberAxis("Rating");
        yAxis.setRange(1100, 2000);

        DecimalFormat df = new DecimalFormat("0"); // Override the decimal format to get integer numbers on the axis (1.800 -> 1800)
        yAxis.setNumberFormatOverride(df);
        yAxis.setAutoRangeIncludesZero(false);

        MyMinMaxCategoryRenderer renderer = new MyMinMaxCategoryRenderer();

        renderer.setSeriesPaint(0, new Color(0, 0, 0, 0)); // invisible
        renderer.setSeriesPaint(1, Color.black);
        renderer.setSeriesPaint(2, new Color(0, 0, 0, 0)); // invisible

        renderer.setSeriesShape(1, new Line2D.Double(0, -6, 0, 6)); //not working
        renderer.setSeriesVisible(0, false); // not working

        renderer.setMinIcon(getIcon(new Line2D.Double(0, -6, 0, 6), true, true));
        renderer.setMaxIcon(getIcon(new Line2D.Double(0, -6, 0, 6), true, true));
        renderer.setObjectIcon(getIcon(new Line2D.Double(0, -4, 0, 4), true, true));

        final CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer);
        plot.setOrientation(PlotOrientation.HORIZONTAL);
        plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);

        final JFreeChart chart = new JFreeChart(
            title,
            new Font("SansSerif", Font.BOLD, 16),
            plot,
            false
        );

        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new java.awt.Dimension(1000, 400));
        setContentPane(chartPanel);

        chart.setBackgroundPaint(Color.white);

        chart.setPadding(new RectangleInsets(10, 10, 10, 10)); // Fix: tick label cut off

    }

    private Icon getIcon(Shape shape, final boolean fill,
        final boolean outline) {
        final int width = shape.getBounds().width;
        final int height = shape.getBounds().height;
        final GeneralPath path = new GeneralPath(shape);
        return new Icon() {
            public void paintIcon(Component c, Graphics g, int x, int y) {
                Graphics2D g2 = (Graphics2D) g;
                path.transform(AffineTransform.getTranslateInstance(x, y));
                if (fill) {
                    g2.fill(path);
                }
                if (outline) {
                    g2.draw(path);
                }
                path.transform(AffineTransform.getTranslateInstance(-x, -y));
            }

            public int getIconWidth() {
                return width;
            }

            public int getIconHeight() {
                return height;
            }
        };
    }

    public static void main(String[] args) {

        MinMaxCategoryPlotTest demo = new MinMaxCategoryPlotTest("Rating Interval");
        demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);

    }

    public class MyMinMaxCategoryRenderer extends MinMaxCategoryRenderer {

        @Override
        public void drawItem(Graphics2D g2, CategoryItemRendererState state,
            Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
            ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
            int pass) {
            super.drawItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column, pass);

            //Draw label
            if (dataset.getRowCount() - 1 == row) { //last row

                Number value = dataset.getValue(row, column);
                double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
                    dataArea, plot.getDomainAxisEdge());
                double y1 = rangeAxis.valueToJava2D(value.doubleValue(), dataArea,
                    plot.getRangeAxisEdge());

                g2.setFont(new Font("SansSerif", Font.BOLD, 14));
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                String name = dataset.getColumnKey(column).toString();
                int width = g2.getFontMetrics().stringWidth(name);

                g2.drawString(name, (int) y1 - width, (int) x1 - 12);

            }
        }

    }
}

评级/置信区间的最终完整结果:

【问题讨论】:

    标签: java bar-chart jfreechart


    【解决方案1】:

    使用MinMaxCategoryRendererPlotOrientation.HORIZONTAL 和自定义AxisLocation,而不是BoxAndWhiskerRenderer

    CategoryPlot plot = (CategoryPlot) chart.getPlot();
    plot.setOrientation(PlotOrientation.HORIZONTAL);
    MinMaxCategoryRenderer renderer = new MinMaxCategoryRenderer();
    plot.setRenderer(renderer);
    plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
    

    您可以像这样更改使用的Icon,也可以根据this approach创建自己的图标。

    renderer.setObjectIcon(renderer.getMinIcon());
    

    【讨论】:

    • 谢谢,MinMaxCategoryRenderer 可以解决问题。然而,y 轴上的最后一个数字位于框架之外。并且仍然不知道如何像我的示例那样在线条上方显示标签。
    • 使用CategoryItemLabelGenerator,代表example;请编辑您的问题以包含显示任何其他问题的minimal reproducible example
    • 这适用于IntervalBarRenderer,但不适用于MinMaxCategoryRendererBoxAndWhiskerRenderer
    • 看来您必须覆盖 drawItem() 或者您可以尝试注释。
    • 谢谢,看来覆盖drawItem() 是最好的选择。我还通过添加chart.setPadding(new RectangleInsets(10, 10, 10, 10)); 解决了最后一个刻度标签被切断的问题,我发现here。我更新了包含最终解决方案的代码。
    猜你喜欢
    • 1970-01-01
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多