JFreeChart - change SeriesStroke of chart lines from solid to dashed in one line

19,134

Solution 1

Have you tried implementing AbstractRenderer#getItemStroke?

enter image description here

In this example I'm using a dashed line for x > 4 for series 2:

XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(){
    Stroke soild = new BasicStroke(2.0f);
    Stroke dashed =  new BasicStroke(1.0f,BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, new float[] {10.0f}, 0.0f);
    @Override
    public Stroke getItemStroke(int row, int column) {
if (row == 2){
  double x = dataset.getXValue(row, column);
  if ( x > 4){
    return dashed;
  } else {
    return soild;
  } 
} else
  return super.getItemStroke(row, column);
    }
  };
  renderer.setBaseShapesVisible(true);
  renderer.setBaseShapesFilled(true);
  renderer.setBaseStroke(new BasicStroke(3));
  plot.setRenderer(renderer);

Although this example is using and XYSeries and not dates you shold be able to modify it for you needs.

Here is the full example

import java.awt.BasicStroke;
import java.awt.Stroke;

import javax.swing.JPanel;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;

public class LineChartDemo2 extends ApplicationFrame {

    public LineChartDemo2(String title) {
        super(title);
        JPanel chartPanel = createDemoPanel();
        chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
        setContentPane(chartPanel);
    }

    private static JFreeChart createChart(final XYDataset dataset) {

        JFreeChart chart = ChartFactory.createXYLineChart(
            "Line Chart Demo: XYLineAndShapeRenderer",     
            "X",                     
            "Y",                     
            dataset,              
            PlotOrientation.VERTICAL,
            false,                     
            false,                 
            false             
        );

        XYPlot plot = (XYPlot) chart.getPlot();
        plot.setDomainPannable(true);
        plot.setRangePannable(true);

        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(){
            Stroke soild = new BasicStroke(2.0f);
            Stroke dashed =  new BasicStroke(1.0f,BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, new float[] {10.0f}, 0.0f);
            @Override
            public Stroke getItemStroke(int row, int column) {
                if (row == 2){
                    double x = dataset.getXValue(row, column);
                    if ( x > 4){
                        return dashed;
                    } else {
                        return soild;
                    } 
                } else
                    return super.getItemStroke(row, column);
            }
        };

        renderer.setBaseShapesVisible(true);
        renderer.setBaseShapesFilled(true);
        renderer.setBaseStroke(new BasicStroke(3));
        plot.setRenderer(renderer);
        return chart;
    }

    public static JPanel createDemoPanel() {
        JFreeChart chart = createChart(createDataset());
        ChartPanel panel = new ChartPanel(chart);
        panel.setMouseWheelEnabled(true);
        return panel;
    }

    public static void main(String[] args) {
        LineChartDemo2 demo = new LineChartDemo2(
                "JFreeChart");
        demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);
    }

    private static XYDataset createDataset() {

        XYSeries series1 = new XYSeries("First");
        series1.add(1.0, 1.0);
        series1.add(2.0, 4.0);
        series1.add(3.0, 3.0);
        series1.add(4.0, 5.0);
        series1.add(5.0, 5.0);
        series1.add(6.0, 7.0);
        series1.add(7.0, 7.0);
        series1.add(8.0, 8.0);

        XYSeries series2 = new XYSeries("Second");
        series2.add(1.0, 5.0);
        series2.add(2.0, 7.0);
        series2.add(3.0, 6.0);
        series2.add(4.0, 8.0);
        series2.add(5.0, 4.0);
        series2.add(6.0, 4.0);
        series2.add(7.0, 2.0);
        series2.add(8.0, 1.0);

        XYSeries series3 = new XYSeries("Third");
        series3.add(3.0, 4.0);
        series3.add(4.0, 3.0);
        series3.add(5.0, 2.0);
        series3.add(6.0, 3.0);
        series3.add(7.0, 6.0);
        series3.add(8.0, 3.0);
        series3.add(9.0, 4.0);
        series3.add(10.0, 3.0);

        XYSeriesCollection dataset = new XYSeriesCollection();
        dataset.addSeries(series1);
        dataset.addSeries(series2);
        dataset.addSeries(series3);

        return dataset;

    }


}

Solution 2

I'm still having the same problem of the line not changing from solid to dashed, but being one or the other.

The setDrawSeriesLineAsPath() method "controls whether or not each series is drawn as a single path." This precludes changing the Stroke dynamically, as getItemStroke() will be called only once per series.

Addendum: An easy way to verify this is to invoke setDrawSeriesLineAsPath() in @GrahamA's example and break in drawFirstPassShape() in the debugger.

Share:
19,134
MAK Design Labs
Author by

MAK Design Labs

Software developement, community management, web design.

Updated on July 25, 2022

Comments

  • MAK Design Labs
    MAK Design Labs almost 2 years

    The answer accepted here (JFreechart(Java) - How to draw lines that is partially dashed lines and partially solid lines?) helped me start down the path of changing my seriesstroke lines on my chart. After stepping through my code and watching the changes, I see that my seriesstroke does in fact change to "dashedStroke" when it is supposed to (after a certain date "dashedAfter"), but when the chart is rendered the entire series line is dashed. How can I get a series line to be drawn solid at first and dashed after a set date?

    /* series line modifications */
    final Number dashedAfter = timeNowDate.getTime();
    
    XYLineAndShapeRenderer render = new XYLineAndShapeRenderer() {
      Stroke regularStroke = new BasicStroke();
      Stroke dashedStroke = new BasicStroke(
                                1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
                                1.0f, new float[] {10.0f, 6.0f}, 0.0f );
      @Override
      public Stroke getItemStroke(int row, int column) {
        Number xVal = cd.getXValue(row, column);
        if (xVal.doubleValue() > dashedAfter.doubleValue()) { 
          return dashedStroke; 
        } else { 
          return regularStroke; 
        }
      }
    };
    render.setBaseShapesVisible(false);
    render.setBaseShapesFilled(true);
    render.setDrawSeriesLineAsPath(true);
    plot.setRenderer(render);
    
  • trashgod
    trashgod over 11 years
    +1 for following the API advice
  • MAK Design Labs
    MAK Design Labs over 11 years
    This is more along the lines of the example I linked to, and closer to where I started trying this whole thing. I was hoping some of these suggestions would help, but I'm still having the same problem of the line not changing from solid to dashed, but being one or the other. I've re-posted my code to reflect the changes. Any further help is appreciated!
  • GrahamA
    GrahamA over 11 years
    @MisterMichaelK if you can out together a self contained example (SSCCE) I'll try the code for you. Its hard to see whats happing without the data
  • MAK Design Labs
    MAK Design Labs over 11 years
    Actually, it looks like I was having some variable problems and that's why this wasn't working properly.
  • MAK Design Labs
    MAK Design Labs over 11 years
    This is also a key component to what I'm trying to accomplish. As of now, setting setDrawSeriesLineAsPath() to false makes my chart behave correctly in that the line changes from solid to dashed when I want. Unfortunately, the dashed is mostly solid except when my graph rises considerably. I know this is related to the sDSLAP() method, I have a lot of data points and there simply isn't enough space between each point to effectively dash the lines unless there is a large rise between points. Is there a way to set this conditionally as well like getItemStroke() was?
  • trashgod
    trashgod over 11 years
    NAFAIK, although I like ChartPanel#setMouseWheelEnabled(true) for easy zooming. I suppose you could let the user toggle the view as desired, for example.
  • GrahamA
    GrahamA over 11 years
    @MisterMichaelK if the points are close together why not vary the colour rather then the linestyle, example here.
  • simgineer
    simgineer about 5 years
    @GrahamA I tried this solution but my dataset was always null. Additionally eclipse also informed me that dataset would always be null. What could I be doing wrong? FYI - I am using a ChartFactory.createTimeSeriesChart. Would this be the issue?