LayeredBarRenderer 标签
Labels with LayeredBarRenderer
考虑 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);
}
}
它产生:
我想在上面添加标签,让它看起来像这样:
我已经尝试过适用于其他渲染器的方法:
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。
有没有办法添加标签?
明显bug identified by @George Z. hinges on an incorrect value calculated by the LayeredBarRenderer
implementation of drawVerticalItem()
when it calls drawItemLabel()
as shown here。谓词transX1 > transX2
应该倒过来。
作为使用 v1.5 时的解决方法,可以指定 negative ItemLabelPosition
,如下所示。
ItemLabelPosition position = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER);
renderer.setDefaultNegativeItemLabelPosition(position);
PlotOrientation.VERTICAL
:
由于 drawHorizontalItem()
的实施看起来是正确的,因此 positive ItemLabelPosition
按预期工作。
ItemLabelPosition position = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE3, TextAnchor.CENTER_LEFT);
renderer.setDefaultPositiveItemLabelPosition(position);
PlotOrientation.HORIZONTAL
:
另外,一个StandardCategoryToolTipGenerator
works as expected. You can customize the DEFAULT_TOOL_TIP_FORMAT_STRING
as shown here and here.
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 */
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);
});
}
}
我自己找到了解决方案,这是一个非正统的解决方案,但至少它看起来符合我的要求。
在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
和 @Override
这个方法,将 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);
结果:
完全覆盖class:
@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) {
// 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 <= 0.0) { // cases 1, 2, 3 and 4
if (value >= uclip) {
return; // bar is not visible
}
base = uclip;
if (value <= lclip) {
value = lclip;
}
} else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
if (value >= uclip) {
value = uclip;
} else {
if (value <= lclip) {
value = lclip;
}
}
} else { // cases 9, 10, 11 and 12
if (value <= lclip) {
return; // bar is not visible
}
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);
// 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 > 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 && 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);
}
// 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));
}
// collect entity and tool tip information...
EntityCollection entities = state.getEntityCollection();
if (entities != null) {
addItemEntity(entities, dataset, row, column, bar);
}
}
}
P.S:我没有测试当绘图水平方向时它是否默认工作以及是否需要为 drawHorizontalItem
做同样的事情。
考虑 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);
}
}
它产生:
我想在上面添加标签,让它看起来像这样:
我已经尝试过适用于其他渲染器的方法:
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。
有没有办法添加标签?
明显bug identified LayeredBarRenderer
implementation of drawVerticalItem()
when it calls drawItemLabel()
as shown here。谓词transX1 > transX2
应该倒过来。
作为使用 v1.5 时的解决方法,可以指定 negative ItemLabelPosition
,如下所示。
ItemLabelPosition position = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER);
renderer.setDefaultNegativeItemLabelPosition(position);
PlotOrientation.VERTICAL
:
由于 drawHorizontalItem()
的实施看起来是正确的,因此 positive ItemLabelPosition
按预期工作。
ItemLabelPosition position = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE3, TextAnchor.CENTER_LEFT);
renderer.setDefaultPositiveItemLabelPosition(position);
PlotOrientation.HORIZONTAL
:
另外,一个StandardCategoryToolTipGenerator
works as expected. You can customize the DEFAULT_TOOL_TIP_FORMAT_STRING
as shown here and here.
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 */
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);
});
}
}
我自己找到了解决方案,这是一个非正统的解决方案,但至少它看起来符合我的要求。
在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
和 @Override
这个方法,将 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);
结果:
完全覆盖class:
@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) {
// 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 <= 0.0) { // cases 1, 2, 3 and 4
if (value >= uclip) {
return; // bar is not visible
}
base = uclip;
if (value <= lclip) {
value = lclip;
}
} else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
if (value >= uclip) {
value = uclip;
} else {
if (value <= lclip) {
value = lclip;
}
}
} else { // cases 9, 10, 11 and 12
if (value <= lclip) {
return; // bar is not visible
}
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);
// 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 > 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 && 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);
}
// 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));
}
// collect entity and tool tip information...
EntityCollection entities = state.getEntityCollection();
if (entities != null) {
addItemEntity(entities, dataset, row, column, bar);
}
}
}
P.S:我没有测试当绘图水平方向时它是否默认工作以及是否需要为 drawHorizontalItem
做同样的事情。