newuser
newuser

Reputation: 183

JFreeCharts Subtitles show up below legend?

I am using JFreeCharts and I have both the legend and subtitles positioned at the top of the chart. However, my desire is to have the title, then the subtitle in the second line, and then the legend underneath. Instead, it has the title, then legend, and then subtitle. This is the current layout:

As you can see, the legend is above the subtitles, whereas it should be the other way around. All of this, the title, legend, and subtitles, should be above the chart.

My current code to make the chart and customize the titles, subtitles, and legend are:

public JFreeChart createStackedChart(final CategoryDataset categorydataset, String Title) throws DocumentException, IOException {
    final JFreeChart chart = ChartFactory.createStackedBarChart(
            Title, // chart title
            "", // domain axis label
            "", // range axis label
            categorydataset, // data
            PlotOrientation.VERTICAL, // the plot orientation
            true, // legend
            true, // tooltips
            false // urls
    );

    chart.setTitle(
            new org.jfree.chart.title.TextTitle(Title,
                    new java.awt.Font("Calibri", java.awt.Font.PLAIN, 12)
            ));
    chart.getTitle().setPaint(Color.GRAY);
    Color subExc = new Color(237,125,49);
    chart.addSubtitle(new TextTitle("Title",
            new Font("Calibri", Font.PLAIN, 12), subExc,
            RectangleEdge.TOP, HorizontalAlignment.CENTER,
            VerticalAlignment.TOP, RectangleInsets.ZERO_INSETS));
    chart.addSubtitle(new TextTitle("Title2",
            new Font("Calibri", Font.PLAIN, 12), Color.GRAY,
            RectangleEdge.TOP, HorizontalAlignment.CENTER,
            VerticalAlignment.TOP, RectangleInsets.ZERO_INSETS));


    LegendTitle legend = chart.getLegend();
    legend.setPosition(RectangleEdge.TOP);
    chart.getLegend().setFrame(BlockBorder.NONE);
    legend.setItemPaint(Color.GRAY);
    Font labelFont = new Font("Calibri", Font.PLAIN, 8);
    legend.setItemFont(labelFont);

    int columnCount = categorydataset.getColumnCount();
    chart.setBackgroundPaint(Color.white);
    chart.getTitle().setPadding(10, 2, 0, 2);
    chart.setBorderVisible(true);
    chart.setBorderPaint(Color.lightGray);

    final CategoryPlot plot = chart.getCategoryPlot();
    plot.setBackgroundPaint(Color.white);

    plot.setDomainGridlinePaint(Color.lightGray);
    plot.setRangeGridlinePaint(Color.lightGray);
    plot.setDomainGridlineStroke(new BasicStroke(0.5f));
    plot.setRangeGridlineStroke(new BasicStroke(0.5f));
    plot.setOutlineVisible(true);
    plot.setOutlinePaint(Color.lightGray);
    plot.setAxisOffset(new RectangleInsets(0, 0, 0, 0));

    final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
    rangeAxis.setTickLabelFont(new Font("Calibri", Font.PLAIN, 9)); 
    rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
    rangeAxis.setAutoRangeStickyZero(false);
    rangeAxis.setLowerMargin(0.0);

    rangeAxis.setTickLabelPaint(Color.GRAY);
    rangeAxis.setTickMarkPaint(Color.lightGray);
    rangeAxis.setTickMarkStroke(new BasicStroke(0.3f));
    rangeAxis.setAxisLineStroke(new BasicStroke(0.3f));
    rangeAxis.setLowerBound(0.0f);
    rangeAxis.setAxisLinePaint(Color.lightGray);

    GroupedStackedBarRenderer renderer = new GroupedStackedBarRenderer();
    renderer.setDrawBarOutline(false);
    renderer.setBarPainter(new StandardBarPainter());

    Paint p1 = new GradientPaint(
            0.0f, 0.0f, new Color(0x22, 0x22, 0xFF), 0.0f, 0.0f, new Color(0x88, 0x88, 0xFF)
    );
    renderer.setSeriesPaint(0, p1);
    renderer.setSeriesPaint(4, p1);
    renderer.setSeriesPaint(8, p1);

    Paint p2 = new GradientPaint(
            0.0f, 0.0f, new Color(0x22, 0xFF, 0x22), 0.0f, 0.0f, new Color(0x88, 0xFF, 0x88)
    );
    renderer.setSeriesPaint(1, p2);
    renderer.setSeriesPaint(5, p2);
    renderer.setSeriesPaint(9, p2);

    Paint p3 = new GradientPaint(
            0.0f, 0.0f, new Color(0xFF, 0x22, 0x22), 0.0f, 0.0f, new Color(0xFF, 0x88, 0x88)
    );
    renderer.setSeriesPaint(2, p3);
    renderer.setSeriesPaint(6, p3);
    renderer.setSeriesPaint(10, p3);

    Paint p4 = new GradientPaint(
            0.0f, 0.0f, new Color(0xFF, 0xFF, 0x22), 0.0f, 0.0f, new Color(0xFF, 0xFF, 0x88)
    );
    renderer.setSeriesPaint(3, p4);
    renderer.setSeriesPaint(7, p4);
    renderer.setSeriesPaint(11, p4);
    renderer.setGradientPaintTransformer(
            new StandardGradientPaintTransformer(GradientPaintTransformType.HORIZONTAL)
    );

    final CategoryAxis domainAxis = plot.getDomainAxis();
    domainAxis.setTickLabelPaint(Color.GRAY);
    domainAxis.setTickLabelFont(new Font("Calibri", Font.PLAIN, 9));
    Color transparent = new Color(0, 0, 0, 0);
    domainAxis.setAxisLinePaint(transparent);
    domainAxis.setTickMarkPaint(Color.lightGray);
    domainAxis.setTickMarkStroke(new BasicStroke(0.3f));
    domainAxis.setMaximumCategoryLabelWidthRatio(4f);

    if (columnCount == 2) {
        domainAxis.setCategoryMargin(.6);
        domainAxis.setLowerMargin(0.015);
        domainAxis.setUpperMargin(0.015);
    } else if (columnCount == 3) {
        domainAxis.setCategoryMargin(.35);
        domainAxis.setLowerMargin(0.15);
        domainAxis.setUpperMargin(0.15);
    } else {
        domainAxis.setCategoryMargin(.55);
        domainAxis.setLowerMargin(0.015);
        domainAxis.setUpperMargin(0.015);
    }

    if (columnCount >= 5) {
        domainAxis.setCategoryLabelPositions(
                CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 4.0));
        chart.setPadding(new RectangleInsets(0, 5, 0, 5));
    } else {
        domainAxis.setCategoryLabelPositions(
                STANDARD);
    }
    plot.setDomainAxis(domainAxis);

    return chart;

}

What should I do besides RectangleEdge TOP to be able to determine the stacking order of the legend and subtitles? Thanks!

Upvotes: 1

Views: 783

Answers (2)

Eric Leibenguth
Eric Leibenguth

Reputation: 4277

Here is a work-around that might do the trick for you:

  • Do not plot the legend in your chart: chart.removeLegend();
  • Wrap your ChartPanel inside a JPanel (with BorderLayout)
  • At the southern position of the panel, add your manually-generated legend:

Should look like:

public static JPanel wrapChart(ChartPanel chartPanel) {
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(chartPanel, BorderLayout.CENTER);
    panel.add(createLegend(chartPanel.getChart().getPlot()), BorderLayout.SOUTH);
    return panel;
}

private static JPanel createLegend(Plot plot) {
    JPanel panel = new JPanel(); // Using FlowLayout here
    Iterator iterator = plot.getLegendItems().iterator();
    while (iterator.hasNext()) {
        LegendItem item = (LegendItem) iterator.next();
        JLabel label = new JLabel(item.getLabel());
        label.setIcon(new ColorIcon(8, item.getFillPaint()));
        panel.add(label);
    }
    return panel;
}

(createLegend() method borrowed from this answer)

Upvotes: 1

newuser
newuser

Reputation: 183

Actually, I have found a solution. Just in case this helps others out there. The source of inspiration for the answer was found in:

http://www.jfree.org/phpBB2/viewtopic.php?f=3&t=23187

I had to change my code to incorporate this. I wrote

    chart.addSubtitle(0,new TextTitle("Title",
            new Font("Calibri", Font.PLAIN, 12), subExc,
            RectangleEdge.TOP, HorizontalAlignment.CENTER,
            VerticalAlignment.TOP, RectangleInsets.ZERO_INSETS));
    chart.addSubtitle(1,new TextTitle("Title2",
            new Font("Calibri", Font.PLAIN, 12), Color.GRAY,
            RectangleEdge.TOP, HorizontalAlignment.CENTER,
            VerticalAlignment.TOP, RectangleInsets.ZERO_INSETS));

Adding the 0 and 1 made these subtitles get written BEFORE the legend.

Upvotes: 2

Related Questions