具有LayeredBarRenderer的标签

huangapple go评论76阅读模式
英文:

Labels with LayeredBarRenderer

问题

考虑一个较小版本的这个示例

public class LayeredBarChartDemo2 extends ApplicationFrame {
    
    public LayeredBarChartDemo2(final String title) {
        super(title);
        final double[][] data = new double[][] { { 55, 60 }, { 25.0, 13.0 } };
    
        final CategoryDataset dataset = DatasetUtils.createCategoryDataset("Series ", "Factor ", data);
    
        // create the chart...
        final CategoryAxis categoryAxis = new CategoryAxis("Category");
        final ValueAxis valueAxis = new NumberAxis("Score (%)");
    
        final CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, new LayeredBarRenderer());
        final JFreeChart chart = new JFreeChart("Layered Bar Chart Demo 2", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
    
        final LayeredBarRenderer renderer = (LayeredBarRenderer) plot.getRenderer();
    
        // add the chart to a panel...
        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
        setContentPane(chartPanel);
    }
    
    public static void main(final String[] args) {
        final LayeredBarChartDemo2 demo = new LayeredBarChartDemo2("Layered Bar Chart Demo 2");
        demo.pack();
        demo.setVisible(true);
    }
}

它产生的图像如下:

具有LayeredBarRenderer的标签

我想在图表上添加标签,并使其看起来像这样:

具有LayeredBarRenderer的标签

我已经尝试了其他渲染器可行的方法:

renderer.setDefaultItemLabelsVisible(true);

或者:

renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, true);

我还尝试了显式声明 CategoryItemLabelGenerator

renderer.setDefaultItemLabelGenerator(new CategoryItemLabelGenerator() {

    @Override
    public String generateRowLabel(CategoryDataset dataset, int row) {
        return "sasa";
    }

    @Override
    public String generateLabel(CategoryDataset dataset, int row, int column) {
        return "lalal";
    }

    @Override
    public String generateColumnLabel(CategoryDataset dataset, int column) {
        return "ababa";
    }
});

(我还尝试了使用 setSeriesItemLabelGenerator(series, CategoryLabelGenerator) )。

我已经更改了字体和颜色,以防它们存在,但我就是看不到它们。

我使用的是 JFreeChart 1.5.0 版本。

有没有一种方法可以添加这些标签?

英文:

Consider a smaller version of this example:

public class LayeredBarChartDemo2 extends ApplicationFrame {

	public LayeredBarChartDemo2(final String title) {
		super(title);
		final double[][] data = new double[][] { { 55, 60 }, { 25.0, 13.0 } };

		final CategoryDataset dataset = DatasetUtils.createCategoryDataset("Series ", "Factor ", data);

		// create the chart...
		final CategoryAxis categoryAxis = new CategoryAxis("Category");
		final ValueAxis valueAxis = new NumberAxis("Score (%)");

		final CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, new LayeredBarRenderer());
		final JFreeChart chart = new JFreeChart("Layered Bar Chart Demo 2", JFreeChart.DEFAULT_TITLE_FONT, plot, true);

		final LayeredBarRenderer renderer = (LayeredBarRenderer) plot.getRenderer();

		// add the chart to a panel...
		final ChartPanel chartPanel = new ChartPanel(chart);
		chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
		setContentPane(chartPanel);

	}

	public static void main(final String[] args) {
		final LayeredBarChartDemo2 demo = new LayeredBarChartDemo2("Layered Bar Chart Demo 2");
		demo.pack();
		demo.setVisible(true);
	}
}

It produces:

具有LayeredBarRenderer的标签

I want to add labels on it, and make it look like this:

具有LayeredBarRenderer的标签

I have tried what works for other renderers:

renderer.setDefaultItemLabelsVisible(true);

Or:

renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, true);

I also tried to declare a CategoryItemLabelGenerator explicitly:

renderer.setDefaultItemLabelGenerator(new CategoryItemLabelGenerator() {

	@Override
	public String generateRowLabel(CategoryDataset dataset, int row) {
		return "sasa";
	}

	@Override
	public String generateLabel(CategoryDataset dataset, int row, int column) {
		return "lalal";
	}

	@Override
	public String generateColumnLabel(CategoryDataset dataset, int column) {
		return "ababa";
	}
});

(I also tried using setSeriesItemLabelGenerator(series, CategoryLabelGenerator) too).

I changed the font & the paint in case they are there but I just do not see them.

I use JFreeChart 1.5.0.

Is there a way to add the labels?

答案1

得分: 2

以下是你要翻译的内容:

错误1明显地由@George Z.在这里2所识别出,其关键在于LayeredBarRenderer实现的drawVerticalItem()在调用drawItemLabel()时计算的一个错误值,如此处所示6。谓词transX1 > transX2应该被反转。

在使用v1.5时的解决方法是,可以像下面的变化所示,指定一个 ItemLabelPosition

ItemLabelPosition position = new ItemLabelPosition(
    ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER);
renderer.setDefaultNegativeItemLabelPosition(position);

PlotOrientation.VERTICAL
具有LayeredBarRenderer的标签

由于drawHorizontalItem()的实现似乎是正确的,ItemLabelPosition可以按预期工作。

ItemLabelPosition position = new ItemLabelPosition(
    ItemLabelAnchor.OUTSIDE3, TextAnchor.CENTER_LEFT);
renderer.setDefaultPositiveItemLabelPosition(position);

PlotOrientation.HORIZONTAL
具有LayeredBarRenderer的标签

此外,StandardCategoryToolTipGenerator按预期工作。你可以像这里10和这里11所示地自定义DEFAULT_TOOL_TIP_FORMAT_STRING

renderer.setDefaultToolTipGenerator(new StandardCategoryToolTipGenerator());

代码:

import java.awt.Dimension;
import java.awt.EventQueue;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LayeredBarRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtils;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.ui.TextAnchor;

/** @see https://stackoverflow.com/a/63464855/230513 */
public class LayeredBarChartDemo2 extends ApplicationFrame {
    
    private static final String TITLE = "Layered Bar Chart Demo 2";
    
    public LayeredBarChartDemo2(final String title) {
        super(title);
        final double[][] data = new double[][]{{55, 60}, {25, 13}};
        final CategoryDataset dataset = DatasetUtils.createCategoryDataset("Series ", "Factor ", data);
        final CategoryAxis categoryAxis = new CategoryAxis("Category");
        final ValueAxis valueAxis = new NumberAxis("Score (%)");
        final LayeredBarRenderer renderer = new LayeredBarRenderer();
        renderer.setDefaultToolTipGenerator(new StandardCategoryToolTipGenerator());
        renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
        renderer.setDefaultItemLabelsVisible(true);
        ItemLabelPosition position = new ItemLabelPosition(
            ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER);
        renderer.setDefaultNegativeItemLabelPosition(position);
        final CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer);
        plot.setOrientation(PlotOrientation.VERTICAL);
        final JFreeChart chart = new JFreeChart(TITLE, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        ChartUtils.applyCurrentTheme(chart);
        add(new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(640, 480);
            }
        });
    }
    
    public static void main(final String[] args) {
        EventQueue.invokeLater(() -> {
            final LayeredBarChartDemo2 demo = new LayeredBarChartDemo2(TITLE);
            demo.pack();
            demo.setLocationRelativeTo(null);
            demo.setVisible(true);
        });
    }
}
英文:

The apparent bug identified here by @George Z. hinges on an incorrect value calculated by the LayeredBarRenderer implementation of drawVerticalItem() when it calls drawItemLabel() as shown here. The predicate transX1 > transX2 should be inverted.

As a workaround when using v1.5, it's possible to specify a negative ItemLabelPosition, as shown the variation below.

ItemLabelPosition position = new ItemLabelPosition(
    ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER);
renderer.setDefaultNegativeItemLabelPosition(position);

PlotOrientation.VERTICAL:
具有LayeredBarRenderer的标签

As the implementation of drawHorizontalItem() appears correct, a positive ItemLabelPosition works as expected.

ItemLabelPosition position = new ItemLabelPosition(
    ItemLabelAnchor.OUTSIDE3, TextAnchor.CENTER_LEFT);
renderer.setDefaultPositiveItemLabelPosition(position);

PlotOrientation.HORIZONTAL:
具有LayeredBarRenderer的标签

In addition, a StandardCategoryToolTipGenerator works as expected. You can customize the DEFAULT_TOOL_TIP_FORMAT_STRING as shown here and here.

renderer.setDefaultToolTipGenerator(new StandardCategoryToolTipGenerator());

Code:

import java.awt.Dimension;
import java.awt.EventQueue;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LayeredBarRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtils;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.ui.TextAnchor;
/** @see https://stackoverflow.com/a/63464855/230513 */
public class LayeredBarChartDemo2 extends ApplicationFrame {
private static final String TITLE = "Layered Bar Chart Demo 2";
public LayeredBarChartDemo2(final String title) {
super(title);
final double[][] data = new double[][]{{55, 60}, {25, 13}};
final CategoryDataset dataset = DatasetUtils.createCategoryDataset("Series ", "Factor ", data);
final CategoryAxis categoryAxis = new CategoryAxis("Category");
final ValueAxis valueAxis = new NumberAxis("Score (%)");
final LayeredBarRenderer renderer = new LayeredBarRenderer();
renderer.setDefaultToolTipGenerator(new StandardCategoryToolTipGenerator());
renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setDefaultItemLabelsVisible(true);
ItemLabelPosition position = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER);
renderer.setDefaultNegativeItemLabelPosition(position);
final CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer);
plot.setOrientation(PlotOrientation.VERTICAL);
final JFreeChart chart = new JFreeChart(TITLE, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
ChartUtils.applyCurrentTheme(chart);
add(new ChartPanel(chart) {
@Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
});
}
public static void main(final String[] args) {
EventQueue.invokeLater(() -> {
final LayeredBarChartDemo2 demo = new LayeredBarChartDemo2(TITLE);
demo.pack();
demo.setLocationRelativeTo(null);
demo.setVisible(true);
});
}
}

答案2

得分: 2

我自己找到了解决方案,虽然有些非传统,但至少似乎实现了我想要的效果。

LayeredBarRenderer#drawVerticalItem 方法中有以下部分:

// draw the item labels if there are any...
CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
if (generator != null && isItemLabelVisible(row, column)) {
double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
drawItemLabel(g2, dataset, row, column, plot, generator, bar, (transX1 > transX2));
}

我扩展了 LayeredBarRenderer 并重写了这个方法,将 transX1 > transX2 反转为 transX1 <= transX2

//....
// draw the item labels if there are any...
CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
if (generator != null && isItemLabelVisible(row, column)) {
double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
drawItemLabel(g2, dataset, row, column, plot, generator, bar, !(transX1 > transX2)); //here
}
//....

结合使用专属的 CategoryItemLabelGenerator 给我带来了想要的结果:

renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, true);

结果:

具有LayeredBarRenderer的标签

完整的重写类:

@SuppressWarnings("serial")
public class LabelFixedLayeredBarRenderer extends LayeredBarRenderer {
@Override
protected void drawVerticalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot,
CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column) {
// 无数据值不绘制任何内容...
Number dataValue = dataset.getValue(row, column);
if (dataValue == null) {
return;
}
// 柱状图 X 坐标
double rectX = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge())
- state.getBarWidth() / 2.0;
int seriesCount = getRowCount();
// 柱状图 Y 坐标
double value = dataValue.doubleValue();
double base = 0.0;
double lclip = getLowerClip();
double uclip = getUpperClip();
if (uclip <= 0.0) { // 情况 1、2、3 和 4
if (value >= uclip) {
return; // 柱状图不可见
}
base = uclip;
if (value <= lclip) {
value = lclip;
}
} else if (lclip <= 0.0) { // 情况 5、6、7 和 8
if (value >= uclip) {
value = uclip;
} else {
if (value <= lclip) {
value = lclip;
}
}
} else { // 情况 9、10、11 和 12
if (value <= lclip) {
return; // 柱状图不可见
}
base = getLowerClip();
if (value >= uclip) {
value = uclip;
}
}
RectangleEdge edge = plot.getRangeAxisEdge();
double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge);
double rectY = Math.min(transY2, transY1);
double rectWidth;
double rectHeight = Math.abs(transY2 - transY1);
// 绘制柱状图...
double shift = 0.0;
double widthFactor = 1.0;
double seriesBarWidth = getSeriesBarWidth(row);
if (!Double.isNaN(seriesBarWidth)) {
widthFactor = seriesBarWidth;
}
rectWidth = widthFactor * state.getBarWidth();
rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0;
if (seriesCount > 1) {
// 需要改进 !!!
shift = rectWidth * 0.20 / (seriesCount - 1);
}
Rectangle2D bar = new Rectangle2D.Double((rectX + ((seriesCount - 1 - row) * shift)), rectY,
(rectWidth - (seriesCount - 1 - row) * shift * 2), rectHeight);
if (state.getElementHinting()) {
beginElementGroup(g2, dataset.getRowKey(row), dataset.getColumnKey(column));
}
Paint itemPaint = getItemPaint(row, column);
GradientPaintTransformer t = getGradientPaintTransformer();
if (t != null && itemPaint instanceof GradientPaint) {
itemPaint = t.transform((GradientPaint) itemPaint, bar);
}
g2.setPaint(itemPaint);
g2.fill(bar);
if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
g2.setStroke(getItemOutlineStroke(row, column));
g2.setPaint(getItemOutlinePaint(row, column));
g2.draw(bar);
}
if (state.getElementHinting()) {
endElementGroup(g2);
}
// 绘制项目标签(如果有的话)...
CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
if (generator != null && isItemLabelVisible(row, column)) {
double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
drawItemLabel(g2, dataset, row, column, plot, generator, bar, !(transX1 > transX2));
}
// 收集实体和工具提示信息...
EntityCollection entities = state.getEntityCollection();
if (entities != null) {
addItemEntity(entities, dataset, row, column, bar);
}
}
}

附:我没有测试在图表定向为水平时是否默认工作,并且是否需要对 `drawHorizontal

英文:

I found the solution myself and it is an unorthodox one, but at least it seems to do what I want.

In LayeredBarRenderer#drawVerticalItem there is this part:

// draw the item labels if there are any...
CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
if (generator != null &amp;&amp; isItemLabelVisible(row, column)) {
double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
drawItemLabel(g2, dataset, row, column, plot, generator, bar, (transX1 &gt; transX2));
}

I extended LayeredBarRenderer and @Override this method with the transX1&gt;transX2 inverted to transX1&lt;=transX2:

//....
// draw the item labels if there are any...
CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
if (generator != null &amp;&amp; isItemLabelVisible(row, column)) {
double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
drawItemLabel(g2, dataset, row, column, plot, generator, bar, !(transX1 &gt; transX2)); //here
}
//....

This combined with an exclusive CategoryItemLabelGenerator gave me the result I want:

renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, true);

The result:

具有LayeredBarRenderer的标签

The full overrided class:

@SuppressWarnings(&quot;serial&quot;)
public class LabelFixedLayeredBarRenderer extends LayeredBarRenderer {
@Override
protected void drawVerticalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot,
CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column) {
// nothing is drawn for null values...
Number dataValue = dataset.getValue(row, column);
if (dataValue == null) {
return;
}
// BAR X
double rectX = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge())
- state.getBarWidth() / 2.0;
int seriesCount = getRowCount();
// BAR Y
double value = dataValue.doubleValue();
double base = 0.0;
double lclip = getLowerClip();
double uclip = getUpperClip();
if (uclip &lt;= 0.0) { // cases 1, 2, 3 and 4
if (value &gt;= uclip) {
return; // bar is not visible
}
base = uclip;
if (value &lt;= lclip) {
value = lclip;
}
} else if (lclip &lt;= 0.0) { // cases 5, 6, 7 and 8
if (value &gt;= uclip) {
value = uclip;
} else {
if (value &lt;= lclip) {
value = lclip;
}
}
} else { // cases 9, 10, 11 and 12
if (value &lt;= lclip) {
return; // bar is not visible
}
base = getLowerClip();
if (value &gt;= uclip) {
value = uclip;
}
}
RectangleEdge edge = plot.getRangeAxisEdge();
double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge);
double rectY = Math.min(transY2, transY1);
double rectWidth;
double rectHeight = Math.abs(transY2 - transY1);
// draw the bar...
double shift = 0.0;
double widthFactor = 1.0;
double seriesBarWidth = getSeriesBarWidth(row);
if (!Double.isNaN(seriesBarWidth)) {
widthFactor = seriesBarWidth;
}
rectWidth = widthFactor * state.getBarWidth();
rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0;
if (seriesCount &gt; 1) {
// needs to be improved !!!
shift = rectWidth * 0.20 / (seriesCount - 1);
}
Rectangle2D bar = new Rectangle2D.Double((rectX + ((seriesCount - 1 - row) * shift)), rectY,
(rectWidth - (seriesCount - 1 - row) * shift * 2), rectHeight);
if (state.getElementHinting()) {
beginElementGroup(g2, dataset.getRowKey(row), dataset.getColumnKey(column));
}
Paint itemPaint = getItemPaint(row, column);
GradientPaintTransformer t = getGradientPaintTransformer();
if (t != null &amp;&amp; itemPaint instanceof GradientPaint) {
itemPaint = t.transform((GradientPaint) itemPaint, bar);
}
g2.setPaint(itemPaint);
g2.fill(bar);
if (isDrawBarOutline() &amp;&amp; state.getBarWidth() &gt; BAR_OUTLINE_WIDTH_THRESHOLD) {
g2.setStroke(getItemOutlineStroke(row, column));
g2.setPaint(getItemOutlinePaint(row, column));
g2.draw(bar);
}
if (state.getElementHinting()) {
endElementGroup(g2);
}
// draw the item labels if there are any...
CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
if (generator != null &amp;&amp; isItemLabelVisible(row, column)) {
double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
drawItemLabel(g2, dataset, row, column, plot, generator, bar, !(transX1 &gt; transX2));
}
// collect entity and tool tip information...
EntityCollection entities = state.getEntityCollection();
if (entities != null) {
addItemEntity(entities, dataset, row, column, bar);
}
}
}

P.S: I did not test if it works by default when the plot is oriented horizontally and if it requires to do the same thing for drawHorizontalItem.

huangapple
  • 本文由 发表于 2020年8月18日 02:12:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/63456424.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定