Ravby Ravby - 10 months ago 52
Java Question

JFreeChart: customizing BoxAndWhisker chart

I started using JFreeChart to do some plotting.
I would like my chart to look like the image below:

Interval graph

I came very close by using BoxAndWhisker chart:

my graph

However, there are certain things that I would still like to change. Is there a way to remove the boxes so I only have the middle line? And how can I add labels to the bars? Also for some reason the last number of the y axis (bottom right in the image) is cut off.

Here is my sample code:

public class MinMaxCategoryPlotTest extends ApplicationFrame {

public MinMaxCategoryPlotTest(String 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("");

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)

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);

final JFreeChart chart = new JFreeChart(
new Font("SansSerif", Font.BOLD, 16),

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


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) {
if (outline) {
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");


public class MyMinMaxCategoryRenderer extends MinMaxCategoryRenderer {

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,

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);



Final complete result for Rating / Confidence Intervals:
Final result

Answer Source

Instead of a BoxAndWhiskerRenderer, examined here, use a MinMaxCategoryRenderer with PlotOrientation.HORIZONTAL and a custom AxisLocation.

CategoryPlot plot = (CategoryPlot) chart.getPlot();
MinMaxCategoryRenderer renderer = new MinMaxCategoryRenderer();


You can change the Icon used like this, or you can create your own icons based on this approach.