Search code examples
javachartsjfreechart

Change line color above a threshold with jfreechart


I want to use JFreeChart to draw a line that changes its color above a threshold. It should look like this,but not smoothed: https://www.amcharts.com/demos/smoothed-line-chart/. This is what I got so far:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.DefaultXYDataset;
import org.jfree.data.xy.XYDataset;

public class Charty extends ApplicationFrame {

    public Charty(String applicationTitle, String chartTitle) {
        super(applicationTitle);
        JFreeChart lineChart = ChartFactory.createXYLineChart(chartTitle, "Years",
            "Number of Schools", createDataset(), PlotOrientation.VERTICAL,
            true, true, false);
        XYPlot plot = lineChart.getXYPlot();
        XYItemRenderer renderer = new StandardXYItemRenderer() {

            @Override
            public Paint getItemPaint(int series, int item) {
                int value = 0;
                // how do I get the value of the current point?
                return value > 50 ? Color.red : Color.yellow;
            }
        };
        plot.setRenderer(renderer);
        ChartPanel chartPanel = new ChartPanel(lineChart);
        chartPanel.setPreferredSize(new Dimension(560, 367));
        setContentPane(chartPanel);
    }

    private XYDataset createDataset() {
        int nPoints = 200;
        final double[][] data = new double[2][nPoints];
        for (int i = 0, j = 0; i < nPoints; i++, j++) {
            data[0][j] = i;
            data[1][j] = Math.random() * 10;
        }
        DefaultXYDataset dataset = new DefaultXYDataset();
        dataset.addSeries("points", data);
        return dataset;
    }

    public static void main(String[] args) {
        Charty chart = new Charty("School Vs Years", "Numer of Schools vs years");
        chart.pack();
        RefineryUtilities.centerFrameOnScreen(chart);
        chart.setVisible(true);
    }
}

Two issues here:

  1. I don't know how to get the value of the current item.

  2. Even if I get the value, the color would apply at the vertex, but I need the color to change as soon as the line crosses the threshold.

There must be some way to define areas that have their own color but I can't find anything relevant. What is the simplest way to achieve this? If this is not possible or if it is really complicated to implement, would it be simpler if the line keeps its color but the background color above the threshold is different from the background color below the threshold?


Solution

    1. Give the renderer access to an effectively final reference to your data; you'll need to interpolate values on the threshold, as shown here.

      XYDataset data = createDataset();
      XYItemRenderer renderer = new StandardXYItemRenderer() {
      
          @Override
          public Paint getItemPaint(int series, int item) {
              double value = data.getYValue(series, item);
              return …;
          }
      };
      
    2. Add an IntervalMarker to the range, as shown here for the domain.

      IntervalMarker target = new IntervalMarker(7.0, 10.0, new Color(1, 0, 0, 1/2f));
      XYPlot plot = lineChart.getXYPlot();
      plot.addRangeMarker(target, Layer.BACKGROUND);
      

    image