Creating an area graph below a XYDifference(Render

2019-08-02 16:12发布

问题:

I have been trying for the last week to find a way to make JFreeChart display something similar to the image below. Basically you are looking at three series (upper, middle, lower) with a fill inbetween. And underneath there is a (light green) fill color, or an area chart as some would perhaps call it - no meaning, just for looks.

The only thing really missing from what I have come up with is the last part: the fill underneath / area chart:

I even tried to subclass XYDifferenceRenderer and combine it with the renderer for Areachart, but I could not control the height of the areachart, basically filling up the plot to the top. So that was a no-go. Having created as simple rendererer to create rounded bar charts earlier, I thought that I might be able to change the code for XYDifferenceRenderer. But the code for XYDifferenceRenderer is quite a handful of geometry and inner workings of JFree chart, and the task was a bit overwhelming. So any tips on how to achieve this effect in any "normal" way (that does not involve hacking JFreeChart's inner workings)?

回答1:

Found an old post describing how to use two renderers in the same plot, which was just the thing in this case.

To get a fill underneath you need to

  1. create two new series
    • one is the lower bound of the difference plot
    • the other is the values at the bottom of the plot - often just zero. Easily got by calling plot.getRangeAxis().getLowerBound()
  2. add them to a new dataset and add this to the plot I was unaware that a plot could have several datasets. Turns out one can just use an index to access them.
  3. create a new renderer for the "fill" dataset
    • create a new renderer
    • set the right fill paint
    • set the rendererer for the new dataset to be the new renderer

The code is something akin to the following, where the fill Paint obviously is up to you:

static void addFill(Plot plot) {

    XYSeries lowerLimitSeries = ((XYSeriesCollection) (plot.getDataset())).getSeries(1);
    XYSeriesCollection fillSet = new XYSeriesCollection();
    double lowerBound = plot.getRangeAxis().getLowerBound();
    fillSet.addSeries(lowerLimitSeries);
    fillSet.addSeries(createLowerFillSeries(lowerLimitSeries, lowerBound));
    plot.setDataset(1, fillSet);
    Paint fillPaint = Color.GREEN;
    XYDifferenceRenderer fillRenderer = new XYDifferenceRenderer(fillPaint, fillPaint, false);
    fillRenderer.setSeriesStroke(0, new BasicStroke(0)); //do not show
    fillRenderer.setSeriesStroke(1, new BasicStroke(0)); //do not show
    plot.setRenderer(1, fillRenderer);
    ...
}

static XYSeries createLowerFillSeries(XYSeries lowerLimitSeries, double lowerLimit) {
    int size = lowerLimitSeries.getItems().size();
    XYSeries res = new XYSeries("lowerFillSeries");
    for (int i = 0; i < size; i++) res.add(new XYDataItem(lowerLimitSeries.getX(i), lowerLimit));
    return res;
}